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, MenuBuilder};
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//! ## Enable mouse click-to-cursor
179//!
180//! ```rust,no_run
181//! use reedline::{MouseClickMode, Reedline};
182//!
183//! let mut line_editor =
184//! Reedline::create().with_mouse_click(MouseClickMode::EnabledWithOsc133);
185//! ```
186//!
187//! ## Crate features
188//!
189//! - `clipboard`: Enable support to use the `SystemClipboard`. Enabling this feature will return a `SystemClipboard` instead of a local clipboard when calling `get_default_clipboard()`.
190//! - `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).
191//! - `sqlite`: Provides the `SqliteBackedHistory` to store richer information in the history. Statically links the required sqlite version.
192//! - `sqlite-dynlib`: Alternative to the feature `sqlite`. Will not statically link. Requires `sqlite >= 3.38` to link dynamically!
193//! - `external_printer`: **Experimental:** Thread-safe `ExternalPrinter` handle to print lines from concurrently running threads.
194//!
195//! ## Are we prompt yet? (Development status)
196//!
197//! Reedline has now all the basic features to become the primary line editor for [nushell](https://github.com/nushell/nushell
198//! )
199//!
200//! - General editing functionality, that should feel familiar coming from other shells (e.g. bash, fish, zsh).
201//! - Configurable keybindings (emacs-style bindings and basic vi-style).
202//! - Configurable prompt
203//! - Content-aware syntax highlighting.
204//! - Autocompletion (With graphical selection menu or simple cycling inline).
205//! - History with interactive search options (optionally persists to file, can support multiple sessions accessing the same file)
206//! - Fish-style history autosuggestion hints
207//! - Undo support.
208//! - Clipboard integration
209//! - Line completeness validation for seamless entry of multiline command sequences.
210//!
211//! ### Areas for future improvements
212//!
213//! - [ ] Support for Unicode beyond simple left-to-right scripts
214//! - [ ] Easier keybinding configuration
215//! - [ ] Support for more advanced vi commands
216//! - [ ] Visual selection
217//! - [ ] Smooth experience if completion or prompt content takes long to compute
218//! - [ ] Support for a concurrent output stream from background tasks to be displayed, while the input prompt is active. ("Full duplex" mode)
219//!
220//! 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).
221//!
222//! ### Development history
223//!
224//! 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).
225//!
226//! [Playlist: Creating a line editor in Rust](https://youtube.com/playlist?list=PLP2yfE2-FXdQw0I6O4YdIX_mzBeF5TDdv)
227//!
228//! ### Alternatives
229//!
230//! For currently more mature Rust line editing check out:
231//!
232//! - [rustyline](https://crates.io/crates/rustyline)
233//!
234#![warn(rustdoc::missing_crate_level_docs)]
235#![warn(missing_docs)]
236// #![deny(warnings)]
237mod core_editor;
238pub use core_editor::Editor;
239pub use core_editor::LineBuffer;
240
241mod enums;
242pub use enums::{
243 EditCommand, MouseButton, ReedlineEvent, ReedlineRawEvent, Signal, TextObject, TextObjectScope,
244 TextObjectType, UndoBehavior,
245};
246
247mod painting;
248pub use painting::{Painter, StyledText};
249
250mod engine;
251pub use engine::{MouseClickMode, Reedline};
252
253mod result;
254pub use result::{ReedlineError, ReedlineErrorVariants, Result};
255
256mod history;
257#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))]
258pub use history::SqliteBackedHistory;
259pub use history::{
260 CommandLineSearch, FileBackedHistory, History, HistoryItem, HistoryItemExtraInfo,
261 HistoryItemId, HistoryNavigationQuery, HistorySessionId, IgnoreAllExtraInfo, SearchDirection,
262 SearchFilter, SearchQuery, HISTORY_SIZE,
263};
264
265mod prompt;
266pub use prompt::{
267 DefaultPrompt, DefaultPromptSegment, Prompt, PromptEditMode, PromptHistorySearch,
268 PromptHistorySearchStatus, PromptViMode,
269};
270
271mod edit_mode;
272pub use edit_mode::{
273 default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
274 CursorConfig, EditMode, Emacs, Keybindings, Vi,
275};
276
277mod highlighter;
278pub use highlighter::{ExampleHighlighter, Highlighter, SimpleMatchHighlighter};
279
280mod completion;
281pub use completion::{Completer, DefaultCompleter, Span, Suggestion};
282
283mod hinter;
284pub use hinter::CwdAwareHinter;
285pub use hinter::{DefaultHinter, Hinter};
286
287mod validator;
288pub use validator::{DefaultValidator, ValidationResult, Validator};
289
290mod menu;
291pub use menu::{
292 menu_functions, ColumnarMenu, DescriptionMenu, DescriptionMode, IdeMenu, ListMenu, Menu,
293 MenuBuilder, MenuEvent, MenuSettings, MenuTextStyle, ReedlineMenu, TraversalDirection,
294};
295
296mod terminal_extensions;
297pub use terminal_extensions::kitty_protocol_available;
298pub use terminal_extensions::semantic_prompt::{
299 Osc133ClickEventsMarkers, Osc133Markers, Osc633Markers, PromptKind, SemanticPromptMarkers,
300};
301
302mod utils;
303
304mod external_printer;
305pub use utils::{
306 get_reedline_default_keybindings, get_reedline_edit_commands,
307 get_reedline_keybinding_modifiers, get_reedline_keycodes, get_reedline_prompt_edit_modes,
308 get_reedline_reedline_events,
309};
310
311// Reexport the key types to be independent from an explicit crossterm dependency.
312pub use crossterm::{
313 event::{KeyCode, KeyModifiers},
314 style::Color,
315};
316#[cfg(feature = "external_printer")]
317pub use external_printer::ExternalPrinter;