A fully-featured code editor that lives in the terminal. Not a simplified terminal editor — a real alternative to VS Code, Zed, and Sublime that happens to run in your terminal. Single Go binary, zero config.
curl -sSfL https://raw.githubusercontent.com/eugenioenko/ttt/main/install.sh | shThis downloads the latest release binary for your OS/architecture and installs it to /usr/local/bin. To install to a different directory:
INSTALL_DIR=~/.local/bin curl -sSfL https://raw.githubusercontent.com/eugenioenko/ttt/main/install.sh | shComing soon — a Homebrew tap is planned. In the meantime, use the install script or download a binary from Releases.
Pre-built binaries for Linux, macOS, and Windows are available on the Releases page. Download the one for your platform, make it executable, and put it in your PATH.
Requires Go 1.18 or newer:
go install github.com/eugenioenko/ttt/cmd/ttt@latestThis installs the ttt binary to your $GOPATH/bin (or $HOME/go/bin by default). Make sure that directory is in your PATH.
git clone https://github.com/eugenioenko/ttt.git
cd ttt
make buildThis produces an optimized binary at bin/ttt. Add it to your PATH or copy it somewhere convenient:
cp bin/ttt ~/.local/bin/- Syntax highlighting via chroma — supports hundreds of languages with automatic detection
- Bracket matching with highlighted pairs
- Find and Replace — inline find bar (Ctrl+F) with match navigation, replace bar (Ctrl+H) with replace-one and replace-all
- Go to Line (Ctrl+G)
- Selection, copy/cut/paste (Ctrl+C/X/V) with system clipboard support
- Undo/redo (Ctrl+Z/Y) via a command-pattern undo stack
.editorconfigsupport — indent size is picked up automatically per file- Indent detection — auto-detects indentation from file content; manual override via the status bar indent picker
- Mouse support — click to position cursor, click tabs, drag sidebar/panel dividers, right-click context menus
- Auto-completion — LSP-powered completions with live filtering, debounce, and auto-import support
- Signature help — parameter hints shown automatically on
(and, - Diagnostics — inline curly underline squiggles, problems panel, hover popup, and status bar counts
- Document formatting — format document, format selection, and format-on-save via LSP (command palette)
- Git blame — inline blame info for the current line shown in the status bar (author, relative time, summary)
- Line numbers with current-line highlighting
- Integrated terminal emulator via vt10x — multiple tabs with vertical inner tab bar, full VT escape sequence support
- Bottom panel — tabbed panel with integrated terminal and problems list
- Diff-based renderer for efficient terminal updates (double-buffered cell grid)
Open multiple project directories in a single session. Each root appears as a collapsible group in the explorer, search, and changes panels.
ttt # opens the current directory
ttt /path/to/dir # opens that directory as the workspace
ttt /path/to/file.go # opens the file; workspace is the git repo root
# (falls back to the file's parent dir if not in a repo)
ttt dir1 dir2 # opens multiple folders as a multi-root workspace
ttt --workspace project.ttt # loads a saved workspace fileWorkspace files use the .ttt extension and store a list of folders as relative paths:
{
"folders": [
{ "path": "." },
{ "path": "../other-project" }
]
}- Save Workspace As... from the File menu to create a workspace file
- Add Folder to Workspace and Remove Folder from Workspace via the command palette
- The git branch in the status bar switches automatically based on which workspace folder the active file belongs to
Multi-root file tree in the sidebar (Ctrl+Shift+E). When multiple folders are open, each root is shown as a collapsible group.
- Directories sorted before files, both alphabetically
- Expand/collapse with Enter or arrow keys
- Right-click context menu: New File, New Folder, Rename, Delete
- Sidebar actions button for Refresh and New File
Sidebar search panel (Ctrl+Shift+F) powered by ripgrep. Results are grouped by file with match counts.
- Smart-case matching by default
- Include/Exclude glob filters — click the toggle arrow to reveal filter inputs (e.g.
*.go,vendor/**) - Tab between search, include, and exclude inputs
- Searches across all workspace folders simultaneously
- Click a result to jump to the file and line
Changes panel in the sidebar (Ctrl+Shift+G) with full staging workflow.
Staging:
- Spacebar — toggle stage/unstage on the selected file
a— stage all unstaged filesu— unstage all staged files+button on the "Changes" section header — stage all files in that section-button on the "Staged" section header — unstage all files in that section
Committing:
- Inline commit message input at the top of each group
- Type a message and press Enter to commit all staged files
Remote operations:
- Pull, Push, Sync (pull then push) from the sidebar actions button
- Per-repo actions via the group header menu button in multi-root workspaces
Diff view:
- Select a changed file to open a diff with syntax highlighting layered on diff backgrounds
- Untracked files open directly in the editor
Multi-root:
- Changes are grouped by repository, each with its own collapsible Staged/Changes sections
- Each group has a commit input and a menu button for pull/push/sync on that specific repo
- File status badges: M (modified), A (added), D (deleted), R (renamed), U (untracked)
- Ctrl+Shift+P — opens the command palette with all available commands
- Ctrl+P — opens quick file open (searches all files across workspace folders)
- Type
>in quick-open mode to switch to command mode - Delete the
>in command mode to switch to file mode - Menu shortcuts resolve dynamically from your keybindings
The bottom panel (Ctrl+J to toggle) contains the Terminal, Problems, and References tabs.
- Problems tab — lists all LSP diagnostics (errors, warnings) grouped by file; click to jump to location
- References tab — shows results from Find All References; click to jump to location
- Terminal tab — integrated terminal emulator (see below)
Built-in terminal emulator. Press Ctrl+` to toggle the terminal panel.
- Ctrl+K T to spawn a new terminal tab
- Multiple terminal tabs with a vertical inner tab bar on the left edge
- Full VT escape sequence support via
hinshun/vt10xand PTY management viacreack/pty - True color (24-bit) and 256-color rendering
- When the terminal is focused, all keys go to the PTY except Ctrl+` (to toggle the panel)
- Terminal shell and scrollback are configurable in
settings.json - Terminal ANSI colors are theme-configurable via the
terminalfield intheme.json - Close all terminals from the panel actions menu
TTT has built-in LSP support for language-aware editing features. Install a language server and TTT detects it automatically — zero configuration needed. If a server isn't installed, TTT shows a brief notification with a link to install instructions.
| Language | Server | Install |
|---|---|---|
| Go | gopls | go install golang.org/x/tools/gopls@latest |
| TypeScript / JavaScript | typescript-language-server | npm i -g typescript typescript-language-server |
| Python | pyright | pip install pyright |
| C / C++ | clangd | sudo apt install clangd |
| Rust | rust-analyzer | rustup component add rust-analyzer |
| Vue | vue-language-server | npm i -g @vue/language-server |
| Svelte | svelteserver | npm i -g svelte-language-server |
| CSS / SCSS / Less | vscode-css-language-server | npm i -g vscode-langservers-extracted |
| HTML | vscode-html-language-server | npm i -g vscode-langservers-extracted |
| JSON | vscode-json-language-server | npm i -g vscode-langservers-extracted |
| YAML | yaml-language-server | npm i -g yaml-language-server |
| Bash | bash-language-server | npm i -g bash-language-server |
| Lua | lua-language-server | LuaLS releases |
| Zig | zls | ZLS releases |
| Kotlin | kotlin-language-server | Releases |
| Java | jdtls | Eclipse JDT.LS |
| Ruby | ruby-lsp | gem install ruby-lsp |
| Dart | dart language-server | Included with Dart SDK |
| Elixir | elixir-ls | Releases |
| PHP | phpactor | composer global require phpactor/phpactor |
| Terraform | terraform-ls | Releases |
| Markdown | marksman | Releases |
| Tailwind CSS | tailwindcss-language-server | npm i -g @tailwindcss/language-server |
| Docker | docker-langserver | npm i -g dockerfile-language-server-nodejs |
You can also add custom servers or override built-in ones in ~/.config/ttt/settings.json. See the LSP docs for details.
To disable LSP entirely: "lsp": { "enabled": false } in settings.
| Feature | Keybinding | Description |
|---|---|---|
| Autocomplete | Ctrl+U | Trigger completion at cursor position |
| Signature Help | (automatic) | Parameter hints shown on ( and , |
| Go to Definition | F12 | Jump to the definition of the symbol under the cursor |
| Go to Implementation | Shift+F12 | Jump to the implementation of the symbol under the cursor |
| Go to Type Definition | (command palette) | Jump to the type definition of the symbol under the cursor |
| Find References | (command palette) | Find all references to the symbol under the cursor (results in bottom panel REFERENCES tab) |
| Rename Symbol | F2 | Rename the symbol under the cursor across all files in the workspace |
| Hover | Ctrl+K I | Show type information and documentation for the symbol under the cursor |
| Format Document | (command palette) | Format the entire document using the language server |
| Format Selection | (command palette) | Format the selected range |
| Diagnostics | (automatic) | Error/warning squiggles inline, status bar summary, hover popup |
Completions trigger automatically as you type with a configurable debounce (default 150ms, set autocomplete.debounce in settings.json). The completion list filters live as you continue typing, and supports completionItem/resolve for additional details and auto-imports.
The LSP server publishes diagnostics (errors, warnings, hints) which are displayed as:
- Inline squiggles — curly underlines on the affected range, colored by severity
- Problems panel — a tab in the bottom panel listing all diagnostics grouped by file
- Hover popup — hover over a squiggle to see the diagnostic message
- Status bar — error/warning counts shown in the status bar
- Format Document — formats the entire file via the language server (available from the command palette)
- Format Selection — formats only the selected range (available from the command palette)
- Format on Save — enable
formatOnSaveinsettings.jsonto auto-format when saving
- Find All References — search for all usages of the symbol under the cursor; results appear in the bottom panel REFERENCES tab (available from the command palette)
- Rename Symbol (F2) — rename the symbol under the cursor across all files in the workspace. Applies multi-file workspace edits automatically. Enable
lsp.saveOnRenameinsettings.jsonto auto-save affected files after renaming.
Tabs follow a pin-on-reclick model similar to VS Code:
- Opening a file from the explorer or search replaces the current unpinned tab
- Clicking on an already-open tab (or opening the same file again) pins it
- Ctrl+W to close a tab, Ctrl+PgDn/PgUp to switch tabs
- Right-click a tab for Close, Close Others, Close All
- Tab bar actions button for Close All
TTT supports fully customizable themes via JSON files. You can change every color in the editor — from syntax highlighting and diff backgrounds to the sidebar, tabs, status bar, terminal ANSI colors, borders, and semantic colors (success, danger, warning).
10 themes ship in the sample-config/ directory:
- Aurora
- Bubblegum
- Default Dark
- Default Light
- Hotline
- Monokai
- One Dark
- Solarized Dark
- Solarized Light
- Virtru Dark
Press Ctrl+K Ctrl+T (or use View > Switch Theme from the menu bar) to open the theme picker with a live preview.
To create a custom theme, copy one of the built-in theme files to your config directory and edit it:
cp sample-config/theme.monokai.json ~/.config/ttt/theme.jsonRestart TTT (or switch themes) to pick up changes.
To use your terminal's native colors instead of the theme's, set foreground/background to empty strings in your theme file.
File, Edit, Selection, View, and Help menus accessible via the menu bar or keyboard shortcuts. Menus display resolved keybindings next to each command. Navigate between menus with left/right arrow keys.
Config files are loaded from <exe-dir>/config/ (bundled defaults) or ~/.config/ttt/ (user overrides):
| File | Purpose |
|---|---|
keybindings.json |
Custom keybindings (VS Code key format) |
settings.json |
Editor settings (tabSize, insertSpaces, wordWrap, lineNumbers, sidebarWidth, explorer, terminal, theme, lsp, formatOnSave, autocomplete) |
theme.json |
Colors and styles (or use theme.<name>.json for named themes) |
| Key | Type | Default | Description |
|---|---|---|---|
tabSize |
int | 4 |
Number of spaces per indentation level |
insertSpaces |
bool | true |
Use spaces instead of tabs for indentation |
wordWrap |
bool | false |
Wrap long lines at the editor width |
lineNumbers |
bool | true |
Show line numbers in the gutter |
sidebarVisible |
bool | true |
Show the sidebar on startup |
sidebarWidth |
int | 30 |
Width of the sidebar in columns |
cursorStyle |
string | "" |
Cursor style: "block", "underline", or "bar" |
theme |
string | "" |
Theme name (from ~/.config/ttt/themes/) |
debugMode |
bool | false |
Enable debug logging to ~/.config/ttt/debug.log |
formatOnSave |
bool | false |
Auto-format the document via LSP on save |
explorer.showHidden |
bool | true |
Show hidden files (dot-prefixed) in the file explorer |
explorer.showGitIgnored |
bool | true |
Show gitignored files in the file explorer |
terminal.shell |
string | "" |
Shell command for the integrated terminal (empty = system default) |
terminal.scrollback |
int | 1000 |
Number of scrollback lines to retain in the terminal |
lsp.saveOnRename |
bool | false |
Auto-save all files affected by a rename operation |
lsp.servers |
object | {} |
Map of language ID to { "command": [...], "languages": {...} } for LSP servers |
autocomplete.enabled |
bool | true |
Enable LSP-powered autocompletion |
autocomplete.autoSuggest |
bool | true |
Show completions automatically as you type |
autocomplete.debounce |
int | 150 |
Milliseconds to wait after typing before requesting completions |
autocomplete.signatureHelp |
bool | true |
Show function signature help on ( and , |
Example ~/.config/ttt/settings.json:
{
"tabSize": 4,
"insertSpaces": true,
"wordWrap": false,
"lineNumbers": true,
"sidebarVisible": true,
"sidebarWidth": 30,
"theme": "default-dark",
"formatOnSave": true,
"explorer": {
"showHidden": true,
"showGitIgnored": true
},
"terminal": {
"shell": "/bin/zsh",
"scrollback": 1000
},
"lsp": {
"saveOnRename": true,
"servers": {
"go": { "command": ["gopls"] },
"typescript": {
"command": ["typescript-language-server", "--stdio"],
"languages": {
".ts": "typescript",
".tsx": "typescriptreact",
".js": "javascript",
".jsx": "javascriptreact"
}
}
}
},
"autocomplete": {
"enabled": true,
"autoSuggest": true,
"debounce": 150,
"signatureHelp": true
}
}Settings and keybindings can also be opened from the command palette: Preferences: Open Settings and Preferences: Open Keyboard Shortcuts.
All keybindings are customizable via keybindings.json. Supports chord sequences (e.g. ctrl+k ctrl+t). These are the defaults:
| Shortcut | Action |
|---|---|
| General | |
| Ctrl+Q | Quit |
| Ctrl+P | Command palette |
| Alt+P | Quick open file |
| Escape | Focus editor |
| File | |
| Ctrl+N | New file |
| Ctrl+S | Save |
| Ctrl+K S | Save as |
| Editor | |
| Ctrl+Z | Undo |
| Ctrl+Y | Redo |
| Ctrl+A | Select all |
| Ctrl+C | Copy |
| Ctrl+X | Cut |
| Ctrl+V | Paste |
| Ctrl+G | Go to line |
| Search | |
| Ctrl+F | Find |
| Ctrl+H | Find and replace |
| F3 / Shift+F3 | Find next / previous |
| View | |
| Ctrl+B | Toggle sidebar |
| Ctrl+J | Toggle bottom panel |
| Ctrl+K E | Show file explorer |
| Ctrl+K F | Search across files |
| Ctrl+K C | Show changes |
| Ctrl+0 | Focus sidebar |
| Ctrl+K Ctrl+T | Switch theme |
| Tabs | |
| Ctrl+PgDn / PgUp | Next / previous tab |
| Ctrl+W | Close tab |
| LSP | |
| Ctrl+U | Autocomplete |
| F12 | Go to definition |
| Shift+F12 | Go to implementation |
| F2 | Rename symbol |
| Ctrl+K I | Hover info |
| Terminal / Bottom Panel | |
| Ctrl+` | Toggle terminal |
| Ctrl+J | Toggle bottom panel |
| Ctrl+K T | New terminal tab |
| Changes Panel | |
| Space | Toggle stage/unstage file |
| A | Stage all |
| U | Unstage all |
| R | Refresh |
| Enter | Open diff / activate |
| Menu Bar | |
| F10 / Alt+F | File menu |
| Alt+E / S / V / H | Edit / Selection / View / Help |
TTT is tested at two levels to catch bugs across the entire stack — from core data structures to end-to-end user workflows.
The core editor engine (internal/core/) is fully unit-testable in isolation with zero terminal dependencies. Buffer operations, cursor math, undo/redo commands, and syntax highlighting all have dedicated Go tests.
make test # run all unit tests
go test ./internal/core/buffer/ # test a single packageBlack-box tests that launch the real ttt binary in a real terminal, type keys, and assert on screen output and file contents. These catch integration issues that unit tests can't — keybinding wiring, rendering, file I/O round-trips, and cross-component interactions.
Built with vitest and tui-use (terminal automation for TUI apps). 44 tests across 16 files covering:
- File operations — open, edit, save, Save As, new file, dirty indicator
- Editing — undo/redo, select all + overwrite, line delete/move/duplicate, word delete
- Unicode — accented characters, CJK, emoji
- Find & Replace — search, match navigation, replace, save verification
- Navigation — go to line, tab switching
- UI panels — sidebar toggle, panel switching, terminal toggle, command palette
- Tab management — multi-tab, close, unsaved changes dialog
cd tests/functional
pnpm install
pnpm test # run all functional tests
pnpm test:watch # watch mode
pnpm test:stress # run 10 times to catch flaky testsFunctional tests run in CI on every push and pull request.
The codebase follows a strict layered architecture: core -> view -> render -> term -> ui.
internal/core/— UI-agnostic editor engine (buffer, cursor, undo, syntax highlighting, multi-buffer management)internal/view/— Viewport scrolling and status barinternal/render/— Diff-based double-buffered renderer for minimal terminal writesinternal/terminal/— Integrated terminal emulator (vt10x + pty)internal/term/— Terminal abstraction viaScreeninterface (only package that imports tcell)internal/ui/— Window manager, pane system, and all widgetsinternal/lsp/— Language Server Protocol client (JSON-RPC 2.0 over stdio, per-language server management)internal/workspace/— Multi-folder workspace management with git detectioninternal/git/— Git operations (status, stage, unstage, commit, pull, push, diff, blame)internal/config/— Configuration loading (settings, themes, keybindings, editorconfig)
MIT
