Crate modalkit

source ·
Expand description

modalkit

Overview

This crate allows building terminal applications that support modal input, such as the Vim text editor.

Example

The following example shows a program that opens a single textbox where the user can enter text using Vim keybindings.

For a more complete example that includes a command bar and window splitting, see examples/editor.rs in the source repository.

use modalkit::{
    editing::{context::Resolve, key::KeyManager, store::Store},
    editing::action::{Action, Editable, Jumpable, Scrollable, UIResult},
    editing::application::EmptyInfo,
    env::vim::keybindings::VimMachine,
    input::{bindings::BindingMachine, key::TerminalKey},
    widgets::textbox::{TextBoxState, TextBox},
    widgets::TerminalExtOps,
};

use modalkit::crossterm::event::{read, Event};
use modalkit::crossterm::terminal::EnterAlternateScreen;
use modalkit::tui::{backend::CrosstermBackend, Terminal};
use std::io::stdout;

fn main() -> UIResult<(), EmptyInfo> {
    let mut stdout = stdout();

    crossterm::terminal::enable_raw_mode()?;
    crossterm::execute!(stdout, EnterAlternateScreen)?;

    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;
    let mut store = Store::default();
    let mut bindings = KeyManager::new(VimMachine::<TerminalKey>::default());
    let mut tbox = TextBoxState::new(store.load_buffer(String::from("*scratch*")));

    terminal.clear()?;

    loop {
        terminal.draw(|f| f.render_stateful_widget(TextBox::new(), f.size(), &mut tbox))?;

        if let Event::Key(key) = read()? {
            bindings.input_key(key.into());
        } else {
            continue;
        };

        while let Some((act, ctx)) = bindings.pop() {
            let store = &mut store;

            let _ = match act {
                Action::Editor(act) => tbox.editor_command(&act, &ctx, store)?,
                Action::Macro(act) => bindings.macro_command(&act, &ctx, store)?,
                Action::Scroll(style) => tbox.scroll(&style, &ctx, store)?,
                Action::Repeat(rt) => {
                    bindings.repeat(rt, Some(ctx));
                    None
                },
                Action::Jump(l, dir, count) => {
                    let _ = tbox.jump(l, dir, ctx.resolve(&count), &ctx)?;
                    None
                },
                Action::Suspend => terminal.program_suspend()?,
                Action::NoOp => None,
                _ => continue,
            };
        }
    }
}

Re-exports

pub use crossterm;
pub use tui;

Modules

Editing support
Editing environments
Input processing
Readline-style editor
Terminal UI widgets