1 use crate::autocomplete::{self, Autocomplete, FileCompleter, FileCompleterOpts}; 2 use crate::brush::{Brush, BrushMode}; 3 use crate::history::History; 4 use crate::parser::{Error, Parse, Parser, Result}; 5 use crate::platform; 6 use crate::session::{Direction, Mode, PanState, Tool, VisualState}; 7 8 use rgx::kit::Rgba8; 9 use rgx::rect::Rect; 10 11 use std::fmt; 12 use std::path::Path; 13 use std::result; 14 use std::str::FromStr; 15 16 pub const COMMENT: char = '-'; 17 18 #[derive(Clone, PartialEq, Debug)] 19 pub enum Op { 20 Incr, 21 Decr, 22 Set(f32), 23 } 24 25 /// User command. Most of the interactions available to 26 /// the user are modeled as commands that are processed 27 /// by the session. 28 #[derive(PartialEq, Debug, Clone)] 29 pub enum Command { 30 Brush, 31 BrushSet(BrushMode), 32 BrushToggle(BrushMode), 33 BrushSize(Op), 34 BrushUnset(BrushMode), 35 #[allow(dead_code)] 36 Crop(Rect<u32>), 37 ChangeDir(Option<String>), 38 Echo(Value), 39 Edit(Vec<String>), 40 EditFrames(Vec<String>), 41 Fill(Rgba8), 42 ForceQuit, 43 ForceQuitAll, 44 Map(Box<KeyMapping>), 45 MapClear, 46 Mode(Mode), 47 AddFrame, 48 CloneFrame(i32), 49 RemoveFrame, 50 Noop, 51 PaletteAdd(Rgba8), 52 PaletteClear, 53 PaletteSample, 54 Pan(i32, i32), 55 Quit, 56 QuitAll, 57 Reset, 58 Redo, 59 ResizeFrame(u32, u32), 60 SelectionMove(i32, i32), 61 SelectionResize(i32, i32), 62 SelectionOffset(i32, i32), 63 SelectionExpand, 64 SelectionPaste, 65 SelectionYank, 66 SelectionCut, 67 SelectionFill(Option<Rgba8>), 68 SelectionErase, 69 SelectionJump(Direction), 70 Set(String, Value), 71 Slice(Option<usize>), 72 Source(Option<String>), 73 SwapColors, 74 Toggle(String), 75 Tool(Tool), 76 ToolPrev, 77 Undo, 78 ViewCenter, 79 ViewNext, 80 ViewPrev, 81 WriteFrames(Option<String>), 82 Write(Option<String>), 83 WriteQuit, 84 Zoom(Op), 85 } 86 87 impl Command { repeats(&self) -> bool88 pub fn repeats(&self) -> bool { 89 match self { 90 Self::Zoom(_) 91 | Self::BrushSize(_) 92 | Self::Pan(_, _) 93 | Self::Undo 94 | Self::Redo 95 | Self::ViewNext 96 | Self::ViewPrev 97 | Self::SelectionMove(_, _) 98 | Self::SelectionJump(_) 99 | Self::SelectionResize(_, _) 100 | Self::SelectionOffset(_, _) => true, 101 _ => false, 102 } 103 } 104 } 105 106 impl fmt::Display for Command { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 108 match self { 109 Self::Brush => write!(f, "Reset brush"), 110 Self::BrushSet(m) => write!(f, "Set brush mode to `{}`", m), 111 Self::BrushToggle(m) => write!(f, "Toggle `{}` brush mode", m), 112 Self::BrushSize(Op::Incr) => write!(f, "Increase brush size"), 113 Self::BrushSize(Op::Decr) => write!(f, "Decrease brush size"), 114 Self::BrushSize(Op::Set(s)) => write!(f, "Set brush size to {}", s), 115 Self::BrushUnset(m) => write!(f, "Unset brush `{}` mode", m), 116 Self::Crop(_) => write!(f, "Crop view"), 117 Self::ChangeDir(_) => write!(f, "Change the current working directory"), 118 Self::Echo(_) => write!(f, "Echo a value"), 119 Self::Edit(_) => write!(f, "Edit path(s)"), 120 Self::EditFrames(_) => write!(f, "Edit path(s) as animation frames"), 121 Self::Fill(c) => write!(f, "Fill view with {color}", color = c), 122 Self::ForceQuit => write!(f, "Quit view without saving"), 123 Self::ForceQuitAll => write!(f, "Quit all views without saving"), 124 Self::Map(_) => write!(f, "Map a key combination to a command"), 125 Self::MapClear => write!(f, "Clear all key mappings"), 126 Self::Mode(m) => write!(f, "Switch session mode to {}", m), 127 Self::AddFrame => write!(f, "Add a blank frame to the view"), 128 Self::CloneFrame(i) => write!(f, "Clone frame {} and add it to the view", i), 129 Self::RemoveFrame => write!(f, "Remove the last frame of the view"), 130 Self::Noop => write!(f, "No-op"), 131 Self::PaletteAdd(c) => write!(f, "Add {color} to palette", color = c), 132 Self::PaletteClear => write!(f, "Clear palette"), 133 Self::PaletteSample => write!(f, "Sample palette from view"), 134 Self::Pan(x, 0) if *x > 0 => write!(f, "Pan workspace right"), 135 Self::Pan(x, 0) if *x < 0 => write!(f, "Pan workspace left"), 136 Self::Pan(0, y) if *y > 0 => write!(f, "Pan workspace up"), 137 Self::Pan(0, y) if *y < 0 => write!(f, "Pan workspace down"), 138 Self::Pan(x, y) => write!(f, "Pan workspace by {},{}", x, y), 139 Self::Quit => write!(f, "Quit active view"), 140 Self::QuitAll => write!(f, "Quit all views"), 141 Self::Redo => write!(f, "Redo view edit"), 142 Self::ResizeFrame(_, _) => write!(f, "Resize active view frame"), 143 Self::Tool(Tool::Pan(_)) => write!(f, "Pan tool"), 144 Self::Tool(Tool::Brush(_)) => write!(f, "Brush tool"), 145 Self::Tool(Tool::Sampler) => write!(f, "Color sampler tool"), 146 Self::ToolPrev => write!(f, "Switch to previous tool"), 147 Self::Set(s, v) => write!(f, "Set {setting} to {val}", setting = s, val = v), 148 Self::Slice(Some(n)) => write!(f, "Slice view into {} frame(s)", n), 149 Self::Slice(None) => write!(f, "Reset view slices"), 150 Self::Source(_) => write!(f, "Source an rx script (eg. a palette)"), 151 Self::SwapColors => write!(f, "Swap foreground & background colors"), 152 Self::Toggle(s) => write!(f, "Toggle {setting} on/off", setting = s), 153 Self::Undo => write!(f, "Undo view edit"), 154 Self::ViewCenter => write!(f, "Center active view"), 155 Self::ViewNext => write!(f, "Go to next view"), 156 Self::ViewPrev => write!(f, "Go to previous view"), 157 Self::Write(None) => write!(f, "Write view to disk"), 158 Self::Write(Some(_)) => write!(f, "Write view to disk as..."), 159 Self::WriteQuit => write!(f, "Write file to disk and quit"), 160 Self::Zoom(Op::Incr) => write!(f, "Zoom in view"), 161 Self::Zoom(Op::Decr) => write!(f, "Zoom out view"), 162 Self::Zoom(Op::Set(z)) => write!(f, "Set view zoom to {:.1}", z), 163 Self::Reset => write!(f, "Reset all settings to default"), 164 Self::SelectionFill(None) => write!(f, "Fill selection with foreground color"), 165 Self::SelectionYank => write!(f, "Yank (copy) selection"), 166 Self::SelectionCut => write!(f, "Cut selection"), 167 Self::SelectionPaste => write!(f, "Paste selection"), 168 Self::SelectionExpand => write!(f, "Expand selection to frame"), 169 Self::SelectionOffset(1, 1) => write!(f, "Outset selection"), 170 Self::SelectionOffset(-1, -1) => write!(f, "Inset selection"), 171 Self::SelectionOffset(x, y) => write!(f, "Offset selection by {:2},{:2}", x, y), 172 Self::SelectionMove(x, 0) if *x > 0 => write!(f, "Move selection right"), 173 Self::SelectionMove(x, 0) if *x < 0 => write!(f, "Move selection left"), 174 Self::SelectionMove(0, y) if *y > 0 => write!(f, "Move selection up"), 175 Self::SelectionMove(0, y) if *y < 0 => write!(f, "Move selection down"), 176 Self::SelectionJump(Direction::Forward) => { 177 write!(f, "Move selection forward by one frame") 178 } 179 Self::SelectionJump(Direction::Backward) => { 180 write!(f, "Move selection backward by one frame") 181 } 182 Self::SelectionErase => write!(f, "Erase selection contents"), 183 _ => write!(f, "..."), 184 } 185 } 186 } 187 188 impl From<Command> for String { from(cmd: Command) -> Self189 fn from(cmd: Command) -> Self { 190 match cmd { 191 Command::Brush => format!("brush"), 192 Command::BrushSet(m) => format!("brush/set {}", m), 193 Command::BrushSize(Op::Incr) => format!("brush/size +"), 194 Command::BrushSize(Op::Decr) => format!("brush/size -"), 195 Command::BrushSize(Op::Set(s)) => format!("brush/size {}", s), 196 Command::BrushUnset(m) => format!("brush/unset {}", m), 197 Command::Echo(_) => unimplemented!(), 198 Command::Edit(_) => unimplemented!(), 199 Command::Fill(c) => format!("v/fill {}", c), 200 Command::ForceQuit => format!("q!"), 201 Command::ForceQuitAll => format!("qa!"), 202 Command::Map(_) => format!("map <key> <command> {{<command>}}"), 203 Command::Mode(m) => format!("mode {}", m), 204 Command::AddFrame => format!("f/add"), 205 Command::CloneFrame(i) => format!("f/clone {}", i), 206 Command::RemoveFrame => format!("f/remove"), 207 Command::Noop => format!(""), 208 Command::PaletteAdd(c) => format!("p/add {}", c), 209 Command::PaletteClear => format!("p/clear"), 210 Command::PaletteSample => unimplemented!(), 211 Command::Pan(x, y) => format!("pan {} {}", x, y), 212 Command::Quit => format!("q"), 213 Command::Redo => format!("redo"), 214 Command::ResizeFrame(w, h) => format!("f/resize {} {}", w, h), 215 Command::Set(s, v) => format!("set {} = {}", s, v), 216 Command::Slice(Some(n)) => format!("slice {}", n), 217 Command::Slice(None) => format!("slice"), 218 Command::Source(Some(path)) => format!("source {}", path), 219 Command::SwapColors => format!("swap"), 220 Command::Toggle(s) => format!("toggle {}", s), 221 Command::Undo => format!("undo"), 222 Command::ViewCenter => format!("v/center"), 223 Command::ViewNext => format!("v/next"), 224 Command::ViewPrev => format!("v/prev"), 225 Command::Write(None) => format!("w"), 226 Command::Write(Some(path)) => format!("w {}", path), 227 Command::WriteQuit => format!("wq"), 228 Command::Zoom(Op::Incr) => format!("v/zoom +"), 229 Command::Zoom(Op::Decr) => format!("v/zoom -"), 230 Command::Zoom(Op::Set(z)) => format!("v/zoom {}", z), 231 _ => unimplemented!(), 232 } 233 } 234 } 235 236 impl<'a> Parse<'a> for platform::Key { parse(p: Parser<'a>) -> Result<'a, Self>237 fn parse(p: Parser<'a>) -> Result<'a, Self> { 238 if let Ok((_, p)) = p.clone().sigil('<') { 239 let (key, p) = p.alpha()?; 240 let (_, p) = p.sigil('>')?; 241 let key = match key { 242 "up" => platform::Key::Up, 243 "down" => platform::Key::Down, 244 "left" => platform::Key::Left, 245 "right" => platform::Key::Right, 246 "ctrl" => platform::Key::Control, 247 "alt" => platform::Key::Alt, 248 "shift" => platform::Key::Shift, 249 "space" => platform::Key::Space, 250 "return" => platform::Key::Return, 251 "backspace" => platform::Key::Backspace, 252 "tab" => platform::Key::Tab, 253 "end" => platform::Key::End, 254 "esc" => platform::Key::Escape, 255 other => return Err(Error::new(format!("unknown key <{}>", other))), 256 }; 257 Ok((key, p)) 258 } else { 259 let (c, p) = p.parse::<char>()?; 260 let key: platform::Key = c.into(); 261 262 if key == platform::Key::Unknown { 263 return Err(Error::new(format!("unknown key {:?}", c))); 264 } 265 Ok((key, p)) 266 } 267 } 268 } 269 270 /////////////////////////////////////////////////////////////////////////////// 271 272 #[derive(PartialEq, Debug, Clone)] 273 pub struct KeyMapping { 274 pub key: platform::Key, 275 pub press: Command, 276 pub release: Option<Command>, 277 pub modes: Vec<Mode>, 278 } 279 280 impl KeyMapping { parse<'a>(p: Parser<'a>, modes: &[Mode]) -> Result<'a, Self>281 fn parse<'a>(p: Parser<'a>, modes: &[Mode]) -> Result<'a, Self> { 282 let modes = modes.to_vec(); 283 284 let (key, p) = p.parse::<platform::Key>()?; 285 let (_, p) = p.whitespace()?; 286 let (press, p) = p.parse::<Command>()?; 287 let (_, p) = p.whitespace()?; 288 289 let (release, p) = if let Ok((_, p)) = p.clone().sigil('{') { 290 let (cmd, p) = p.parse::<Command>()?; 291 let (_, p) = p.sigil('}')?; 292 (Some(cmd), p) 293 } else { 294 (None, p) 295 }; 296 Ok(( 297 KeyMapping { 298 key, 299 press, 300 release, 301 modes, 302 }, 303 p, 304 )) 305 } 306 } 307 308 //////////////////////////////////////////////////////////////////////////////// 309 310 #[derive(Clone, PartialEq, Debug)] 311 pub enum Value { 312 Bool(bool), 313 U32(u32), 314 U32Tuple(u32, u32), 315 F64(f64), 316 F64Tuple(f32, f32), 317 Str(String), 318 Ident(String), 319 Rgba8(Rgba8), 320 } 321 322 impl Value { is_set(&self) -> bool323 pub fn is_set(&self) -> bool { 324 if let Value::Bool(b) = self { 325 return *b; 326 } 327 panic!("expected {:?} to be a `bool`", self); 328 } 329 to_f64(&self) -> f64330 pub fn to_f64(&self) -> f64 { 331 if let Value::F64(n) = self { 332 return *n; 333 } 334 panic!("expected {:?} to be a `float`", self); 335 } 336 to_u64(&self) -> u64337 pub fn to_u64(&self) -> u64 { 338 if let Value::U32(n) = self { 339 return *n as u64; 340 } 341 panic!("expected {:?} to be a `uint`", self); 342 } 343 to_rgba8(&self) -> Rgba8344 pub fn to_rgba8(&self) -> Rgba8 { 345 if let Value::Rgba8(rgba8) = self { 346 return *rgba8; 347 } 348 panic!("expected {:?} to be a `Rgba8`", self); 349 } 350 description(&self) -> &'static str351 pub fn description(&self) -> &'static str { 352 match self { 353 Self::Bool(_) => "on / off", 354 Self::U32(_) => "positive integer, eg. 32", 355 Self::F64(_) => "float, eg. 1.33", 356 Self::U32Tuple(_, _) => "two positive integers, eg. 32, 48", 357 Self::F64Tuple(_, _) => "two floats , eg. 32.17, 48.29", 358 Self::Str(_) => "string, eg. \"fnord\"", 359 Self::Rgba8(_) => "color, eg. #ffff00", 360 Self::Ident(_) => "identifier, eg. fnord", 361 } 362 } 363 } 364 365 impl Into<(u32, u32)> for Value { into(self) -> (u32, u32)366 fn into(self) -> (u32, u32) { 367 if let Value::U32Tuple(x, y) = self { 368 return (x, y); 369 } 370 panic!("expected {:?} to be a `(u32, u32)`", self); 371 } 372 } 373 374 impl Into<f32> for Value { into(self) -> f32375 fn into(self) -> f32 { 376 if let Value::F64(x) = self { 377 return x as f32; 378 } 379 panic!("expected {:?} to be a `f64`", self); 380 } 381 } 382 383 impl Into<f64> for Value { into(self) -> f64384 fn into(self) -> f64 { 385 if let Value::F64(x) = self { 386 return x as f64; 387 } 388 panic!("expected {:?} to be a `f64`", self); 389 } 390 } 391 392 impl<'a> Parse<'a> for Value { parse(p: Parser<'a>) -> Result<'a, Self>393 fn parse(p: Parser<'a>) -> Result<'a, Self> { 394 let c = p.peek(); 395 396 if c == Some('"') { 397 let (v, p) = p.string()?; 398 Ok((Value::Str(v.to_string()), p)) 399 } else if c == Some('#') { 400 let (v, p) = p.parse::<Rgba8>()?; 401 Ok((Value::Rgba8(v), p)) 402 } else if c.map_or(false, |c| c.is_digit(10)) { 403 if let Ok(((x, y), p)) = p.clone().parse::<(u32, u32)>() { 404 Ok((Value::U32Tuple(x, y), p)) 405 } else if let Ok((v, p)) = p.clone().parse::<u32>() { 406 Ok((Value::U32(v), p)) 407 } else if let Ok((v, p)) = p.clone().parse::<f64>() { 408 Ok((Value::F64(v), p)) 409 } else { 410 let (input, _) = p.until(|c| c.is_whitespace())?; 411 Err(Error::new(format!("malformed number: `{}`", input))) 412 } 413 } else { 414 let (i, p) = p.identifier()?; 415 match i { 416 "on" => Ok((Value::Bool(true), p)), 417 "off" => Ok((Value::Bool(false), p)), 418 _ => Ok((Value::Ident(i.to_string()), p)), 419 } 420 } 421 } 422 } 423 424 impl fmt::Display for Value { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 426 match self { 427 Value::Bool(true) => "on".fmt(f), 428 Value::Bool(false) => "off".fmt(f), 429 Value::U32(u) => u.fmt(f), 430 Value::F64(x) => x.fmt(f), 431 Value::U32Tuple(x, y) => write!(f, "{},{}", x, y), 432 Value::F64Tuple(x, y) => write!(f, "{},{}", x, y), 433 Value::Str(s) => s.fmt(f), 434 Value::Rgba8(c) => c.fmt(f), 435 Value::Ident(i) => i.fmt(f), 436 } 437 } 438 } 439 440 //////////////////////////////////////////////////////////////////////////////// 441 442 #[derive(Debug)] 443 pub struct CommandLine { 444 /// The history of commands entered. 445 pub history: History, 446 /// Command auto-complete. 447 pub autocomplete: Autocomplete<CommandCompleter>, 448 /// Input cursor position. 449 pub cursor: usize, 450 /// The current input string displayed to the user. 451 input: String, 452 /// File extensions supported. 453 extensions: Vec<String>, 454 } 455 456 impl CommandLine { 457 const MAX_INPUT: usize = 256; 458 new<P: AsRef<Path>>(cwd: P, history_path: P, extensions: &[&str]) -> Self459 pub fn new<P: AsRef<Path>>(cwd: P, history_path: P, extensions: &[&str]) -> Self { 460 Self { 461 input: String::with_capacity(Self::MAX_INPUT), 462 cursor: 0, 463 history: History::new(history_path, 1024), 464 autocomplete: Autocomplete::new(CommandCompleter::new(cwd, extensions)), 465 extensions: extensions.iter().map(|e| (*e).into()).collect(), 466 } 467 } 468 set_cwd(&mut self, path: &Path)469 pub fn set_cwd(&mut self, path: &Path) { 470 let exts: Vec<_> = self.extensions.iter().map(|s| s.as_str()).collect(); 471 self.autocomplete = Autocomplete::new(CommandCompleter::new(path, exts.as_slice())); 472 } 473 input(&self) -> String474 pub fn input(&self) -> String { 475 self.input.clone() 476 } 477 is_empty(&self) -> bool478 pub fn is_empty(&self) -> bool { 479 self.input.is_empty() 480 } 481 history_prev(&mut self)482 pub fn history_prev(&mut self) { 483 let prefix = self.prefix(); 484 485 if let Some(entry) = self.history.prev(&prefix).map(str::to_owned) { 486 self.replace(&entry); 487 } 488 } 489 history_next(&mut self)490 pub fn history_next(&mut self) { 491 let prefix = self.prefix(); 492 493 if let Some(entry) = self.history.next(&prefix).map(str::to_owned) { 494 self.replace(&entry); 495 } else { 496 self.reset(); 497 } 498 } 499 completion_next(&mut self)500 pub fn completion_next(&mut self) { 501 let prefix = self.prefix(); 502 503 if let Some((completion, range)) = self.autocomplete.next(&prefix, self.cursor) { 504 // Replace old completion with new one. 505 self.cursor = range.start + completion.len(); 506 self.input.replace_range(range, &completion); 507 } 508 } 509 cursor_backward(&mut self) -> Option<char>510 pub fn cursor_backward(&mut self) -> Option<char> { 511 if let Some(c) = self.peek_back() { 512 let cursor = self.cursor - c.len_utf8(); 513 514 // Don't allow deleting the `:` prefix of the command. 515 if c != ':' || cursor > 0 { 516 self.cursor = cursor; 517 self.autocomplete.invalidate(); 518 return Some(c); 519 } 520 } 521 None 522 } 523 cursor_forward(&mut self) -> Option<char>524 pub fn cursor_forward(&mut self) -> Option<char> { 525 if let Some(c) = self.input[self.cursor..].chars().next() { 526 self.cursor += c.len_utf8(); 527 self.autocomplete.invalidate(); 528 Some(c) 529 } else { 530 None 531 } 532 } 533 putc(&mut self, c: char)534 pub fn putc(&mut self, c: char) { 535 if self.input.len() + c.len_utf8() > self.input.capacity() { 536 return; 537 } 538 self.input.insert(self.cursor, c); 539 self.cursor += c.len_utf8(); 540 self.autocomplete.invalidate(); 541 } 542 puts(&mut self, s: &str)543 pub fn puts(&mut self, s: &str) { 544 // TODO: Check capacity. 545 self.input.push_str(s); 546 self.cursor += s.len(); 547 self.autocomplete.invalidate(); 548 } 549 delc(&mut self)550 pub fn delc(&mut self) { 551 match self.peek_back() { 552 // Don't allow deleting the ':' unless it's the last remaining character. 553 Some(c) if self.cursor > 1 || self.input.len() == 1 => { 554 self.cursor -= c.len_utf8(); 555 self.input.remove(self.cursor); 556 self.autocomplete.invalidate(); 557 } 558 _ => {} 559 } 560 } 561 clear(&mut self)562 pub fn clear(&mut self) { 563 self.cursor = 0; 564 self.input.clear(); 565 self.history.reset(); 566 self.autocomplete.invalidate(); 567 } 568 569 //////////////////////////////////////////////////////////////////////////// 570 replace(&mut self, s: &str)571 fn replace(&mut self, s: &str) { 572 // We don't re-assign `input` here, because it 573 // has a fixed capacity we want to preserve. 574 self.input.clear(); 575 self.input.push_str(s); 576 self.autocomplete.invalidate(); 577 } 578 reset(&mut self)579 fn reset(&mut self) { 580 self.clear(); 581 self.putc(':'); 582 } 583 prefix(&self) -> String584 fn prefix(&self) -> String { 585 self.input[..self.cursor].to_owned() 586 } 587 588 #[cfg(test)] peek(&self) -> Option<char>589 fn peek(&self) -> Option<char> { 590 self.input[self.cursor..].chars().next() 591 } 592 peek_back(&self) -> Option<char>593 fn peek_back(&self) -> Option<char> { 594 self.input[..self.cursor].chars().next_back() 595 } 596 } 597 598 /////////////////////////////////////////////////////////////////////////////// 599 600 impl FromStr for Command { 601 type Err = Error; 602 from_str(input: &str) -> result::Result<Self, Self::Err>603 fn from_str(input: &str) -> result::Result<Self, Self::Err> { 604 let p = Parser::new(input); 605 match p.parse::<Command>() { 606 Ok((cmd, p)) => { 607 let (_, p) = p.clone().comment().unwrap_or(("", p)); 608 p.finish()?; // Make sure we've consumed all the input 609 Ok(cmd) 610 } 611 // TODO: Use `enum` for error. 612 Err(e) => Err(e), 613 } 614 } 615 } 616 617 impl<'a> Parse<'a> for Command { parse(p: Parser<'a>) -> Result<'a, Self>618 fn parse(p: Parser<'a>) -> Result<'a, Self> { 619 let (_, p) = p.sigil(':')?; 620 let (_, p) = p.whitespace()?; 621 622 if p.is_empty() { 623 return Ok((Command::Noop, p)); 624 } 625 626 if Some('#') == p.peek() { 627 let (rgba, p) = p.parse::<Rgba8>()?; 628 return Ok((Command::PaletteAdd(rgba), p)); 629 } 630 631 let (cmd, p) = p.identifier()?; 632 let (_, p) = p.whitespace()?; 633 634 match cmd { 635 "q" => Ok((Command::Quit, p)), 636 "qa" => Ok((Command::QuitAll, p)), 637 "q!" => Ok((Command::ForceQuit, p)), 638 "qa!" => Ok((Command::ForceQuitAll, p)), 639 "wq" | "x" => Ok((Command::WriteQuit, p)), 640 "w" => { 641 if p.is_empty() { 642 Ok((Command::Write(None), p)) 643 } else { 644 let (path, p) = p.path()?; 645 Ok((Command::Write(Some(path)), p)) 646 } 647 } 648 "w/frames" => { 649 if p.is_empty() { 650 Ok((Command::WriteFrames(None), p)) 651 } else { 652 let (dir, p) = p.path()?; 653 Ok((Command::WriteFrames(Some(dir)), p)) 654 } 655 } 656 "e" => { 657 let (paths, p) = p.paths()?; 658 Ok((Command::Edit(paths), p)) 659 } 660 "e/frames" => { 661 let (paths, p) = p.paths()?; 662 Ok((Command::EditFrames(paths), p)) 663 } 664 "help" => Ok((Command::Mode(Mode::Help), p)), 665 "set" => { 666 let (k, p) = p.identifier()?; 667 let (_, p) = p.whitespace()?; 668 669 if p.is_empty() { 670 Ok((Command::Set(k.to_string(), Value::Bool(true)), p)) 671 } else { 672 let (_, p) = p.sigil('=')?; 673 let (_, p) = p.whitespace()?; 674 let (v, p) = p.parse::<Value>()?; 675 Ok((Command::Set(k.to_string(), v), p)) 676 } 677 } 678 "unset" => { 679 let (k, p) = p.identifier()?; 680 Ok((Command::Set(k.to_string(), Value::Bool(false)), p)) 681 } 682 "toggle" => { 683 let (k, p) = p.identifier()?; 684 Ok((Command::Toggle(k.to_string()), p)) 685 } 686 "echo" => { 687 let (v, p) = p.parse::<Value>()?; 688 Ok((Command::Echo(v), p)) 689 } 690 "slice" => { 691 if p.is_empty() { 692 Ok((Command::Slice(None), p)) 693 } else { 694 let (n, p) = p.parse::<u32>()?; 695 Ok((Command::Slice(Some(n as usize)), p)) 696 } 697 } 698 "source" => { 699 if p.is_empty() { 700 Ok((Command::Source(None), p)) 701 } else { 702 let (path, p) = p.path()?; 703 Ok((Command::Source(Some(path)), p)) 704 } 705 } 706 "cd" => { 707 if p.is_empty() { 708 Ok((Command::ChangeDir(None), p)) 709 } else { 710 let (path, p) = p.path()?; 711 Ok((Command::ChangeDir(Some(path)), p)) 712 } 713 } 714 "zoom" => { 715 if let Ok((_, p)) = p.clone().sigil('+') { 716 Ok((Command::Zoom(Op::Incr), p)) 717 } else if let Ok((_, p)) = p.clone().sigil('-') { 718 Ok((Command::Zoom(Op::Decr), p)) 719 } else if let Ok((z, p)) = p.parse::<f64>() { 720 Ok((Command::Zoom(Op::Set(z as f32)), p)) 721 } else { 722 Err(Error::new("couldn't parse zoom parameter")) 723 } 724 } 725 "brush" => Ok((Command::Tool(Tool::Brush(Brush::default())), p)), 726 "brush/size" => { 727 let (c, p) = p.parse::<char>()?; 728 match c { 729 '+' => Ok((Command::BrushSize(Op::Incr), p)), 730 '-' => Ok((Command::BrushSize(Op::Decr), p)), 731 _ => Err(Error::new("invalid parameter")), 732 } 733 } 734 "brush/set" => { 735 let (mode, p) = p.parse::<BrushMode>()?; 736 Ok((Command::BrushSet(mode), p)) 737 } 738 "brush/unset" => { 739 let (mode, p) = p.parse::<BrushMode>()?; 740 Ok((Command::BrushUnset(mode), p)) 741 } 742 "brush/toggle" => { 743 let (mode, p) = p.parse::<BrushMode>()?; 744 Ok((Command::BrushToggle(mode), p)) 745 } 746 "mode" => { 747 let (mode, p) = p.parse::<Mode>()?; 748 Ok((Command::Mode(mode), p)) 749 } 750 "visual" => Ok((Command::Mode(Mode::Visual(VisualState::default())), p)), 751 "sampler" => Ok((Command::Tool(Tool::Sampler), p)), 752 "sampler/off" => Ok((Command::ToolPrev, p)), 753 "v/next" => Ok((Command::ViewNext, p)), 754 "v/prev" => Ok((Command::ViewPrev, p)), 755 "v/center" => Ok((Command::ViewCenter, p)), 756 "v/clear" => { 757 if let Ok((rgba, p)) = p.clone().parse::<Rgba8>() { 758 Ok((Command::Fill(rgba), p)) 759 } else { 760 Ok((Command::Fill(Rgba8::TRANSPARENT), p)) 761 } 762 } 763 "pan" => { 764 let ((x, y), p) = p.parse::<(i32, i32)>()?; 765 Ok((Command::Pan(x, y), p)) 766 } 767 "map/visual" => { 768 let (km, p) = KeyMapping::parse( 769 p, 770 &[ 771 Mode::Visual(VisualState::selecting()), 772 Mode::Visual(VisualState::Pasting), 773 ], 774 )?; 775 Ok((Command::Map(Box::new(km)), p)) 776 } 777 "map/normal" => { 778 let (km, p) = KeyMapping::parse(p, &[Mode::Normal])?; 779 Ok((Command::Map(Box::new(km)), p)) 780 } 781 "map" => { 782 let (km, p) = KeyMapping::parse( 783 p, 784 &[ 785 Mode::Normal, 786 Mode::Visual(VisualState::selecting()), 787 Mode::Visual(VisualState::Pasting), 788 ], 789 )?; 790 Ok((Command::Map(Box::new(km)), p)) 791 } 792 "map/clear!" => Ok((Command::MapClear, p)), 793 "p/add" => { 794 let (rgba, p) = p.parse::<Rgba8>()?; 795 Ok((Command::PaletteAdd(rgba), p)) 796 } 797 "p/clear" => Ok((Command::PaletteClear, p)), 798 "p/sample" => Ok((Command::PaletteSample, p)), 799 "undo" => Ok((Command::Undo, p)), 800 "redo" => Ok((Command::Redo, p)), 801 "f/new" => Err(Error::new( 802 "parsing failed: `f/new` has been renamed to `f/add`", 803 )), 804 "f/add" => Ok((Command::AddFrame, p)), 805 "f/clone" => match p.clone().parse::<i32>().or_else(|_| Ok((-1, p))) { 806 Ok((index, p)) => Ok((Command::CloneFrame(index), p)), 807 Err(e) => Err(e), 808 }, 809 "f/remove" => Ok((Command::RemoveFrame, p)), 810 "f/resize" => { 811 let ((w, h), p) = p.parse::<(u32, u32)>()?; 812 Ok((Command::ResizeFrame(w, h), p)) 813 } 814 "tool" => { 815 let (t, p) = p.word()?; 816 match t { 817 "pan" => Ok((Command::Tool(Tool::Pan(PanState::default())), p)), 818 "brush" => Ok((Command::Tool(Tool::Brush(Brush::default())), p)), 819 "sampler" => Ok((Command::Tool(Tool::Sampler), p)), 820 _ => Err(Error::new(format!("unknown tool {:?}", t))), 821 } 822 } 823 "tool/prev" => Ok((Command::ToolPrev, p)), 824 "swap" => Ok((Command::SwapColors, p)), 825 "reset!" => Ok((Command::Reset, p)), 826 "selection/move" => { 827 let ((x, y), p) = p.parse::<(i32, i32)>()?; 828 Ok((Command::SelectionMove(x, y), p)) 829 } 830 "selection/resize" => { 831 let ((x, y), p) = p.parse::<(i32, i32)>()?; 832 Ok((Command::SelectionResize(x, y), p)) 833 } 834 "selection/yank" => Ok((Command::SelectionYank, p)), 835 "selection/cut" => Ok((Command::SelectionCut, p)), 836 "selection/paste" => Ok((Command::SelectionPaste, p)), 837 "selection/expand" => Ok((Command::SelectionExpand, p)), 838 "selection/erase" => Ok((Command::SelectionErase, p)), 839 "selection/offset" => { 840 let ((x, y), p) = p.parse::<(i32, i32)>()?; 841 Ok((Command::SelectionOffset(x, y), p)) 842 } 843 "selection/jump" => { 844 let (dir, p) = p.parse::<Direction>()?; 845 Ok((Command::SelectionJump(dir), p)) 846 } 847 "selection/fill" => { 848 if let Ok((rgba, p)) = p.clone().parse::<Rgba8>() { 849 Ok((Command::SelectionFill(Some(rgba)), p)) 850 } else { 851 Ok((Command::SelectionFill(None), p)) 852 } 853 } 854 unrecognized => Err(Error::new(format!( 855 "unrecognized command ':{}'", 856 unrecognized 857 ))), 858 } 859 } 860 } 861 862 #[derive(Debug)] 863 pub struct CommandCompleter { 864 file_completer: FileCompleter, 865 } 866 867 impl CommandCompleter { new<P: AsRef<Path>>(cwd: P, exts: &[&str]) -> Self868 fn new<P: AsRef<Path>>(cwd: P, exts: &[&str]) -> Self { 869 Self { 870 file_completer: FileCompleter::new(cwd, exts), 871 } 872 } 873 } 874 875 impl autocomplete::Completer for CommandCompleter { 876 type Options = (); 877 complete(&self, input: &str, _opts: ()) -> Vec<String>878 fn complete(&self, input: &str, _opts: ()) -> Vec<String> { 879 let p = Parser::new(input); 880 881 match p.parse::<Command>() { 882 Ok((cmd, _)) => match cmd { 883 Command::ChangeDir(path) | Command::WriteFrames(path) => self.complete_path( 884 path.as_ref(), 885 input, 886 FileCompleterOpts { directories: true }, 887 ), 888 Command::Source(path) | Command::Write(path) => { 889 self.complete_path(path.as_ref(), input, Default::default()) 890 } 891 Command::Edit(paths) | Command::EditFrames(paths) => { 892 self.complete_path(paths.last(), input, Default::default()) 893 } 894 _ => vec![], 895 }, 896 Err(_) => vec![], 897 } 898 } 899 } 900 901 impl CommandCompleter { complete_path( &self, path: Option<&String>, input: &str, opts: FileCompleterOpts, ) -> Vec<String>902 fn complete_path( 903 &self, 904 path: Option<&String>, 905 input: &str, 906 opts: FileCompleterOpts, 907 ) -> Vec<String> { 908 use crate::autocomplete::Completer; 909 910 let empty = "".to_owned(); 911 let path = path.unwrap_or(&empty); 912 913 // If there's whitespace between the path and the cursor, don't complete the path. 914 // Instead, complete as if the input was empty. 915 match input.chars().next_back() { 916 Some(c) if c.is_whitespace() => self.file_completer.complete("", opts), 917 _ => self.file_completer.complete(path, opts), 918 } 919 } 920 } 921 922 #[cfg(test)] 923 mod test { 924 use super::*; 925 use std::{fs, fs::File}; 926 927 #[test] test_command_completer()928 fn test_command_completer() { 929 let tmp = tempfile::tempdir().unwrap(); 930 931 fs::create_dir(tmp.path().join("assets")).unwrap(); 932 for file_name in &["one.png", "two.png", "three.png"] { 933 let path = tmp.path().join(file_name); 934 File::create(path).unwrap(); 935 } 936 for file_name in &["four.png", "five.png", "six.png"] { 937 let path = tmp.path().join("assets").join(file_name); 938 File::create(path).unwrap(); 939 } 940 941 let cc = CommandCompleter::new(tmp.path(), &["png"]); 942 let mut auto = Autocomplete::new(cc); 943 944 assert_eq!(auto.next(":e |", 3), Some(("three.png".to_owned(), 3..3))); 945 auto.invalidate(); 946 assert_eq!( 947 auto.next(":e |one.png", 3), 948 Some(("three.png".to_owned(), 3..3)) 949 ); 950 951 auto.invalidate(); 952 assert_eq!( 953 auto.next(":e one.png | two.png", 11), 954 Some(("three.png".to_owned(), 11..11)) 955 ); 956 assert_eq!( 957 auto.next(":e one.png three.png| two.png", 20), 958 Some(("two.png".to_owned(), 11..20)) 959 ); 960 assert_eq!( 961 auto.next(":e one.png two.png| two.png", 18), 962 Some(("one.png".to_owned(), 11..18)) 963 ); 964 965 auto.invalidate(); 966 assert_eq!( 967 auto.next(":e assets/|", 10), 968 Some(("six.png".to_owned(), 10..10)) 969 ); 970 } 971 972 #[test] test_command_line()973 fn test_command_line() { 974 let tmp = tempfile::tempdir().unwrap(); 975 976 fs::create_dir(tmp.path().join("assets")).unwrap(); 977 for file_name in &["one.png", "two.png", "three.png"] { 978 let path = tmp.path().join(file_name); 979 File::create(path).unwrap(); 980 } 981 for file_name in &["four.png", "five.png"] { 982 let path = tmp.path().join("assets").join(file_name); 983 File::create(path).unwrap(); 984 } 985 986 let mut cli = CommandLine::new(tmp.path(), &tmp.path().join(".history"), &["png"]); 987 988 cli.puts(":e one"); 989 cli.completion_next(); 990 assert_eq!(cli.input(), ":e one.png"); 991 992 cli.completion_next(); 993 assert_eq!(cli.input(), ":e one.png"); 994 995 cli.clear(); 996 cli.puts(":e "); 997 cli.completion_next(); 998 assert_eq!(cli.input(), ":e three.png"); 999 1000 cli.completion_next(); 1001 assert_eq!(cli.input(), ":e two.png"); 1002 1003 cli.completion_next(); 1004 assert_eq!(cli.input(), ":e one.png"); 1005 1006 cli.completion_next(); 1007 assert_eq!(cli.input(), ":e assets"); 1008 1009 cli.putc('/'); 1010 cli.completion_next(); 1011 assert_eq!(cli.input(), ":e assets/five.png"); 1012 1013 cli.completion_next(); 1014 assert_eq!(cli.input(), ":e assets/four.png"); 1015 1016 cli.completion_next(); 1017 assert_eq!(cli.input(), ":e assets/five.png"); 1018 1019 cli.putc(' '); 1020 cli.completion_next(); 1021 assert_eq!(cli.input(), ":e assets/five.png three.png"); 1022 1023 cli.putc(' '); 1024 cli.putc('t'); 1025 cli.completion_next(); 1026 assert_eq!(cli.input(), ":e assets/five.png three.png three.png"); 1027 1028 cli.completion_next(); 1029 assert_eq!(cli.input(), ":e assets/five.png three.png two.png"); 1030 1031 cli.completion_next(); 1032 assert_eq!(cli.input(), ":e assets/five.png three.png three.png"); 1033 1034 for _ in 0..10 { 1035 cli.cursor_backward(); 1036 } 1037 cli.putc(' '); 1038 cli.putc('o'); 1039 cli.completion_next(); 1040 assert_eq!( 1041 cli.input(), 1042 ":e assets/five.png three.png one.png three.png" 1043 ); 1044 1045 cli.clear(); 1046 cli.puts(":e assets"); 1047 cli.completion_next(); 1048 assert_eq!(cli.input(), ":e assets/"); 1049 1050 cli.clear(); 1051 cli.puts(":e asset"); 1052 1053 cli.completion_next(); 1054 assert_eq!(cli.input(), ":e assets/"); 1055 1056 cli.completion_next(); 1057 assert_eq!(cli.input(), ":e assets/five.png"); 1058 } 1059 1060 #[test] test_command_line_change_dir()1061 fn test_command_line_change_dir() { 1062 let tmp = tempfile::tempdir().unwrap(); 1063 1064 fs::create_dir(tmp.path().join("assets")).unwrap(); 1065 for file_name in &["four.png", "five.png"] { 1066 let path = tmp.path().join("assets").join(file_name); 1067 File::create(path).unwrap(); 1068 } 1069 1070 let mut cli = CommandLine::new(tmp.path(), Path::new("/dev/null"), &["png"]); 1071 1072 cli.set_cwd(tmp.path().join("assets/").as_path()); 1073 cli.puts(":e "); 1074 1075 cli.completion_next(); 1076 assert_eq!(cli.input(), ":e five.png"); 1077 1078 cli.completion_next(); 1079 assert_eq!(cli.input(), ":e four.png"); 1080 } 1081 1082 #[test] test_command_line_cd()1083 fn test_command_line_cd() { 1084 let tmp = tempfile::tempdir().unwrap(); 1085 1086 fs::create_dir(tmp.path().join("assets")).unwrap(); 1087 fs::create_dir(tmp.path().join("assets").join("1")).unwrap(); 1088 fs::create_dir(tmp.path().join("assets").join("2")).unwrap(); 1089 File::create(tmp.path().join("assets").join("rx.png")).unwrap(); 1090 1091 let mut cli = CommandLine::new(tmp.path(), Path::new("/dev/null"), &["png"]); 1092 1093 cli.clear(); 1094 cli.puts(":cd assets/"); 1095 1096 cli.completion_next(); 1097 assert_eq!(cli.input(), ":cd assets/2"); 1098 1099 cli.completion_next(); 1100 assert_eq!(cli.input(), ":cd assets/1"); 1101 1102 cli.completion_next(); 1103 assert_eq!(cli.input(), ":cd assets/2"); 1104 } 1105 1106 #[test] test_command_line_cursor()1107 fn test_command_line_cursor() { 1108 let mut cli = CommandLine::new("/dev/null", "/dev/null", &[]); 1109 1110 cli.puts(":echo"); 1111 cli.delc(); 1112 assert_eq!(cli.input(), ":ech"); 1113 cli.delc(); 1114 assert_eq!(cli.input(), ":ec"); 1115 cli.delc(); 1116 assert_eq!(cli.input(), ":e"); 1117 cli.delc(); 1118 assert_eq!(cli.input(), ":"); 1119 cli.delc(); 1120 assert_eq!(cli.input(), ""); 1121 1122 cli.clear(); 1123 cli.puts(":e"); 1124 1125 assert_eq!(cli.peek(), None); 1126 cli.cursor_backward(); 1127 1128 assert_eq!(cli.peek(), Some('e')); 1129 cli.cursor_backward(); 1130 1131 assert_eq!(cli.peek(), Some('e')); 1132 assert_eq!(cli.peek_back(), Some(':')); 1133 1134 cli.delc(); 1135 assert_eq!(cli.input(), ":e"); 1136 } 1137 } 1138