With the new cursor as defined in the previous poss about forest cursors we can take the next step towards making a Purely Functional Semantic Forests Editor like Smos. In this post we will write a simple purely functional forest editor using brick
, building on the previous simple tree editor: microsmos.
From a tree editor to a forest editor
The semantic difference between a tree and a forest is simple. A forest is just a list of trees.
That means that if we take the code from the microsmos blogpost, and change the cursor within the state to a forest cursor, we should be most of the way to a semantic forest editor.
data State = State
stateCursor :: ForestCursor TextCursor Text
{ stateMode :: Mode
,
}
data Mode
= EditForest
| EditText
deriving (Show, Eq)
Constructive Compile Errors
To recap, the first type parameter to ForestCursor
is the type of the selected node and Text
is the type of the other nodes in the forest.
The compile errors that we'll need to fix mostly come from functions that work on a TreeCursor
. In that case there are corresponding functions for ForestCursor
s.
treeCursorAppend
~>forestCursorAppend
treeCursorRemoveElem
~>forestCursorRemoveElem
treeCursorRemoveSubTree
~>forestCursorRemoveSubTree
...
New rendering
Since the microsmos
blogpost, the new cursor-brick
library has come out. This has made a lot of the rendering code redundant.
Now we can just import Cursor.Brick
and simply combine verticalForestCursorWidget
and treeCursorWidget
to easily build a forest cursor widget for our State
.
verticalForestCursorWidget drawTextCTree (treeCursorWidget wrap cur) drawTextCTree (stateCursor s)
Here we can just re-use the drawTextCTree
function from microsmos
.
Extra features
To finish off the forest editor, we can add two more features: promoting and demoting nodes. Fortunately, there are functions for this in the cursor library that make this change really easy. This means that all we need to do is to add the following to the case expression for what to do with user input:
KChar 'h' -> mDo $ forestCursorPromoteElem toText toTextCursor
KChar 'H' -> mDo $ forestCursorPromoteSubTree toText toTextCursor
KChar 'l' -> mDo $ forestCursorDemoteElem toText toTextCursor
KChar 'L' -> mDo $ forestCursorDemoteSubTree toText toTextCursor
References
The full code can be found on github. Text cursors are available in the cursor
package on Hackage. This post is part of an effort to encourage contributions to Smos. The simplest contribution could be to just try Smos out and provide feedback on the experience. Smos is a purely functional semantic editor of a subset of YAML that is intended to replace Emacs' Org-mode for Getting Things Done.