964 lines
18 KiB
Markdown
964 lines
18 KiB
Markdown
# Lime Implementation Plan
|
|
|
|
Lime is a clean, modern terminal/TUI text editor written in Rust. It should feel approachable like Nano, efficient and clean like Vim, and easy to use like VS Code, while using a standard non-modal cursor editing model.
|
|
|
|
This plan intentionally avoids time estimates so it can be handed directly to an implementation agent.
|
|
|
|
## 1. Product Direction
|
|
|
|
Initial product goal:
|
|
|
|
```bash
|
|
lime path/to/file.rs
|
|
```
|
|
|
|
opens a clean single-file terminal editor with:
|
|
|
|
- Standard typing/editing behavior
|
|
- Save and quit shortcuts
|
|
- Unsaved-changes confirmation
|
|
- Syntax highlighting
|
|
- Line numbers
|
|
- Status bar and help bar
|
|
- File search/open popup with `Ctrl-F`
|
|
- Recursive file discovery from the current working directory
|
|
- Large-file warning before opening very large files
|
|
- Good behavior on macOS and Linux
|
|
|
|
Lime should not be modal like Vim. It should be keyboard-first, but obvious and friendly.
|
|
|
|
## 2. Recommended Technical Stack
|
|
|
|
Use a Rust-native terminal stack:
|
|
|
|
- `crossterm` for terminal control and input events
|
|
- `ratatui` for layout, widgets, status bars, popups, and drawing
|
|
- `ropey` for text storage
|
|
- `tree-sitter` for syntax highlighting
|
|
- `ignore` for recursive file scanning with `.gitignore` support
|
|
- `nucleo-matcher` for fuzzy file matching
|
|
|
|
Do not build a custom terminal renderer at first. `ratatui + crossterm` gives enough control for a polished TUI while keeping implementation manageable.
|
|
|
|
Suggested dependencies:
|
|
|
|
```toml
|
|
[dependencies]
|
|
anyhow = "1"
|
|
thiserror = "1"
|
|
clap = { version = "4", features = ["derive"] }
|
|
ropey = "1"
|
|
crossterm = "0.28"
|
|
ratatui = "0.29"
|
|
unicode-segmentation = "1"
|
|
unicode-width = "0.2"
|
|
tree-sitter = "0.24"
|
|
tree-sitter-rust = "0.23"
|
|
tree-sitter-javascript = "0.23"
|
|
tree-sitter-typescript = "0.23"
|
|
tree-sitter-python = "0.23"
|
|
tree-sitter-json = "0.23"
|
|
tree-sitter-toml-ng = "0.6"
|
|
tree-sitter-md = "0.3"
|
|
ignore = "0.4"
|
|
nucleo-matcher = "0.3"
|
|
notify = "7"
|
|
serde = { version = "1", features = ["derive"] }
|
|
toml = "0.8"
|
|
dirs = "5"
|
|
```
|
|
|
|
Optional later:
|
|
|
|
```toml
|
|
arboard = "3"
|
|
similar = "2"
|
|
tempfile = "3"
|
|
insta = "1"
|
|
```
|
|
|
|
## 3. Repository Structure
|
|
|
|
Use a Cargo workspace from the beginning:
|
|
|
|
```txt
|
|
lime/
|
|
Cargo.toml
|
|
README.md
|
|
LICENSE
|
|
PLAN.md
|
|
crates/
|
|
lime-core/
|
|
Cargo.toml
|
|
src/
|
|
lib.rs
|
|
buffer.rs
|
|
cursor.rs
|
|
selection.rs
|
|
edit.rs
|
|
history.rs
|
|
movement.rs
|
|
search.rs
|
|
file.rs
|
|
command.rs
|
|
lime-syntax/
|
|
Cargo.toml
|
|
src/
|
|
lib.rs
|
|
language.rs
|
|
highlighter.rs
|
|
theme.rs
|
|
lime-ui/
|
|
Cargo.toml
|
|
src/
|
|
lib.rs
|
|
app.rs
|
|
terminal.rs
|
|
layout.rs
|
|
editor_view.rs
|
|
status_bar.rs
|
|
command_bar.rs
|
|
file_picker.rs
|
|
prompt.rs
|
|
theme.rs
|
|
input.rs
|
|
lime-cli/
|
|
Cargo.toml
|
|
src/
|
|
main.rs
|
|
```
|
|
|
|
Root `Cargo.toml`:
|
|
|
|
```toml
|
|
[workspace]
|
|
members = [
|
|
"crates/lime-core",
|
|
"crates/lime-syntax",
|
|
"crates/lime-ui",
|
|
"crates/lime-cli"
|
|
]
|
|
resolver = "2"
|
|
```
|
|
|
|
The installed binary should be named `lime`.
|
|
|
|
## 4. Crate Responsibilities
|
|
|
|
### `lime-core`
|
|
|
|
Pure editor logic.
|
|
|
|
Must not depend on terminal UI, Ratatui, themes, syntax highlighting, or input events.
|
|
|
|
Responsibilities:
|
|
|
|
- Text buffer
|
|
- Cursor state
|
|
- Selections, even if selection editing comes later
|
|
- Editing operations
|
|
- Undo/redo
|
|
- File load/save
|
|
- Dirty state
|
|
- Search inside the current buffer
|
|
- Line/column movement
|
|
- Large-file metadata and open policy types
|
|
|
|
### `lime-syntax`
|
|
|
|
Syntax parsing and highlighting.
|
|
|
|
Responsibilities:
|
|
|
|
- Language detection
|
|
- Tree-sitter parser setup
|
|
- Highlight spans for visible ranges
|
|
- Theme scopes
|
|
- Graceful fallback for unknown languages or parser failures
|
|
|
|
### `lime-ui`
|
|
|
|
Terminal application.
|
|
|
|
Responsibilities:
|
|
|
|
- Raw mode
|
|
- Alternate screen
|
|
- Event loop
|
|
- Keybindings
|
|
- Rendering
|
|
- Editor viewport
|
|
- Popups
|
|
- File picker
|
|
- Status bar
|
|
- Help bar
|
|
- Prompts and warnings
|
|
|
|
### `lime-cli`
|
|
|
|
CLI entry point.
|
|
|
|
Responsibilities:
|
|
|
|
- Parse CLI arguments
|
|
- Resolve starting path
|
|
- Load config
|
|
- Start app
|
|
- Ensure terminal cleanup on error or panic
|
|
|
|
## 5. Core Data Model
|
|
|
|
Use `ropey::Rope` for buffer storage.
|
|
|
|
```rust
|
|
pub struct TextBuffer {
|
|
rope: Rope,
|
|
path: Option<PathBuf>,
|
|
dirty: bool,
|
|
line_ending: LineEnding,
|
|
}
|
|
```
|
|
|
|
Track positions as line/column character positions, not byte offsets.
|
|
|
|
```rust
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct Position {
|
|
pub line: usize,
|
|
pub column: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct Cursor {
|
|
pub position: Position,
|
|
pub preferred_column: Option<usize>,
|
|
}
|
|
```
|
|
|
|
Use `preferred_column` for vertical movement so up/down preserve the intended visual column across lines of different lengths.
|
|
|
|
Also define:
|
|
|
|
```rust
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct TextRange {
|
|
pub start: Position,
|
|
pub end: Position,
|
|
}
|
|
|
|
pub enum LineEnding {
|
|
Lf,
|
|
Crlf,
|
|
}
|
|
```
|
|
|
|
## 6. Editing Commands
|
|
|
|
Create a command abstraction in `lime-core`:
|
|
|
|
```rust
|
|
pub enum EditorCommand {
|
|
InsertChar(char),
|
|
InsertText(String),
|
|
Newline,
|
|
Backspace,
|
|
Delete,
|
|
MoveLeft,
|
|
MoveRight,
|
|
MoveUp,
|
|
MoveDown,
|
|
MoveLineStart,
|
|
MoveLineEnd,
|
|
MoveFileStart,
|
|
MoveFileEnd,
|
|
PageUp,
|
|
PageDown,
|
|
Save,
|
|
Undo,
|
|
Redo,
|
|
Search(String),
|
|
}
|
|
```
|
|
|
|
The UI should convert terminal key events into app actions or editor commands. The core applies commands and returns effects.
|
|
|
|
```rust
|
|
pub enum CommandResult {
|
|
None,
|
|
Modified,
|
|
Saved,
|
|
CursorMoved,
|
|
NeedsPath,
|
|
}
|
|
```
|
|
|
|
## 7. Undo/Redo
|
|
|
|
Implement undo/redo early.
|
|
|
|
Represent edits as reversible transactions:
|
|
|
|
```rust
|
|
pub struct EditTransaction {
|
|
pub edits: Vec<TextEdit>,
|
|
pub before_cursor: Cursor,
|
|
pub after_cursor: Cursor,
|
|
}
|
|
|
|
pub enum TextEdit {
|
|
Insert {
|
|
at: Position,
|
|
text: String,
|
|
},
|
|
Delete {
|
|
range: TextRange,
|
|
deleted_text: String,
|
|
},
|
|
}
|
|
```
|
|
|
|
Typing consecutive characters should be grouped into one undo transaction until one of these happens:
|
|
|
|
- Cursor movement
|
|
- Newline
|
|
- Backspace/delete
|
|
- Save
|
|
- Different command type
|
|
- Focus changes to a popup/prompt
|
|
|
|
## 8. Large File Handling
|
|
|
|
Before reading a file, inspect metadata.
|
|
|
|
Recommended thresholds:
|
|
|
|
```txt
|
|
<= 5 MB: open normally
|
|
5-25 MB: open with warning prompt
|
|
> 25 MB: require explicit confirmation
|
|
> 100 MB: refuse by default unless --force
|
|
```
|
|
|
|
CLI/TUI warning example:
|
|
|
|
```txt
|
|
This file is 84 MB. Lime may be slower with very large files.
|
|
Open anyway? [y/N]
|
|
```
|
|
|
|
Core policy type:
|
|
|
|
```rust
|
|
pub struct FileOpenPolicy {
|
|
pub warn_threshold_bytes: u64,
|
|
pub confirm_threshold_bytes: u64,
|
|
pub hard_threshold_bytes: u64,
|
|
pub force: bool,
|
|
}
|
|
```
|
|
|
|
The file picker should also use this policy before replacing the current buffer.
|
|
|
|
## 9. Terminal UI Layout
|
|
|
|
Default layout:
|
|
|
|
```txt
|
|
┌────────────────────────────────────────────┐
|
|
│ 1 │ fn main() { │
|
|
│ 2 │ println!("hello"); │
|
|
│ 3 │ } │
|
|
│ │ │
|
|
│ │ │
|
|
├────────────────────────────────────────────┤
|
|
│ lime file.rs rust Ln 2, Col 5 modified │
|
|
│ Ctrl-S Save Ctrl-F Files Ctrl-Q Quit │
|
|
└────────────────────────────────────────────┘
|
|
```
|
|
|
|
Main regions:
|
|
|
|
1. Editor viewport
|
|
2. Status bar
|
|
3. Help/action bar
|
|
4. Optional popup overlay
|
|
|
|
Use `ratatui::Layout` for the main shell.
|
|
|
|
## 10. Rendering Strategy
|
|
|
|
Render only visible lines.
|
|
|
|
UI viewport state:
|
|
|
|
```rust
|
|
pub struct Viewport {
|
|
pub top_line: usize,
|
|
pub left_col: usize,
|
|
pub height: usize,
|
|
pub width: usize,
|
|
}
|
|
```
|
|
|
|
Editor rendering should:
|
|
|
|
- Render visible lines only
|
|
- Draw line numbers
|
|
- Draw cursor
|
|
- Draw selected text later
|
|
- Apply syntax spans when available
|
|
- Horizontally scroll for long lines
|
|
- Vertically scroll when the cursor nears viewport edges
|
|
- Avoid allocating the entire file each frame
|
|
|
|
Gutter width:
|
|
|
|
```rust
|
|
let gutter_width = total_lines.to_string().len() + 2;
|
|
```
|
|
|
|
## 11. Input Model
|
|
|
|
Recommended shortcuts:
|
|
|
|
```txt
|
|
Ctrl-S Save
|
|
Ctrl-Q Quit
|
|
Ctrl-F File picker
|
|
Ctrl-G Go to line
|
|
Ctrl-Z Undo
|
|
Ctrl-Y Redo
|
|
Ctrl-A Start of line
|
|
Ctrl-E End of line
|
|
Ctrl-R Search in current file
|
|
Esc Close popup/cancel prompt
|
|
Arrow keys Move cursor
|
|
PageUp Page up
|
|
PageDown Page down
|
|
Home Start of line
|
|
End End of line
|
|
Backspace Delete backward
|
|
Delete Delete forward
|
|
Enter Newline
|
|
Tab Insert spaces according to config
|
|
```
|
|
|
|
Use an app-level action enum:
|
|
|
|
```rust
|
|
pub enum AppAction {
|
|
Editor(EditorCommand),
|
|
Save,
|
|
Quit,
|
|
OpenFilePicker,
|
|
OpenGoToLine,
|
|
OpenSearch,
|
|
ClosePopup,
|
|
Confirm,
|
|
Cancel,
|
|
}
|
|
```
|
|
|
|
Do not hardcode behavior directly inside the event loop. Route input through a mapping layer.
|
|
|
|
## 12. File Picker
|
|
|
|
`Ctrl-F` opens a centered popup listing files recursively from the current directory.
|
|
|
|
Behavior:
|
|
|
|
- Scan current working directory or selected workspace root
|
|
- Respect `.gitignore`
|
|
- Skip hidden files by default
|
|
- Skip common bulky directories:
|
|
- `.git`
|
|
- `target`
|
|
- `node_modules`
|
|
- `dist`
|
|
- `.next`
|
|
- `build`
|
|
- Fuzzy filter as the user types
|
|
- Arrow up/down changes selection
|
|
- Enter opens selected file
|
|
- Escape closes picker
|
|
- Warn if current file has unsaved changes before replacing it
|
|
- Warn before opening very large files
|
|
|
|
Popup layout:
|
|
|
|
```txt
|
|
╭─ Open File ─────────────────────────────╮
|
|
│ search: main │
|
|
│ │
|
|
│ > crates/lime-cli/src/main.rs │
|
|
│ crates/lime-core/src/buffer.rs │
|
|
│ README.md │
|
|
╰─────────────────────────────────────────╯
|
|
```
|
|
|
|
State:
|
|
|
|
```rust
|
|
pub struct FilePickerState {
|
|
pub query: String,
|
|
pub all_files: Vec<PathBuf>,
|
|
pub matches: Vec<FileMatch>,
|
|
pub selected: usize,
|
|
}
|
|
|
|
pub struct FileMatch {
|
|
pub path: PathBuf,
|
|
pub score: u32,
|
|
}
|
|
```
|
|
|
|
## 13. Syntax Highlighting
|
|
|
|
Start with these languages:
|
|
|
|
- Rust
|
|
- JavaScript
|
|
- TypeScript
|
|
- Python
|
|
- JSON
|
|
- TOML
|
|
- Markdown
|
|
|
|
Language detection:
|
|
|
|
```rust
|
|
pub enum Language {
|
|
Rust,
|
|
JavaScript,
|
|
TypeScript,
|
|
Python,
|
|
Json,
|
|
Toml,
|
|
Markdown,
|
|
PlainText,
|
|
}
|
|
```
|
|
|
|
Detection should use file extension first.
|
|
|
|
Highlighting should be resilient:
|
|
|
|
- Unknown language falls back to plain text
|
|
- Parser failure falls back to plain text
|
|
- Initial implementation may highlight visible ranges only
|
|
- Incremental parsing can come later
|
|
|
|
Theme model:
|
|
|
|
```rust
|
|
pub struct Theme {
|
|
pub background: Color,
|
|
pub foreground: Color,
|
|
pub gutter: Color,
|
|
pub cursor: Color,
|
|
pub selection: Color,
|
|
pub keyword: Color,
|
|
pub string: Color,
|
|
pub comment: Color,
|
|
pub function: Color,
|
|
pub type_name: Color,
|
|
pub number: Color,
|
|
pub error: Color,
|
|
}
|
|
```
|
|
|
|
Use a clean default dark theme with calm contrast.
|
|
|
|
## 14. Save/Quit Behavior
|
|
|
|
If there are no unsaved changes, `Ctrl-Q` exits immediately.
|
|
|
|
If the buffer is dirty:
|
|
|
|
```txt
|
|
Unsaved changes. Save before quitting? [s]ave [d]iscard [c]ancel
|
|
```
|
|
|
|
If saving an unnamed buffer:
|
|
|
|
```txt
|
|
Save as: _
|
|
```
|
|
|
|
Save operation should:
|
|
|
|
- Write to a temporary file
|
|
- Flush data
|
|
- Rename over original path
|
|
- Preserve line endings where practical
|
|
- Clear dirty flag after success
|
|
- Show a status message after success or failure
|
|
|
|
## 15. Config
|
|
|
|
Keep config simple at first.
|
|
|
|
Locations:
|
|
|
|
```txt
|
|
macOS: ~/Library/Application Support/lime/config.toml
|
|
Linux: ~/.config/lime/config.toml
|
|
```
|
|
|
|
Initial config:
|
|
|
|
```toml
|
|
theme = "lime-dark"
|
|
tab_width = 4
|
|
insert_spaces = true
|
|
show_line_numbers = true
|
|
confirm_large_files = true
|
|
```
|
|
|
|
Rust model:
|
|
|
|
```rust
|
|
pub struct Config {
|
|
pub theme: String,
|
|
pub tab_width: usize,
|
|
pub insert_spaces: bool,
|
|
pub show_line_numbers: bool,
|
|
pub confirm_large_files: bool,
|
|
}
|
|
```
|
|
|
|
If config is missing or invalid, use defaults and show a non-blocking warning.
|
|
|
|
## 16. Error Handling
|
|
|
|
Use `anyhow` at app/CLI boundaries.
|
|
|
|
Use `thiserror` for library errors.
|
|
|
|
Example:
|
|
|
|
```rust
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum LimeError {
|
|
#[error("file is too large: {size} bytes")]
|
|
FileTooLarge { size: u64 },
|
|
|
|
#[error("buffer has no file path")]
|
|
MissingPath,
|
|
|
|
#[error("invalid cursor position")]
|
|
InvalidPosition,
|
|
}
|
|
```
|
|
|
|
Terminal cleanup must always happen.
|
|
|
|
Use a guard:
|
|
|
|
```rust
|
|
pub struct TerminalGuard;
|
|
|
|
impl Drop for TerminalGuard {
|
|
fn drop(&mut self) {
|
|
// disable raw mode
|
|
// leave alternate screen
|
|
// show cursor
|
|
}
|
|
}
|
|
```
|
|
|
|
Never leave the terminal in raw mode after an error or panic.
|
|
|
|
## 17. CLI Design
|
|
|
|
Basic usage:
|
|
|
|
```bash
|
|
lime
|
|
lime file.rs
|
|
lime .
|
|
lime --force huge.log
|
|
lime --config path/to/config.toml
|
|
```
|
|
|
|
CLI args:
|
|
|
|
```rust
|
|
#[derive(Parser)]
|
|
pub struct Cli {
|
|
pub path: Option<PathBuf>,
|
|
|
|
#[arg(long)]
|
|
pub force: bool,
|
|
|
|
#[arg(long)]
|
|
pub config: Option<PathBuf>,
|
|
}
|
|
```
|
|
|
|
Behavior:
|
|
|
|
- No path: open empty unnamed buffer
|
|
- File path: open file
|
|
- Directory path: open empty buffer with file picker rooted at that directory
|
|
- Nonexistent file: create a new buffer with that path
|
|
- Huge file: warn/confirm unless `--force`
|
|
|
|
## 18. Testing Strategy
|
|
|
|
### `lime-core`
|
|
|
|
Most tests should live here.
|
|
|
|
Test:
|
|
|
|
- Insert characters
|
|
- Insert text
|
|
- Insert newline
|
|
- Backspace in middle of line
|
|
- Backspace at start of line joins lines
|
|
- Delete in middle of line
|
|
- Delete at end of line joins lines
|
|
- Cursor movement left/right/up/down
|
|
- Line start/end
|
|
- File start/end
|
|
- Undo/redo
|
|
- Search
|
|
- Save/load roundtrip
|
|
- Unicode text
|
|
- Tabs
|
|
- Long lines
|
|
- Empty file behavior
|
|
|
|
Example test:
|
|
|
|
```rust
|
|
#[test]
|
|
fn backspace_joins_lines() {
|
|
let mut editor = Editor::from_text("hello\nworld");
|
|
editor.set_cursor(Position { line: 1, column: 0 });
|
|
editor.backspace();
|
|
|
|
assert_eq!(editor.text(), "helloworld");
|
|
assert_eq!(editor.cursor().position, Position { line: 0, column: 5 });
|
|
}
|
|
```
|
|
|
|
### `lime-syntax`
|
|
|
|
Test:
|
|
|
|
- Language detection
|
|
- Parser setup does not panic
|
|
- Highlight spans are valid
|
|
- Unknown files fall back to plain text
|
|
|
|
### `lime-ui`
|
|
|
|
Test pure/non-terminal pieces:
|
|
|
|
- Keybinding mapping
|
|
- Viewport scrolling
|
|
- File picker filtering
|
|
- Status string formatting
|
|
- Layout calculations
|
|
- Prompt state transitions
|
|
|
|
Avoid depending heavily on full terminal snapshot tests in the first version.
|
|
|
|
## 19. Implementation Order
|
|
|
|
### Step 1: Create workspace
|
|
|
|
Create all workspace crates and make `cargo test` pass.
|
|
|
|
Deliverables:
|
|
|
|
- Root `Cargo.toml`
|
|
- `lime-core`
|
|
- `lime-syntax`
|
|
- `lime-ui`
|
|
- `lime-cli`
|
|
- Minimal compiling binary named `lime`
|
|
|
|
### Step 2: Implement `lime-core`
|
|
|
|
Implement:
|
|
|
|
- `TextBuffer`
|
|
- `Position`
|
|
- `TextRange`
|
|
- `Cursor`
|
|
- Load from string
|
|
- Load from file
|
|
- Save to file
|
|
- Insert char/text
|
|
- Newline
|
|
- Backspace
|
|
- Delete
|
|
- Cursor movement
|
|
- Dirty flag
|
|
|
|
Add comprehensive tests.
|
|
|
|
### Step 3: Implement undo/redo
|
|
|
|
Add:
|
|
|
|
- `EditTransaction`
|
|
- Undo stack
|
|
- Redo stack
|
|
- Transaction grouping for typing
|
|
- Tests for edit operations and grouped undo behavior
|
|
|
|
### Step 4: Implement basic terminal app
|
|
|
|
In `lime-ui`, implement:
|
|
|
|
- Raw mode
|
|
- Alternate screen
|
|
- Panic-safe cleanup
|
|
- Event loop
|
|
- Render empty editor
|
|
- Render file content
|
|
- Status bar
|
|
- Help bar
|
|
- Cursor rendering
|
|
|
|
At this point:
|
|
|
|
```bash
|
|
cargo run -p lime-cli -- README.md
|
|
```
|
|
|
|
should open a navigable file.
|
|
|
|
### Step 5: Wire editing input
|
|
|
|
Add keyboard handling for:
|
|
|
|
- Text input
|
|
- Enter
|
|
- Tab
|
|
- Backspace
|
|
- Delete
|
|
- Arrow movement
|
|
- Home/end
|
|
- Page up/down
|
|
- Save
|
|
- Quit
|
|
|
|
After this step, Lime should be a usable single-file editor.
|
|
|
|
### Step 6: Add viewport scrolling
|
|
|
|
Implement:
|
|
|
|
- Vertical scroll
|
|
- Horizontal scroll
|
|
- Cursor-follow behavior
|
|
- Page up/down
|
|
- Long-line handling
|
|
|
|
Do not render the whole file.
|
|
|
|
### Step 7: Add file open policy
|
|
|
|
Before reading files:
|
|
|
|
- Check size
|
|
- Show warning prompt in TUI
|
|
- Support `--force`
|
|
- Refuse extreme files by default
|
|
|
|
### Step 8: Add file picker
|
|
|
|
Implement:
|
|
|
|
- Recursive file scan
|
|
- Fuzzy matching
|
|
- Popup rendering
|
|
- Query input
|
|
- Selection movement
|
|
- Opening selected files
|
|
- Dirty-buffer confirmation before replacing current file
|
|
|
|
Bind to `Ctrl-F`.
|
|
|
|
### Step 9: Add syntax highlighting
|
|
|
|
Implement `lime-syntax`.
|
|
|
|
Start with:
|
|
|
|
- Language detection
|
|
- Rust highlighting
|
|
- Plain-text fallback
|
|
|
|
Then add:
|
|
|
|
- JavaScript
|
|
- TypeScript
|
|
- Python
|
|
- JSON
|
|
- TOML
|
|
- Markdown
|
|
|
|
Integrate highlighting into editor rendering.
|
|
|
|
### Step 10: Add quality-of-life features
|
|
|
|
Add:
|
|
|
|
- Go to line popup
|
|
- Search in current file
|
|
- Unsaved quit prompt
|
|
- Save-as prompt
|
|
- Status messages
|
|
- Error display
|
|
- Config loading
|
|
- Tab width setting
|
|
- Insert spaces setting
|
|
|
|
## 20. Minimum Definition of Done
|
|
|
|
The first complete version should support:
|
|
|
|
```bash
|
|
lime file.rs
|
|
```
|
|
|
|
and include:
|
|
|
|
- Clean terminal UI
|
|
- Standard text editing
|
|
- Natural cursor movement
|
|
- Save with `Ctrl-S`
|
|
- Quit with `Ctrl-Q`
|
|
- Unsaved changes warning
|
|
- Syntax highlighting for known languages
|
|
- File picker with `Ctrl-F`
|
|
- Recursive current-directory browsing
|
|
- Fuzzy file filtering
|
|
- Moderately large file handling without freezing
|
|
- Warning before opening very large files
|
|
- Passing core editing tests
|
|
- macOS and Linux support
|
|
|
|
## 21. Explicit Non-Goals For First Version
|
|
|
|
Do not implement these yet:
|
|
|
|
- Plugin system
|
|
- Extension marketplace
|
|
- LSP
|
|
- Multi-pane editor
|
|
- Integrated terminal
|
|
- Git UI
|
|
- Debugger
|
|
- AI features
|
|
- Remote editing
|
|
- Modal Vim emulation
|
|
|
|
These can be added later only after the core editor feels excellent.
|