How the editor works inside

This blog post is about the dk.salza.liq.editor namespace. 

The editor state

The editor is a state that exists as soon as the namespace is loaded or required. Most data structures in Liquid are immutable, but this single one, the editor, is contained in a ref. All operations on the editor are acting on this ref. 


When a function like dk.salza.liq.editor/beginning-of-line is called from somewhere, the editor ref content is replaced with a new editor data structure, where the current buffer is replaced with a new buffer, where the cursor is moved to the beginning of the line. 


All operations like jumping between buffers, creating buffers, adding keymappings, etc. can be narrowed down to replacing the editor with a new one reflecting the given change. 


Making your own functions that manipulate the editor is usually very easy, since most tasks can be done by just combining already existing functions, so there will be no need to act on the editor data structure directly, and there should not be: The lowlevel actions should be kept inside the namespace. 

Acting on the editor

Anything can act on the editor. Functions can be invoked directly or keywords (resembling keypresses) can be send through dk.salza.liq.editor/handle-input allowing the keymappings to decide which functions to call. 


This means that interfaces just need to capture user action and translate those into functions or keycodes, like "a", "f5", "C-t" or something similar and invoke the functions on the editor. It could be a tty interface, a webserver, a JFrame or a robot, you decide. 


The editor does not render anything by itself. It just contains the information and can provide what is needed. The function dk.salza.liq.editor/get-windows will give the expected dimensions of the visual buffer, and the content of each buffer can be accessed by calling dk.salza.liq.editor/get-buffer with the name of the buffer as input. 


There is a bultint renderer dk.salza.liq.renderer/render-screen which will translate the windows and buffer into a map of lines like this: 


    ({:column 44, :row 1, :line ("(" {:face :type1, :bgface :plain} "d" "e" "f" "n"
                                 {:face :type2, :bgface :plain} " " "m" "y" "f" "u" "n")}
     {:column 44, :row 2, :line ({:face :plain, :bgface :plain} " " " " "[" "]")}
     {:column 44, :row 3, :line (" " " " "(" "p" "r" "i" "n" {:face :plain, :bgface :cursor2} "t"
                                 {:face :plain, :bgface :plain} "l" "n" " "
                                 {:face :string, :bgface :plain} "\"" "T" "h" "i" "s" " "
                                 "i" "s" " ")}
     {:column 44, :row 4, :line ({:face :string, :bgface :plain} "a" " " "l" "o" "n" "g"
                                 " " "l" "i" "n" "e" "\"" {:face :plain, :bgface :plain} ")" ")")}
     {:column 44, :row 5, :line ("")})


It contains information about where to start printing the line an when to change face (foreground and background style). This rendered content can be easier translated into terminal output, html or something else to be displayed. 


One does not have to used the builtin renderer. What information to drag from the editor and how to display it is completely up to you.