Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

:h[elp] command and documentation #997

Open
wants to merge 55 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f5b53e0
Help command WIP
Omnikar Nov 6, 2021
00f70e9
Add documentation
Omnikar Nov 7, 2021
ababd05
Fix typo
Omnikar Nov 7, 2021
3d2e7ad
Simplify
Omnikar Nov 8, 2021
833e3e1
Fix lint error
Omnikar Nov 8, 2021
15af3a3
Use `anyhow::bail`
Omnikar Nov 8, 2021
53dd41b
Shorten lines
Omnikar Dec 6, 2021
063edb1
Merge branch 'master' into help-command
Omnikar Dec 6, 2021
fc2ec0f
Use different bracket types to show cursor position
Omnikar Dec 6, 2021
d48c555
Add documentation
Omnikar Dec 6, 2021
d28b6fb
Use `anyhow::ensure`
Omnikar Dec 6, 2021
196dda7
Add extending command docs
Omnikar Dec 6, 2021
46d9f49
Merge branch 'master' into 'help-command'
Omnikar Dec 24, 2021
6f3930c
Update `help` function signature
Omnikar Dec 24, 2021
bf644d6
Remove backticks around command names
Omnikar Dec 24, 2021
0196aee
`cargo xtask docgen`
Omnikar Dec 24, 2021
a3dd3e0
Merge branch 'master' into 'help-command'
Omnikar Dec 24, 2021
3dd9e29
Merge branch 'master' into help-command
Omnikar Dec 24, 2021
897fca7
Re-add backticks around command names
Omnikar Dec 24, 2021
e1283fc
Document commands
Omnikar Dec 24, 2021
de6fb00
Move command help files into `runtime/help/commands/`
Omnikar Dec 24, 2021
9bcc8a6
Merge branch 'master' into help-command
Omnikar Dec 27, 2021
b9d2daf
Update `help` to parse keypresses like a macro
Omnikar Dec 27, 2021
911efce
Merge branch 'master' into help-command
Omnikar Dec 27, 2021
4c02ab0
Move `runtime/help/commands/` to `runtime/help/static-commands/`
Omnikar Dec 27, 2021
eda195e
Support `:help` for typable commands
Omnikar Dec 27, 2021
d524000
Reorder typable command help
Omnikar Dec 27, 2021
c31cb68
Update documentation
Omnikar Dec 28, 2021
3dcd18d
Merge branch 'master' into help-command
Omnikar Dec 29, 2021
aee10cf
Add documentation
Omnikar Dec 30, 2021
32d0ef4
Add documentation
Omnikar Dec 30, 2021
efbd4e9
Add documentation
Omnikar Dec 31, 2021
c108d56
Add documentation
Omnikar Dec 31, 2021
835d515
Implement `:help topics` and document "Words vs. WORDS"
Omnikar Dec 31, 2021
cc4c786
Make `:help topics` open in a split
Omnikar Dec 31, 2021
d18d745
Implement `:help <keybind>` for non-branching key sequences
Omnikar Dec 31, 2021
94eade9
Fix lint issues
Omnikar Dec 31, 2021
f0c1c2d
Add documentation
Omnikar Dec 31, 2021
592fa1d
Merge branch 'master' into help-command
Omnikar Dec 31, 2021
43c1704
Handle `NotFound` and `Cancelled` for keymap lookup
Omnikar Dec 31, 2021
c039c9b
Merge branch 'master' into help-command
Omnikar Jan 3, 2022
c95317a
Merge branch 'master' into help-command
Omnikar Feb 16, 2022
ff9c104
Add documentation regarding Insert mode
Omnikar Feb 16, 2022
d7fb82f
Merge branch 'master' into help-command
Omnikar Mar 31, 2022
1e136c6
Merge branch 'master' into help-command
Omnikar Oct 1, 2022
10d3d1c
Merge branch 'master' into help-command
Omnikar Dec 23, 2022
7249c6f
Remove `.txt` extensions
Omnikar Dec 23, 2022
54219fc
Make `:help` with no arguments open command info
Omnikar Dec 27, 2022
afc1e7f
Refactor and remove panics
Omnikar Dec 27, 2022
ea40659
Refactor `:help` completion function
Omnikar Dec 27, 2022
6ad6009
Fix `:help` help file
Omnikar Dec 27, 2022
dd4ec8a
Merge branch 'master' into help-command
Omnikar Oct 28, 2023
7f3a49e
Use an overlaid picker for `:h topics`
Omnikar Oct 28, 2023
9db0321
Fix bug where `:h` would leave pending keys
Omnikar Oct 28, 2023
5f885cf
Merge branch 'master' into help-command
Omnikar Apr 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions book/src/generated/typable-cmd.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@
| `:move` | Move the current buffer and its corresponding file to a different path |
| `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default |
| `:read`, `:r` | Load a file into buffer |
| `:help`, `:h` | Open documentation for a command or keybind. |
118 changes: 117 additions & 1 deletion helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2401,7 +2401,6 @@ fn move_buffer(
if event != PromptEvent::Validate {
return Ok(());
}

ensure!(args.len() == 1, format!(":move takes one argument"));
let doc = doc!(cx.editor);
let old_path = doc
Expand Down Expand Up @@ -2488,6 +2487,116 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
Ok(())
}

fn help(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}

const STATIC_HELP_DIR: &str = "static-commands";
const TYPABLE_HELP_DIR: &str = "typable-commands";

let args_msg = args.join(" ");
let open_help =
move |help_dir: &str, command: &str, editor: &mut Editor| -> anyhow::Result<()> {
let path = Path::new("help").join(help_dir).join(command);
let path = helix_loader::runtime_file(&path);
ensure!(path.is_file(), "No help available for '{args_msg}'");
let id = editor.open(&path, Action::HorizontalSplit)?;
editor.document_mut(id).unwrap().set_path(None);

Ok(())
};

if args.is_empty() {
return open_help(TYPABLE_HELP_DIR, "help", cx.editor);
}

if args[0] == "topics" {
let dir_path = helix_loader::runtime_file(Path::new("help/topics"));

let callback = async move {
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
move |editor: &mut Editor, compositor: &mut Compositor| {
let picker = ui::file_picker(dir_path, &editor.config());
compositor.push(Box::new(overlaid(picker)));
},
));
Ok(call)
};
cx.jobs.callback(callback);

return Ok(());
}

let (help_dir, command): (&str, &str) = {
let arg = &args[0];
if let Some(command) = arg.strip_prefix(':').and_then(|arg| {
TYPABLE_COMMAND_LIST.iter().find_map(|command| {
(command.name == arg || command.aliases.contains(&arg)).then_some(command.name)
})
}) {
(TYPABLE_HELP_DIR, command)
} else if MappableCommand::STATIC_COMMAND_LIST
.iter()
.any(|command| command.name() == arg)
{
(STATIC_HELP_DIR, arg)
} else {
let arg = arg.clone().into_owned();
let keys = arg
.parse::<KeyEvent>()
.map(|key| vec![key])
.or_else(|_| helix_view::input::parse_macro(&arg))?;
let callback = async move {
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
move |editor: &mut Editor, compositor: &mut Compositor| {
use crate::keymap::KeymapResult;
let editor_view = compositor.find::<ui::EditorView>().unwrap();
let mode = editor.mode;
let keymaps = &mut editor_view.keymaps;
let (keys, last_key) = (&keys[..keys.len() - 1], keys.last().unwrap());
keys.iter().for_each(|key| {
keymaps.get(mode, *key);
});
let key_result = keymaps.get(mode, *last_key);
let result: anyhow::Result<(&str, &str)> = match &key_result {
KeymapResult::Matched(command) => match command {
MappableCommand::Static { name, .. } => Ok((STATIC_HELP_DIR, name)),
MappableCommand::Typable { name, .. } => {
Ok((TYPABLE_HELP_DIR, name))
}
},
KeymapResult::NotFound | KeymapResult::Cancelled(_) => {
Err(anyhow!("No command found for '{}'", arg))
}
KeymapResult::Pending(_) => {
// Clear pending keys
keymaps.get(mode, crate::keymap::macros::key!(Esc));
Err(anyhow!(
"`:help` for branching keybinds is not yet supported."
))
}
KeymapResult::MatchedSequence(_) => Err(anyhow!(
"`:help` for sequence bindings is not yet supported."
)),
};
if let Err(e) = result
.and_then(|(help_dir, command)| open_help(help_dir, command, editor))
{
editor.set_error(e.to_string());
}
},
));
Ok(call)
};
cx.jobs.callback(callback);
return Ok(());
}
};

open_help(help_dir, command, cx.editor)
}

pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "quit",
Expand Down Expand Up @@ -3109,6 +3218,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: read,
signature: CommandSignature::positional(&[completers::filename]),
},
TypableCommand {
name: "help",
aliases: &["h"],
doc: "Open documentation for a command or keybind.",
fun: help,
signature: CommandSignature::positional(&[completers::help]),
},
];

pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
Expand Down
36 changes: 36 additions & 0 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,42 @@ pub mod completers {
})
}

pub fn help(_editor: &Editor, input: &str) -> Vec<Completion> {
use std::path::Path;
let static_cmds_path = helix_loader::runtime_file(Path::new("help/static-commands"));
let typable_cmds_path = helix_loader::runtime_file(Path::new("help/typable-commands"));

let static_cmds = std::fs::read_dir(static_cmds_path)
.into_iter()
.flat_map(|entries| {
entries.filter_map(|entry| {
let path = entry.ok()?.path();
path.extension()
.is_none()
.then(|| path.file_stem().unwrap().to_string_lossy().into_owned())
})
});
let typable_cmds = std::fs::read_dir(typable_cmds_path)
.into_iter()
.flat_map(|entries| {
entries.filter_map(|entry| {
let path = entry.ok()?.path();
path.extension()
.is_none()
.then(|| format!(":{}", path.file_stem().unwrap().to_string_lossy()))
})
});

let items = std::iter::once("topics".to_owned())
.chain(static_cmds)
.chain(typable_cmds);

fuzzy_match(input, items, false)
.into_iter()
.map(|(name, _)| (0.., name.into()))
.collect()
}

#[derive(Copy, Clone, PartialEq, Eq)]
enum FileMatch {
/// Entry should be ignored
Expand Down
5 changes: 5 additions & 0 deletions runtime/help/static-commands/append_mode
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`append_mode`

Enters Insert mode at the end of the selection.

For information about Insert mode, see "Primary Modes".
5 changes: 5 additions & 0 deletions runtime/help/static-commands/append_to_line
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`append_to_line`

Enters Insert mode at the end of the line.

For information about Insert mode, see "Primary Modes".
18 changes: 18 additions & 0 deletions runtime/help/static-commands/copy_selection_on_next_line
Original file line number Diff line number Diff line change
@@ -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. │
└──────────────────────────────────┘ └──────────────────────────────────┘
18 changes: 18 additions & 0 deletions runtime/help/static-commands/copy_selection_on_prev_line
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
`copy_selection_on_prev_line`

Copies the current primary selection to the first previous line long enough to accomodate it.

--- Examples ---

The selection is copied from line 2 to line 1.
┌───────────────────────────┐ ┌───────────────────────────┐
│ 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 line of text. │ │ This is a longer li(ne of t]ext. │
│ This is a shorter line. │ --> │ This is a shorter line. │
│ This is another lon(ger lin]e. │ │ This is another lon(ger lin]e. │
└──────────────────────────────────┘ └──────────────────────────────────┘
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_char_left
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_char_left`

