ee is a fast terminal-first editor written in Rust for editing large files, language-aware text, and plugin-driven workflows. It combines a reusable backend core with a polished ee-cli terminal UI, tree-sitter parsing, and RPC/plugin extensibility.
Install release build with bundled runtime:
curl -fsSL https://raw.githubusercontent.com/ffimnsr/ee/main/install.sh | shInstall development build from source:
cargo install --path crates/ee-cliOpen a file:
ee path/to/file- Fast and responsive: backend edits, parsing, and rendering are designed to avoid stalls, even for very large buffers.
- Large-file friendly: persistent rope storage, streaming workflows, and efficient buffer operations make gigabyte-scale files practical.
- Terminal-first UI:
eeusesratatuiandcrosstermto deliver a polished terminal editing experience. - Reusable Rust core:
xi-core-libis frontend-agnostic and can be reused by multiple UIs. - Tree-sitter powered: syntax parsing, highlighting, and language-aware features are based on tree-sitter grammars.
- LSP and plugin integration:
xi-lsp-liband RPC-based plugin support enable diagnostics, completions, and external tooling. - Extensible backend architecture: the editor core communicates over JSON/RPC, making integrations language-agnostic and easier to evolve.
crates/ee-cli: terminal frontend and user interface foreecrates/xi-core-lib: shared editor core, language support, async runtime glue, and text model APIscrates/xi-core: original xi backend adapter cratecrates/xi-lsp-lib: LSP integration and language service supportcrates/xi-plugin-lib: plugin RPC helperscrates/xi-plugin-derive: derive macros for plugin-related typescrates/xi-rope: rope text storage implementationcrates/xi-rpc: RPC layer for backend/frontend communicationcrates/xi-unicode: unicode support utilitiesfuzz: fuzzing targets and artifacts
The easiest way to install locally from this repository is:
cargo install --path crates/ee-cli --lockedcargo install is development-oriented. It installs the ee binary, but it does not stage a bundled runtime next to the executable. For tree-sitter grammars and queries, build runtime assets separately and point EE_RUNTIME_DIR at them.
Once installed, run the editor with:
ee <path/to/file>This repository includes a Unix installer at install.sh that downloads and installs a release binary from GitHub.
scpr install eecurl -fsSL https://raw.githubusercontent.com/ffimnsr/ee/main/install.sh | shwget -qO- https://raw.githubusercontent.com/ffimnsr/ee/main/install.sh | shIf you prefer to inspect the script first, download it explicitly and run it locally:
curl -fsSL -o install.sh https://raw.githubusercontent.com/ffimnsr/ee/main/install.sh
sh install.shThe installer supports bash, zsh, and fish completions and installs the binary into ~/.local/bin by default.
On Linux and macOS the installer also places bundled runtime assets under ~/.local/share/ee, which matches the release runtime layout resolved relative to ~/.local/bin/ee.
If ~/.local/bin is not on your PATH, add it to your shell profile:
export PATH="$HOME/.local/bin:$PATH"Runtime grammar lookup uses this precedence:
EE_RUNTIME_DIRfor explicit bundled-runtime override- bundled release layout relative to the executable:
<prefix>/share/ee/on Linux/macOS,<install_dir>/runtime/on Windows - user overlay at
dirs::data_dir()/ee/ - optional workspace overlay at
<workspace>/.ee/when caller explicitly enables trusted workspace runtime roots
Bundled runtime is treated as read-only. Bundled and user/workspace overlays all use the same on-disk contract:
grammars/for compiled parser librariesqueries/<language>/for.scmquery files
Query overlays merge deterministically in bundled, then user, then workspace order for each language and query kind.
ee ships bundled LSP definitions for Rust, JSON, YAML, and TypeScript/JavaScript. Add or override servers in ee config TOML with [lsp.servers.<id>], where <id> is stable server id sent to xi-lsp-plugin.
Enabled servers require language_name and command. extensions stays supported as legacy extension fallback and server metadata, but preferred routing now lives under [languages.<id>].lsp. Optional fields are args, extensions, supports_single_file, workspace_identifier, enabled, env, and initialization_options. Defaults are args = [], supports_single_file = true, enabled = true, env = {}, and initialization_options = null. Extension matching strips a leading . from configured extensions; empty extension strings are ignored.
[lsp.servers.gleam]
language_name = "Gleam"
command = "gleam"
args = ["lsp"]
extensions = ["gleam"]
supports_single_file = false
workspace_identifier = "gleam.toml"
env = { GLEAM_LOG = "info" }
[lsp.servers.rust]
command = "rust-analyzer"
args = []
workspace_identifier = "Cargo.toml"
[lsp.servers.typescript]
enabled = false
[lsp.servers.json]
initialization_options = { provideFormatter = true }
[lsp.servers.eslint]
language_name = "ESLint"
command = "vscode-eslint-language-server"
args = ["--stdio"]
[languages.typescript]
lsp = ["typescript", "eslint"]Config precedence, from lowest to highest, is /etc/ee/config.toml, $XDG_CONFIG_HOME/ee/config.toml, legacy ~/.ee.toml only when XDG config is missing, then ancestor .ee.toml files from outermost to innermost. root = true stops discovery above that config file. Later layers replace scalar fields, replace arrays, shallow-merge env, replace initialization_options, and enabled = false disables that server id.
Routing now resolves runtime language id first, then maps [languages.<id>].lsp attachments to candidate servers. Legacy extension matching remains as fallback when a language has no explicit lsp attachment list. Multiple attached servers are allowed. First attached server is primary for interactive pull-style features such as completion, hover, go-to-definition, references, symbols, formatting, and rename. All attached servers still receive document lifecycle sync and can publish diagnostics. Missing executables, disabled attached servers, and workspace-root-only servers opened outside a matching root fail closed with status items instead of blocking editing.
Runtime language configuration lives under [languages.<id>], where <id> is the stable runtime language id. Enabled entries need name, file_types, and a nested [languages.<id>.grammar] table with library, symbol, and exactly one source definition.
[languages.gleam]
name = "Gleam"
file_types = ["gleam"]
scope = "source.gleam"
aliases = ["gleam"]
lsp = ["gleam"]
[languages.gleam.grammar]
library = "tree-sitter-gleam"
symbol = "tree_sitter_gleam"
[languages.gleam.grammar.source.crate]
name = "tree-sitter-gleam"
version = "1.0.0"
[languages.demo_branch]
name = "DemoBranch"
file_types = ["demo-branch"]
[languages.demo_branch.grammar]
library = "tree-sitter-demo"
symbol = "tree_sitter_demo"
[languages.demo_branch.grammar.source.git]
url = "https://github.com/example/tree-sitter-demo"
branch = "main"
[languages.demo_tag]
name = "DemoTag"
file_types = ["demo-tag"]
[languages.demo_tag.grammar]
library = "tree-sitter-demo"
symbol = "tree_sitter_demo"
[languages.demo_tag.grammar.source.git]
url = "https://github.com/example/tree-sitter-demo"
tag = "v1.0.0"
[languages.demo_rev]
name = "DemoRev"
file_types = ["demo-rev"]
[languages.demo_rev.grammar]
library = "tree-sitter-demo"
symbol = "tree_sitter_demo"
[languages.demo_rev.grammar.source.git]
url = "https://github.com/example/tree-sitter-demo"
rev = "33f12ef0f6f2d9f2fcb6f6c2d69b4eb9b6a0b4d2"Use rev for reproducible release builds and packaged runtimes. branch is best kept for local development where moving heads are acceptable.
Runtime grammar sources compile native code. Workspace .ee.toml runtime languages should only be trusted when workspace itself is trusted. Bundled runtime assets stay read-only, user runtime build output stays writable, and one effective runtime language still owns each file type after config merge. LSP server definitions stay canonical under [lsp.servers.<id>], while language attachments live under [languages.<id>].lsp.
Development builds use fetched runtime assets, not vendored parser sources in this repository. Build the runtime package with:
scripts/build-runtime.sh --output-root target/runtime-packageThe lower-level commands stay available when you want to inspect each step explicitly:
ee do runtime fetch --all
ee do runtime build --allFor test-focused local setup, install runtime into user runtime directory
(~/.local/share/ee or XDG_DATA_HOME/ee) with:
scripts/install-tree-sitter-runtime.shInstall bundled plugins into user config plugin directory
(~/.config/ee/plugins or XDG_CONFIG_HOME/ee/plugins) with:
scripts/install-plugins.shTo build runtime and run tests in one step:
scripts/install-tree-sitter-runtime.sh -- cargo test -p ee-cliThen point the editor at that runtime:
EE_RUNTIME_DIR="$PWD/target/runtime-package" cargo run -p ee-cli -- path/to/file.rsscripts/build-runtime.sh drives ee do runtime fetch and ee do runtime build against the merged ee language configuration, fetches grammar crate sources into a staging directory, then writes a runtime tree containing grammars/ and queries/.
New runtime languages should be described in runtime language metadata with a grammar crate name and exact crate version. Runtime fetch now resolves those crates through a temporary cargo manifest, so adding a language no longer requires editing workspace Cargo.toml just to stage grammar sources.
Release artifacts should build runtime assets first:
scripts/build-runtime.sh --output-root target/runtime-packageArchive that runtime tree next to the release binary as:
share/ee/on Linux and macOSruntime/on Windows
The official installer copies that bundled runtime tree into the resolved bundled runtime root instead of downloading grammars on first launch.
- Rust
1.95or newer - Unix-like shell for
install.sh cargotoolchain for local development and builds
cargo build --workspacecargo build --workspace --releasecargo run -p ee-cli -- <path/to/file>Open a file for editing:
ee samples/sample.txtCreate or open a new file:
ee new-file.rsRun the bundled terminal frontend from source:
cargo run -p ee-cli -- <path/to/file>cargo fmt --allcargo clippy --all -- -D warningsFor stable-toolchain checks:
cargo +stable clippy --workspace --all-targets --all-features -- -D warningscargo test --workspaceFor full workspace coverage with stable Rust:
cargo +stable test --workspace --all-featuresThis repository provides tasks.yaml for common development flows:
format: format source withcargo fmtlint: runcargo clippy --all -D warningstest-stable: run stable Rust testsinstall: installeelocally fromcrates/ee-cli
ee keeps the terminal UI separate from the editor core. The frontend handles input, layout, and rendering, while the backend owns buffer state, edit operations, parsing, and language-aware features.
xi-core-lib is designed to be reusable without tying it to a specific UI. That makes it possible to build multiple frontends on the same editor runtime.
The project uses tree-sitter for syntax parsing and language features. There is also first-class support for LSP and completion workflows through xi-lsp-lib.
The editor core communicates through JSON/RPC messages. This keeps external integrations and plugin extensions language-agnostic and easier to evolve.
Contributions are welcome. Open issues and pull requests on GitHub and follow the repository's existing code style.
This fork is maintained by the ee project contributors. See AUTHORS for history and acknowledgements.
This project is licensed under the Apache 2.0 license.