diff options
Diffstat (limited to 'helix-term/src/line.rs')
-rw-r--r-- | helix-term/src/line.rs | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/helix-term/src/line.rs b/helix-term/src/line.rs new file mode 100644 index 00000000..90fb5e6f --- /dev/null +++ b/helix-term/src/line.rs @@ -0,0 +1,111 @@ +use termwiz::cell::AttributeChange; +use termwiz::color::{AnsiColor, ColorAttribute, RgbColor}; +use termwiz::lineedit::*; + +#[derive(Default)] +struct Host { + history: BasicHistory, +} + +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( + RgbColor::from_named("darkslateblue").unwrap(), + AnsiColor::Navy.into(), + ), + )), + OutputElement::Text(prompt.to_owned()), + ] + } + + fn history(&mut self) -> &mut dyn History { + &mut self.history + } + + /// 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 + } +} + +fn main() -> anyhow::Result<()> { + println!("Type `exit` to quit this example, or start a word with `h` and press Tab."); + let mut terminal = line_editor_terminal()?; + let mut editor = LineEditor::new(&mut terminal); + + 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); + } + } + + Ok(()) +} |