root-editor
A terminal based text editor written in C. Made and tested on Arch Linux and Debian-based distros.
← Back to portfolioInstallation
root-editor is a terminal text editor written in C. It has been made and tested on Arch Linux and Debian-based distros (Tails specifically), and should work fine on all major distributions.
- Clone the repository:
git clone https://github.com/execRooted/root-editor.git - Navigate to the project directory:
cd root-editor - Run the installer for system-wide installation:
sudo ./install.sh
If installed system-wide, simply run:
re [filename]
or
root-editor [filename]
Features
- Syntax Highlighting — supports highlighting for various programming languages (configurable via
config/syntax.json). - Plugin Support — extensible architecture allowing users to add custom functionality via shared libraries (
.sofiles on Linux).
Control keys
| Keybind | Action |
|---|---|
Ctrl+Q | Quit |
Ctrl+S | Save |
Ctrl+O | Open file |
Ctrl+W | Enter select mode |
Ctrl+A | Select all |
Ctrl+X | Cut |
Ctrl+C | Copy |
Ctrl+V | Paste |
Ctrl+F | Find (enters Find mode) |
Ctrl+R | Replace |
Ctrl+L | Jump to line |
Ctrl+T | Auto indent |
Ctrl+K | Auto complete |
Ctrl+U | Comment toggle |
Ctrl+H / F1 | Help |
Function keys
| Key | Action |
|---|---|
F1 | Help |
F2 | Find |
F3 | Replace |
F4 | Cut |
F5 | Copy |
F6 | Paste |
F7 | Word wrap |
F8 | Syntax highlighting |
F9 | Autosave |
Selection mode
root-editor features a selection mode that allows you to select and edit text efficiently. When in selection mode, syntax highlighting is disabled to ensure clear visibility of selected text.
Entering selection mode
Ctrl + W— enter selection mode.Shift + Arrow Keys— start selection and extend it (works left → right and top → bottom).
Navigation in selection mode
- Arrow Keys — move cursor and extend selection.
- Enter — move cursor down (like the down arrow).
- Backspace — deletes selection/word and exits selection mode.
- Home / End — move to line start/end and extend selection.
Exiting selection mode
- Esc — first press marks for exit (shows
SELECTin the status bar). - Enter — second press exits selection mode, re-enables syntax highlighting, and enables writing.
Selection operations
Ctrl+W— start selection mode.Ctrl+A— select all text.Ctrl+X— cut selected text.Ctrl+C— copy selected text.Delete / Backspace— delete selected text (when a selection exists).
Visual feedback
- Selected text appears with reverse video (highlighted background).
- Status bar shows
SELECTwhen in selection mode. - Syntax highlighting is disabled during selection for better visibility.
Find mode
Enter Find mode with Ctrl+F. It is a straightforward implementation to help you find text more easily.
Exiting Find mode
- Esc — first press marks for exit (shows
FINDin the status bar). - Enter — second press exits Find mode, re-enables syntax highlighting, and enables writing.
Plugin system
root-editor features a powerful and flexible plugin system that allows developers to extend the editor's functionality through shared libraries (.so files on Linux). Plugins can hook into various editor events, enabling custom features such as enhanced rendering, specialized key handling, file processing, and syntax highlighting extensions.
Plugins are loaded dynamically at runtime and can access the full editor state, including the current file content, cursor position, selection, and configuration settings. The system is designed to be safe and stable, with proper error handling to prevent crashes from malfunctioning plugins.
Plugin architecture
Plugins interact with the editor through a well-defined interface consisting of hook functions that are called at specific points during editor operation. Each plugin must export a PluginInterface structure that describes the plugin and provides function pointers for the hooks it implements.
Available hook functions
| Hook | Description |
|---|---|
on_load | Called when the plugin is first loaded. Initialize plugin-specific data, allocate memory, or set up initial state. Receives EditorState*. |
on_unload | Called when the plugin is unloaded (editor quits or manual unload). Clean up resources and free memory. Receives EditorState*. |
on_keypress | Called for every key press before default handling. Return non-zero to indicate the key was handled and should not be processed by default. Receives EditorState* and key code int ch. |
on_render | Called during rendering, after main content is drawn but before the final refresh. Add custom UI, overlays, or status info. Receives EditorState*. |
on_file_load | Called after a file is successfully loaded. Receives EditorState* and const char* filename. |
on_file_save | Called before a file is saved. Allows modifying content or preprocessing. Receives EditorState* and const char* filename. |
on_quit | Called when the editor is about to quit. Final cleanup or saving plugin state. Receives EditorState*. |
on_highlight_line | Called for each line during syntax highlighting. Implement custom highlighting rules. Receives EditorState*, line number int, screen row int, line number width int, and horizontal scroll offset int. |
Creating a plugin
- Create a plugin source file in the
plugins/directory (e.g.my_plugin.c). - Include the necessary headers:
#include "../src/editor.h" #include <stdlib.h> // For memory management #include <string.h> // For string operations - Implement hook functions. Each hook receives an
EditorState*giving access to the full editor state:static void my_on_render(EditorState* state) { // Access editor state int cursor_y = state->cursor_y; int cursor_x = state->cursor_x; // Add custom rendering logic here // Use ncurses functions like mvprintw(), attron(), etc. } static int my_on_keypress(EditorState* state, int ch) { if (ch == 'm') { // Example: handle 'm' key // Custom key handling return 1; // Return 1 to prevent default handling } return 0; // Return 0 to allow default handling } - Define the
PluginInterfacestruct as a static instance:static PluginInterface plugin_interface = { .name = "My Custom Plugin", .version = "1.0.0", .description = "A plugin that adds custom functionality to Root Editor", .on_load = NULL, // Set to your function or NULL .on_unload = NULL, // Set to your function or NULL .on_keypress = my_on_keypress, .on_render = my_on_render, .on_file_load = NULL, .on_file_save = NULL, .on_quit = NULL, .on_highlight_line = NULL }; - Export the interface by providing
get_plugin_interface:PluginInterface* get_plugin_interface(void) { return &plugin_interface; } - Compile the plugin using the provided Makefile:
This generates acd plugins/ make.sofile with the same name as your source file.
Accessing editor state
Plugins receive a pointer to the EditorState struct, which contains all the editor's current state. Key fields include:
char** lines— array of strings representing file content.int line_count— number of lines in the file.int cursor_x, cursor_y— current cursor position.char filename[256]— current file name.int select_mode— whether text is selected.int select_start_x, select_start_y, select_end_x, select_end_y— selection boundaries.- Various configuration flags like
syntax_enabled,autosave_enabled, etc.
Important: Always check for NULL pointers and validate array bounds when accessing editor state to prevent crashes.
Loading and managing plugins
- Automatic loading — plugins in the
plugins/directory are automatically loaded when the editor starts. - Manual loading — use the interactive plugin loader (via editor commands) to load plugins from custom locations.
- System-wide installation — after building new plugins, run
sudo ./install.shto install them system-wide. - Plugin management — the editor provides commands to list, load, and unload plugins dynamically.
Example plugin
See plugins/word_count_plugin.c for a complete example that displays the word count in the status bar. This plugin demonstrates:
- Accessing selected text or entire file content.
- Using ncurses functions for rendering.
- Proper memory management.
- Integration with the editor's rendering pipeline.