Improve command mode handling.

This commit is contained in:
Blaž Hrastnik 2021-05-07 17:08:07 +09:00
parent 243456a583
commit 9604a0c294

View File

@ -809,7 +809,115 @@ pub fn append_mode(cx: &mut Context) {
doc.set_selection(view.id, selection);
}
const COMMAND_LIST: &[&str] = &["write", "open", "quit", "quit!"];
mod cmd {
use super::*;
use std::collections::HashMap;
use helix_view::editor::Action;
#[derive(Clone)]
pub struct Command {
pub name: &'static str,
pub alias: Option<&'static str>,
pub doc: &'static str,
// params, flags, helper, completer
pub fun: fn(&mut Editor, &[&str], PromptEvent),
}
fn quit(editor: &mut Editor, args: &[&str], event: PromptEvent) {
// last view and we have unsaved changes
if editor.tree.views().count() == 1 {
let modified: Vec<_> = editor
.documents()
.filter(|doc| doc.is_modified())
.map(|doc| {
doc.relative_path()
.and_then(|path| path.to_str())
.unwrap_or("[scratch]")
})
.collect();
if !modified.is_empty() {
let err = format!(
"{} unsaved buffer(s) remaining: {:?}",
modified.len(),
modified
);
editor.set_error(err);
return;
}
}
editor.close(editor.view().id, /* close_buffer */ false);
}
fn force_quit(editor: &mut Editor, args: &[&str], event: PromptEvent) {
editor.close(editor.view().id, /* close_buffer */ false);
}
fn open(editor: &mut Editor, args: &[&str], event: PromptEvent) {
let path = args[0];
editor.open(path.into(), Action::Replace);
}
fn write(editor: &mut Editor, args: &[&str], event: PromptEvent) {
let id = editor.view().doc;
let doc = &mut editor.documents[id];
if doc.path().is_none() {
editor.set_error("cannot write a buffer without a filename".to_string());
return;
}
tokio::spawn(doc.save());
}
fn new_file(editor: &mut Editor, args: &[&str], event: PromptEvent) {
editor.new_file(Action::Replace);
}
pub const COMMAND_LIST: &[Command] = &[
Command {
name: "quit",
alias: Some("q"),
doc: "Close the current view.",
fun: quit,
},
Command {
name: "quit!",
alias: Some("q!"),
doc: "Close the current view.",
fun: force_quit,
},
Command {
name: "open",
alias: Some("o"),
doc: "Open a file from disk into the current view.",
fun: open,
},
Command {
name: "write",
alias: Some("w"),
doc: "Write changes to disk.",
fun: write,
},
Command {
name: "new",
alias: Some("n"),
doc: "Create a new scratch buffer.",
fun: new_file,
},
];
pub static COMMANDS: Lazy<HashMap<&'static str, Command>> = Lazy::new(|| {
let mut map = HashMap::new();
for cmd in COMMAND_LIST {
map.insert(cmd.name, cmd.clone());
if let Some(alias) = cmd.alias {
map.insert(alias, cmd.clone());
}
}
map
});
}
pub fn command_mode(cx: &mut Context) {
let prompt = Prompt::new(
@ -823,10 +931,10 @@ pub fn command_mode(cx: &mut Context) {
if parts.len() <= 1 {
use std::{borrow::Cow, ops::Range};
let end = 0..;
COMMAND_LIST
cmd::COMMAND_LIST
.iter()
.filter(|command| command.contains(input))
.map(|command| (end.clone(), Cow::Borrowed(*command)))
.filter(|command| command.name.contains(input))
.map(|command| (end.clone(), Cow::Borrowed(command.name)))
.collect()
} else {
let part = parts.last().unwrap();
@ -854,52 +962,11 @@ pub fn command_mode(cx: &mut Context) {
let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
match *parts.as_slice() {
["q"] | ["quit"] => {
// last view and we have unsaved changes
if editor.tree.views().count() == 1 {
let modified: Vec<_> = editor
.documents()
.filter(|doc| doc.is_modified())
.map(|doc| {
doc.relative_path()
.and_then(|path| path.to_str())
.unwrap_or("[scratch]")
})
.collect();
if !modified.is_empty() {
let err = format!(
"{} unsaved buffer(s) remaining: {:?}",
modified.len(),
modified
);
editor.set_error(err);
return;
}
}
editor.close(editor.view().id, /* close_buffer */ false);
}
["q!"] | ["quit!"] => {
editor.close(editor.view().id, /* close_buffer */ false);
}
["o", path] | ["open", path] => {
editor.open(path.into(), Action::Replace);
}
["w"] | ["write"] => {
let id = editor.view().doc;
let doc = &mut editor.documents[id];
if doc.path().is_none() {
editor.set_error("cannot write a buffer without a filename".to_string());
return;
}
tokio::spawn(doc.save());
}
["new"] => {
editor.new_file(Action::Replace);
}
_ => (),
}
if let Some(cmd) = cmd::COMMANDS.get(parts[0]) {
(cmd.fun)(editor, &parts[1..], event);
} else {
editor.set_error(format!("no such command: '{}'", parts[0]));
};
},
);
cx.push_layer(Box::new(prompt));