Zep uses a flexible keymap system that allows you to add custom keybindings to any mode. The system supports command composition with counts, registers, and special key sequences.
KeyMap Basics
The KeyMap class provides a tree-based structure for matching key sequences to command IDs. Each mode (Vim, Standard, etc.) maintains multiple keymaps for different contexts.
Keymaps are organized as trees where each node represents a token (key or special sequence). This allows efficient matching of multi-key sequences like dd or ci(.
Special Keys Zep uses angle brackets for special keys: "<C-x>" // Ctrl+x
"<S-Return>" // Shift+Return
"<C-S-z>" // Ctrl+Shift+z
"<Left>" // Left arrow
"<PageDown>" // Page down
"<F8>" // Function key F8
Regular Keys Regular characters are used directly: "dd" // d followed by d
"yy" // y followed by y
"gg" // g followed by g
"abc" // a, b, c sequence
Wildcards Special tokens for pattern matching:
<D> - Matches digits (for command counts)
<.> - Matches any single character
<R> - Matches register specification ("a, "b, etc.)
Available Special Keys
// Navigation
< Left > , < Right > , < Up > , < Down >
< Home > , < End >
< PageUp > , < PageDown >
// Editing
< Return > , < Escape > , < Backspace > , < Tab > , < Del >
// Function Keys
< F1 > through < F12 >
// Modifiers (can be combined)
C - // Control
S - // Shift
Modifier keys must be capitalized and separated by hyphens: <C-S-x> not <c-s-x>
Adding Keybindings
The keymap_add function adds a command to one or more keymaps: #include "zep/keymap.h"
// Add to single keymap
keymap_add (myKeyMap, "<C-x>" , id_MyCommand);
// Add to multiple keymaps
keymap_add ({ & normalMap, & insertMap }, { "<C-s>" }, id_Save);
// Add multiple key sequences for same command
keymap_add (myKeyMap, { "dd" , "<Del>" }, id_DeleteLine);
Function Signature bool keymap_add (
KeyMap & map , // Target keymap
const std :: string & strCommand , // Key sequence
const StringId & commandId , // Command to execute
KeyMapAdd opt = KeyMapAdd :: Replace // Replace or error on duplicate
);
// Multi-map version
bool keymap_add (
const std :: vector < KeyMap * > & maps , // Multiple keymaps
const std :: vector < std :: string > & commands , // Multiple sequences
const StringId & commandId ,
KeyMapAdd opt = KeyMapAdd :: Replace
);
Command IDs
Commands are identified by StringId objects. Zep provides a macro for declaring them: // Define a command ID
DECLARE_COMMANDID (MyCustomCommand)
// This expands to:
// const StringId id_MyCustomCommand("MyCustomCommand");
Built-in Command IDs Zep includes many predefined commands in include/zep/keymap.h:18-173: id_InsertMode, id_NormalMode, id_VisualMode
id_Undo, id_Redo
id_Copy, id_Paste
id_MotionUp, id_MotionDown, id_MotionLeft, id_MotionRight
id_DeleteLine, id_DeleteWord
id_Save
// ... and many more
Extending a Mode
Custom Mode Implementation
Create a custom mode by inheriting from ZepMode or an existing mode: class MyCustomMode : public ZepMode_Vim
{
public:
MyCustomMode ( ZepEditor & editor ) : ZepMode_Vim (editor) {}
virtual void SetupKeyMaps () override
{
// Call parent to get standard Vim keys
ZepMode_Vim :: SetupKeyMaps ();
// Add custom keybindings
keymap_add ({ & m_normalMap }, { "<C-t>" }, id_MyCustomCommand);
keymap_add ({ & m_insertMap }, { "<C-Space>" }, id_AutoComplete);
// Override existing binding
keymap_add (m_normalMap, "dd" , id_MyDeleteLine, KeyMapAdd ::Replace);
}
const char* Name () const override { return "MyMode" ; }
};
Command Counts and Registers
Add keybindings that accept counts and registers: // Accept optional count before command
// Example: 5dd (delete 5 lines)
AddKeyMapWithCountRegisters (
{ & m_normalMap },
{ "<D>d<D>d" , "dd" },
id_DeleteLine
);
// Accept register specification
// Example: "ayy (yank to register a)
AddKeyMapWithCountRegisters (
{ & m_normalMap },
{ "<R>yy" },
id_YankLine
);
// Accept any character
// Example: f<char> (find character)
AddKeyMapWithCountRegisters (
{ & m_normalMap },
{ "f<.>" },
id_Find
);
Accessing Captured Data In your command handler: KeyMapResult result;
keymap_find (map, "5dd" , result);
// result.TotalCount() returns 5
// result.captureNumbers contains [5]
// result.foundMapping == id_DeleteLine
Querying Keymaps
Find what command a key sequence maps to: KeyMapResult result;
keymap_find (myKeyMap, "<C-x>5dd" , result);
if ( result . foundMapping != StringId ())
{
// Command found
int count = result . TotalCount (); // Total count
char reg = result . RegisterName (); // Register ('a', 'b', etc.)
auto & nums = result . captureNumbers ; // All captured numbers
auto & chars = result . captureChars ; // All captured chars
// Execute the command
ExecuteCommand ( result . foundMapping , count, reg);
}
else if ( result . needMoreChars )
{
// Partial match, need more input
DisplayStatus ( "Waiting for more keys..." );
}
else
{
// No match, treat as text input
InsertText ( result . commandWithoutGroups );
}
Debugging Keymaps
Visualize the keymap structure: #include <sstream>
std ::ostringstream str;
keymap_dump (myKeyMap, str);
std ::cout << str . str ();
// Output shows tree structure:
// d
// d : DeleteLine
// w : DeleteWord
// <D>
// w : DeleteWord
// y
// y : YankLine
Complete Example
Custom Command with Keybinding
#include "zep/mode.h"
#include "zep/keymap.h"
// 1. Define command ID
DECLARE_COMMANDID (DuplicateLine)
// 2. Create custom mode
class MyEditorMode : public ZepMode_Vim
{
public:
MyEditorMode ( ZepEditor & editor ) : ZepMode_Vim (editor) {}
void SetupKeyMaps () override
{
ZepMode_Vim :: SetupKeyMaps ();
// Bind Ctrl+D to duplicate current line
keymap_add ({ & m_normalMap, & m_insertMap },
{ "<C-d>" },
id_DuplicateLine);
}
void HandleCommand ( const StringId & cmd , const KeyMapResult & result ) override
{
if (cmd == id_DuplicateLine)
{
// Get current line
auto & buffer = GetCurrentWindow ()-> GetBuffer ();
auto cursor = GetCurrentWindow ()-> GetBufferCursor ();
auto lineStart = buffer . GetLinePos (cursor, LineLocation ::LineBegin);
auto lineEnd = buffer . GetLinePos (cursor, LineLocation ::LineEnd);
// Duplicate it
std ::string line ( buffer . begin () + lineStart,
buffer . begin () + lineEnd);
buffer . Insert (lineEnd, " \n " + line);
return ;
}
// Fallback to parent
ZepMode_Vim :: HandleCommand (cmd, result);
}
};
// 3. Use the mode
void SetupEditor ( ZepEditor & editor )
{
auto * mode = new MyEditorMode (editor);
editor . RegisterMode (mode);
editor . SetGlobalMode ( "MyMode" );
}
Implementation Reference
The keymap system is implemented in:
include/zep/keymap.h:174-232 - KeyMap structures and functions
src/keymap.cpp - Implementation details
src/mode_vim.cpp:78-269 - Example usage in Vim mode
src/mode_standard.cpp:49-89 - Example usage in Standard mode
Vim Mode See how Vim mode uses keymaps
Standard Mode See standard mode keybindings