1
0

Show completion hints for built-in values

This commit is contained in:
Jeff 2024-01-24 19:41:47 -05:00
parent 848a0cadb6
commit f2e7badf4b
3 changed files with 68 additions and 10 deletions

View File

@ -1,5 +1,6 @@
use std::{collections::BTreeMap, env::args, sync::OnceLock}; use std::{collections::BTreeMap, env::args, sync::OnceLock};
use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -13,7 +14,11 @@ static JSON: OnceLock<Value> = OnceLock::new();
static RANDOM: OnceLock<Value> = OnceLock::new(); static RANDOM: OnceLock<Value> = OnceLock::new();
static STRING: OnceLock<Value> = OnceLock::new(); static STRING: OnceLock<Value> = OnceLock::new();
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub fn built_in_values() -> impl Iterator<Item = BuiltInValue> {
all()
}
#[derive(Sequence, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInValue { pub enum BuiltInValue {
Args, Args,
AssertEqual, AssertEqual,
@ -39,7 +44,7 @@ impl BuiltInValue {
} }
} }
fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self { match self {
BuiltInValue::Args => Type::list(Type::String), BuiltInValue::Args => Type::list(Type::String),
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(), BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
@ -52,7 +57,7 @@ impl BuiltInValue {
} }
} }
fn get(&self) -> &Value { pub fn get(&self) -> &Value {
match self { match self {
BuiltInValue::Args => ARGS.get_or_init(|| { BuiltInValue::Args => ARGS.get_or_init(|| {
let args = args().map(|arg| Value::string(arg.to_string())).collect(); let args = args().map(|arg| Value::string(arg.to_string())).collect();

View File

@ -142,6 +142,14 @@ impl Type {
pub fn is_function(&self) -> bool { pub fn is_function(&self) -> bool {
matches!(self, Type::Function { .. }) matches!(self, Type::Function { .. })
} }
pub fn is_list(&self) -> bool {
matches!(self, Type::List(_))
}
pub fn is_map(&self) -> bool {
matches!(self, Type::Map(_))
}
} }
impl AbstractTree for Type { impl AbstractTree for Type {

View File

@ -3,16 +3,17 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use rustyline::{ use rustyline::{
completion::FilenameCompleter, completion::FilenameCompleter,
config::Builder,
error::ReadlineError, error::ReadlineError,
highlight::Highlighter, highlight::Highlighter,
hint::{Hint, Hinter, HistoryHinter}, hint::{Hint, Hinter, HistoryHinter},
history::DefaultHistory, history::DefaultHistory,
Completer, Context, Editor, Helper, Validator, ColorMode, Completer, CompletionType, Context, Editor, Helper, Validator,
}; };
use std::{borrow::Cow, fs::read_to_string}; use std::{borrow::Cow, fs::read_to_string};
use dust_lang::{Interpreter, Map, Value}; use dust_lang::{built_in_values, Interpreter, Map, Value};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -112,7 +113,7 @@ struct DustReadline {
#[rustyline(Completer)] #[rustyline(Completer)]
completer: FilenameCompleter, completer: FilenameCompleter,
tool_hints: Vec<ToolHint>, hints: Vec<ToolHint>,
#[rustyline(Hinter)] #[rustyline(Hinter)]
_hinter: HistoryHinter, _hinter: HistoryHinter,
@ -120,10 +121,49 @@ struct DustReadline {
impl DustReadline { impl DustReadline {
fn new() -> Self { fn new() -> Self {
let mut hints = Vec::new();
for built_in_value in built_in_values() {
let mut display = built_in_value.name().to_string();
if built_in_value.r#type().is_function() {
display.push_str("()");
}
if built_in_value.r#type().is_map() {
let value = built_in_value.get();
if let Value::Map(map) = value {
for (key, (value, _)) in map.variables().unwrap().iter() {
let display = if value.is_function() {
format!("{display}:{key}()")
} else {
format!("{display}:{key}")
};
hints.push(ToolHint {
complete_to: display.len(),
display,
})
}
}
}
hints.push(ToolHint {
complete_to: display.len(),
display,
})
}
hints.push(ToolHint {
display: "output".to_string(),
complete_to: 0,
});
Self { Self {
completer: FilenameCompleter::new(), completer: FilenameCompleter::new(),
_hinter: HistoryHinter {}, _hinter: HistoryHinter {},
tool_hints: Vec::new(), hints,
} }
} }
} }
@ -164,7 +204,7 @@ impl Hinter for DustReadline {
return None; return None;
} }
self.tool_hints.iter().find_map(|tool_hint| { self.hints.iter().find_map(|tool_hint| {
if tool_hint.display.starts_with(line) { if tool_hint.display.starts_with(line) {
Some(tool_hint.suffix(pos)) Some(tool_hint.suffix(pos))
} else { } else {
@ -176,7 +216,7 @@ impl Hinter for DustReadline {
impl Highlighter for DustReadline { impl Highlighter for DustReadline {
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
let highlighted = ansi_term::Colour::Red.paint(hint).to_string(); let highlighted = ansi_term::Colour::Yellow.paint(hint).to_string();
Cow::Owned(highlighted) Cow::Owned(highlighted)
} }
@ -184,7 +224,12 @@ impl Highlighter for DustReadline {
fn run_cli_shell(context: Map) { fn run_cli_shell(context: Map) {
let mut interpreter = Interpreter::new(context); let mut interpreter = Interpreter::new(context);
let mut rl: Editor<DustReadline, DefaultHistory> = Editor::new().unwrap(); let config = Builder::new()
.color_mode(ColorMode::Enabled)
.completion_type(CompletionType::List)
.build();
let mut rl: Editor<DustReadline, DefaultHistory> =
Editor::with_config(config).expect("Line editor could not be configured properly.");
rl.set_helper(Some(DustReadline::new())); rl.set_helper(Some(DustReadline::new()));