September Summary
Bold is a fast text editor. You can find an overview on the home page.
This entire month was spent on the text editing object, which handles text insertions, deletions, the undo/redo history, and bookkeeping for jump-to-line and code folding. The text editor object is broken up into 3 objects: 1) Text, which is the main interface of the code, 2) TextIterator, which allows text rendering and other parts of the code to easily look at the text 3) TextInternal, which handles a good portion of work, so the Text object isn't so big. Here's more detail
Text:
The main object which does enough that it's broken up into several internal objects. Here are some actions it provides
- Insert/Paste letters, clipboard, files, etc
- Undo/Redo
- Cursor actions (move cursor, select range, add/remove/clear cursor)
- Get iterator (by offset or line number)
- Copy text (to save into a file or set into the clipboard)
It's big enough that a month isn't enough; it's several thousand lines and not done. I'm not sure how much work the above sounds like, but that's a list of actions; it doesn't cover most of the details. For example, the insert and paste action needs to produce a change log for the LSP.
For the undo log, there's logic to fold actions, so typing function_name will have one undo for the entire word, which is obvious, but annoying if I want to change behavior based on language. CSS prefers to treat class-name as a single word; normally it'd be considered as 3 units, 'class', a subtract, then 'name'. The undo code would also need to carry all the cursor information if there are multiple cursors being used, and what text is deleted when replacing a selection. There are a lot of details to handle for this to feel right.
TextIterator:
This is meant for the renderer to use, but it's heavily used in test. Usually, iterators are simple; it has a starting point and walks to the end. However, the text object doesn't actually insert data with every keystroke. As an example, imagine you have a cursor at the start of the last and second last line. When you type "ABC" the text object tracks it as one unit of work (3 letters, two cursors). The text object doesn't know if the unit of work is done, as you might type "DEF" next. Therefore, the iterator has to figure out how the text would look as if the unit of work is done.
The Iterator has to look at the cursors, once it reaches a cursor, it'll have to skip text (in the buffer) if text is being overwritten, then iterator over letters being inserted, then switch back to the main text buffer and adjust it's internal counter of where the next cursor is (if you deleted more letters than insert, the next cursor will be sooner). The original implementation (before this rewrite) was painful, but not so bad in this rewrite since I understood the problem space.
TextInternal:
This was created so the Text class wouldn't have so much state inside it. This class is only used within the text object and specifically handles inserting and deleting text, along with the bookkeeping around it. As an example, if you have "Hello" selected and paste a\nb the object would note a newline has been inserted. The insert and paste function returns a change log for an LSP to use. The code for this is over a thousand lines and growing since the bookkeeping for code-folding isn't finished.
The text object might not be complete, but there's enough done that I can implement rendering and editing a file. In the original implementation, I implemented a basic version of the text object in the first few weeks. Since I wanted to tackle things I didn't understand well in this rewrite, I went ahead and did those first; I never got around to implementing a text object until now.