<div align="center">

### `EdTUI`

[![Crate Badge]](https://crates.io/crates/edtui) [![Continuous Integration](https://github.com/preiter93/edtui/actions/workflows/ci.yml/badge.svg)](https://github.com/preiter93/edtui/actions/workflows/ci.yml) [![Deps Status](https://deps.rs/repo/github/preiter93/edtui/status.svg)](https://deps.rs/repo/github/preiter93/edtui) [![License Badge]](./LICENSE)

</div>

### Overview
`EdTUI` is a text editor widget for the [Ratatui](https://github.com/ratatui-org/ratatui) ecosystem.
It is designed to provide a user experience inspired by Vim. Edtui is developed to be used as an
editor in ratatui apps. It is not supposed to be a stand-alone code editor.

Create a new `EditorState` and render it using `EditorView`.
You can customize the theme, enable line wrapping, syntax highlight the text or set the tab
width:
```rust
use edtui::{EditorState, EditorTheme, EditorView};
use ratatui::widgets::Widget;

let mut state = EditorState::default();
EditorView::new(&mut state)
        .theme(EditorTheme::default())
        .wrap(true)
        .syntax_highlighter(None)
        .tab_width(2)
        .render(area, buf);
```

Handle events (Vim mode by default):
```rust
use edtui::EditorEventHandler;

let mut event_handler = EditorEventHandler::default();
event_handler.on_key_event(key_event, &mut state);
```

Or use Emacs mode (modeless editing):
```rust
use edtui::{EditorState, EditorEventHandler, Lines};

let mut state = EditorState::new(Lines::from("Hello World"));
let mut event_handler = EditorEventHandler::emacs_mode();
event_handler.on_key_event(key_event, &mut state);
```

Or customize keybindings:
```rust
let mut key_handler = KeyEventHandler::vim_mode();
key_handler.insert(
    KeyEventRegister::n(vec![KeyInput::ctrl('x')]),
    SwitchMode(EditorMode::Insert),
);
let event_handler = EditorEventHandler::new(key_handler);
```

### Demo

![](resources/app.gif)

### Features
- Custom theming.
- Mouse events.
- Vim and Emacs keybindings.
- Copy paste using the systems clipboard.
- Line wrapping.
- Syntax highlighting.
- Line numbers (absolute and relative).
- System editor support (optional, via `system-editor` feature).

### Theming

Customize the editor `EditorTheme`:

```rust
use edtui::{EditorTheme, EditorStatusLine};
use ratatui::style::{Style, Color};
use ratatui::widgets::Block;

let theme = EditorTheme::default()
    .block(Block::default())
    .base(Style::default().bg(Color::Black).fg(Color::White))
    .cursor_style(Style::default().bg(Color::White).fg(Color::Black))
    .selection_style(Style::default().bg(Color::Yellow).fg(Color::Black))
    .hide_status_line(); // or use `.status_line(..)` for styling the status line
```

### Line Numbers

Display absolute or relative line numbers:

```rust
use edtui::{EditorView, EditorState, EditorTheme, LineNumbers};
use ratatui::style::{Style, Color};

EditorView::new(&mut EditorState::default())
        .theme(EditorTheme::default().line_numbers_style(Style::default().fg(Color::DarkGray)))
        .line_numbers(LineNumbers::Absolute)  // or LineNumbers::Relative
        .render(area, buf);
```

![](resources/line_numbers.png)

### Single-Line Mode

For search boxes and single-line input fields, enable single-line mode to block newline insertion:

```rust
use edtui::{EditorState, EditorView};

let mut state = EditorState::default();
EditorView::new(&mut state)
        .single_line(true)
        .render(area, buf);
```

When enabled, newline insertion is blocked and pasting text with newlines will replace them with spaces.

### Mouse Events

`Edtui` supports mouse input for moving the cursor and selecting text.
Mouse handling is **enabled by default** via a feature toggle.
Typically, mouse events are processed automatically when you call `on_event`:
```rust
let event_handler = EditorEventHandler::default();
event_handler.on_event(event, &mut state); // handles mouse events too
```
If you want finer control you can handle mouse events explicitly using `on_mouse_event`:
```rust
event_handler.on_mouse_event(mouse_event, &mut state);
```

### Syntax highlighting

Syntax highlighting was added in version `0.8.4`.

`Edtui` offers a number of custom themes, see [`SyntaxHighlighter::theme`] for a complete list.
If you want to use a custom theme, see [`SyntaxHighlighter::custom_theme`]. Check [syntect](https://github.com/trishume/syntect)
for more details about themes and extensions.

```rust
use edtui::{EditorView, EditorState, SyntaxHighlighter};

let syntax_highlighter = SyntaxHighlighter::new("dracula", "rs");
EditorView::new(&mut EditorState::default())
        .syntax_highlighter(Some(syntax_highlighter))
        .render(area, buf);
```

![](resources/syntax_highlighting.gif)

### Paste Support

If you want to enable paste (via ctrl+y or cmd+y) you must explicitly enable it at the start of your app:

```rust
use ratatui::crossterm::event::EnableBracketedPaste;
let mut stdout = std::io::stdout();
ratatui::crossterm::xecute!(stdout, EnableBracketedPaste);
```

and disable it during cleanup:

```rust
use ratatui::crossterm::event::DisableBracketedPaste;
ratatui::crossterm::execute!(std::io::stdout(), DisableBracketedPaste);
```

See `examples/app/term.rs` for a an example.

### System Editor

With the `system-editor` feature enabled you can open the editor content in an external
text editor (e.g., nvim) using `Ctrl+e` in normal mode (or `Alt+e` in Emacs mode).

The system editor is decoupled from the event handler. After handling events, check
if a system editor request is pending and call `open` yourself:

```rust
use edtui::system_editor;

event_handler.on_event(event, &mut state);

if system_editor::is_pending(&state) {
    system_editor::open(&mut state, &mut terminal)?;
    // Re-enable mouse capture or other terminal modes if necessary
    // crossterm::execute!(stdout(), EnableMouseCapture, EnableBracketedPaste)?;
}
```

The editor used is determined by the `VISUAL` or `EDITOR` environment variables,
falling back to a platform-specific default if neither is set.

### Keybindings
`EdTUI` offers Vim keybindings by default and Emacs keybindings as an alternative.

#### Vim Mode (default)

##### Normal Mode:

| Keybinding                | Description                                              |
|---------------------------|----------------------------------------------------------|
| `i`                       | Enter Insert mode                                        |
| `v`                       | Enter Visual mode                                        |
| `h`, `j`, `k`, `l`        | Navigate left, down, up, and right                       |
| `w`                       | Move forward to the start of a word                      |
| `e`                       | Move forward to the end of a word                        |
| `b`                       | Move backward to the start of a word                     |
| `ctrl+d`                  | Jump a half page down                                    |
| `ctrl+u`                  | Jump a half page up                                      |
| `PageDown`                | Jump a full page down                                    |
| `PageUp`                  | Jump a full page up                                      |
| `x`                       | Delete the character under the cursor                    |
| `u`, `ctrl+r`             | Undo/Redo last action                                    |
| `Esc`                     | Escape Visual mode                                       |
| `0`                       | Move cursor to start of line                             |
| `_`                       | Move cursor to first non-blank character                 |
| `$`                       | Move cursor to end of line                               |
| `gg`                      | Move cursor to the first row                             |
| `G `                      | Move cursor to the last row                              |
| `%`                       | Move cursor to closing/opening bracket                   |
| `a`                       | Append after the cursor                                  |
| `A`                       | Append at the end of the line                            |
| `o`                       | Add a new line below and enter Insert mode               |
| `O`                       | Add a new line above and enter Insert mode               |
| `J`                       | Join current line with the line below                    |
| `d`                       | Delete the selection (Visual mode)                       |
| `dd`                      | Delete the current line                                  |
| `D`                       | Delete to the end of the line                            |
| `viw`                     | Select between word.                                     |
| `ciw`                     | Change between word.                                     |
| `vi` + `", ', (, [ or {`  | Select between delimiter `", ', (, [ or {`               |
| `ci` + `", ', (, [ or {`  | Change between delimiter `", ', (, [ or {`               |
| `u`                       | Undo the last change                                     |
| `r`                       | Redo the last undone action                              |
| `y`                       | Copy the selected text in visual mode                    |
| `yy`                      | Copy the current line in normal mode                     |
| `p`                       | Paste the copied text                                    |
| `Home`                    | Move cursor to start of line                             |
| `End`                     | Move cursor to end of line                               |
| `ctrl+e`                  | Open in system editor (requires `system-editor` feature) |

##### Insert Mode:

| Keybinding  | Description                             |
|-------------|-----------------------------------------|
| `Esc`       | Return to Normal mode                   |
| `Backspace` | Delete the previous character           |
| `Delete`    | Delete the character after the cursor   |
| `Enter`     | Insert line break                       |
| `Arrows`    | Navigation                              |
| `Home`      | Move cursor to start of line            |
| `End`       | Move cursor to end of line              |
| `Ctrl+Left` | Move backward to the start of a word    |
| `Ctrl+Right`| Move forward to the start of a word     |
| `PageDown`  | Jump a full page down                   |
| `PageUp`    | Jump a full page up                     |
| `ctrl+u`    | Delete until first character            |

#### Emacs Mode

Emacs Mode was added in version 0.10.1.

Note that Emacs Mode is less feature complete and less tested than vim mode.

| Keybinding      | Description                                              |
|-----------------|----------------------------------------------------------|
| `Ctrl+f`        | Move forward                                             |
| `Ctrl+b`        | Move backward                                            |
| `Ctrl+n`        | Move to next line                                        |
| `Ctrl+p`        | Move to previous line                                    |
| `Ctrl+a`        | Move to start of line                                    |
| `Ctrl+e`        | Move to end of line                                      |
| `Ctrl+v`        | Half page down                                           |
| `Alt+v`         | Half page up                                             |
| `PageDown`      | Full page down                                           |
| `PageUp`        | Full page up                                             |
| `Alt+f`         | Forward word                                             |
| `Alt+b`         | Backward word                                            |
| `Alt+<`         | Beginning of buffer                                      |
| `Alt+>`         | End of buffer                                            |
| `Ctrl+d`        | Delete character forward                                 |
| `Ctrl+h`        | Delete character backward                                |
| `Alt+d`         | Delete word forward                                      |
| `Alt+Backspace` | Delete word backward                                     |
| `Ctrl+k`        | Delete to end of line                                    |
| `Alt+u`         | Delete to start of line                                  |
| `Ctrl+o`        | Open line (insert newline, stay)                         |
| `Ctrl+j`        | Newline                                                  |
| `Ctrl+y`        | Paste                                                    |
| `Ctrl+u`        | Undo                                                     |
| `Ctrl+r`        | Redo                                                     |
| `Ctrl+g`        | Cancel search                                            |
| `Enter`         | Insert line break                                        |
| `Backspace`     | Delete previous character                                |
| `Arrows`        | Navigation                                               |
| `Home`          | Move to start of line                                    |
| `End`           | Move to end of line                                      |
| `Ctrl+Left`     | Move backward to the start of a word                     |
| `Ctrl+Right`    | Move forward to the start of a word                      |
| `Alt+e`         | Open in system editor (requires `system-editor` feature) |
| `Ctrl+s`        | Start search                                             |
| `Ctrl+s`        | Search mode: Go to next match                            |
| `Ctrl+r`        | Search mode: Go to previous match                        |
| `Enter`         | Search mode: Select current match                        |

#### Roadmap
- [ ] Support termwiz and termion

[Crate Badge]: https://img.shields.io/crates/v/edtui?logo=rust&style=flat-square&logoColor=E05D44&color=E05D44
[License Badge]: https://img.shields.io/crates/l/edtui?style=flat-square&color=1370D3

License: MIT