Extending version of `move_char_left`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_char_right
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_char_right`

Extending version of `move_char_right`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_line_down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_line_down`

Extending version of `move_line_down`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_line_up
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_line_up`

Extending version of `move_line_up`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_next_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_next_char`

Extending version of `find_next_char`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_next_long_word_end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_next_long_word_end`

Extending version of `move_next_long_word_end`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_next_long_word_start
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_next_long_word_start`

Extending version of `move_next_long_word_start`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_next_word_end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_next_word_end`

Extending version of `move_next_word_end`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_next_word_start
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_next_word_start`

Extending version of `move_next_word_start`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_prev_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_prev_char`

Extending version of `find_prev_char`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_prev_long_word_start
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_prev_long_word_start`

Extending version of `move_prev_long_word_start`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_prev_word_start
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_prev_word_start`

Extending version of `move_prev_word_start`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_till_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_till_char`

Extending version of `find_till_char`.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/extend_till_prev_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`extend_till_prev_char`

Extending version of `till_prev_char`.
22 changes: 22 additions & 0 deletions runtime/help/static-commands/find_next_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
`find_next_char`

Waits for another keypress, then moves and
selects forward, stopping at the first
instance of the pressed key. Can take
a count, which will cause it to stop
at the nth instance of the keypress,
rather than the first.

