2022-07-27 05:39:53 +03:00
|
|
|
use std::str::FromStr;
|
2019-05-27 22:42:19 +03:00
|
|
|
use termwiz::cell::AttributeChange;
|
2022-07-27 05:39:53 +03:00
|
|
|
use termwiz::color::{AnsiColor, ColorAttribute, SrgbaTuple};
|
2019-05-27 23:35:24 +03:00
|
|
|
use termwiz::lineedit::*;
|
2019-05-27 22:42:19 +03:00
|
|
|
|
2019-05-27 23:35:24 +03:00
|
|
|
#[derive(Default)]
|
|
|
|
struct Host {
|
|
|
|
history: BasicHistory,
|
|
|
|
}
|
2019-05-27 22:42:19 +03:00
|
|
|
|
|
|
|
impl LineEditorHost for Host {
|
|
|
|
// Render the prompt with a darkslateblue background color if
|
|
|
|
// the terminal supports true color, otherwise render it with
|
|
|
|
// a navy blue ansi color.
|
|
|
|
fn render_prompt(&self, prompt: &str) -> Vec<OutputElement> {
|
|
|
|
vec![
|
|
|
|
OutputElement::Attribute(AttributeChange::Background(
|
|
|
|
ColorAttribute::TrueColorWithPaletteFallback(
|
2022-07-27 05:39:53 +03:00
|
|
|
SrgbaTuple::from_str("darkslateblue").unwrap(),
|
2019-05-27 22:42:19 +03:00
|
|
|
AnsiColor::Navy.into(),
|
|
|
|
),
|
|
|
|
)),
|
|
|
|
OutputElement::Text(prompt.to_owned()),
|
|
|
|
]
|
|
|
|
}
|
2019-05-27 23:35:24 +03:00
|
|
|
|
2019-09-29 06:29:48 +03:00
|
|
|
fn history(&mut self) -> &mut dyn History {
|
2019-05-27 23:35:24 +03:00
|
|
|
&mut self.history
|
|
|
|
}
|
2019-05-28 03:39:10 +03:00
|
|
|
|
|
|
|
/// Demo of the completion API for words starting with "h" or "he"
|
|
|
|
fn complete(&self, line: &str, cursor_position: usize) -> Vec<CompletionCandidate> {
|
|
|
|
let mut candidates = vec![];
|
|
|
|
if let Some((range, word)) = word_at_cursor(line, cursor_position) {
|
|
|
|
let words = &["hello", "help", "he-man"];
|
|
|
|
|
|
|
|
for w in words {
|
|
|
|
if w.starts_with(word) {
|
|
|
|
candidates.push(CompletionCandidate {
|
|
|
|
range: range.clone(),
|
|
|
|
text: w.to_string(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
candidates
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This is a conceptually simple function that computes the bounds
|
|
|
|
/// of the whitespace delimited word at the specified cursor position
|
|
|
|
/// in the supplied line string.
|
|
|
|
/// It returns the range and the corresponding slice out of the line.
|
|
|
|
/// This function is sufficient for example purposes; in a real application
|
|
|
|
/// the equivalent function would need to be aware of quoting and other
|
|
|
|
/// application specific context.
|
|
|
|
fn word_at_cursor(line: &str, cursor_position: usize) -> Option<(std::ops::Range<usize>, &str)> {
|
|
|
|
let char_indices: Vec<(usize, char)> = line.char_indices().collect();
|
|
|
|
if char_indices.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let char_position = char_indices
|
|
|
|
.iter()
|
|
|
|
.position(|(idx, _)| *idx == cursor_position)
|
|
|
|
.unwrap_or(char_indices.len());
|
|
|
|
|
|
|
|
// Look back until we find whitespace
|
|
|
|
let mut start_position = char_position;
|
|
|
|
while start_position > 0
|
|
|
|
&& start_position <= char_indices.len()
|
|
|
|
&& !char_indices[start_position - 1].1.is_whitespace()
|
|
|
|
{
|
|
|
|
start_position -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look forwards until we find whitespace
|
|
|
|
let mut end_position = char_position;
|
|
|
|
while end_position < char_indices.len() && !char_indices[end_position].1.is_whitespace() {
|
|
|
|
end_position += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if end_position > start_position {
|
|
|
|
let range = char_indices[start_position].0
|
|
|
|
..char_indices
|
|
|
|
.get(end_position)
|
|
|
|
.map(|c| c.0 + 1)
|
|
|
|
.unwrap_or(line.len());
|
|
|
|
Some((range.clone(), &line[range]))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2019-05-27 22:42:19 +03:00
|
|
|
}
|
2019-05-27 07:10:37 +03:00
|
|
|
|
2021-01-08 11:21:54 +03:00
|
|
|
fn main() -> termwiz::Result<()> {
|
2019-05-28 03:39:10 +03:00
|
|
|
println!("Type `exit` to quit this example, or start a word with `h` and press Tab.");
|
2020-04-09 17:37:23 +03:00
|
|
|
let mut terminal = line_editor_terminal()?;
|
|
|
|
let mut editor = LineEditor::new(&mut terminal);
|
2019-05-27 07:10:37 +03:00
|
|
|
|
2019-05-27 23:35:24 +03:00
|
|
|
let mut host = Host::default();
|
|
|
|
loop {
|
|
|
|
if let Some(line) = editor.read_line(&mut host)? {
|
|
|
|
println!("read line: {:?}", line);
|
|
|
|
if line == "exit" {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
host.history().add(&line);
|
|
|
|
}
|
|
|
|
}
|
2019-05-27 07:10:37 +03:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|