Flatten schala-repl files
This commit is contained in:
parent
3cbe80e933
commit
76c2257c7e
@ -1,6 +1,6 @@
|
|||||||
use super::{InterpreterDirectiveOutput, Repl};
|
use crate::directive_actions::DirectiveAction;
|
||||||
use crate::language::ProgrammingLanguageInterface;
|
use crate::language::ProgrammingLanguageInterface;
|
||||||
use crate::repl::directive_actions::DirectiveAction;
|
use crate::{InterpreterDirectiveOutput, Repl};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
|
/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
|
@ -1,8 +1,8 @@
|
|||||||
use super::{InterpreterDirectiveOutput, Repl};
|
use crate::help::help;
|
||||||
use crate::language::{
|
use crate::language::{
|
||||||
DebugAsk, DebugResponse, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
|
DebugAsk, DebugResponse, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
|
||||||
};
|
};
|
||||||
use crate::repl::help::help;
|
use crate::{InterpreterDirectiveOutput, Repl};
|
||||||
use std::fmt::Write as FmtWrite;
|
use std::fmt::Write as FmtWrite;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
@ -1,5 +1,5 @@
|
|||||||
use crate::repl::command_tree::CommandTree;
|
use crate::command_tree::CommandTree;
|
||||||
use crate::repl::directive_actions::DirectiveAction;
|
use crate::directive_actions::DirectiveAction;
|
||||||
|
|
||||||
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
|
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
|
||||||
let passes_directives: Vec<CommandTree> = pass_names
|
let passes_directives: Vec<CommandTree> = pass_names
|
@ -1,8 +1,8 @@
|
|||||||
use std::fmt::Write as FmtWrite;
|
use std::fmt::Write as FmtWrite;
|
||||||
|
|
||||||
use super::command_tree::CommandTree;
|
use crate::command_tree::CommandTree;
|
||||||
use super::{InterpreterDirectiveOutput, Repl};
|
|
||||||
use crate::language::ProgrammingLanguageInterface;
|
use crate::language::ProgrammingLanguageInterface;
|
||||||
|
use crate::{InterpreterDirectiveOutput, Repl};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
pub fn help<L: ProgrammingLanguageInterface>(
|
pub fn help<L: ProgrammingLanguageInterface>(
|
@ -7,15 +7,278 @@ extern crate includedir;
|
|||||||
extern crate phf;
|
extern crate phf;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
mod command_tree;
|
||||||
mod language;
|
mod language;
|
||||||
mod repl;
|
use self::command_tree::CommandTree;
|
||||||
|
mod repl_options;
|
||||||
|
use repl_options::ReplOptions;
|
||||||
|
mod directive_actions;
|
||||||
|
mod directives;
|
||||||
|
use directives::directives_from_pass_names;
|
||||||
|
mod help;
|
||||||
|
mod response;
|
||||||
|
use response::ReplResponse;
|
||||||
|
|
||||||
|
use colored::*;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use language::{
|
pub use language::{
|
||||||
ComputationRequest, ComputationResponse, DebugAsk, DebugResponse, GlobalOutputStats,
|
ComputationRequest, ComputationResponse, DebugAsk, DebugResponse, GlobalOutputStats,
|
||||||
LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
|
LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use repl::Repl;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/static.rs"));
|
include!(concat!(env!("OUT_DIR"), "/static.rs"));
|
||||||
const VERSION_STRING: &'static str = "0.1.0";
|
const VERSION_STRING: &'static str = "0.1.0";
|
||||||
|
|
||||||
|
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
|
||||||
|
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
|
||||||
|
|
||||||
|
type InterpreterDirectiveOutput = Option<String>;
|
||||||
|
|
||||||
|
pub struct Repl<L: ProgrammingLanguageInterface> {
|
||||||
|
/// If this is the first character typed by a user into the repl, the following
|
||||||
|
/// will be interpreted as a directive to the REPL rather than a command in the
|
||||||
|
/// running programming language.
|
||||||
|
sigil: char,
|
||||||
|
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
|
||||||
|
language_state: L,
|
||||||
|
options: ReplOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum PromptStyle {
|
||||||
|
Normal,
|
||||||
|
Multiline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: ProgrammingLanguageInterface> Repl<L> {
|
||||||
|
pub fn new(initial_state: L) -> Self {
|
||||||
|
use linefeed::Interface;
|
||||||
|
let line_reader = Interface::new("schala-repl").unwrap();
|
||||||
|
let sigil = ':';
|
||||||
|
|
||||||
|
Repl {
|
||||||
|
sigil,
|
||||||
|
line_reader,
|
||||||
|
language_state: initial_state,
|
||||||
|
options: ReplOptions::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_repl(&mut self) {
|
||||||
|
println!("Schala meta-interpeter version {}", VERSION_STRING);
|
||||||
|
println!(
|
||||||
|
"Type {} for help with the REPL",
|
||||||
|
format!("{}help", self.sigil).bright_green().bold()
|
||||||
|
);
|
||||||
|
self.load_options();
|
||||||
|
self.handle_repl_loop();
|
||||||
|
self.save_before_exit();
|
||||||
|
println!("Exiting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_options(&mut self) {
|
||||||
|
self.line_reader
|
||||||
|
.load_history(HISTORY_SAVE_FILE)
|
||||||
|
.unwrap_or(());
|
||||||
|
match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) {
|
||||||
|
Ok(options) => {
|
||||||
|
self.options = options;
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_repl_loop(&mut self) {
|
||||||
|
use linefeed::ReadResult::*;
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
macro_rules! match_or_break {
|
||||||
|
($line:expr) => {
|
||||||
|
match $line {
|
||||||
|
Err(e) => {
|
||||||
|
println!("readline IO Error: {}", e);
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
Ok(Eof) | Ok(Signal(_)) => break 'main,
|
||||||
|
Ok(Input(ref input)) => input,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.update_line_reader();
|
||||||
|
let line = self.line_reader.read_line();
|
||||||
|
let input: &str = match_or_break!(line);
|
||||||
|
|
||||||
|
self.line_reader.add_history_unique(input.to_string());
|
||||||
|
let mut chars = input.chars().peekable();
|
||||||
|
let repl_responses = match chars.nth(0) {
|
||||||
|
Some(ch) if ch == self.sigil => {
|
||||||
|
if chars.peek() == Some(&'{') {
|
||||||
|
let mut buf = String::new();
|
||||||
|
buf.push_str(input.get(2..).unwrap());
|
||||||
|
'multiline: loop {
|
||||||
|
self.set_prompt(PromptStyle::Multiline);
|
||||||
|
let new_line = self.line_reader.read_line();
|
||||||
|
let new_input = match_or_break!(new_line);
|
||||||
|
if new_input.starts_with(":}") {
|
||||||
|
break 'multiline;
|
||||||
|
} else {
|
||||||
|
buf.push_str(new_input);
|
||||||
|
buf.push_str("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.handle_input(&buf)
|
||||||
|
} else {
|
||||||
|
match self.handle_interpreter_directive(input.get(1..).unwrap()) {
|
||||||
|
Some(directive_output) => println!("{}", directive_output),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.handle_input(input),
|
||||||
|
};
|
||||||
|
|
||||||
|
for repl_response in repl_responses.iter() {
|
||||||
|
println!("{}", repl_response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_line_reader(&mut self) {
|
||||||
|
let tab_complete_handler = TabCompleteHandler::new(self.sigil, self.get_directives());
|
||||||
|
self.line_reader
|
||||||
|
.set_completer(Arc::new(tab_complete_handler)); //TODO fix this here
|
||||||
|
self.set_prompt(PromptStyle::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_prompt(&mut self, prompt_style: PromptStyle) {
|
||||||
|
let prompt_str = match prompt_style {
|
||||||
|
PromptStyle::Normal => ">> ",
|
||||||
|
PromptStyle::Multiline => ">| ",
|
||||||
|
};
|
||||||
|
|
||||||
|
self.line_reader.set_prompt(&prompt_str).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_before_exit(&self) {
|
||||||
|
self.line_reader
|
||||||
|
.save_history(HISTORY_SAVE_FILE)
|
||||||
|
.unwrap_or(());
|
||||||
|
self.options.save_to_file(OPTIONS_SAVE_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
|
||||||
|
let arguments: Vec<&str> = input.split_whitespace().collect();
|
||||||
|
|
||||||
|
if arguments.len() < 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let directives = self.get_directives();
|
||||||
|
directives.perform(self, &arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
|
||||||
|
let mut debug_requests = HashSet::new();
|
||||||
|
for ask in self.options.debug_asks.iter() {
|
||||||
|
debug_requests.insert(ask.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = ComputationRequest {
|
||||||
|
source: input,
|
||||||
|
debug_requests,
|
||||||
|
};
|
||||||
|
let response = self.language_state.run_computation(request);
|
||||||
|
response::handle_computation_response(response, &self.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_directives(&mut self) -> CommandTree {
|
||||||
|
let pass_names = match self
|
||||||
|
.language_state
|
||||||
|
.request_meta(LangMetaRequest::StageNames)
|
||||||
|
{
|
||||||
|
LangMetaResponse::StageNames(names) => names,
|
||||||
|
_ => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
directives_from_pass_names(&pass_names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TabCompleteHandler {
|
||||||
|
sigil: char,
|
||||||
|
top_level_commands: CommandTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
use linefeed::complete::{Completer, Completion};
|
||||||
|
use linefeed::terminal::Terminal;
|
||||||
|
|
||||||
|
impl TabCompleteHandler {
|
||||||
|
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
|
||||||
|
TabCompleteHandler {
|
||||||
|
top_level_commands,
|
||||||
|
sigil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Terminal> Completer<T> for TabCompleteHandler {
|
||||||
|
fn complete(
|
||||||
|
&self,
|
||||||
|
word: &str,
|
||||||
|
prompter: &::linefeed::prompter::Prompter<T>,
|
||||||
|
start: usize,
|
||||||
|
_end: usize,
|
||||||
|
) -> Option<Vec<Completion>> {
|
||||||
|
let line = prompter.buffer();
|
||||||
|
|
||||||
|
if !line.starts_with(self.sigil) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
|
||||||
|
let mut completions = Vec::new();
|
||||||
|
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match words.next() {
|
||||||
|
None => {
|
||||||
|
let top = match command_tree {
|
||||||
|
Some(CommandTree::Top(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let word = if top { word.get(1..).unwrap() } else { word };
|
||||||
|
for cmd in command_tree
|
||||||
|
.map(|x| x.get_subcommands())
|
||||||
|
.unwrap_or(vec![])
|
||||||
|
.into_iter()
|
||||||
|
{
|
||||||
|
if cmd.starts_with(word) {
|
||||||
|
completions.push(Completion {
|
||||||
|
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
||||||
|
display: Some(cmd.to_string()),
|
||||||
|
suffix: ::linefeed::complete::Suffix::Some(' '),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(s) => {
|
||||||
|
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
|
||||||
|
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
|
||||||
|
CommandTree::NonTerminal { children, .. } => {
|
||||||
|
children.iter().find(|c| c.get_cmd() == s)
|
||||||
|
}
|
||||||
|
CommandTree::Terminal { children, .. } => {
|
||||||
|
children.iter().find(|c| c.get_cmd() == s)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
command_tree = new_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(completions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,270 +0,0 @@
|
|||||||
use colored::*;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::language::{
|
|
||||||
ComputationRequest, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod command_tree;
|
|
||||||
use self::command_tree::CommandTree;
|
|
||||||
mod repl_options;
|
|
||||||
use repl_options::ReplOptions;
|
|
||||||
mod directive_actions;
|
|
||||||
mod directives;
|
|
||||||
use directives::directives_from_pass_names;
|
|
||||||
mod help;
|
|
||||||
mod response;
|
|
||||||
use response::ReplResponse;
|
|
||||||
|
|
||||||
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
|
|
||||||
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
|
|
||||||
|
|
||||||
type InterpreterDirectiveOutput = Option<String>;
|
|
||||||
|
|
||||||
pub struct Repl<L: ProgrammingLanguageInterface> {
|
|
||||||
/// If this is the first character typed by a user into the repl, the following
|
|
||||||
/// will be interpreted as a directive to the REPL rather than a command in the
|
|
||||||
/// running programming language.
|
|
||||||
sigil: char,
|
|
||||||
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
|
|
||||||
language_state: L,
|
|
||||||
options: ReplOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum PromptStyle {
|
|
||||||
Normal,
|
|
||||||
Multiline,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L: ProgrammingLanguageInterface> Repl<L> {
|
|
||||||
pub fn new(initial_state: L) -> Self {
|
|
||||||
use linefeed::Interface;
|
|
||||||
let line_reader = Interface::new("schala-repl").unwrap();
|
|
||||||
let sigil = ':';
|
|
||||||
|
|
||||||
Repl {
|
|
||||||
sigil,
|
|
||||||
line_reader,
|
|
||||||
language_state: initial_state,
|
|
||||||
options: ReplOptions::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_repl(&mut self) {
|
|
||||||
println!("Schala meta-interpeter version {}", crate::VERSION_STRING);
|
|
||||||
println!(
|
|
||||||
"Type {} for help with the REPL",
|
|
||||||
format!("{}help", self.sigil).bright_green().bold()
|
|
||||||
);
|
|
||||||
self.load_options();
|
|
||||||
self.handle_repl_loop();
|
|
||||||
self.save_before_exit();
|
|
||||||
println!("Exiting...");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_options(&mut self) {
|
|
||||||
self.line_reader
|
|
||||||
.load_history(HISTORY_SAVE_FILE)
|
|
||||||
.unwrap_or(());
|
|
||||||
match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) {
|
|
||||||
Ok(options) => {
|
|
||||||
self.options = options;
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_repl_loop(&mut self) {
|
|
||||||
use linefeed::ReadResult::*;
|
|
||||||
|
|
||||||
'main: loop {
|
|
||||||
macro_rules! match_or_break {
|
|
||||||
($line:expr) => {
|
|
||||||
match $line {
|
|
||||||
Err(e) => {
|
|
||||||
println!("readline IO Error: {}", e);
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
Ok(Eof) | Ok(Signal(_)) => break 'main,
|
|
||||||
Ok(Input(ref input)) => input,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
self.update_line_reader();
|
|
||||||
let line = self.line_reader.read_line();
|
|
||||||
let input: &str = match_or_break!(line);
|
|
||||||
|
|
||||||
self.line_reader.add_history_unique(input.to_string());
|
|
||||||
let mut chars = input.chars().peekable();
|
|
||||||
let repl_responses = match chars.nth(0) {
|
|
||||||
Some(ch) if ch == self.sigil => {
|
|
||||||
if chars.peek() == Some(&'{') {
|
|
||||||
let mut buf = String::new();
|
|
||||||
buf.push_str(input.get(2..).unwrap());
|
|
||||||
'multiline: loop {
|
|
||||||
self.set_prompt(PromptStyle::Multiline);
|
|
||||||
let new_line = self.line_reader.read_line();
|
|
||||||
let new_input = match_or_break!(new_line);
|
|
||||||
if new_input.starts_with(":}") {
|
|
||||||
break 'multiline;
|
|
||||||
} else {
|
|
||||||
buf.push_str(new_input);
|
|
||||||
buf.push_str("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.handle_input(&buf)
|
|
||||||
} else {
|
|
||||||
match self.handle_interpreter_directive(input.get(1..).unwrap()) {
|
|
||||||
Some(directive_output) => println!("{}", directive_output),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.handle_input(input),
|
|
||||||
};
|
|
||||||
|
|
||||||
for repl_response in repl_responses.iter() {
|
|
||||||
println!("{}", repl_response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_line_reader(&mut self) {
|
|
||||||
let tab_complete_handler = TabCompleteHandler::new(self.sigil, self.get_directives());
|
|
||||||
self.line_reader
|
|
||||||
.set_completer(Arc::new(tab_complete_handler)); //TODO fix this here
|
|
||||||
self.set_prompt(PromptStyle::Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_prompt(&mut self, prompt_style: PromptStyle) {
|
|
||||||
let prompt_str = match prompt_style {
|
|
||||||
PromptStyle::Normal => ">> ",
|
|
||||||
PromptStyle::Multiline => ">| ",
|
|
||||||
};
|
|
||||||
|
|
||||||
self.line_reader.set_prompt(&prompt_str).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_before_exit(&self) {
|
|
||||||
self.line_reader
|
|
||||||
.save_history(HISTORY_SAVE_FILE)
|
|
||||||
.unwrap_or(());
|
|
||||||
self.options.save_to_file(OPTIONS_SAVE_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
|
|
||||||
let arguments: Vec<&str> = input.split_whitespace().collect();
|
|
||||||
|
|
||||||
if arguments.len() < 1 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let directives = self.get_directives();
|
|
||||||
directives.perform(self, &arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
|
|
||||||
let mut debug_requests = HashSet::new();
|
|
||||||
for ask in self.options.debug_asks.iter() {
|
|
||||||
debug_requests.insert(ask.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let request = ComputationRequest {
|
|
||||||
source: input,
|
|
||||||
debug_requests,
|
|
||||||
};
|
|
||||||
let response = self.language_state.run_computation(request);
|
|
||||||
response::handle_computation_response(response, &self.options)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_directives(&mut self) -> CommandTree {
|
|
||||||
let pass_names = match self
|
|
||||||
.language_state
|
|
||||||
.request_meta(LangMetaRequest::StageNames)
|
|
||||||
{
|
|
||||||
LangMetaResponse::StageNames(names) => names,
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
directives_from_pass_names(&pass_names)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TabCompleteHandler {
|
|
||||||
sigil: char,
|
|
||||||
top_level_commands: CommandTree,
|
|
||||||
}
|
|
||||||
|
|
||||||
use linefeed::complete::{Completer, Completion};
|
|
||||||
use linefeed::terminal::Terminal;
|
|
||||||
|
|
||||||
impl TabCompleteHandler {
|
|
||||||
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
|
|
||||||
TabCompleteHandler {
|
|
||||||
top_level_commands,
|
|
||||||
sigil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Terminal> Completer<T> for TabCompleteHandler {
|
|
||||||
fn complete(
|
|
||||||
&self,
|
|
||||||
word: &str,
|
|
||||||
prompter: &::linefeed::prompter::Prompter<T>,
|
|
||||||
start: usize,
|
|
||||||
_end: usize,
|
|
||||||
) -> Option<Vec<Completion>> {
|
|
||||||
let line = prompter.buffer();
|
|
||||||
|
|
||||||
if !line.starts_with(self.sigil) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
|
|
||||||
let mut completions = Vec::new();
|
|
||||||
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match words.next() {
|
|
||||||
None => {
|
|
||||||
let top = match command_tree {
|
|
||||||
Some(CommandTree::Top(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
let word = if top { word.get(1..).unwrap() } else { word };
|
|
||||||
for cmd in command_tree
|
|
||||||
.map(|x| x.get_subcommands())
|
|
||||||
.unwrap_or(vec![])
|
|
||||||
.into_iter()
|
|
||||||
{
|
|
||||||
if cmd.starts_with(word) {
|
|
||||||
completions.push(Completion {
|
|
||||||
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
|
||||||
display: Some(cmd.to_string()),
|
|
||||||
suffix: ::linefeed::complete::Suffix::Some(' '),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Some(s) => {
|
|
||||||
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
|
|
||||||
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
|
|
||||||
CommandTree::NonTerminal { children, .. } => {
|
|
||||||
children.iter().find(|c| c.get_cmd() == s)
|
|
||||||
}
|
|
||||||
CommandTree::Terminal { children, .. } => {
|
|
||||||
children.iter().find(|c| c.get_cmd() == s)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
command_tree = new_ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(completions)
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,8 @@ use colored::*;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use super::ReplOptions;
|
|
||||||
use crate::language::{ComputationResponse, DebugAsk};
|
use crate::language::{ComputationResponse, DebugAsk};
|
||||||
|
use crate::ReplOptions;
|
||||||
|
|
||||||
pub struct ReplResponse {
|
pub struct ReplResponse {
|
||||||
label: Option<String>,
|
label: Option<String>,
|
Loading…
Reference in New Issue
Block a user