--- Examples ---

The cursor moves forward, stopping at 'c'
and selecting everything along the way.
┌───────────────────────┐ c ┌───────────────────────┐
│ This i[s] a sentence. │ --> │ This i(s a sentenc]e. │
└───────────────────────┘ └───────────────────────┘

The cursor is not stopped by line breaks.
┌───────────────────────────┐ ┌────────────────────────────┐
│ This is the fi[r]st line. │ Q │ This is the fi(rst line. │
│ This second line has a Q. │ --> │ This second line has a Q]. │
└───────────────────────────┘ └────────────────────────────┘
22 changes: 22 additions & 0 deletions runtime/help/static-commands/find_prev_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
`find_prev_char`

Waits for another keypress, then moves and
selects backward, stopping at the first
instance of the pressed key. Can take
a count, which will cause it to stop
at the nth instance of the keypress,
rather than the first.

--- Examples ---

The cursor moves backward, stopping at 'h'
and selecting everything along the way.
┌───────────────────────┐ h ┌───────────────────────┐
│ This is a sent[e]nce. │ --> │ T[his is a sente)nce. │
└───────────────────────┘ └───────────────────────┘

The cursor is not stopped by line breaks.
┌──────────────────────────────────┐ ┌───────────────────────────────────┐
│ There is a Q in this first line. │ Q │ There is a [Q in this first line. │
│ This is the se[c]ond line. │ --> │ This is the sec)ond line. │
└──────────────────────────────────┘ └───────────────────────────────────┘
22 changes: 22 additions & 0 deletions runtime/help/static-commands/find_till_char
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
`find_till_char`

