-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Custom typable commands #4423
Comments
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Does the editor already support custom commands and just not typables? I was thinking it would be nice to have something like: [custom.commands]
bring_line_up = {
cmds = [ 'push_register', 'extend_line', 'delete_selection', 'move_line_up', 'paste_before', 'pop_register' ] # semicolon separates operations' names from parameters' arguments.
desc = "swap line with above"
mode = editor || prompt || both
} |
or set the mode via |
You can already map keys to lists of commands (https://docs.helix-editor.com/master/remapping.html) to create custom non-typable commands. You can also rebind the |
Could separate func name from params with |
Combining sequences of typable commands is covered by this. For example #6857 might be written as: # Note: this is not implemented.
[commands]
":wcb" = [":write", ":buffer-close"] Handling the arguments of the commands is also important for the design of this feature though. If you wanted There are nuances to this though (optional arguments, aliases, command names / docs) so maybe this issue is better handled by plugins once the plugin system exists instead. We don't want to create new programming languages in the config TOML. |
That looks so sick! Imagine if we could define all key remaps like that! We could just escape for edit: That's all to say, plugin systems can get really hairy. They're complex, and easy to break on user-side. Just save ourselves time and keep it snippety, lol. |
|
Started to prototype this, and am finding that this too is hampered by #5555. The custom commands need to be part of the editor config, so cannot directly store a The implimentation needs to build off of #12288 and #11164 which themselves build off of #11149, so still a ways out, but I think I have a good framework going forward. ConfigBasicThe config will get a [commands]
"waq" = ":write --all --quit" This would provide no completions, only showing up in the list of commands. The prompt would be bare, only the name and the mapping, no description or what it accepts:
AdvancedThe most advanced usage is what I hope to be possible to implement, but as I haven't actually done it, jury is still out on what's actually feasible: [commands]
"wcd!" = {
commands = [ ":write --force %{arg}", ":cd %sh{ %{arg} | path dirname }" ],
desc= "writes buffer forcefully, then changes to its directory"
completions = "write"
accepts = "<path>"
} This would show the commands from the list of commands like before, but now offers the ability to run multiple commands in a chain, provide positional arguments, a description, indicate what it accepts, as well as which completer to run when using the command, sourced from the name of an existing typable command. The prompt now looks like this:
These could be given numbers as well: NestingCustom commands can also be used in other custom commands, though with some limitations to be mindful of: [commands]
"wcd!" = {
# If the above was instead `wcd`, with no `--force`
# and you tried to use it in an `wcd!` impl,
# you would run into an issue with the other commands
# single positional `%{arg}.
#
# Notice even if we tried to pass the flag and the path as
# one argument, using quotes to wrap it, we run into an
# issue with the path dirname evaluation.
#
# When it all gets expanded write should look fine:
# write --force parent/sub/sub/file.txt
#
# The issue is with the `:cd`
# :cd %sh {--force parent/sub/sub/file.txt | path dirname }
# which would error in nushell
commands = ":wcd '--force %{arg}'",
desc= "writes buffer forcefully, then changes to its directory"
completions = "write"
accepts = "<path>"
} The prompt would look like so, again if the first impl was a
In short, it works, but the design of the commands must be able to compose. Implimentation DetailsIts is based around a pub struct CustomTypableCommand {
pub name: String,
pub desc: String,
// TODO: Cannot store a `TypableCommand` directly as this has to live in
// the editor config.
pub commands: Vec<String>,
pub accepts: String,
// Can `get` from the typable command map and then `clone` the signature
pub compeltions: String,
} The config itself would hold a wrapper of this: pub struct CustomTypableCommands {
commands: Vec<CustomTypableCommand>,
} The usage of this would look roughly like: // Checking against user provided commands first gives priority
// to user defined aliases over the built-in, allowing for overriding.
if let Some(custom: &CustomTypeableCommand) = cx.editor.config.load().commands.get(shellwords.command()) {
for command: &str in custom.commands.iter() {
let shellwords = Shellwords::from(command);
if let Some(command: &TypeableCommand) = typed::TYPABLE_COMMAND_MAP.get(shellwords.command()) {
let args = match variables::expand(cx.editor, shellwords.args().raw(), event == PromptEvent::Validate) {
Ok(args) => args,
Err(err) => {
cx.editor.set_error(format!("{err}"));
// short circuit if error
return;
},
}
if let Err(err) = (command.fun)(cx, Args::from(&args), command.flags, event) {
cx.editor.set_error(format!("{err}"));
// short circuit if error
return;
}
} else {
cx.editor.set_error(format!("command `:{}` is not a valid command", shellwords.command()));
// short circuit if error
return;
}
} else if let Some(command) = typed::TYPABLE_COMMAND_MAP.get(shellwords.command()) {
// Current impl
} There is more here that would need to be layered in, but this is the gist. The main points I haven't delved into yet is the parsing from the toml into the struct and add it to the config, which to me is looking like the hardest part (😆). As well as how to provide the command in the list of commands and be able to show the prompt for it properly. Might have to introduce a Id love some feedback on the toml design, as well as some example usage, some ultra creative ones, to make sure I can design around as much as possible. Like I said at the start, this is all a ways out, but this still has issues to work out anyways, so hopefully if we get these ironed out, and the dependent pull requests merge in without issue, and I can work on this in the next release cycle as a major feature of the release. Potentially Common Use-CasesFloating
|
The use case I was looking for that lead me to this ticket:
to be able to
to change the case of all selections to snake case for example |
Regular commands can be rebound but typable commands (anything entered in command mode,
:
) can't be modified in any way.You might use custom typable commands to implement file operations in conjunction with #3134 for example. You might define
:mv
as:sh mv $file $1
or:rm
as:sh rm $1
. (The syntax to use for variables, or whether to use variables at all should be discussed.)Custom typable commands could also be used to create custom abbreviations for commands.
The text was updated successfully, but these errors were encountered: