Skip to content

Commit

Permalink
Moved more things into CommandRunner
Browse files Browse the repository at this point in the history
The `CommandRunner` is now the entry point to commands (no need for eg `SourcelessCommandParser`)
`SessionHistory` is now owned by `CommandRunner`
  • Loading branch information
rben01 committed Nov 30, 2024
1 parent 923d604 commit 321900e
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 99 deletions.
157 changes: 65 additions & 92 deletions numbat-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use config::{ColorMode, Config, ExchangeRateFetchingPolicy, IntroBanner, PrettyP
use highlighter::NumbatHighlighter;

use itertools::Itertools;
use numbat::command::{
self, CommandContext, CommandControlFlow, CommandParser, CommandRunner, SourcelessCommandParser,
};
use numbat::command::{self, CommandControlFlow, CommandRunner};
use numbat::compact_str::{CompactString, ToCompactString};
use numbat::diagnostic::ErrorDiagnostic;
use numbat::help::help_markup;
Expand Down Expand Up @@ -353,61 +351,60 @@ impl Cli {
rl: &mut Editor<NumbatHelper, DefaultHistory>,
interactive: bool,
) -> Result<()> {
let mut session_history = SessionHistory::default();

let cmd_runner = CommandRunner::<Editor<NumbatHelper, DefaultHistory>>::new_all_disabled()
.enable_help(|| {
let help = help_markup();
print!("{}", ansi_format(&help, true));
// currently, the ansi formatter adds indents
// _after_ each newline and so we need to manually
// add an extra blank line to absorb this indent
println!();
CommandControlFlow::Normal
})
.enable_info(|ctx, item| {
let help = ctx.print_info_for_keyword(item);
println!("{}", ansi_format(&help, true));
CommandControlFlow::Normal
})
.enable_list(|ctx, item| {
let m = match item {
None => ctx.print_environment(),
Some(command::ListItems::Functions) => ctx.print_functions(),
Some(command::ListItems::Dimensions) => ctx.print_dimensions(),
Some(command::ListItems::Variables) => ctx.print_variables(),
Some(command::ListItems::Units) => ctx.print_units(),
};
println!("{}", ansi_format(&m, false));
CommandControlFlow::Normal
})
.enable_clear(|rl| match rl.clear_screen() {
Ok(_) => CommandControlFlow::Normal,
Err(_) => CommandControlFlow::Return,
})
.enable_save(|ctx, sh, dst, interactive| {
let save_result = sh.save(
dst,
SessionHistoryOptions {
include_err_lines: false,
trim_lines: true,
},
);
match save_result {
Ok(_) => {
let m = m::text("successfully saved session history to")
+ m::space()
+ m::string(dst.to_compact_string());
println!("{}", ansi_format(&m, interactive));
CommandControlFlow::Normal
}
Err(err) => {
ctx.print_diagnostic(*err);
CommandControlFlow::Continue
let mut cmd_runner =
CommandRunner::<Editor<NumbatHelper, DefaultHistory>>::new_all_disabled()
.enable_help(|| {
let help = help_markup();
print!("{}", ansi_format(&help, true));
// currently, the ansi formatter adds indents
// _after_ each newline and so we need to manually
// add an extra blank line to absorb this indent
println!();
CommandControlFlow::Normal
})
.enable_info(|ctx, item| {
let help = ctx.print_info_for_keyword(item);
println!("{}", ansi_format(&help, true));
CommandControlFlow::Normal
})
.enable_list(|ctx, item| {
let m = match item {
None => ctx.print_environment(),
Some(command::ListItems::Functions) => ctx.print_functions(),
Some(command::ListItems::Dimensions) => ctx.print_dimensions(),
Some(command::ListItems::Variables) => ctx.print_variables(),
Some(command::ListItems::Units) => ctx.print_units(),
};
println!("{}", ansi_format(&m, false));
CommandControlFlow::Normal
})
.enable_clear(|rl| match rl.clear_screen() {
Ok(_) => CommandControlFlow::Normal,
Err(_) => CommandControlFlow::Return,
})
.enable_save(SessionHistory::default(), |ctx, sh, dst, interactive| {
let save_result = sh.save(
dst,
SessionHistoryOptions {
include_err_lines: false,
trim_lines: true,
},
);
match save_result {
Ok(_) => {
let m = m::text("successfully saved session history to")
+ m::space()
+ m::string(dst.to_compact_string());
println!("{}", ansi_format(&m, interactive));
CommandControlFlow::Normal
}
Err(err) => {
ctx.print_diagnostic(*err);
CommandControlFlow::Continue
}
}
}
})
.enable_quit(|| CommandControlFlow::Return);
})
.enable_quit(|| CommandControlFlow::Return);

loop {
let readline = rl.readline(&self.config.prompt);
Expand All @@ -420,41 +417,17 @@ impl Cli {
rl.add_history_entry(&line)?;

// if we enter here, the line looks like a command
if let Some(sourceless_parser) =
SourcelessCommandParser::new(&line, &cmd_runner)
{
let mut parser = CommandParser::new(
sourceless_parser,
self.context
.lock()
.unwrap()
.resolver_mut()
.add_code_source(CodeSource::Text, &line),
);

match parser.parse_command() {
Ok(cmd) => {
let mut context = self.context.lock().unwrap();
match cmd_runner.run(
cmd,
CommandContext {
ctx: &mut context,
editor: rl,
session_history: &session_history,
interactive,
},
) {
CommandControlFlow::Normal => {}
CommandControlFlow::Continue => continue,
CommandControlFlow::Return => return Ok(()),
}
}
Err(e) => {
self.print_diagnostic(e);
continue;
}
if let Some(cmd_cf) = cmd_runner.try_run_line(
&mut self.context.lock().unwrap(),
&line,
rl,
interactive,
) {
match cmd_cf {
CommandControlFlow::Normal => {}
CommandControlFlow::Continue => continue,
CommandControlFlow::Return => return Ok(()),
}

continue;
}

Expand Down Expand Up @@ -482,7 +455,7 @@ impl Cli {
}
}

session_history.push(CompactString::from(line), result);
cmd_runner.push_to_history(&line, result);
}
Err(ReadlineError::Interrupted) => {}
Err(ReadlineError::Eof) => {
Expand Down
64 changes: 57 additions & 7 deletions numbat/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::str::{FromStr, SplitWhitespace};

use compact_str::ToCompactString;

use crate::{
parser::ParseErrorKind,
resolver::CodeSource,
session_history::SessionHistory,
span::{ByteIndex, Span},
Context, ParseError,
Expand Down Expand Up @@ -58,7 +61,6 @@ pub enum CommandControlFlow {
pub struct CommandContext<'ctx, 'aux, Editor> {
pub ctx: &'ctx mut Context,
pub editor: &'aux mut Editor,
pub session_history: &'aux SessionHistory,
pub interactive: bool,
}

Expand All @@ -68,7 +70,10 @@ pub struct CommandRunner<Editor> {
list: Option<fn(&Context, Option<ListItems>) -> CommandControlFlow>,
clear: Option<fn(&mut Editor) -> CommandControlFlow>,
#[allow(clippy::type_complexity)]
save: Option<fn(&Context, &SessionHistory, &str, bool) -> CommandControlFlow>,
save: Option<(
SessionHistory,
fn(&Context, &SessionHistory, &str, bool) -> CommandControlFlow,
)>,
quit: Option<fn() -> CommandControlFlow>,
}

Expand Down Expand Up @@ -115,9 +120,10 @@ impl<Editor> CommandRunner<Editor> {

pub fn enable_save(
mut self,
session_history: SessionHistory,
action: fn(&Context, &SessionHistory, &str, bool) -> CommandControlFlow,
) -> Self {
self.save = Some(action);
self.save = Some((session_history, action));
self
}

Expand All @@ -126,11 +132,50 @@ impl<Editor> CommandRunner<Editor> {
self
}

pub fn run(&self, cmd: Command, args: CommandContext<Editor>) -> CommandControlFlow {
pub fn push_to_history(&mut self, line: &str, result: Result<(), ()>) {
let Some((session_history, _)) = self.save.as_mut() else {
return;
};
session_history.push(line.to_compact_string(), result);
}

/// Try to run the input line as a command. `Return Some(control_flow)` if it is
/// recognized as a command, `None` otherwise (in which case we should just fall
/// back to the normal Numbat evaluator).
pub fn try_run_line(
&self,
ctx: &mut Context,
line: &str,
editor: &mut Editor,
interactive: bool,
) -> Option<CommandControlFlow> {
let sourceless_parser = SourcelessCommandParser::new(line, self)?;

let mut parser = CommandParser::new(
sourceless_parser,
ctx.resolver_mut().add_code_source(CodeSource::Text, line),
);

Some(match parser.parse_command() {
Ok(cmd) => self.run_command(
cmd,
CommandContext {
ctx,
editor,
interactive,
},
),
Err(e) => {
ctx.print_diagnostic(e);
CommandControlFlow::Continue
}
})
}

fn run_command(&self, cmd: Command, args: CommandContext<Editor>) -> CommandControlFlow {
let CommandContext {
ctx,
editor,
session_history,
interactive,
} = args;

Expand All @@ -139,7 +184,10 @@ impl<Editor> CommandRunner<Editor> {
Command::Info { item } => self.info.unwrap()(ctx, item),
Command::List { items } => self.list.unwrap()(ctx, items),
Command::Clear => self.clear.unwrap()(editor),
Command::Save { dst } => self.save.unwrap()(ctx, session_history, dst, interactive),
Command::Save { dst } => {
let (sh, action) = self.save.as_ref().unwrap();
action(ctx, sh, dst, interactive)
}
Command::Quit => self.quit.unwrap()(),
}
}
Expand Down Expand Up @@ -395,7 +443,9 @@ mod test {
info: Some(|_, _| CommandControlFlow::default()),
list: Some(|_, _| CommandControlFlow::default()),
clear: Some(|_| CommandControlFlow::default()),
save: Some(|_, _, _, _| CommandControlFlow::default()),
save: Some((SessionHistory::default(), |_, _, _, _| {
CommandControlFlow::default()
})),
quit: Some(default_cf0),
}
}
Expand Down

0 comments on commit 321900e

Please sign in to comment.