Designing the new `complete-command` command


In recent commits, mawww just introduced a first iteration of a new command called complete-command

Right now, the current impact is that the long list of define-command flags like:


are marked as deprecated. Here’s an example of transition from the old model to the new one :

define-command -params .. -file-completion grep %{ … }
define-command -params .. grep %{ … }
complete-command grep file

Meaning the the definition of a command is to be precised afterwards in independent step.

According to mawww on IRC :

longer term it would be good to specify more complex completions (1st param is client completion, 2nd is buffer name, rest is filename…) and that would be quite ugly as a set of switch to define-command

This approach reminds me a lot of what some shells offers as completion-engines, like for instance the complete command of fish: complete - edit command specific tab-completions — fish-shell 3.3.1 documentation

@krobelus as you’re involved a lot in fish development, what’s your take on this? Are there any pitfalls in fish approach that Kakoune should be aware before consolidating this new feature. Or on the contrary which are the benefits that will emerge?

(of course anyone can shime-in in this reflexion :wink: )

1 Like

Perhaps not coincidentally, bash’s Programmable Completion Builtins work a whole lot like the new complete-command builtin.

  • There’s a few flags for widely-used completion types:
    • -file-completion in Kakoune
    • -f or -A file in bash
  • A way to generate a fixed list of completions:
    • -shell-script-candidates in Kakoune
    • -C command in bash
  • A system for arbitrary customisable cleverness:
    • -shell-script-completion in Kakoune
    • -F function in bash

bash has more built-in convenience completion types that don’t make as much sense for Kakoune (shell aliases, Readline bindable command names, names of running jobs in the current session…) but the basic model seems to be the same.

It’d be neat if there were a way for a Kakoune plugin to globally override the implementation of Kakoune’s -shell-completion option so that instead of Kakoune just scouring $PATH for executables, somebody could write a wrapper around bash’s or fish’s completion systems.

I’m a newly converted fish user and, fwiw I have been making some powerful (and simple!) completion scripts. The user experience has been insanely smooth from my perspective. That said, I don’t have insight into the implementation complexity. I’ve merely managed to make a handful of concise completion scripts that work flawlessly.

aside for non-fish users
I’ve been a zsh power user for years and was instantly converted to fish after reading through their site. The process for switching was oh so smooth because I could often shell out to zsh for existing scripts! I’d highly recommend it to anyone :nerd_face::v:

Today we can use shortcuts like complete-command cmd file or the more flexible complete-command cmd shell-script, but we can’t combine the two.
Fish allows to combine multiple complete specs and offers the union. This makes completion scripts easy to write but also ugly.
Meanwhile, complete-command overwrites any previous completion spec, which is the right behavior IMO.

In a perfectly orthogonal Unix world, the shortcuts would just be shell commands, then shell-script-completion would be able to do everything.

complete-command complete-command shell-script %{
	case $kak_token_to_complete
		kak -complete command $kak_token_to_complete $kak_pos_in_token "$@"
		printf %s\\n file client buffer command shell shell-script shell-script-candidates

Not sure how to fix performance problems though. I guess we cannot use the try %{exec <a-k>...} pattern from indentation hooks?
(Either way, I think making the prompt share more functionality with normal buffers is a good idea. For example, we should allow command completions in insert mode (maybe <c-x>:))

I guess returning a special value in the 0 case (to tell Kakoune to use command-comletion) would avoid some overhead.

The way bash’s completion system works, there’s the complete command that defines what completion to use for a given base command:

complete -A variable unset

…but it also includes the compgen command which takes the same flags and generates output, so that shell-script completion can re-use all the other kinds of completion when necessary:

$ compgen -A variable | head

Of Kakoune’s built-in completions:

  • file is available by regular shell globbing
  • client is $kak_client_list
  • buffer is $kak_buflist
  • shell can be generated by walking $PATH yourself as Kakoune does, or (if you don’t mind a bash dependency) compgen -A command

The only one missing is command completion, since I don’t believe there’s a way to access the list of Kakoune commands from within Kakoune script. On one hand, that probably wouldn’t be difficult to add; on the other, most commands that take another command will only take that other command, so they won’t need the complexity of a shell function that delegates to other completion types.

I wonder if it’s a good idea to specify completion of positional parameters like this:

complete-command -menu complete-command \
	command \
	shell-script-candidates %{ printf '%s\n' file client buffer command shell shell-script shell-script-candidates }

here the first argument gets command completions and all subsequent ones get the shell-script-candidates completions.

Not really exciting but alright. Another thing we might want to figure out is completion of switches. Right now only builtin commands have them.

Luckily, most Kakoune commands are simple and don’t need switches or complex arguments. I hope we keep it that way.

I believe the original motivation for complete-command was to add ninja-completions to :make.
We should think about reusing the OS’s shell completions.
Here’s a PoC to get fish completions in insert mode. I haven’t used it much and it has missing features.
Would be nice to have a polished version available in complete-command. bash/zsh backends would be good too, sadly I haven’t figured out what’s their equivalent of fish’s complete -C.

map global insert <c-f> %{<a-semicolon>: my-complete-fish<ret>}
define-command -override my-complete-fish %{
	evaluate-commands -draft -save-regs /lc %{
		evaluate-commands -draft %{
			# try to guess the token start
			try %{
				execute-keys -draft h<a-h><a-k>\h\z<ret>
			} catch %{
				execute-keys <a-b>
			set-register l "%val{cursor_line}.%val{cursor_column}@%val{timestamp}"
		execute-keys <a-h>
		execute-keys s\a[^\n]*<ret>
		nop %sh{
				fish -c 'complete -c "$argv"' -- "$kak_selection" | perl -ne '
				sub quote {
					$sq = "'\''";
					$token = shift;
					$token =~ s/$sq/$sq$sq/g;
					return "$sq$token$sq";
				begin {
					$cmd = "set-option " . quote("buffer=". $env{kak_bufname}) . " shell_completions $env{kak_reg_l}";
				s/\\/\\/g; s/\|/\\|/g;
				m/([^\t]*)(?:\t([^\n]*))?/ or next;
				$text = $1;
				$description = "{\\}$2";
				$cmd .= " " . quote "$text||$text\t$description";
				end {
					print $cmd;
				' | kak -p "$kak_session" >/dev/null 2>&1 &
			) >/dev/null 2>&1 </dev/null