reedline/
lib.rs

1//! # reedline `\|/`
2//! # A readline replacement written in Rust
3//!
4//! Reedline is a project to create a line editor (like bash's `readline` or
5//! zsh's `zle`) that supports many of the modern conveniences of CLIs,
6//! including syntax highlighting, completions, multiline support, Unicode
7//! support, and more.  It is currently primarily developed as the interactive
8//! editor for [nushell](https://github.com/nushell/nushell) (starting with
9//! `v0.60`) striving to provide a pleasant interactive experience.
10//!
11//! ## Basic example
12//!
13//! ```rust,no_run
14//! // Create a default reedline object to handle user input
15//!
16//! use reedline::{DefaultPrompt, Reedline, Signal};
17//!
18//! let mut line_editor = Reedline::create();
19//! let prompt = DefaultPrompt::default();
20//!
21//! loop {
22//!     let sig = line_editor.read_line(&prompt);
23//!     match sig {
24//!         Ok(Signal::Success(buffer)) => {
25//!             println!("We processed: {}", buffer);
26//!         }
27//!         Ok(Signal::CtrlD) | Ok(Signal::CtrlC) => {
28//!             println!("\nAborted!");
29//!             break;
30//!         }
31//!         x => {
32//!             println!("Event: {:?}", x);
33//!         }
34//!     }
35//! }
36//! ```
37//! ## Integrate with custom keybindings
38//!
39//! ```rust
40//! // Configure reedline with custom keybindings
41//!
42//! //Cargo.toml
43//! //    [dependencies]
44//! //    crossterm = "*"
45//!
46//! use {
47//!   crossterm::event::{KeyCode, KeyModifiers},
48//!   reedline::{default_emacs_keybindings, EditCommand, Reedline, Emacs, ReedlineEvent},
49//! };
50//!
51//! let mut keybindings = default_emacs_keybindings();
52//! keybindings.add_binding(
53//!     KeyModifiers::ALT,
54//!     KeyCode::Char('m'),
55//!     ReedlineEvent::Edit(vec![EditCommand::BackspaceWord]),
56//! );
57//! let edit_mode = Box::new(Emacs::new(keybindings));
58//!
59//! let mut line_editor = Reedline::create().with_edit_mode(edit_mode);
60//! ```
61//!
62//! ## Integrate with [`History`]
63//!
64//! ```rust,no_run
65//! // Create a reedline object with history support, including history size limits
66//!
67//! use reedline::{FileBackedHistory, Reedline};
68//!
69//! let history = Box::new(
70//!     FileBackedHistory::with_file(5, "history.txt".into())
71//!         .expect("Error configuring history with file"),
72//! );
73//! let mut line_editor = Reedline::create()
74//!     .with_history(history);
75//! ```
76//!
77//! ## Integrate with custom syntax [`Highlighter`]
78//!
79//! ```rust
80//! // Create a reedline object with highlighter support
81//!
82//! use reedline::{ExampleHighlighter, Reedline};
83//!
84//! let commands = vec![
85//!   "test".into(),
86//!   "hello world".into(),
87//!   "hello world reedline".into(),
88//!   "this is the reedline crate".into(),
89//! ];
90//! let mut line_editor =
91//! Reedline::create().with_highlighter(Box::new(ExampleHighlighter::new(commands)));
92//! ```
93//!
94//! ## Integrate with custom tab completion
95//!
96//! ```rust
97//! // Create a reedline object with tab completions support
98//!
99//! use reedline::{default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, KeyCode, KeyModifiers, Reedline, ReedlineEvent, ReedlineMenu};
100//!
101//! let commands = vec![
102//!   "test".into(),
103//!   "hello world".into(),
104//!   "hello world reedline".into(),
105//!   "this is the reedline crate".into(),
106//! ];
107//! let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
108//! // Use the interactive menu to select options from the completer
109//! let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
110//! // Set up the required keybindings
111//! let mut keybindings = default_emacs_keybindings();
112//! keybindings.add_binding(
113//!     KeyModifiers::NONE,
114//!     KeyCode::Tab,
115//!     ReedlineEvent::UntilFound(vec![
116//!         ReedlineEvent::Menu("completion_menu".to_string()),
117//!         ReedlineEvent::MenuNext,
118//!     ]),
119//! );
120//!
121//! let edit_mode = Box::new(Emacs::new(keybindings));
122//!
123//! let mut line_editor = Reedline::create()
124//!     .with_completer(completer)
125//!     .with_menu(ReedlineMenu::EngineCompleter(completion_menu))
126//!     .with_edit_mode(edit_mode);
127//! ```
128//!
129//! ## Integrate with [`Hinter`] for fish-style history autosuggestions
130//!
131//! ```rust
132//! // Create a reedline object with in-line hint support
133//!
134//! //Cargo.toml
135//! //    [dependencies]
136//! //    nu-ansi-term = "*"
137//!
138//! use {
139//!   nu_ansi_term::{Color, Style},
140//!   reedline::{DefaultHinter, Reedline},
141//! };
142//!
143//!
144//! let mut line_editor = Reedline::create().with_hinter(Box::new(
145//!   DefaultHinter::default()
146//!   .with_style(Style::new().italic().fg(Color::LightGray)),
147//! ));
148//! ```
149//!
150//!
151//! ## Integrate with custom line completion [`Validator`]
152//!
153//! ```rust
154//! // Create a reedline object with line completion validation support
155//!
156//! use reedline::{DefaultValidator, Reedline};
157//!
158//! let validator = Box::new(DefaultValidator);
159//!
160//! let mut line_editor = Reedline::create().with_validator(validator);
161//! ```
162//!
163//! ## Use custom [`EditMode`]
164//!
165//! ```rust
166//! // Create a reedline object with custom edit mode
167//! // This can define a keybinding setting or enable vi-emulation
168//! use reedline::{
169//!     default_vi_insert_keybindings, default_vi_normal_keybindings, EditMode, Reedline, Vi,
170//! };
171//!
172//! let mut line_editor = Reedline::create().with_edit_mode(Box::new(Vi::new(
173//!     default_vi_insert_keybindings(),
174//!     default_vi_normal_keybindings(),
175//! )));
176//! ```
177//!
178//! ## Crate features
179//!
180//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this feature will return a `SystemClipboard` instead of a local clipboard when calling `get_default_clipboard()`.
181//! - `bashisms`: Enable support for special text sequences that recall components from the history. e.g. `!!` and `!$`. For use in shells like `bash` or [`nushell`](https://nushell.sh).
182//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information in the history. Statically links the required sqlite version.
183//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically link. Requires `sqlite >= 3.38` to link dynamically!
184//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle to print lines from concurrently running threads.
185//!
186//! ## Are we prompt yet? (Development status)
187//!
188//! Nushell has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell
189//! )
190//!
191//! - General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh).
192//! - Configurable keybindings (emacs-style bindings and basic vi-style).
193//! - Configurable prompt
194//! - Content-aware syntax highlighting.
195//! - Autocompletion (With graphical selection menu or simple cycling inline).
196//! - History with interactive search options (optionally persists to file, can support multilple sessions accessing the same file)
197//! - Fish-style history autosuggestion hints
198//! - Undo support.
199//! - Clipboard integration
200//! - Line completeness validation for seamless entry of multiline command sequences.
201//!
202//! ### Areas for future improvements
203//!
204//! - [ ] Support for Unicode beyond simple left-to-right scripts
205//! - [ ] Easier keybinding configuration
206//! - [ ] Support for more advanced vi commands
207//! - [ ] Visual selection
208//! - [ ] Smooth experience if completion or prompt content takes long to compute
209//! - [ ] Support for a concurrent output stream from background tasks to be displayed, while the input prompt is active. ("Full duplex" mode)
210//!
211//! For more ideas check out the [feature discussion](https://github.com/nushell/reedline/issues/63) or hop on the `#reedline` channel of the [nushell discord](https://discordapp.com/invite/NtAbbGn).
212//!
213//! ### Development history
214//!
215//! If you want to follow along with the history how reedline got started, you can watch the [recordings](https://youtube.com/playlist?list=PLP2yfE2-FXdQw0I6O4YdIX_mzBeF5TDdv) of [JT](https://github.com/jntrnr)'s [live-coding streams](https://www.twitch.tv/jntrnr).
216//!
217//! [Playlist: Creating a line editor in Rust](https://youtube.com/playlist?list=PLP2yfE2-FXdQw0I6O4YdIX_mzBeF5TDdv)
218//!
219//! ### Alternatives
220//!
221//! For currently more mature Rust line editing check out:
222//!
223//! - [rustyline](https://crates.io/crates/rustyline)
224//!
225#![warn(rustdoc::missing_crate_level_docs)]
226#![warn(missing_docs)]
227// #![deny(warnings)]
228mod core_editor;
229pub use core_editor::Editor;
230pub use core_editor::LineBuffer;
231
232mod enums;
233pub use enums::{EditCommand, ReedlineEvent, ReedlineRawEvent, Signal, UndoBehavior};
234
235mod painting;
236pub use painting::{Painter, StyledText};
237
238mod engine;
239pub use engine::Reedline;
240
241mod result;
242pub use result::{ReedlineError, Result};
243
244mod history;
245#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))]
246pub use history::SqliteBackedHistory;
247pub use history::{
248    CommandLineSearch, FileBackedHistory, History, HistoryItem, HistoryItemId,
249    HistoryNavigationQuery, HistorySessionId, SearchDirection, SearchFilter, SearchQuery,
250    HISTORY_SIZE,
251};
252
253mod prompt;
254pub use prompt::{
255    DefaultPrompt, DefaultPromptSegment, Prompt, PromptEditMode, PromptHistorySearch,
256    PromptHistorySearchStatus, PromptViMode,
257};
258
259mod edit_mode;
260pub use edit_mode::{
261    default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
262    CursorConfig, EditMode, Emacs, Keybindings, Vi,
263};
264
265mod highlighter;
266pub use highlighter::{ExampleHighlighter, Highlighter, SimpleMatchHighlighter};
267
268mod completion;
269pub use completion::{Completer, DefaultCompleter, Span, Suggestion};
270
271mod hinter;
272pub use hinter::CwdAwareHinter;
273pub use hinter::{DefaultHinter, Hinter};
274
275mod validator;
276pub use validator::{DefaultValidator, ValidationResult, Validator};
277
278mod menu;
279pub use menu::{
280    menu_functions, ColumnarMenu, ListMenu, Menu, MenuEvent, MenuTextStyle, ReedlineMenu,
281};
282
283mod terminal_extensions;
284pub use terminal_extensions::kitty_protocol_available;
285
286mod utils;
287
288mod external_printer;
289pub use utils::{
290    get_reedline_default_keybindings, get_reedline_edit_commands,
291    get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes,
292    get_reedline_reedline_events,
293};
294
295// Reexport the key types to be independent from an explicit crossterm dependency.
296pub use crossterm::{
297    event::{KeyCode, KeyModifiers},
298    style::Color,
299};
300#[cfg(feature = "external_printer")]
301pub use external_printer::ExternalPrinter;