use crate::{Ed, Substitution};
use crate::buffer::{Buffer, verify_selection, verify_index, verify_line};
use crate::ui::{UI, DummyUI};
use crate::error_consts::*;
mod parse_selection;
use parse_selection::*;
mod parse_expressions;
use parse_expressions::*;
mod parse_path;
use parse_path::*;
mod parse_flags;
use parse_flags::*;
mod commands;
use commands::*;
#[derive(Default)]
struct PrintingFlags {
pub p: bool,
pub n: bool,
pub l: bool,
}
pub fn run<B: Buffer>(state: &mut Ed<'_,B>, ui: &mut dyn UI, command: &str)
-> Result<bool, &'static str>
{
let mut pflags = PrintingFlags::default();
let (cmd_i, selection) = parse_selection(command)?;
let ret = match command[cmd_i..].trim().chars().next() {
None => {
if selection.is_some() {
let sel = interpret_selection(selection, state.selection, state.buffer)?;
verify_selection(state.buffer, sel)?;
state.selection = sel;
pflags.p = true; } else {
scroll(state, &mut pflags, selection, 'z', "",
state.selection.1 - state.selection.0 + 1,
)?;
}
Ok(false)
},
Some(ch) => {
match ch {
'q' | 'Q' |
'h' | 'H' |
'#' |
'=' |
'N' | 'L' |
'f' |
'e' | 'E' | 'r' |
'w' | 'W' |
'p' | 'n' | 'l' |
'z' | 'Z' |
'u' | 'U' => {},
_ => {
if ! state.dont_snapshot {
state.buffer.snapshot()?;
}
},
}
let tail = {
let mut x = 1;
while ! command.is_char_boundary(cmd_i + x) { x += 1; }
&command[cmd_i + x ..]
};
let clean = tail.trim();
match ch {
'q' | 'Q' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
parse_flags(clean, "")?;
if state.buffer.saved() || ch == 'Q' {
Ok(true)
}
else {
Err(UNSAVED_CHANGES)
}
}
'h' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
if clean == "elp" {
ui.print_message(HELP_TEXT)?;
}
else {
parse_flags(clean, "")?;
ui.print_message(state.error.unwrap_or(NO_ERROR))?;
}
Ok(false)
},
'H' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
parse_flags(clean, "")?;
state.print_errors = !state.print_errors; Ok(false)
}
'#' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
Ok(false)
},
'=' => { let sel = interpret_selection(selection, state.selection, state.buffer)?;
verify_selection(state.buffer, sel)?;
state.selection = sel;
ui.print_message(&format!("({},{})", sel.0, sel.1) )?;
Ok(false)
},
'N' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
parse_flags(clean, "")?;
state.n = !state.n;
Ok(false)
},
'L' => {
if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
parse_flags(clean, "")?;
state.l = !state.l;
Ok(false)
},
'f' => { if selection.is_some() { return Err(SELECTION_FORBIDDEN); }
filename(state, ui, clean)?;
Ok(false)
}
'e' | 'E' | 'r' => {
read_from_file(state, selection, ch, clean)?;
Ok(false)
},
'w' | 'W' => {
write_to_file(state, selection, ch, clean)
},
'p' | 'n' | 'l' => {
let sel = interpret_selection(selection, state.selection, state.buffer)?;
verify_selection(state.buffer, 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, selection, ch, clean)?;
Ok(false)
},
'c' | 'C' => {
change(state, ui, &mut pflags, selection, ch, clean)?;
Ok(false)
},
'd' => { let sel = interpret_selection(selection, state.selection, state.buffer)?;
parse_flags(clean, "")?;
state.buffer.cut(sel)?;
state.selection =
(1.max(sel.0 - 1), sel.0 - 1)
;
Ok(false)
},
'y' => { let sel = interpret_selection(selection, state.selection, state.buffer)?;
let mut flags = parse_flags(clean, "pnl")?;
state.buffer.copy(sel)?;
state.selection = sel;
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
Ok(false)
},
'x' | 'X' => { let sel = interpret_selection(selection, state.selection, state.buffer)?;
let mut flags = parse_flags(clean, "pnl")?;
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
let index =
if ch == 'X' { sel.0.saturating_sub(1) }
else { sel.1 }
;
let length = state.buffer.paste(index)?;
state.selection =
if length != 0 {
(index + 1, index + length)
}
else { sel }
;
Ok(false)
},
'u' | 'U' => {
if selection.is_some() {return Err(SELECTION_FORBIDDEN); }
let steps = if clean.is_empty() { 1 }
else { clean.parse::<isize>().map_err(|_| INTEGER_PARSE)? };
if ch == 'U' {
state.buffer.undo( -steps )?;
} else {
state.buffer.undo( steps )?;
}
Ok(false)
},
'k' | 'K' => { let sel = interpret_selection(selection, state.selection, state.buffer)?;
if clean.len() > 1 { return Err(INVALID_TAG); }
let index = if ch == 'k' { sel.0 } else { sel.1 };
state.buffer.tag_line(index, clean.chars().next().unwrap_or('\0'))?;
state.selection = sel;
Ok(false)
},
'm' | 't' => {
transfer(state, &mut pflags, selection, ch, clean)?;
Ok(false)
}
'j' => {
let selection = interpret_selection(selection, state.selection, state.buffer)?;
let mut flags = parse_flags(clean, "pnl")?;
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
state.buffer.join(selection)?;
state.selection = (selection.0, selection.0);
Ok(false)
},
'J' => {
let selection = interpret_selection(selection, state.selection, state.buffer)?;
let nr_end = clean.find( | c: char | !c.is_numeric() ).unwrap_or(clean.len());
let width = if nr_end == 0 {
80
} else {
clean[.. nr_end].parse::<usize>().map_err(|_| INTEGER_PARSE)?
};
let mut flags = parse_flags(&clean[nr_end ..], "pnl")?;
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
let end = state.buffer.reflow(selection, width)?;
state.selection = (selection.0.min(end).max(1), end);
Ok(false)
},
's' => {
substitute(state, &mut pflags, selection, tail)?;
Ok(false)
},
'g' | 'v' => {
state.dont_snapshot = true;
let res = global(state, ui, selection, ch, clean);
state.dont_snapshot = false;
res?;
Ok(false)
},
'G' | 'V' => {
state.dont_snapshot = true;
let res = global_inv(state, ui, selection, ch, clean);
state.dont_snapshot = false;
res?;
Ok(false)
},
':' => {
let selection = interpret_selection(selection, state.selection, state.buffer)?;
verify_selection(state.buffer, selection)?;
match state.macros.get(clean) {
Some(m) => {
state.dont_snapshot = true;
let mut dummy = DummyUI{
input: m.lines().map(|x| format!("{}\n",x)).collect(),
print_ui: Some(ui),
};
state.selection = selection;
let res = state.run_macro(&mut dummy);
state.dont_snapshot = false;
res?;
},
None => return Err(UNDEFINED_MACRO),
}
Ok(false)
},
_cmd => {
Err(UNDEFINED_COMMAND)
}
}
}
}?;
if pflags.p | pflags.n | pflags.l {
verify_selection(state.buffer, state.selection)?;
ui.print_selection(
state.see_state(),
state.selection,
state.n^pflags.n,
state.l^pflags.l
)?;
}
Ok(ret)
}