Crate reedline

Source
Expand description

§reedline \|/

§A readline replacement written in Rust

Reedline is a project to create a line editor (like bash’s readline or zsh’s zle) that supports many of the modern conveniences of CLIs, including syntax highlighting, completions, multiline support, Unicode support, and more. It is currently primarily developed as the interactive editor for nushell (starting with v0.60) striving to provide a pleasant interactive experience.

§Basic example

// Create a default reedline object to handle user input

use reedline::{DefaultPrompt, Reedline, Signal};

let mut line_editor = Reedline::create();
let prompt = DefaultPrompt::default();

loop {
    let sig = line_editor.read_line(&prompt);
    match sig {
        Ok(Signal::Success(buffer)) => {
            println!("We processed: {}", buffer);
        }
        Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => {
            println!("\nAborted!");
            break;
        }
        x => {
            println!("Event: {:?}", x);
        }
    }
}

§Integrate with custom keybindings

// Configure reedline with custom keybindings

//Cargo.toml
//    [dependencies]
//    crossterm = "*"

use {
  crossterm::event::{KeyCode, KeyModifiers},
  reedline::{default_emacs_keybindings, EditCommand, Reedline, Emacs, ReedlineEvent},
};

let mut keybindings = default_emacs_keybindings();
keybindings.add_binding(
    KeyModifiers::ALT,
    KeyCode::Char('m'),
    ReedlineEvent::Edit(vec![EditCommand::BackspaceWord]),
);
let edit_mode = Box::new(Emacs::new(keybindings));

let mut line_editor = Reedline::create().with_edit_mode(edit_mode);

§Integrate with History

// Create a reedline object with history support, including history size limits

use reedline::{FileBackedHistory, Reedline};

let history = Box::new(
    FileBackedHistory::with_file(5, "history.txt".into())
        .expect("Error configuring history with file"),
);
let mut line_editor = Reedline::create()
    .with_history(history);

§Integrate with custom syntax Highlighter

// Create a reedline object with highlighter support

use reedline::{ExampleHighlighter, Reedline};

let commands = vec![
  "test".into(),
  "hello world".into(),
  "hello world reedline".into(),
  "this is the reedline crate".into(),
];
let mut line_editor =
Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands)));

§Integrate with custom tab completion

// Create a reedline object with tab completions support

use reedline::{default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, Reedline, ReedlineEvent, ReedlineMenu, MenuBuilder};

let commands = vec![
  "test".into(),
  "hello world".into(),
  "hello world reedline".into(),
  "this is the reedline crate".into(),
];
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
// Use the interactive menu to select options from the completer
let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
// Set up the required keybindings
let mut keybindings = default_emacs_keybindings();
keybindings.add_binding(
    KeyModifiers::NONE,
    KeyCode::Tab,
    ReedlineEvent::UntilFound(vec![
        ReedlineEvent::Menu("completion_menu".to_string()),
        ReedlineEvent::MenuNext,
    ]),
);

let edit_mode = Box::new(Emacs::new(keybindings));

let mut line_editor = Reedline::create()
    .with_completer(completer)
    .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
    .with_edit_mode(edit_mode);

§Integrate with Hinter for fish-style history autosuggestions

// Create a reedline object with in-line hint support

//Cargo.toml
//    [dependencies]
//    nu-ansi-term = "*"

use {
  nu_ansi_term::{Color, Style},
  reedline::{DefaultHinter, Reedline},
};


let mut line_editor = Reedline::create().with_hinter(Box::new(
  DefaultHinter::default()
  .with_style(Style::new().italic().fg(Color::LightGray)),
));

§Integrate with custom line completion Validator

// Create a reedline object with line completion validation support

use reedline::{DefaultValidator, Reedline};

let validator = Box::new(DefaultValidator);

let mut line_editor = Reedline::create().with_validator(validator);

§Use custom EditMode

// Create a reedline object with custom edit mode
// This can define a keybinding setting or enable vi-emulation
use reedline::{
    default_vi_insert_keybindings, default_vi_normal_keybindings, EditMode, Reedline, Vi,
};

