Implement reedline

This commit is contained in:
Jeff 2024-01-25 02:17:45 -05:00
parent ac29f0210f
commit 784e4b309d

View File

@ -4,8 +4,8 @@ use clap::{Parser, Subcommand};
use crossterm::event::{KeyCode, KeyModifiers}; use crossterm::event::{KeyCode, KeyModifiers};
use nu_ansi_term::Style; use nu_ansi_term::Style;
use reedline::{ use reedline::{
default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultPrompt, EditCommand, Emacs, default_emacs_keybindings, DefaultPrompt, EditCommand, Emacs, Highlighter, Reedline,
Highlighter, Reedline, ReedlineEvent, ReedlineMenu, Signal, SqliteBackedHistory, StyledText, ReedlineEvent, Signal, SqliteBackedHistory, StyledText,
}; };
use std::{fs::read_to_string, path::PathBuf}; use std::{fs::read_to_string, path::PathBuf};
@ -126,7 +126,7 @@ impl Highlighter for DustHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> reedline::StyledText { fn highlight(&self, line: &str, _cursor: usize) -> reedline::StyledText {
fn highlight_identifier(styled: &mut StyledText, word: &str, map: &Map) -> bool { fn highlight_identifier(styled: &mut StyledText, word: &str, map: &Map) -> bool {
for (key, (value, _type)) in map.variables().unwrap().iter() { for (key, (value, _type)) in map.variables().unwrap().iter() {
if key == &word[0..word.len() - 1] { if key == &word {
styled.push((Style::new().bold(), word.to_string())); styled.push((Style::new().bold(), word.to_string()));
return true; return true;
@ -138,8 +138,10 @@ impl Highlighter for DustHighlighter {
} }
for built_in_value in built_in_values() { for built_in_value in built_in_values() {
if built_in_value.name() == &word[0..word.len() - 1] { if built_in_value.name() == word {
styled.push((Style::new().bold(), word.to_string())); styled.push((Style::new().bold(), word.to_string()));
return true;
} }
} }
@ -147,11 +149,19 @@ impl Highlighter for DustHighlighter {
} }
let mut styled = StyledText::new(); let mut styled = StyledText::new();
let terminators = [' ', ':', '(', ')', '{', '}', '[', ']'];
for word in line.split_inclusive(&[' ', ':', '(', ')', '{', '}', '[', ']']) { for word in line.split_inclusive(&terminators) {
let word_is_highlighted = highlight_identifier(&mut styled, word, &self.context); let word_is_highlighted =
highlight_identifier(&mut styled, &word[0..word.len() - 1], &self.context);
if !word_is_highlighted { if word_is_highlighted {
let final_char = word.chars().last().unwrap();
if terminators.contains(&final_char) {
styled.push((Style::new(), final_char.to_string()));
}
} else {
styled.push((Style::new(), word.to_string())); styled.push((Style::new(), word.to_string()));
} }
} }
@ -166,9 +176,14 @@ fn run_shell(context: Map) -> Result<()> {
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();
keybindings.add_binding( keybindings.add_binding(
KeyModifiers::ALT, KeyModifiers::NONE,
KeyCode::Char('m'), KeyCode::Enter,
ReedlineEvent::Edit(vec![EditCommand::BackspaceWord]), ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
);
keybindings.add_binding(
KeyModifiers::CONTROL,
KeyCode::Char(' '),
ReedlineEvent::Submit,
); );
keybindings.add_binding( keybindings.add_binding(
KeyModifiers::NONE, KeyModifiers::NONE,
@ -184,26 +199,16 @@ fn run_shell(context: Map) -> Result<()> {
SqliteBackedHistory::with_file(PathBuf::from("target/history"), None, None) SqliteBackedHistory::with_file(PathBuf::from("target/history"), None, None)
.expect("Error loading history."), .expect("Error loading history."),
); );
let mut commands = Vec::new();
for built_in_value in built_in_values() {
commands.push(built_in_value.name().to_string());
}
let completer = Box::new(DefaultCompleter::new_with_wordlen(commands.clone(), 0));
let completion_menu = Box::new(ColumnarMenu::default().with_name("completion_menu"));
let mut line_editor = Reedline::create() let mut line_editor = Reedline::create()
.with_edit_mode(edit_mode) .with_edit_mode(edit_mode)
.with_history(history) .with_history(history)
.with_highlighter(Box::new(DustHighlighter::new(context))) .with_highlighter(Box::new(DustHighlighter::new(context)));
.with_completer(completer)
.with_menu(ReedlineMenu::EngineCompleter(completion_menu));
loop { loop {
let sig = line_editor.read_line(&prompt); let sig = line_editor.read_line(&prompt);
match sig { match sig {
Ok(Signal::Success(buffer)) => { Ok(Signal::Success(buffer)) => {
if buffer.is_empty() { if buffer.trim().is_empty() {
continue; continue;
} }