Writing a Code Editor with Layers (Part 4)

Davin Hills
3 min readAug 8, 2020

In part 3 we discussed syntax matching and file type detection. Here we are going to discuss layers. The thing that brought us to this dance.

In Part 1 we specified this goal: Allow layers (modes) to be defined and the behavior when entering, leaving, and interacting with to be specified. To review, the concept of layers came from a keyboard that I purchased that allows key combinations to change the “layer” of the keyboard changing how keys are interpreted when pressed. Vi has the same concepts baked in. With concepts like Insert, Normal, and Replace modes.

What we want to build is a set of layers that when changed will interpret keypresses and perform different actions. I guess we should start with actions.

And some action definitions.

We define a few properties for actions like ReqBuffer if the action needs a buffer to act upon. ReqParam if a parameter is required.

Next, we define an interface for a generic keypress.

Define mappings from keys to actions.

And at long last a Layer.

There is a lot to unpack for a layer. Let’s walk through the interface.

Name: returns the name of the layer

Map, UnMap: will add and remove Key-Action mappings

Editable: Is for layers that the command itself can be edited. For example, imagine a layer for searching. As we type search characters it’s jumping to those matches and we mistype a key. We want the ability to press backspace to delete the previously typed character.

AllowCursorPastEnd: When in an editing layer allows the cursor to move past the last character to insert new text.

WaitForComplete: will not match keys to actions until the return is pressed

NotStacked: If a layer should not be included in the layer stack. For example, a search layer, when returning to the previous layer we wouldn’t want it to suddenly go into search mode.

A layer can also define actions for various states. Actions to return when a match is partially, fully, or not matched. Actions to take when certain key classes like printable or numeric are received.

Here is an example of the JSON file that defines the Insert Layer. This would be the equivalent of opening Notepad and starting to type.

The only layer required is the default layer. If we wanted to recreate Notepad the above JSON would be named “default” and off to the races we would go! However, let’s define a default that mimics vim.

With just the two layers we have defined a command structure resembling a modal editor. Cool!

Now that we have Actions, Keys, ActionKey, and layers defined, what shall we do with them? We are going to need something that takes keystrokes, returns actions, and keeps track of what layer we are in.

KEYS ==> Actions

We can now convert keystrokes into editor actions all independent of any text storage or display. We could run our interpreter on a different thread or even different machine.

Next time we’ll talk about the next leg of the humble Keystrokes journey.

KEYS ==> ACTIONS ==> Editor

--

--