Refactor the repl loop

This commit is contained in:
Yehuda Katz
2019-05-16 14:43:36 -07:00
parent 34a866df99
commit 98ab5e63fc
8 changed files with 243 additions and 163 deletions

View File

@ -3,7 +3,6 @@
#[allow(unused)]
use crate::prelude::*;
use std::borrow::Cow::{self, Borrowed, Owned};
mod commands;
mod context;
@ -13,6 +12,7 @@ mod format;
mod object;
mod parser;
mod prelude;
mod shell;
use crate::commands::command::ReturnValue;
crate use crate::commands::command::{Command, CommandAction, CommandBlueprint};
@ -21,13 +21,9 @@ crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError;
crate use crate::format::{EntriesListView, GenericView};
use crate::object::Value;
use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::{Hinter, HistoryHinter};
use ansi_term::Color;
use rustyline::error::ReadlineError;
use rustyline::{ColorMode, Config, Editor, Helper, self};
use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque;
use std::error::Error;
use std::sync::{Arc, Mutex};
@ -48,54 +44,10 @@ impl<T> MaybeOwned<'a, T> {
}
}
struct MyHelper(FilenameCompleter, MatchingBracketHighlighter, HistoryHinter);
impl Completer for MyHelper {
type Candidate = Pair;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<Pair>), ReadlineError> {
self.0.complete(line, pos, ctx)
}
}
impl Hinter for MyHelper {
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
self.2.hint(line, pos, ctx)
}
}
impl Highlighter for MyHelper {
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
Owned("\x1b[32m".to_owned() + &prompt[0..prompt.len() - 2] + "\x1b[m> ")
}
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
}
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
self.1.highlight(line, pos)
}
fn highlight_char(&self, line: &str, pos: usize) -> bool {
self.1.highlight_char(line, pos)
}
}
impl Helper for MyHelper {}
fn main() -> Result<(), Box<Error>> {
let config = Config::builder().color_mode(ColorMode::Forced).build();
let h = MyHelper(
FilenameCompleter::new(),
MatchingBracketHighlighter::new(),
HistoryHinter {},
);
let mut rl: Editor<MyHelper> = Editor::with_config(config);
let h = crate::shell::Helper::new();
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
rl.set_helper(Some(h));
if rl.load_history("history.txt").is_err() {
println!("No previous history.");
@ -125,48 +77,25 @@ fn main() -> Result<(), Box<Error>> {
context.lock().unwrap().env.cwd().display().to_string()
));
match readline {
Ok(ref line) if line.trim() == "exit" => {
break;
}
Ok(line) => {
let result = crate::parser::shell_parser(&line)
.map_err(|e| ShellError::string(format!("{:?}", e)))?;
let parsed = result.1;
match process_line(readline, context.clone()) {
LineResult::Success(line) => {
rl.add_history_entry(line.as_ref());
let mut input = VecDeque::new();
for item in parsed {
input = process_command(
crate::parser::print_items(&item),
item.clone(),
input,
context.clone(),
)?;
}
if input.len() > 0 {
if equal_shapes(&input) {
format(crate::commands::to_array(input), context.clone());
} else {
format(input, context.clone());
}
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
LineResult::Error(err) => {
context.lock().unwrap().host.stdout(&err);
}
LineResult::Break => {
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
LineResult::FatalError(err) => {
context
.lock()
.unwrap()
.host
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
}
}
}
@ -175,13 +104,81 @@ fn main() -> Result<(), Box<Error>> {
Ok(())
}
enum LineResult {
Success(String),
Error(String),
Break,
#[allow(unused)]
FatalError(ShellError),
}
fn process_line(
readline: Result<String, ReadlineError>,
context: Arc<Mutex<Context>>,
) -> LineResult {
match &readline {
Ok(line) if line.trim() == "exit" => LineResult::Break,
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
Ok(line) => {
let result = match crate::parser::shell_parser(&line) {
Err(err) => {
return LineResult::Error(format!("{:?}", err));
}
Ok(val) => val,
};
let parsed = result.1;
let mut input = VecDeque::new();
for item in parsed {
input = match process_command(
crate::parser::print_items(&item),
item.clone(),
input,
context.clone(),
) {
Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())),
};
}
if input.len() > 0 {
if equal_shapes(&input) {
format(crate::commands::to_array(input), context.clone());
} else {
format(input, context.clone());
}
}
LineResult::Success(line.to_string())
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
LineResult::Break
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
LineResult::Break
}
Err(err) => {
println!("Error: {:?}", err);
LineResult::Break
}
}
}
fn process_command(
line: String,
parsed: Vec<crate::parser::Item>,
input: VecDeque<Value>,
context: Arc<Mutex<Context>>,
) -> Result<VecDeque<Value>, ShellError> {
let command = &parsed[0].name();
let command = &parsed[0].name()?;
let arg_list = parsed[1..].iter().map(|i| i.as_value()).collect();
if command == &"format" {