src/main.zig: entrypoint, update loop, key mappingssrc/file_type.zig: per-filetype configuration of LSP clients, Tree-sitter parser, other user preferencessrc/lsp.zig: LSP clients configuration & interfacesrc/editor.zig: editor lifecycle, cross-buffer actionssrc/buffer.zig: buffer manipulation, edit engine, change historysrc/core.zig: common structssrc/terminal.zig: tty input read & parse, tty output (draw to terminal)src/ts.zig: Tree-sitter C library wrappersrc/ui/*: dir with UI components used inTerminal.drawsrc/printer.zig: one-shot buffer printer, similar tocat/batsrc/color.zig: helper in theming with ANSI codessrc/input.zig: key abstractionsrc/external.zig: helper in running external programssrc/cli.zig: CLI arg parsesrc/test_runner.zig: custom test runner
Editor is running in fixed-time update loop:
- poll user input (/dev/tty) codes
- convert ANSI codes into
Keys and write intokey_queue - consume keys from queue and match user defined mappings
- apply dirty actions:
- buffer changes
- terminal update
- LSP servers
- Tree-sitter state
- sleep until next loop iteration
Editor is multi-threaded: main thread and one thread for each LSP client, running LspConnection.lspLoop.
Buffer content is stored in two flat array lists:
- .content
[]u21with Unicode codepoints - .content_raw
[]u8with raw bytes
Buffer content is updated using Changes allowing incremental update and history:
Changeis a struct describing the reversible content change (old_text -> new_text, old_span -> new_span)- .history
[][]Changeis list of changelists - .uncommitted_changes
std.array_list.Aligned(cha.Change, null)is a changelist that is not yet committed to history
Editor keeps track of all open buffers (.buffers) and LSP connections (lsp_connections).
Terminal takes editor state and draws it into stdout.
Hat should work out of the box on any
std.posix-compliant operating system with satisfied
dependencies and correct tree-sitter configuration (see file_type in file_type.zig).
- Zig (version >=
minimum_zig_versionin build.zig.zon) - tree-sitter
- pcre2
Development build:
zig buildRelease build:
zig build --release=fastOnce built, executable can be found at zig-out/bin. For more info, see zig build --help.
Run tests:
zig build testUsing patch(1) or git apply:
git apply patch/mypatch/mypatch.diffAdditional flags might help with conflict resolution:
git apply --3way patch/mypatch/mypatch.diffSee src/main.zig, change or add a new clause with your key mapping:
} else if (editor.mode == .normal and eql(u8, key, "G")) {
try editor.sendMessage("G pressed!");Note that ordering is important, first matching condition consumes the key(s).
See attributes and color in src/color.zig.
Add a new entry in file_type in file_type.zig.
For Tree-sitter features, initialize ts: TsConfig.
Example configuration for Rust using nvim-treesitter queries:
.{ ".rs", FileTypeConfig{
.name = "rust",
.ts = TsConfig.from_nvim("rust"),
} },- Add a new entry in
file_typeinfile_type.zigfor your filetype if missing. - Add a new entry in
lsp_configinlsp.zigwith your filetype.
Example configuration for Rust and rust-analyzer:
.{ ".rs", FileTypeConfig{
.name = "rust",
.ts = TsConfig.from_nvim("rust"),
} },LspConfig{
.name = "rust-analyzer",
.cmd = &.{ "rust-analyzer" },
.file_types = &.{"rust"},
},See implementation of Editor.findInFiles.
Hat spawns fzf with piped input and handles output as selected item.
- See
Terminal.drawandTerminal.drawOverlay - If some custom data is needed, expand the
Editorstruct, similar toEditor.completion_menu - To make it performant, make sure to update it only when necessary, using
Editor.dirtyflags - When layout changes are needed, update
Terminal.computeLayoutaccordingly
See Command in command_line.zig and Editor.handleCmd. Add a new value and implement the handler.