From f5b53e074c08a5c6a044c1ef2bae5445ff621028 Mon Sep 17 00:00:00 2001 From: Omnikar Date: Sat, 6 Nov 2021 16:54:26 -0400 Subject: [PATCH] Help command WIP --- helix-term/src/commands.rs | 46 ++++++++++++++++++++ helix-term/src/ui/mod.rs | 30 +++++++++++++ runtime/help/copy_selection_on_next_line.txt | 18 ++++++++ 3 files changed, 94 insertions(+) create mode 100644 runtime/help/copy_selection_on_next_line.txt diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 80cbd6d29..2891d8172 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2229,6 +2229,45 @@ mod cmd { Ok(()) } + fn help( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + if args.is_empty() { + // TODO: Open a list of commands? + todo!() + } + + let command = { + if Command::COMMAND_LIST + .iter() + .any(|command| command.name() == args[0]) + { + args[0] + } else { + let _keys = args + .iter() + .map(|key| key.parse::()) + .collect::, _>>()?; + // TODO: Need to access the keymap here to find the corresponding command + todo!() + } + }; + + let mut path = helix_core::runtime_dir(); + path.push("help"); + path.push(format!("{}.txt", command)); + + if !path.is_file() { + return Err(anyhow!("No help available for '{}'", args.join(" "))); + } + let id = cx.editor.open(path, Action::HorizontalSplit)?; + cx.editor.document_mut(id).unwrap().set_path(None)?; + + Ok(()) + } + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -2489,6 +2528,13 @@ mod cmd { fun: tutor, completer: None, }, + TypableCommand { + name: "help", + aliases: &["h"], + doc: "Open documentation for a command or keybind.", + fun: help, + completer: Some(completers::help), + }, ]; pub static COMMANDS: Lazy> = Lazy::new(|| { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 24eb7acdf..d489abc02 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -220,6 +220,36 @@ pub mod completers { }) } + pub fn help(input: &str) -> Vec { + let path = helix_core::runtime_dir().join("help"); + let commands: Vec = std::fs::read_dir(path) + .map(|entries| { + entries + .filter_map(|entry| { + let entry = entry.ok()?; + let path = entry.path(); + (path.extension()? == "txt") + .then(|| path.file_stem().unwrap().to_string_lossy().into_owned()) + }) + .collect() + }) + .unwrap_or_default(); + + let matcher = Matcher::default(); + + let mut matches: Vec<_> = commands + .into_iter() + .map(|name| ((0..), Cow::from(name))) + .filter_map(|(_range, name)| { + matcher.fuzzy_match(&name, input).map(|score| (name, score)) + }) + .collect(); + + matches.sort_unstable_by_key(|(_file, score)| Reverse(*score)); + + matches.into_iter().map(|(name, _)| ((0..), name)).collect() + } + #[derive(Copy, Clone, PartialEq, Eq)] enum FileMatch { /// Entry should be ignored diff --git a/runtime/help/copy_selection_on_next_line.txt b/runtime/help/copy_selection_on_next_line.txt new file mode 100644 index 000000000..4a29dae4a --- /dev/null +++ b/runtime/help/copy_selection_on_next_line.txt @@ -0,0 +1,18 @@ +`copy_selection_on_next_line` + +Copies the current primary selection to the next line long enough to accomodate it. + +--- Examples --- + +The selection is copied from line 1 to line 2. +┌───────────────────────────┐ ┌───────────────────────────┐ +│ This is text [on line 1]. │ --> │ This is text [on line 1]. │ +│ This is text on line 2. │ │ This is text [on line 2]. │ +└───────────────────────────┘ └───────────────────────────┘ + +The selection duplication skips line 2 because it is too short. +┌──────────────────────────────────┐ ┌──────────────────────────────────┐ +│ This is a longer li[ne of t]ext. │ │ This is a longer li[ne of t]ext. │ +│ This is a shorter line. │ --> │ This is a shorter line. │ +│ This is another longer line. │ │ This is another lon[ger lin]e. │ +└──────────────────────────────────┘ └──────────────────────────────────┘