# cozy

**English** | [日本語](README.ja.md)

**Type like nano, navigate like vim.** A keyboard-driven terminal text editor written in Rust, built on a Redux-style architecture.

## 📋 Overview

**cozy** is a lightweight, keyboard-driven text editor built with:
- **ratatui** (0.29) - Terminal UI framework
- **crossterm** (0.27) - Cross-platform terminal manipulation
- **serde** - Serialization framework for configuration
- **regex** - Pattern matching for search/replace

## 🏗️ Architecture

### Redux-Style Architecture

The editor follows a Redux-inspired architecture with clear separation of concerns:

```
┌─────────────┐
│   Event     │
│   Loop      │
└──────┬──────┘
       │
       ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Keymap    │────▶│   Action    │────▶│  Reducer    │
│  (Input)    │     │  (Intent)   │     │  (Logic)    │
└─────────────┘     └─────────────┘     └──────┬──────┘
                                                │
                                                ▼
                                         ┌─────────────┐
                                         │   State     │
                                         │  (Data)     │
                                         └──────┬──────┘
                                                │
                                                ▼
                                         ┌─────────────┐
                                         │     UI      │
                                         │ (Renderer)  │
                                         └─────────────┘
```

### Module Structure

```
src/
├── main.rs              # Entry point, terminal setup
├── event_loop.rs        # Main event loop
├── glide.rs             # Glide-mode motion engine (Motion / Operator / resolve)
├── shortcuts.rs         # Key binding definitions (source of truth)
├── action/
│   └── action.rs        # Action enum (MoveUp, InsertChar, GlideMove, …)
├── state/               # State (single source of truth)
│   ├── editor.rs        # EditorState
│   ├── buffer.rs        # TextBuffer (line-based storage)
│   ├── cursor.rs        # Cursor position + motions
│   └── key.rs           # Abstract KeyCode/KeyModifiers (decoupled from crossterm)
├── reducer/             # State reducers (business logic)
│   ├── mod.rs           # Main reducer dispatcher (reduce)
│   ├── editor.rs        # Mode transitions, Glide actions
│   ├── operator.rs      # Operator + motion execution (d/c/y)
│   ├── insert.rs / delete.rs / cursor.rs
│   ├── search.rs / replace.rs
│   ├── clipboard.rs     # Unnamed register + system clipboard
│   └── file.rs / status.rs / helper.rs
├── ui/                  # Rendering
│   ├── render/          # screen.rs / body.rs / footer.rs
│   ├── keymap.rs        # Key → Action mapping
│   └── help.rs
└── utils/               # terminal / file / unicode / wrap / syntax / loop_guard
```

## 🎯 Features

### Core Features
- ✅ **Text Editing**: Insert, delete, backspace, enter
- ✅ **Navigation**: Arrow keys, PageUp/Down, Home/End
- ✅ **File Operations**: Open, save, save-as
- ✅ **Search**: Case-sensitive, regex, word-boundary search
- ✅ **Replace**: Find and replace, with `$1` capture substitution in regex mode
- ✅ **Undo/Redo**: History management (`Ctrl+Z` / `Ctrl+Y`)
- ✅ **Clipboard Paste**: Paste from the system clipboard (`Ctrl+V`, cross-platform via arboard)
- ✅ **Glide Mode**: Vim-like modal navigation and editing (`Ctrl+G`)
- ✅ **Syntax Highlighting**: Syntax highlighting for Rust, Python, JavaScript, JSON, TOML
- ✅ **Line Numbers**: Optional line number display
- ✅ **Status Bar**: Status messages and mode indicators
- ✅ **Configuration**: TOML-based configuration file
- ✅ **Loop Protection**: LoopGuard integration to prevent infinite loops

### Editor Modes
- **Edit**: Default editing mode — just type, like nano
- **Glide**: Vim-like modal navigation/editing (`Ctrl+G`)
- **Search**: Incremental search (`Ctrl+F`)
- **Replace**: Find and replace (`Ctrl+R`)
- **Goto**: Jump to a line number (`Ctrl+J`)
- **Save**: Save dialog (`Ctrl+S`)
- **Open**: Open file dialog (`Ctrl+O`)
- **Help**: Help screen (`Ctrl+H`)

## 🚀 Usage

### Building

```bash
cargo build --release
```

### Running

```bash
# Open new file
cargo run

# Open existing file
cargo run <filename>
```

### Configuration

Configuration is loaded from (in order of priority):
1. `./config.toml` (current directory)
2. `~/.config/cozy/config.toml`
3. `~/.cozy/config.toml`

Default configuration is used if none found.

Example `config.toml`:
```toml
page_size = 20
theme = "dark"
show_line_numbers = true
status_duration = 3
line_number_bg = "darkgray"
line_number_fg = "white"
cursor_blink = true
```

## ⌨️ Key Bindings

> The source of truth for key bindings is `src/shortcuts.rs` (`get_shortcuts()`); the defaults are shown here.
> Per-action overrides are possible via the `[keys]` section of `config.toml`.

### File
- `Ctrl+S`: Save (Save dialog)
- `Ctrl+Shift+S`: Save As
- `Ctrl+O`: Open file
- `Ctrl+X`: Exit (prompts to save if modified)
- `Ctrl+Q`: Quit immediately without saving

