tree: 68ac3d82a2b15659c19b039e22fe611bfed9308d
  1. src/
  2. BUILD.gn
  3. OWNERS
  4. README.md
src/lib/line-editor/README.md

line-editor

line-editor is a lightweight line-editing library for Fuchsia. It provides single-line and multi-line terminal line editing, history management, tab completion, inline hints with ANSI colors, and VT100 terminal escape sequence keybindings.

Features

  • Binary String API: Works natively with bstr::BString and bstr::BStr without requiring UTF-8 validation.
  • Generic Stream Support: Operates on any std::io::Read and std::io::Write streams, standard I/O (stdin/stdout), or pipe handles.
  • History Management: In-memory history with deduplication and maximum entry bounds. Callers can manually record history entries using editor.add_history(...).
  • Tab Completion: Register custom completion handlers (CompletionHandler or closures) for tab autocompletion.
  • Inline Hints: Register custom hint callbacks (HintHandler) with optional ANSI foreground colors (Color) and bold formatting.
  • Single-Line & Multi-Line Modes: Supports horizontal scrolling single-line mode as well as automatic multi-line wrapping.
  • VT100 & Emacs Keybindings:
    • Ctrl-A / Home: Move to start of line
    • Ctrl-E / End: Move to end of line
    • Ctrl-B / Left Arrow: Move cursor left
    • Ctrl-F / Right Arrow: Move cursor right
    • Ctrl-P / Up Arrow: Navigate back in history
    • Ctrl-N / Down Arrow: Navigate forward in history
    • Ctrl-K: Kill text to end of line
    • Ctrl-U: Clear entire line
    • Ctrl-W: Delete previous word
    • Ctrl-T: Transpose characters
    • Ctrl-L: Clear screen
    • Ctrl-C: Interrupt (ReadlineError::Interrupted)
    • Ctrl-D: EOF on empty line (ReadlineError::Eof) or delete character

Usage

Simple Standalone Readline

use line_editor::readline;

fn main() -> Result<(), line_editor::ReadlineError> {
    let line = readline("prompt> ")?;
    println!("Entered: {}", line);
    Ok(())
}

Advanced Usage with Editor

use bstr::{BStr, BString};
use line_editor::{Color, Config, Editor, Hint, ReadlineError};

fn main() -> Result<(), ReadlineError> {
    let mut editor = Editor::with_config(Config {
        max_history_len: 100,
        multiline_mode: true,
        max_line_len: 4096,
        ..Default::default()
    });

    // Register a completion handler
    editor.set_completion_handler(|line: &BStr| {
        if line.starts_with(b"h") {
            vec![BString::from("hello"), BString::from("help")]
        } else {
            vec![]
        }
    });

    // Register a hint handler
    editor.set_hint_handler(|line: &BStr| {
        if line == b"hello" {
            Some(Hint::new(" world").with_color(Color::Green).with_bold(true))
        } else {
            None
        }
    });

    // Read lines from stdin/stdout in an interactive loop
    while let Ok(line) = editor.readline("app> ") {
        if !line.is_empty() {
            // Record non-empty lines in history
            editor.add_history(&line);
        }
        println!("Read line: {}", line);
    }

    Ok(())
}

Managing History

You can access and modify the editor's history directly via editor.history() and editor.history_mut():

use line_editor::Editor;

let mut editor = Editor::new();
editor.add_history("first command");
editor.add_history("second command");

println!("History count: {}", editor.history().len());
for entry in editor.history().entries() {
    println!("  {}", entry);
}

// Clear all history entries
editor.history_mut().clear();

Reading from Custom Streams

use line_editor::{Editor, OperatingMode};

fn read_from_stream(mut reader: impl std::io::Read, mut writer: impl std::io::Write) {
    let mut editor = Editor::new();
    let line = editor.readline_from(
        &mut reader,
        &mut writer,
        OperatingMode::Interactive,
        "stream> ",
    );
}

Testing

Run unit tests with fx:

fx test line-editor-tests