let mut line_editor = Reedline::create().with_edit_mode(Box::new(Vi::new(
    default_vi_insert_keybindings(),
    default_vi_normal_keybindings(),
)));

§Crate features

  • clipboard: Enable support to use the SystemClipboard. Enabling this feature will return a SystemClipboard instead of a local clipboard when calling get_default_clipboard().
  • bashisms: Enable support for special text sequences that recall components from the history. e.g. !! and !$. For use in shells like bash or nushell.
  • sqlite: Provides the SqliteBackedHistory to store richer information in the history. Statically links the required sqlite version.
  • sqlite-dynlib: Alternative to the feature sqlite. Will not statically link. Requires sqlite >= 3.38 to link dynamically!
  • external_printer: Experimental: Thread-safe ExternalPrinter handle to print lines from concurrently running threads.

§Are we prompt yet? (Development status)

Reedline has now all the basic features to become the primary line editor for nushell

  • General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh).
  • Configurable keybindings (emacs-style bindings and basic vi-style).
  • Configurable prompt
  • Content-aware syntax highlighting.
  • Autocompletion (With graphical selection menu or simple cycling inline).
  • History with interactive search options (optionally persists to file, can support multilple sessions accessing the same file)
  • Fish-style history autosuggestion hints
  • Undo support.
  • Clipboard integration
  • Line completeness validation for seamless entry of multiline command sequences.

§Areas for future improvements

  • Support for Unicode beyond simple left-to-right scripts
  • Easier keybinding configuration
  • Support for more advanced vi commands
  • Visual selection
  • Smooth experience if completion or prompt content takes long to compute
  • Support for a concurrent output stream from background tasks to be displayed, while the input prompt is active. (“Full duplex” mode)

For more ideas check out the feature discussion or hop on the #reedline channel of the nushell discord.

§Development history

If you want to follow along with the history how reedline got started, you can watch the recordings of JT’s live-coding streams.

Playlist: Creating a line editor in Rust

§Alternatives

For currently more mature Rust line editing check out:

Modules§

menu_functions
Collection of common functions that can be used to create menus

Structs§

ColumnarMenu
Menu to present suggestions in a columnar fashion It presents a description of the suggestion if available
CursorConfig
Maps cursor shapes to each edit mode (emacs, vi normal & vi insert). If any of the fields is None, the cursor won’t get changed by Reedline for that mode.
CwdAwareHinter
A hinter that uses the completions or the history to show a hint to the user
DefaultCompleter
A default completer that can detect keywords
DefaultHinter
A hinter that uses the completions or the history to show a hint to the user
DefaultPrompt
Simple Prompt displaying a configurable left and a right prompt. For more fine-tuned configuration, implement the Prompt trait. For the default configuration, use DefaultPrompt::default()
DefaultValidator
A default validator which checks for mismatched quotes and brackets
DescriptionMenu
Completion menu definition
Editor
Stateful editor executing changes to the underlying LineBuffer
Emacs
This parses the incoming Events like a emacs style-editor
ExampleHighlighter
A simple, example highlighter that shows how to highlight keywords
ExternalPrinter
An ExternalPrinter allows to print messages of text while editing a line. The message is printed as a new line, the line-edit will continue below the output.
FileBackedHistory
Stateful history that allows up/down-arrow browsing with an internal cursor.
HistoryItem
Represents one run command with some optional additional context
HistoryItemId
Unique ID for the HistoryItem. More recent items have higher ids than older ones.
HistorySessionId
Unique ID for the session in which reedline was run to disambiguate different sessions
IdeMenu
Menu to present suggestions like similar to Ide completion menus
KeyModifiers
Represents key modifiers (shift, control, alt, etc.).
Keybindings
Main definition of editor keybindings
LineBuffer
In memory representation of the entered line(s) including a cursor position to facilitate cursor based editing.
ListMenu
Struct to store the menu style Context menu definition
MenuTextStyle
Struct to store the menu style
Painter
Implementation of the output to the terminal
PromptHistorySearch
A representation of the history search
Reedline
Line editor engine
ReedlineError
separate struct to not expose anything to the public (for now)
ReedlineRawEvent
A wrapper for crossterm::event::Event.
SearchFilter
Defines additional filters for querying the History
SearchQuery
Query for search in the potentially rich History
SimpleMatchHighlighter
Highlight all matches for a given search string in a line
Span
A span of source code, with positions in bytes
SqliteBackedHistory
A history that stores the values to an SQLite database. In addition to storing the command, the history can store an additional arbitrary HistoryEntryContext, to add information such as a timestamp, running directory, result…
StyledText
A representation of a buffer with styling, used for doing syntax highlighting
Suggestion
Suggestion returned by the Completer
Vi
This parses incoming input Events like a Vi-Style editor

