Lesson Learned From Making A Text Editor
In the last two weeks I've been working on Penknife. Originally it was a spin-off (?) project of a new visual novel engine (why I decided this has anything to do with visual novel engine developing and why I'm working on a visual novel engine in the first place is another story). Now, I would really like to move on to something else; and before that I would like to record what I've come to know from the whole experience in case I forget them…
You don't have to start by implementing a fancy data structure
You can start by having just a list of lines (as early VSCode and apparently many early IBM text editors do) and a properly designed interface. My first attempt at writing a text editor started with implementing a piece table, which I gave up midway because it had a lot of bugs that I was not willing to fix (because there's too many) and it never felt like I've actually accomplished something. Don't even bother with gap buffer, just do a linked list of lines. You can try fancier stuff once the interface has been relatively stable; coding UI against an interface that easily changes is itself a horrible nightmare.
You can't "code first think later" out of this one
Unless you're willing to invest a lot of time (often more than coding time!) into thinking hard and refactoring early. It's truly a dilemma: if you try to plan everything up front, you'll find the amount of work is hella massive; but if you're starting out doing everything naively and only do the simplest of efforts, then every new features down the line would require deep thinking to prevent the newly added code from becoming a foot gun, like for example, implementing split layout could break your UI code which could've been made to render only one text buffer, and implementing multiple cursor could break your selection management code (since it's no longer one single selection), your copy-and-paste code (e.g. you'll have to account for cases where one switches between having many cursors and having only one cursor during a copy-and-paste sequence) and your undo-redo code. And if you want to make a text editor for developers, then after everything you'll have to consider syntax highlighing (the implementation of which could be in conflict with your current implementation of document model), auto-complete and LSP, and then you'll realize why every single hipster who only wants a shiny new stuff (with integrated LLM AI of course) to show off on their CV just skips the whole bit and goes straight with Monaco and why the guys behind Monaco themselves chose to rely on existing web technologies, and God forbid you want to do rich-text and proportional font like Emacs…
A CJK character can be anything from one character to three characters long
A CJK character is of size 1 when moving the cursor, of size 2 when you consider the fact that it's full-width, and of size 3 when it's encoded in UTF-8. One can easily merge the first case and the last case by treating every character as a rune, but the second case depends on the actual content of the text buffer and would be extra tricky when displaying & handling mouse click events.
You definitely need to render less things and cache your rendering of things
For whatever reasons rendering CJK characters is way slower than "normal" half-width characters with SDLttf. To speed up you have two options: render less and cache more. Fortunately these two are orthogonal with each other so we can do both, but the former needs to be done on a case-to-case basis (e.g. moving a cursor up & down and moving the viewport up & down require rendering of different parts) and thus increasing mental load during development and the latter (which I imagine) would increase memory usage.
Get prepared to grueling work if you plan to do everything on your own
Like for example, if you're writing a graphical mode text editor you would naturally want to support all kinds of fonts, but SDLttf only supports ttf (as its name suggests), so you either switch to a slightly higher-level graphic library that comes with font-related utilities that can handle OpenType fonts or you learn and use a library like FreeType which you then have to try your best to make it work well with the graphic library you're currently using. Since this is the first time I ever have to do this myself so it would take some time for me to become familiar with things and even more time to be able to abstract things for convenience.
Last update: 2025.1.27