I’m cloning vi as a learning exercise.

kuberlog
2 min readSep 17, 2019

Hi. I assume most people have the following learning strategy, especially when it comes to programming: learn a few abstract concepts, see some examples of those concepts, integrate those concepts into your own work, repeat. For me, this cycle generally manifests itself as a pet-project. My current pet project is to learn vi more thoroughly by implementing a clone of it.

I am using C++ and ncurses for this project. My current architecture is very simple and only contains 2 classes: Window and Buffer. . The main.cpp initializes the Buffer and Window singletons and then runs a 2 function call loop with window.display(buffer) and handleCommand(buffer).

The window.display function takes a buffer as an argument and then figures out how to display that buffer using ncurses. This is a bit challenging, because it needs to calculate which lines in the buffer to display based on the logical cursor position. It then needs to calculate the graphical cursor position, which can challenge due to linewrapping. I need to keep track of how many graphical lines each logical line takes up and then factor that into the logical->graphical cursor calculation. I do not want the buffer to be redrawn onto the screen every time the window.display function is called. This is because a simple cursor move does not require a buffer redraw and doing a cursor move on screen redraw will cause the screen to flash. The only time the buffer needs to be redrawn is on scroll and on a change to buffer.contents. In a future version, I may only redraw a single line of the screen when buffer.contents changes, but for now I redraw the entire screen.

The handleCommand function first checks which mode the buffer is in INSERT or NORMAL. It then dispatches a command handling function based on the mode. For normal mode, it will grab a char from stdin and then check if that char will ever form a part of a valid command. If it does, then it checks to see if the current command string is a valid command (such as a h, j, k, l) and if it is not a valid command it keeps reading chars from stdin until the command string can never form a valid command ( da for instance is invalid) or until it forms a valid command at which time the command’s function is dispatched on the buffer.

The source code can be found here: https://github.com/SlightlyCyborg/text and is still a work in progress. I will probably get the project to a point where it can edit its own source with syntax highlighting and then I will stop.

--

--