Skip to content
Erik Assum edited this page Sep 6, 2017 · 3 revisions

Welcome to the editor wiki!

A little bit on terminals

When you normally interact with a terminal, it is in what's called cooked mode. This means that the terminal is somewhat smart, and handles stuff like C-s, C-q, C-c and what not. When we want to write a terminal ourselves, we want to be able to handle these control-characters our selves. This is why we want to put the terminal in raw-mode. In raw-mode, the terminal doesn't help us much, and we need to control everything, from cursor movement to handling the control characters. I couldn't find a way to set the terminal in raw-mode from Clojurescript, so I did that in the run.sh script. This might just be possible if you choose to implement this using lumo instead of planck.

The data structure

So, the aim here is to just make a quick outline of how this thing works. First of all, the state of the editor is kept in a clojure atom called, well, state.

The state is represented as a standard Clojure map, containing a :buffer, which represents the file to be edited, and a :cursor which is a map containing :x and :y. The buffer is simply a vector, one element for each line. So a perfectly valid state would look something like:

{:buffer ["line one" "line two" "line three"]
 :cursor {:x 1 :y 2}}

The main loop

We see that in the function editor which runs the editor, basically does two things. It initialises the state with the content of the file, and then it enters an infinite loop which renders the state, and then updates the state based on the input from the user. Not much magic going on here.

Render

The render function, which one might argue is somewhat naive, clears the screen, puts the cursor at the top left position, prints out each line in the buffer, and finally moves the cursor to where it's supposed to be. There is a small trick to this, and that is that since our terminal is in raw-mode, we must in addition to print out the new-line, also print out the carriage return.

Handle input

Handle input is the meat of the editor, at least at this point in its life. It reads one character at the time, switches on that character, and updates state accordingly, except if our character is C-q, on which we exit the editor. It might be wise, if you're doing this on your own, to start by implementing the exit bit.

Some final thoughts

Looking at how this whole thing is implemented, a lot of the logic is implemented using pure functions. All the functions containing difficult logic are pure, and would be easily tested either with generative or example based testing. Also, since these functions are pure, the core logic of the editor remains unchanged even if we were to rewrite this to run as a Gui-app or even in the browser.