Waits for another keypress, then moves and
selects forward, stopping before the first
instance of the pressed key. Can take
a count, which will cause it to stop
before the nth instance of the keypress,
rather than the first.

--- Examples ---

The cursor moves forward, stopping before 'c'
and selecting everything along the way.
┌───────────────────────┐ c ┌───────────────────────┐
│ This i[s] a sentence. │ --> │ This i(s a senten]ce. │
└───────────────────────┘ └───────────────────────┘

The cursor is not stopped by line breaks.
┌───────────────────────────┐ ┌────────────────────────────┐
│ This is the fi[r]st line. │ Q │ This is the fi(rst line. │
│ This second line has a Q. │ --> │ This second line has a ]Q. │
└───────────────────────────┘ └────────────────────────────┘
3 changes: 3 additions & 0 deletions runtime/help/static-commands/half_page_down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`half_page_down`

Scrolls down by half of a screen.
3 changes: 3 additions & 0 deletions runtime/help/static-commands/half_page_up
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`half_page_up`

Scrolls up by half of a screen.
5 changes: 5 additions & 0 deletions runtime/help/static-commands/insert_mode
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
`insert_mode`

Enters Insert mode at the start of the selection.

For information about Insert mode, see "Primary Modes".
4 changes: 4 additions & 0 deletions runtime/help/static-commands/move_char_left
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
`move_char_left`

Moves all cursors 1 character left, removing
any selections and wrapping across line breaks.
4 changes: 4 additions & 0 deletions runtime/help/static-commands/move_char_right
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
`move_char_right`

Moves all cursors 1 character right, removing
any selections and wrapping across line breaks.
Loading
Loading