Command heirarchy for tab completion

This commit is contained in:
greg 2018-07-02 02:35:48 -07:00
parent 41c9dfae06
commit 947f4f2ea6
2 changed files with 67 additions and 39 deletions

View File

@ -1,4 +1,4 @@
use std::collections::{HashSet, HashMap}; use std::collections::HashSet;
use colored::*; use colored::*;
use std::fmt::Write; use std::fmt::Write;

View File

@ -132,13 +132,33 @@ fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInte
enum CommandTree { enum CommandTree {
Terminal(String), Terminal(String),
NonTerminal(String, Vec<CommandTree>) NonTerminal(String, Vec<CommandTree>),
Top(Vec<CommandTree>),
}
impl CommandTree {
fn term(s: &str) -> CommandTree {
CommandTree::Terminal(s.to_string())
}
fn get_cmd(&self) -> String {
match self {
CommandTree::Terminal(s) => s.to_string(),
CommandTree::NonTerminal(s, _) => s.to_string(),
CommandTree::Top(_) => "".to_string(),
}
}
fn get_children(&self) -> Vec<String> {
match self {
CommandTree::Terminal(_) => vec![],
CommandTree::NonTerminal(_, children) => children.iter().map(|x| x.get_cmd()).collect(),
CommandTree::Top(children) => children.iter().map(|x| x.get_cmd()).collect(),
}
}
} }
struct TabCompleteHandler { struct TabCompleteHandler {
passes: Vec<String>,
sigil: char, sigil: char,
top_level_commands: Vec<String>, top_level_commands: CommandTree,
} }
use linefeed::complete::{Completion, Completer}; use linefeed::complete::{Completion, Completer};
@ -147,54 +167,62 @@ use linefeed::terminal::Terminal;
impl TabCompleteHandler { impl TabCompleteHandler {
fn new(sigil: char, passes: Vec<String>) -> TabCompleteHandler { fn new(sigil: char, passes: Vec<String>) -> TabCompleteHandler {
TabCompleteHandler { TabCompleteHandler {
passes, top_level_commands: CommandTree::Top(vec![
CommandTree::term("exit"),
CommandTree::term("quit"),
CommandTree::NonTerminal(format!("debug"), vec![
CommandTree::term("passes"),
CommandTree::NonTerminal(format!("show"), passes.iter().map(|p| CommandTree::term(p)).collect()),
CommandTree::NonTerminal(format!("hide"), passes.iter().map(|p| CommandTree::term(p)).collect()),
]),
CommandTree::NonTerminal(format!("lang"), vec![
CommandTree::term("next"),
CommandTree::term("prev"),
CommandTree::NonTerminal(format!("go"), vec![])//TODO
]),
]),
sigil, sigil,
top_level_commands: vec![
"exit".to_string(),
"quit".to_string(),
"lang".to_string(),
"debug".to_string(),
]
} }
} }
} }
impl<T: Terminal> Completer<T> for TabCompleteHandler { impl<T: Terminal> Completer<T> for TabCompleteHandler {
fn complete(&self, word: &str, prompter: &linefeed::prompter::Prompter<T>, start: usize, end: usize) -> Option<Vec<Completion>> { fn complete(&self, word: &str, prompter: &linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> {
let line = prompter.buffer(); let line = prompter.buffer();
if line.starts_with(&format!("{}", self.sigil)) { if line.starts_with(&format!("{}", self.sigil)) {
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace(); let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
let mut completions = Vec::new(); let mut completions = Vec::new();
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
loop {
match words.next() { match words.next() {
None => { None => {
let word = word.get(1..).unwrap(); let top = match command_tree {
for cmd in self.top_level_commands.iter() { Some(CommandTree::Top(_)) => true,
_ => false
};
let word = if top { word.get(1..).unwrap() } else { word };
for cmd in command_tree.map(|x| x.get_children()).unwrap_or(vec![]).into_iter() {
if cmd.starts_with(word) { if cmd.starts_with(word) {
completions.push( completions.push(Completion {
Completion { completion: format!("{}{}", if top { ":" } else { "" }, cmd),
completion: format!(":{}", cmd),
display: Some(cmd.clone()), display: Some(cmd.clone()),
suffix: linefeed::complete::Suffix::Some(' ') suffix: linefeed::complete::Suffix::Some(' ')
} })
)
} }
} }
break;
}, },
Some("debug") => match words.next() { Some(s) => {
None => for cmd in ["show", "hide", "passes"].iter() { let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
if cmd.starts_with(word) { CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
completions.push(Completion::simple(cmd.to_string())); CommandTree::NonTerminal(_, children) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::Terminal(_) => None,
});
command_tree = new_ptr;
} }
},
Some("show") | Some("hide") => for pass in self.passes.iter() {
if pass.starts_with(word) {
completions.push(Completion::simple(pass.to_string()));
} }
},
_ => return None,
},
_ => return None
} }
Some(completions) Some(completions)
} else { } else {