-
Notifications
You must be signed in to change notification settings - Fork 0
Home
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
.
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}}
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.
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 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.
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.