Vvvim is lightweight Vim implementation built as a C++ practice. Don't use it for anything serious.
- Basic Vim key bindings
- Basic Vim commands (e.g.
- Fully remappable keys and key sequences (both recursive and non-recursive) powered by prefix trees
- A homemade data structure for text buffer -- Nested Gap Buffer
- Configuration file, see
.vvvimrcfor an example
- Key/Operator repeat
Vvvim will probably implement:
- Visual mode
- Syntax highlighting
- Color schemes
setoptions (All hard-coded to my preference, so things like
wrapcan't be disabled)
Vvvim will not implement:
- Vimscript support
Template pattern for supporting multiple frontends
The logic for buffer management, key remapping, key handling etc. are all implemented in
Editor, the abstract class with the Template pattern. Editor frontends extends the
Editor class and implements methods such as
Nested Gap Buffer
Homemade data structure for interacting with the editor buffer. It has two layers:
- Inner layer: Just a normal gap buffer with
tail_strings, representing the line currently being edited, and the line can be reconstructed by
head_ + reverse(tail_)
"hello w|orld" head = "hello w" tail = "dlro"
- Outer layer: Gap buffer of strings,
lines_tail_are lists of strings. The full buffer can be reconstructed with:
lines_head_ + [head_ + reverse(tail_) + reverse(lines_tail_)
Important operations and complexities:
MoveInsertionPointTo(int row, int col): changes the place where
Insert(char c)will take place. O(r + c) time where
ris the number of rows and
cis the length of the row.
- This operation is only invoked upon entering insert mode, not with any normal mode movement operator.
GetLineView(int line_number): random access of buffer lines, returns a
std::string_view. O(c) time when accessing current line which needs a merge, otherwise O(1).
ToString(): creates a flattened copy of the buffer. O(n) time where
nis the length of the buffer.
Delete(): insert or delete a character from insertion point. O(1) time.
Key Mapping Layer
ncurses Editor KeyMap Editor getch() -> MapAndExecuteKey ---------> Map --> ExecuteKey --> action ^ | | | <--- if recursive ---
Implementation: Using one prefix tree for each editor mode.
Bindings that starts with an existing binding is not allowed. E.g.
imap a bc and
imap ad ef cannot both be active.