Syntax highlighted text editor using Hiccup

There's a demo of a little syntax-highlighted text editor here if you enable JavaScript.

I understand not wanting to run JavaScript on the modern web. My JavaScript respects you! There's no tracking, there are no dependencies, I wrote all the JavaScript. The bundle size is small.

But if you don't want to enable JS, you can still read this post.

I needed a really small text editor (in terms of bundle size) for an in-browser video game I'm working on. I want the game to load very fast even on poor internet connections, and I have found that as soon as you open the dependency flood-gates your bundle size grows enormously.

Challenging myself to not use any dependencies also means being forced to learn a lot more than you would otherwise, since you can't just import parsers, DOM utilities, and so on. You have to do it yourself and learn something along the way. And who knows, maybe you'll end up writing something better than what you would have imported.

Tokenising

Tokens for the editor are identified using a set of parser combinators.

They look like this:

const string = or(
    sequence(
        parseCharacter('"'),
        maybe(parseWhileChar(c => c !== '"')),
        parseCharacter('"')
    ),
    sequence(
        parseCharacter("'"),
        maybe(parseWhileChar(c => c !== "'")),
        parseCharacter("'")
    )
);

The nice thing about parser combinators (over something like regex) is that they are readable and composable. Creating entirely new language parsers is easy by combining combinators differently. They're also fast, but I haven't benchmarked them against regex.

You can see all of the other combinators used in the above text editor here.

Hiccup

The remaining work to produce an interactive text editor on the basis of the parser combinators is some utilities to patch changes to the DOM using a simple representation.

I used hiccup for the reasons outlined in this post.

Why?

Well, I can use it in my game without shipping megabytes of JS to users. I can also use it for blogging purposes: I can use the above with different parser combinators and then simply copy the inner HTML of the resulting code element and paste into the source. That's how I generated the code block showing what the parser combinators look like without requiring JS at all (try this page without JS).