use crate::{Ed, Substitution, Line, Clipboard, Buffer};
use crate::ui::{UI, ScriptedUI};
use crate::error::*;
use crate::messages::*;
mod parsing;
use parsing::*;
mod io_commands;
use io_commands::*;
mod editing_commands;
use editing_commands::*;
mod regex_commands;
use regex_commands::*;
mod undo;
use undo::*;
#[derive(Default)]
pub struct PrintingFlags {
pub p: bool,
pub n: bool,
pub l: bool,
}
pub(crate) fn run(
state: &mut Ed<'_>,
ui: &mut dyn UI,
command: &str,
recursion_depth: usize,
) -> Result<bool> {
if recursion_depth > state.recursion_limit {
return Err(EdError::InfiniteRecursion);
}
let mut pflags = PrintingFlags::default();
let (cmd_i, selection) = parse_selection(command)?;
let ret = match command[cmd_i..].trim_end().chars().next() {
None => {
if selection.is_some() {
let sel = interpret_selection(&state, selection, state.selection)?;
state.history.current().verify_selection(sel)?;
state.selection = sel;
pflags.p = true; } else {
state.history.current().verify_selection(state.selection)?;
scroll(state, &mut pflags, selection, 'z', "",
state.selection.1 - state.selection.0 + 1,
)?;
}
Ok(false)
},
Some(ch) => {
let tail = {
let mut x = 1;
while ! command.is_char_boundary(cmd_i + x) { x += 1; }
&command[cmd_i + x ..]
};
let clean = tail.trim_end_matches('\n');
let clean_command = command.trim_end_matches('\n');
match ch {
'q' | 'Q' => {
if selection.is_some() { return Err(EdError::SelectionForbidden); }
parse_flags(clean, "")?;
if state.history.saved() || ch == 'Q' {
Ok(true)
}
else {
Err(EdError::UnsavedChanges)
}
}
'h' => {
if selection.is_some() { return Err(EdError::SelectionForbidden); }
if clean == "elp" { ui.print_commands()?; }
else {
parse_flags(clean, "")?;
match &state.error {
Some(e) => {
let msg = e.to_string();
ui.print_message(&msg)?;
},
None => ui.print_message(NO_ERROR)?,
}
}
Ok(false)
},
'H' => {
if selection.is_some() { return Err(EdError::SelectionForbidden); }
if clean == "elp" { ui.print_command_documentation()?; }
else {
parse_flags(clean, "")?;
state.print_errors = !state.print_errors; }
Ok(false)
}
'=' | '#' => {
let sel = interpret_selection(&state, selection, state.selection)?;
state.history.current().verify_selection(sel)?;
if ch== '=' { parse_flags(clean, "")?; }
state.selection = sel;
if ch == '=' { ui.print_message(&format!("({},{})", sel.0, sel.1) )?; }
Ok(false)
},
'P' => {
if selection.is_some() { return Err(EdError::SelectionForbidden); }
let mut flags = parse_flags(clean, "nl")?;
if flags.remove(&'l').unwrap() {
state.l = !state.l;
}
if flags.remove(&'n').unwrap() {
state.n = !state.n;
}
Ok(false)
},
'f' => { if selection.is_some() { return Err(EdError::SelectionForbidden); }
filename(state, ui, clean)?;
Ok(false)
},
'!' | '|' => {
run_command(state, ui, clean_command, selection, ch, clean)?;
Ok(false)
},
'e' | 'E' | 'r' => {
read_from_file(state, ui, clean_command, selection, ch, clean)?;
Ok(false)
},
'w' | 'W' => {
write_to_file(state, ui, selection, ch, clean)
},
'p' | 'n' | 'l' => {
let sel = interpret_selection(&state, selection, state.selection)?;
state.history.current().verify_selection(sel)?;
let mut flags = parse_flags(&command[cmd_i..], "pnl")?;
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
state.selection = sel;
Ok(false)
},
'z' | 'Z' => {
scroll(state, &mut pflags, selection, ch, clean, 3)?;
Ok(false)
},
'a' | 'i' | 'A' | 'I' => {
input(state, ui, &mut pflags, clean_command, selection, ch, clean)?;
Ok(false)
},
'c' | 'C' => {
change(state, ui, &mut pflags, clean_command, selection, ch, clean)?;
Ok(false)
},
'd' => { cut(state, &mut pflags, clean_command, selection, clean)?;
Ok(false)
},
'y' => { copy(state, &mut pflags, selection, clean)?;
Ok(false)
},
'x' | 'X' => { paste(state, &mut pflags, clean_command, selection, ch, clean)?;
Ok(false)
},
'U' => {
manage_history(state, ui, selection, clean)?;
Ok(false)
},
'u' => { undo(state, ui, selection, clean)?;
Ok(false)
},
'k' | 'K' => { tag(state, selection, ch, clean)?;
Ok(false)
},
'm' | 't' => {
transfer(state, &mut pflags, clean_command, selection, ch, clean)?;
Ok(false)
}
'j' => {
join(state, &mut pflags, clean_command, selection, clean)?;
Ok(false)
},
's' => {
substitute(state, &mut pflags, clean_command, selection, tail)?;
Ok(false)
},
'g' | 'v' | 'G' | 'V' => {
state.history.snapshot(clean_command.to_string());
let orig_dont_snapshot = state.history.dont_snapshot;
state.history.dont_snapshot = true;
let res = if ch == 'g' || ch == 'v' {
global(state, ui, selection, ch, clean, recursion_depth)
} else {
global_interactive(state, ui, selection, ch, clean, recursion_depth)
};
state.history.dont_snapshot = orig_dont_snapshot;
if !orig_dont_snapshot { state.history.dedup_present(); }
res?;
Ok(false)
},
':' => {
let given_selection = if selection.is_some() {
let s = interpret_selection(&state, selection, state.selection)?;
state.history.current().verify_selection(s)?;
Some(s)
}
else {
None
};
let mut args = clean.split(' ');
let macro_name = args.next().unwrap_or("");
let args: Vec<&str> = args.collect();
match state.macro_getter.get_macro(macro_name)? {
Some(m) => {
state.history.snapshot(clean_command.into());
let orig_dont_snapshot = state.history.dont_snapshot;
state.history.dont_snapshot = true;
if let Some(selection) = given_selection {
state.selection = selection;
}
let res = state.private_run_macro(ui, m, &args, recursion_depth+1);
state.history.dont_snapshot = orig_dont_snapshot;
if !orig_dont_snapshot { state.history.dedup_present(); }
res
},
None => Err(EdError::MacroUndefined(macro_name.to_owned())),
}?;
Ok(false)
},
_cmd => {
Err(EdError::CommandUndefined(ch))
}
}
}
}?;
if pflags.p | pflags.n | pflags.l {
state.history.current().verify_selection(state.selection)?;
ui.print_selection(
state,
state.selection,
state.n^pflags.n,
state.l^pflags.l
)?;
}
Ok(ret)
}