Enums§

Color
Represents a color.
CommandLineSearch
Ways to search for a particular command line in the History
DefaultPromptSegment
A struct to control the appearance of the left or right prompt in a DefaultPrompt
DescriptionMode
The direction of the description box
EditCommand
Editing actions which can be mapped to key bindings.
HistoryNavigationQuery
Browsing modes for a History
KeyCode
Represents a key.
MenuEvent
Defines all possible events that could happen with a menu.
PromptEditMode
Modes that the prompt can be in
PromptHistorySearchStatus
The current success/failure of the history search
PromptViMode
The vi-specific modes that the prompt can be in
ReedlineErrorVariants
non-public (for now)
ReedlineEvent
Reedline supported actions.
ReedlineMenu
Allowed menus in Reedline
SearchDirection
Defines how to traverse the history when executing a SearchQuery
Signal
Valid ways how Reedline::read_line() can return
UndoBehavior
Every line change should come with an UndoBehavior tag, which can be used to calculate how the change should be reflected on the undo stack
ValidationResult
Whether or not the validation shows the input was complete

Constants§

HISTORY_SIZE
Default size of the FileBackedHistory used when calling FileBackedHistory::default()

Traits§

Completer
A trait that defines how to convert some text and a position to a list of potential completions in that position. The text could be a part of the whole line, and the position is the index of the end of the text in the original line.
EditMode
Define the style of parsing for the edit events Available default options:
Highlighter
The syntax highlighting trait. Implementers of this trait will take in the current string and then return a StyledText object, which represents the contents of the original line as styled strings
Hinter
A trait that’s responsible for returning the hint for the current line and position Hints are often shown in-line as part of the buffer, showing the user text they can accept or ignore
History
Represents a history file or database Data could be stored e.g. in a plain text file, in a JSONL file, in a SQLite database
Menu
Trait that defines how a menu will be printed by the painter
MenuBuilder
Common builder for all menus
Prompt
API to provide a custom prompt.
Validator
The syntax validation trait. Implementers of this trait will check to see if the current input is incomplete and spans multiple lines

Functions§

default_emacs_keybindings
Returns the current default emacs keybindings
default_vi_insert_keybindings
Default Vi insert keybindings
default_vi_normal_keybindings
Default Vi normal keybindings
get_reedline_default_keybindings
Get the default keybindings and return a Vec<(String, String, String, String)> where String 1 is mode, String 2 is key_modifiers, String 3 is key_code, and Sting 4 is event
get_reedline_edit_commands
Return a Vec<String> of the Reedline EditCommands
get_reedline_keybinding_modifiers
Return a Vec of the Reedline Keybinding Modifiers
get_reedline_keycodes
Return a Vec<String> of the Reedline KeyCodes
get_reedline_prompt_edit_modes
Return a Vec<String> of the Reedline PromptEditModes
get_reedline_reedline_events
Return a Vec<String> of the Reedline ReedlineEvents
kitty_protocol_available
Return if the terminal supports the kitty keyboard enhancement protocol

Type Aliases§

Result
Standard std::result::Result, with ReedlineError as the error variant