mirror of
https://github.com/nushell/reedline.git
synced 2024-08-16 19:30:25 +03:00
Add list style completions (#162)
This commit is contained in:
parent
de0f399650
commit
88bded3417
@ -104,7 +104,7 @@ Reedline::create()?.with_highlighter(Box::new(DefaultHighlighter::new(commands))
|
|||||||
```rust,no_run
|
```rust,no_run
|
||||||
// Create a reedline object with tab completions support
|
// Create a reedline object with tab completions support
|
||||||
|
|
||||||
use reedline::{DefaultCompleter, DefaultCompletionActionHandler, Reedline};
|
use reedline::{DefaultCompleter, CircularCompletionHandler, Reedline};
|
||||||
|
|
||||||
let commands = vec![
|
let commands = vec![
|
||||||
"test".into(),
|
"test".into(),
|
||||||
@ -115,7 +115,7 @@ let commands = vec![
|
|||||||
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
||||||
|
|
||||||
let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
||||||
DefaultCompletionActionHandler::default().with_completer(completer),
|
CircularCompletionHandler::default().with_completer(completer),
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{core_editor::LineBuffer, Completer, CompletionActionHandler, DefaultCompleter};
|
use crate::{core_editor::LineBuffer, Completer, CompletionActionHandler, DefaultCompleter};
|
||||||
|
|
||||||
/// A simple handler that will do a cycle-based rotation through the options given by the Completer
|
/// A simple handler that will do a cycle-based rotation through the options given by the Completer
|
||||||
pub struct DefaultCompletionActionHandler {
|
pub struct CircularCompletionHandler {
|
||||||
completer: Box<dyn Completer>,
|
completer: Box<dyn Completer>,
|
||||||
initial_line: LineBuffer,
|
initial_line: LineBuffer,
|
||||||
index: usize,
|
index: usize,
|
||||||
@ -9,8 +9,8 @@ pub struct DefaultCompletionActionHandler {
|
|||||||
last_buffer: Option<LineBuffer>,
|
last_buffer: Option<LineBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultCompletionActionHandler {
|
impl CircularCompletionHandler {
|
||||||
/// Build a `DefaultCompletionActionHandler` configured to use a specific completer
|
/// Build a `CircularCompletionHandler` configured to use a specific completer
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
@ -18,7 +18,7 @@ impl DefaultCompletionActionHandler {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// use reedline::{DefaultCompletionActionHandler, DefaultCompleter, Completer, Span};
|
/// use reedline::{CircularCompletionHandler, DefaultCompleter, Completer, Span};
|
||||||
///
|
///
|
||||||
/// let mut completer = DefaultCompleter::default();
|
/// let mut completer = DefaultCompleter::default();
|
||||||
/// completer.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect());
|
/// completer.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect());
|
||||||
@ -26,19 +26,16 @@ impl DefaultCompletionActionHandler {
|
|||||||
/// completer.complete("te",2),
|
/// completer.complete("te",2),
|
||||||
/// vec![(Span { start: 0, end: 2 }, "test".into())]);
|
/// vec![(Span { start: 0, end: 2 }, "test".into())]);
|
||||||
///
|
///
|
||||||
/// let mut completions = DefaultCompletionActionHandler::default().with_completer(Box::new(completer));
|
/// let mut completions = CircularCompletionHandler::default().with_completer(Box::new(completer));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_completer(
|
pub fn with_completer(mut self, completer: Box<dyn Completer>) -> CircularCompletionHandler {
|
||||||
mut self,
|
|
||||||
completer: Box<dyn Completer>,
|
|
||||||
) -> DefaultCompletionActionHandler {
|
|
||||||
self.completer = completer;
|
self.completer = completer;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Default for DefaultCompletionActionHandler {
|
impl Default for CircularCompletionHandler {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
DefaultCompletionActionHandler {
|
CircularCompletionHandler {
|
||||||
completer: Box::new(DefaultCompleter::default()),
|
completer: Box::new(DefaultCompleter::default()),
|
||||||
initial_line: LineBuffer::new(),
|
initial_line: LineBuffer::new(),
|
||||||
index: 0,
|
index: 0,
|
||||||
@ -47,13 +44,13 @@ impl Default for DefaultCompletionActionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultCompletionActionHandler {
|
impl CircularCompletionHandler {
|
||||||
fn reset_index(&mut self) {
|
fn reset_index(&mut self) {
|
||||||
self.index = 0;
|
self.index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompletionActionHandler for DefaultCompletionActionHandler {
|
impl CompletionActionHandler for CircularCompletionHandler {
|
||||||
// With this function we handle the tab events.
|
// With this function we handle the tab events.
|
||||||
//
|
//
|
||||||
// If completions vector is not empty we proceed to replace
|
// If completions vector is not empty we proceed to replace
|
||||||
@ -106,11 +103,11 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
fn get_tab_handler_with(values: Vec<&'_ str>) -> DefaultCompletionActionHandler {
|
fn get_tab_handler_with(values: Vec<&'_ str>) -> CircularCompletionHandler {
|
||||||
let mut completer = DefaultCompleter::default();
|
let mut completer = DefaultCompleter::default();
|
||||||
completer.insert(values.iter().map(|s| s.to_string()).collect());
|
completer.insert(values.iter().map(|s| s.to_string()).collect());
|
||||||
|
|
||||||
DefaultCompletionActionHandler::default().with_completer(Box::new(completer))
|
CircularCompletionHandler::default().with_completer(Box::new(completer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buffer_with(content: &str) -> LineBuffer {
|
fn buffer_with(content: &str) -> LineBuffer {
|
@ -11,7 +11,7 @@ use crate::{Completer, Span};
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// use reedline::{DefaultCompleter, DefaultCompletionActionHandler, Reedline};
|
/// use reedline::{DefaultCompleter, CircularCompletionHandler, Reedline};
|
||||||
///
|
///
|
||||||
/// let commands = vec![
|
/// let commands = vec![
|
||||||
/// "test".into(),
|
/// "test".into(),
|
||||||
@ -23,7 +23,7 @@ use crate::{Completer, Span};
|
|||||||
///
|
///
|
||||||
/// let mut line_editor = Reedline::create().unwrap()
|
/// let mut line_editor = Reedline::create().unwrap()
|
||||||
/// .with_completion_action_handler(Box::new(
|
/// .with_completion_action_handler(Box::new(
|
||||||
/// DefaultCompletionActionHandler::default().with_completer(completer),
|
/// CircularCompletionHandler::default().with_completer(completer),
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
194
src/completion/list.rs
Normal file
194
src/completion/list.rs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
use crate::{core_editor::LineBuffer, Completer, CompletionActionHandler, DefaultCompleter, Span};
|
||||||
|
|
||||||
|
/// A simple handler that will do a cycle-based rotation through the options given by the Completer
|
||||||
|
pub struct ListCompletionHandler {
|
||||||
|
completer: Box<dyn Completer>,
|
||||||
|
complete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListCompletionHandler {
|
||||||
|
/// Build a `ListCompletionHandler` configured to use a specific completer
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `completer` The completion logic to use
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use reedline::{ListCompletionHandler, DefaultCompleter, Completer, Span};
|
||||||
|
///
|
||||||
|
/// let mut completer = DefaultCompleter::default();
|
||||||
|
/// completer.insert(vec!["test-hyphen","test_underscore"].iter().map(|s| s.to_string()).collect());
|
||||||
|
/// assert_eq!(
|
||||||
|
/// completer.complete("te",2),
|
||||||
|
/// vec![(Span { start: 0, end: 2 }, "test".into())]);
|
||||||
|
///
|
||||||
|
/// let mut completions = ListCompletionHandler::default().with_completer(Box::new(completer));
|
||||||
|
/// ```
|
||||||
|
pub fn with_completer(mut self, completer: Box<dyn Completer>) -> ListCompletionHandler {
|
||||||
|
self.completer = completer;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for ListCompletionHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
ListCompletionHandler {
|
||||||
|
completer: Box::new(DefaultCompleter::default()),
|
||||||
|
complete: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompletionActionHandler for ListCompletionHandler {
|
||||||
|
// With this function we handle the tab events.
|
||||||
|
//
|
||||||
|
// If completions vector is not empty we proceed to replace
|
||||||
|
// in the line_buffer only the specified range of characters.
|
||||||
|
// If internal index is 0 it means that is the first tab event pressed.
|
||||||
|
// If internal index is greater than completions vector, we bring it back to 0.
|
||||||
|
fn handle(&mut self, present_buffer: &mut LineBuffer) {
|
||||||
|
// if let Some(last_buffer) = &self.last_buffer {
|
||||||
|
// if last_buffer != present_buffer {
|
||||||
|
// self.reset_index();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // NOTE: This is required to cycle through the tabs for what is presently present in the
|
||||||
|
// // buffer. Without this `repetitive_calls_to_handle_works` will not work
|
||||||
|
// if self.index == 0 {
|
||||||
|
// self.initial_line = present_buffer.clone();
|
||||||
|
// } else {
|
||||||
|
// *present_buffer = self.initial_line.clone();
|
||||||
|
// }
|
||||||
|
|
||||||
|
let completions = self
|
||||||
|
.completer
|
||||||
|
.complete(present_buffer.get_buffer(), present_buffer.offset());
|
||||||
|
|
||||||
|
if completions.is_empty() {
|
||||||
|
// do nothing
|
||||||
|
} else if completions.len() == 1 {
|
||||||
|
let span = completions[0].0;
|
||||||
|
|
||||||
|
let mut offset = present_buffer.offset();
|
||||||
|
offset += completions[0].1.len() - (span.end - span.start);
|
||||||
|
|
||||||
|
// TODO improve the support for multiline replace
|
||||||
|
present_buffer.replace(span.start..span.end, &completions[0].1);
|
||||||
|
present_buffer.set_insertion_point(offset);
|
||||||
|
self.complete = true;
|
||||||
|
} else {
|
||||||
|
let prefix = calculate_prefix(&completions);
|
||||||
|
|
||||||
|
let span = completions[0].0;
|
||||||
|
let mut offset = present_buffer.offset();
|
||||||
|
offset += prefix.len() - (span.end - span.start);
|
||||||
|
|
||||||
|
present_buffer.replace(span.start..span.end, &prefix);
|
||||||
|
present_buffer.set_insertion_point(offset);
|
||||||
|
|
||||||
|
print!("\r\n");
|
||||||
|
for completion in completions {
|
||||||
|
// TODO: make this list pretty
|
||||||
|
print!("{}\r\n", completion.1);
|
||||||
|
}
|
||||||
|
print!("\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_prefix(inputs: &[(Span, String)]) -> String {
|
||||||
|
let mut iter = inputs.iter();
|
||||||
|
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
let prefix = first.1.clone();
|
||||||
|
let prefix_bytes = prefix.as_bytes();
|
||||||
|
|
||||||
|
let mut longest_match = prefix.len();
|
||||||
|
|
||||||
|
for i in iter {
|
||||||
|
longest_match = std::cmp::min(
|
||||||
|
longest_match,
|
||||||
|
i.1.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.zip(prefix_bytes)
|
||||||
|
.take_while(|(x, y)| x == y)
|
||||||
|
.count(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix[0..longest_match].to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
fn get_tab_handler_with(values: Vec<&'_ str>) -> ListCompletionHandler {
|
||||||
|
let mut completer = DefaultCompleter::default();
|
||||||
|
completer.insert(values.iter().map(|s| s.to_string()).collect());
|
||||||
|
|
||||||
|
ListCompletionHandler::default().with_completer(Box::new(completer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_with(content: &str) -> LineBuffer {
|
||||||
|
let mut line_buffer = LineBuffer::new();
|
||||||
|
line_buffer.insert_str(content);
|
||||||
|
|
||||||
|
line_buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn repetitive_calls_to_handle_works() {
|
||||||
|
let mut tab = get_tab_handler_with(vec!["login", "logout"]);
|
||||||
|
let mut buf = buffer_with("lo");
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf, buffer_with("log"));
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
assert_eq!(buf, buffer_with("log"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn behaviour_with_hyphens_and_underscores() {
|
||||||
|
let mut tab = get_tab_handler_with(vec!["test-hyphen", "test_underscore"]);
|
||||||
|
let mut buf = buffer_with("te");
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf, buffer_with("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn auto_resets_on_new_query() {
|
||||||
|
let mut tab = get_tab_handler_with(vec!["login", "exit"]);
|
||||||
|
let mut buf = buffer_with("log");
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
|
||||||
|
assert_eq!(buf, buffer_with("login"));
|
||||||
|
let mut new_buf = buffer_with("ex");
|
||||||
|
tab.handle(&mut new_buf);
|
||||||
|
assert_eq!(new_buf, buffer_with("exit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn same_string_different_places() {
|
||||||
|
let mut tab = get_tab_handler_with(vec!["that", "another"]);
|
||||||
|
let mut buf = buffer_with("th is my test th");
|
||||||
|
|
||||||
|
// Hitting tab after `th` fills the first completion `that`
|
||||||
|
buf.set_insertion_point(2);
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
let mut expected_buffer = buffer_with("that is my test th");
|
||||||
|
expected_buffer.set_insertion_point(4);
|
||||||
|
assert_eq!(buf, expected_buffer);
|
||||||
|
|
||||||
|
// updating the cursor to end should reset the completions
|
||||||
|
buf.set_insertion_point(18);
|
||||||
|
tab.handle(&mut buf);
|
||||||
|
assert_eq!(buf, buffer_with("that is my test that"));
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
mod base;
|
mod base;
|
||||||
|
mod circular;
|
||||||
mod default;
|
mod default;
|
||||||
mod tab_handler;
|
mod list;
|
||||||
|
|
||||||
pub use base::{Completer, CompletionActionHandler, Span};
|
pub use base::{Completer, CompletionActionHandler, Span};
|
||||||
|
pub use circular::CircularCompletionHandler;
|
||||||
pub use default::DefaultCompleter;
|
pub use default::DefaultCompleter;
|
||||||
pub use tab_handler::DefaultCompletionActionHandler;
|
pub use list::ListCompletionHandler;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
completion::{CompletionActionHandler, DefaultCompletionActionHandler},
|
completion::{CircularCompletionHandler, CompletionActionHandler},
|
||||||
core_editor::Editor,
|
core_editor::Editor,
|
||||||
edit_mode::{EditMode, Emacs},
|
edit_mode::{EditMode, Emacs},
|
||||||
enums::ReedlineEvent,
|
enums::ReedlineEvent,
|
||||||
@ -128,7 +128,7 @@ impl Reedline {
|
|||||||
input_mode: InputMode::Regular,
|
input_mode: InputMode::Regular,
|
||||||
painter,
|
painter,
|
||||||
edit_mode,
|
edit_mode,
|
||||||
tab_handler: Box::new(DefaultCompletionActionHandler::default()),
|
tab_handler: Box::new(CircularCompletionHandler::default()),
|
||||||
terminal_size,
|
terminal_size,
|
||||||
prompt_widget,
|
prompt_widget,
|
||||||
highlighter: buffer_highlighter,
|
highlighter: buffer_highlighter,
|
||||||
@ -178,7 +178,7 @@ impl Reedline {
|
|||||||
/// // Create a reedline object with tab completions support
|
/// // Create a reedline object with tab completions support
|
||||||
///
|
///
|
||||||
/// use std::io;
|
/// use std::io;
|
||||||
/// use reedline::{DefaultCompleter, DefaultCompletionActionHandler, Reedline};
|
/// use reedline::{DefaultCompleter, CircularCompletionHandler, Reedline};
|
||||||
///
|
///
|
||||||
/// let commands = vec![
|
/// let commands = vec![
|
||||||
/// "test".into(),
|
/// "test".into(),
|
||||||
@ -189,7 +189,7 @@ impl Reedline {
|
|||||||
/// let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
/// let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
||||||
///
|
///
|
||||||
/// let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
/// let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
||||||
/// DefaultCompletionActionHandler::default().with_completer(completer),
|
/// CircularCompletionHandler::default().with_completer(completer),
|
||||||
/// ));
|
/// ));
|
||||||
/// # Ok::<(), io::Error>(())
|
/// # Ok::<(), io::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
//! // Create a reedline object with tab completions support
|
//! // Create a reedline object with tab completions support
|
||||||
//!
|
//!
|
||||||
//! use std::io;
|
//! use std::io;
|
||||||
//! use reedline::{DefaultCompleter, DefaultCompletionActionHandler, Reedline};
|
//! use reedline::{DefaultCompleter, CircularCompletionHandler, Reedline};
|
||||||
//!
|
//!
|
||||||
//! let commands = vec![
|
//! let commands = vec![
|
||||||
//! "test".into(),
|
//! "test".into(),
|
||||||
@ -118,7 +118,7 @@
|
|||||||
//! let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
//! let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 2));
|
||||||
//!
|
//!
|
||||||
//! let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
//! let mut line_editor = Reedline::create()?.with_completion_action_handler(Box::new(
|
||||||
//! DefaultCompletionActionHandler::default().with_completer(completer),
|
//! CircularCompletionHandler::default().with_completer(completer),
|
||||||
//! ));
|
//! ));
|
||||||
//! # Ok::<(), io::Error>(())
|
//! # Ok::<(), io::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -218,7 +218,8 @@ pub use styled_text::StyledText;
|
|||||||
|
|
||||||
mod completion;
|
mod completion;
|
||||||
pub use completion::{
|
pub use completion::{
|
||||||
Completer, CompletionActionHandler, DefaultCompleter, DefaultCompletionActionHandler, Span,
|
CircularCompletionHandler, Completer, CompletionActionHandler, DefaultCompleter,
|
||||||
|
ListCompletionHandler, Span,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod hinter;
|
mod hinter;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use reedline::{EditMode, Emacs, Vi};
|
use reedline::{EditMode, Emacs, ListCompletionHandler, Vi};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crossterm::{
|
crossterm::{
|
||||||
@ -7,9 +7,8 @@ use {
|
|||||||
},
|
},
|
||||||
nu_ansi_term::{Color, Style},
|
nu_ansi_term::{Color, Style},
|
||||||
reedline::{
|
reedline::{
|
||||||
default_emacs_keybindings, DefaultCompleter, DefaultCompletionActionHandler,
|
default_emacs_keybindings, DefaultCompleter, DefaultHighlighter, DefaultHinter,
|
||||||
DefaultHighlighter, DefaultHinter, DefaultPrompt, EditCommand, FileBackedHistory, Reedline,
|
DefaultPrompt, EditCommand, FileBackedHistory, Reedline, ReedlineEvent, Signal,
|
||||||
ReedlineEvent, Signal,
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
io::{stdout, Write},
|
io::{stdout, Write},
|
||||||
@ -60,7 +59,7 @@ fn main() -> Result<()> {
|
|||||||
.with_edit_mode(edit_mode)
|
.with_edit_mode(edit_mode)
|
||||||
.with_highlighter(Box::new(DefaultHighlighter::new(commands)))
|
.with_highlighter(Box::new(DefaultHighlighter::new(commands)))
|
||||||
.with_completion_action_handler(Box::new(
|
.with_completion_action_handler(Box::new(
|
||||||
DefaultCompletionActionHandler::default().with_completer(completer.clone()),
|
ListCompletionHandler::default().with_completer(completer.clone()),
|
||||||
))
|
))
|
||||||
.with_hinter(Box::new(
|
.with_hinter(Box::new(
|
||||||
DefaultHinter::default()
|
DefaultHinter::default()
|
||||||
|
Loading…
Reference in New Issue
Block a user