### Navigation
- `↑` `↓` `←` `→`: Move cursor
- `Ctrl+A`: Line start / `Ctrl+E`: Line end
- `PageUp` / `PageDown`: Scroll page
- `Ctrl+J`: Jump to line number (Goto mode)
- `Ctrl+G`: Enter Glide (modal) mode

### Editing
- `Enter`: Insert newline
- `Backspace` / `Delete`: Delete before / at cursor
- `Ctrl+K`: Delete (cut) current line
- `Ctrl+V`: Paste from system clipboard
- `Ctrl+Z`: Undo / `Ctrl+Y`: Redo

### Search & Replace
- `Ctrl+F`: Enter search mode
- `Ctrl+N`: Find next / `Ctrl+P`: Find previous
- `Ctrl+T`: Toggle search options (regex / case / word)
- `Ctrl+R`: Enter replace mode; press again to Replace All
- `Tab` (in replace): Switch between query/replace fields
- `Enter` (in replace): Replace current match

### View / Other
- `Ctrl+H` / `F1`: Show help (`F1` is an unambiguous fallback)
- `Ctrl+L`: Toggle line numbers
- `Ctrl+W`: Toggle line wrap
- `Esc` / `Ctrl+[`: Cancel current operation / Exit mode

### Glide Mode (`Ctrl+G`)
Vim-like modal editing. A leading number repeats a motion (count).
- Move: `h` `j` `k` `l`, `w`/`b`/`e` word (stops at punctuation), `W`/`B`/`E` WORD (whitespace-delimited), `0`/`^`/`$` line start / first non-blank / end, `>`/`<` jump onto next/previous char and `t`/`T` jump just before/after a char (till), `.`/`,` repeat the last such jump forward/back, `gg`/`G` file top/bottom, `H`/`M`/`L` screen top/middle/bottom
- Operators: `d`/`c`/`y` (delete/change/yank) + a motion — `dw`, `de`, `d$`, `dj`, `cw`, `yw`, `d3w`; to-char operands `d>)`/`d<(` (onto the char) and `dt,`/`dT_` (up to, before the char)
- Linewise: `dd`/`cc`/`yy` whole line (`3dd` = 3 lines); `D`/`C`/`Y` = `d$`/`c$`/`yy`
- Edit: `x` delete char, `X` backspace, `~` toggle case, `J` join
- Paste: `p`/`P` paste the last cut below/above (line) or after/before (inline)
- Insert: `i`/`I` insert, `a`/`A` append, `o`/`O` open line
- `f` search, `r` replace, `Esc` back to Edit

## 📊 Code Statistics

- **Total Files**: 41 Rust files
- **Total Lines**: ~5,200 lines of code
- **Dependencies**: 8 crates (ratatui, crossterm, serde, toml, dirs, regex, unicode-width, arboard)

## 🔧 Technical Details

### State Management

The editor uses a centralized `EditorState` that contains:
- `buffer: TextBuffer` - Line-based text storage
- `cursor: Cursor` - Cursor position (row, col)
- `mode: EditorMode` - Current editor mode
- `filename: Option<PathBuf>` - Current file path
- `modified: bool` - Unsaved changes flag
- `status_message: Option<String>` - Status bar message
- `config: Config` - Configuration settings

### Text Buffer

Text is stored as a `Vec<String>` (line-based), which provides:
- Efficient line operations
- Easy scrolling and rendering
- Simple undo/redo implementation

### Event Handling

The event loop (`event_loop.rs`) handles:
- Keyboard input → `Keymap::map_key_to_action()`
- Paste events → `Action::InsertString`
- Terminal resize events
- Cursor blink animation (500ms timeout)

### Rendering

Rendering is done with ratatui:
- **Body**: Text content with line numbers
- **Footer**: Status bar with mode and messages
- Layout is recalculated on each render

## 🛡️ Loop Protection

The editor includes **LoopGuard** integration to prevent infinite loops in the event loop:

- **Maximum Iterations**: 10,000,000 (Relaxed level)
- **Timeout**: 30 seconds
- **Soft Warning**: At 90% (9,000,000 iterations)
- **Statistics**: Automatic logging on exit

See `docs/integration/LOOP_GUARD_INTEGRATION.md` for details.

## 🐛 Known Issues / TODO

- [ ] Unicode handling improvements
- [ ] Multi-file editing support
- [ ] Plugin system
- [ ] Better error messages

## 📝 Development Notes

### Architecture Decisions

1. **Redux-Style**: Clear separation between actions, reducers, and state makes the codebase maintainable
2. **Line-Based Buffer**: Simpler than gap buffers for this use case
3. **Mode-Based UI**: The same input is handled differently per mode (e.g. search vs. text entry)
4. **Configuration**: TOML-based config with multiple search paths

### Testing

Unit tests are available in `src/reducer/editor_test.rs`:
```bash
cargo test
```

## 📚 Documentation

Further documentation lives in the `docs/` directory of the repository:

- **Architecture**: `docs/architecture/ARCHITECTURE.md`
- **LoopGuard integration**: `docs/integration/LOOP_GUARD_INTEGRATION.md`
- **Bug reports**: `docs/development/BUGS.md`

See `docs/README.md` for the index.

## 📄 License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.

## 🤝 Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
