Having finished day 4 of my December Adventure by getting distracted trying to improve the appearance of code on my website just before going to bed, I decided to skip the random selection and continue with this for day 5—I regularly write about code and I’d like to improve the reading experience a little.

JetBrains Mono

I’ve used JetBrains Mono as my terminal font ever since reading Mike’s post exploring different monospaced fonts—it took a little getting used to, but it’s got quite a bit of personality, which I appreciate.

Now JetBrains Mono is available in Google Fonts, I updated my code blocks to use this instead of the default monospaced browser font.

OPL Syntax Highlighting

Having nerd-sniped myself a few days ago, I decided it was time to look into adding OPL support to highlight.js. It’s a language I seem to write about a lot, and it would be great to have syntax highlighting when I do.

Thankfully, highlight.js has pretty good documentation explaining how to define and register additional languages, making the process pretty simple. The current definition looks like this:

hljs.registerLanguage('opl', function() {
  return {
    case_insensitive: true,
    keywords: {
      $pattern: /[a-z$&%:]+/,
      keyword: 'abs acos acs addr adjustalloc alert alias alloc alog and app append appendsprite asc asin asn at atan atn back beep begintrans bookmark break busy byref cache cachehdr cacherec cachetidy call cancel changesprite chr$ clearflags clock close closesprite cls cmd$ committrans compact compress const continue copy copyw cos cosh count create createsprite cursor datetosecs datim$ day dayname$ days daystodate dbuttons dcheckbox dchoice ddate declare declare external dedit deditmulti defaultwin deg delete deletew dfile dfloat dialog diaminit diampos dinit dinits dir$ dirw$ disp dlong do dow dposition drawsprite dtext dtime dxinput edit else elseif end enda endif endp endv endwh eng entersend entersend0 eof erase err err$ errx$ escape eval exist exp external fac find findfield findlib findw first fix fix$ flt font free freealloc gat gborder gbox gbutton gcircle gclock gclose gcls gcolor gcolorbackground gcolorinfo gcopy gcreate gcreatebit gdrawobject gellipse gen$ get get$ getcmd$ getdoc$ getevent getevent32 geteventa32 geteventc getkey getlibh gfill gfont ggmode ggrey gheight gidentity ginfo ginfo32 ginvert giprint glineby glineto gloadbit gloadfont global gmove gorder goriginx goriginy goto gotomark gpatt gpeekline gpixel gpoly gprint gprintb gprintclip grank gsavebit gscroll gsetpenwidth gsetwin gstyle gtmode gtwidth gunloadfont gupdate guse gvisible gwidth gx gxborder gxborder32 gxprint gy hex$ hour iabs if in include input insert int intf intrans ioa ioc iocancel ioclose ioopen ioread ioseek iosignal iow iowait iowaitstat iowaitstat32 iowrite ioyield key key$ keya keyc killmark kmod kstat last lclose left$ len lenalloc linklib ln loadlib loadm loc local lock log lopen lower$ lprint m0 m1 m2 m3 m4 m5 m6 m7 m8 m9 max mcard mcardx mcasc mean menu menun mid$ min minit minute mkdir mod modify month month$ mpopup newobj newobjh next not num$ odbinfo off onerr open openr opx or os out p1 p2 p3 p4 p5 parse$ pause peek$ peekb peekf peekl peekw pi pointerfilter poke$ pokeb pokef pokel pokew pos position possprite print proc put rad raise rand randomize realloc recall recsize rename rept$ return right$ rmdir rnd rollback round sci$ screen screeninfo second secstodate send setdoc setflags sethelp sethelpuid setname setpath sgn showhelp sin sinh size space sqr sqrt statuswin statwininfo std stdev stop store style sum tan tanh testevent trap uadd udg unloadlib unloadm until update upper$ use usesprite usr usr$ usub val var vector view week while year'
    },
    contains: [
      {
        className: 'string',
        begin: '"',
        end: '"'
      },
      hljs.COMMENT(
        'REM',
        '\n',
        {
          contains: [{
            className: 'doc',
            begin: '@\\w+'
          }]
        }
      )
    ]
  }
});

There’s a couple of things to note with this implementation:

  • $pattern

    By default highlight.js uses \w+ to detect and split keyword tokens. This doesn’t work for OPL as operations and functions can be optionally post-fixed with $, &, or $, to indicate the return type, necessitating a different regex pattern. To make things a little more complex, OPL also permits user-defined procedures with names that collide with the built-in operations and functions, relying on a trailing colon to differentiate them—the pattern therefore also includes : to ensure keywords don’t over-match on user procedures.

  • keyword

    Huge thanks to Fabrice for collating all the OPL operations and functions which made it incredibly painless to put together the list of keywords, saving me quite a bit of time.

With this new language definition in place, OPL looks just that a little bit prettier:

PROC hello:
  PRINT "Hello, World!"
  GET
ENDP