Completion: Delegate kitty +complete to kitten
Implement `kitten __complete__ setup` in Go. Fix zsh completion script to check `kitten`.
This commit is contained in:
parent
bed4f33be8
commit
370aa3aaa6
@ -50,6 +50,9 @@ Detailed list of changes
|
|||||||
|
|
||||||
- Fix regression in previous release that caused incorrect entries in terminfo for the modifer+F3 key combinations (:pull:`5970`)
|
- Fix regression in previous release that caused incorrect entries in terminfo for the modifer+F3 key combinations (:pull:`5970`)
|
||||||
|
|
||||||
|
- Bring back the deprecated and removed ``kitty +complete`` and delegate it to :program:`kitten` for backward compatibility (:pull:`5977`)
|
||||||
|
|
||||||
|
|
||||||
0.27.0 [2023-01-31]
|
0.27.0 [2023-01-31]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,17 @@ def hold(args: List[str]) -> None:
|
|||||||
os.execvp(kitten_exe(), args)
|
os.execvp(kitten_exe(), args)
|
||||||
|
|
||||||
|
|
||||||
|
def complete(args: List[str]) -> None:
|
||||||
|
# Delegate to kitten to maintain backward compatibility
|
||||||
|
if len(args) < 2 or args[1] not in ('setup', 'zsh', 'fish2', 'bash'):
|
||||||
|
raise SystemExit(1)
|
||||||
|
if args[1] == 'fish2':
|
||||||
|
args[1:1] = ['fish', '_legacy_completion=fish2']
|
||||||
|
from kitty.constants import kitten_exe
|
||||||
|
args = ['kitten', '__complete__'] + args[1:]
|
||||||
|
os.execvp(kitten_exe(), args)
|
||||||
|
|
||||||
|
|
||||||
def open_urls(args: List[str]) -> None:
|
def open_urls(args: List[str]) -> None:
|
||||||
setattr(sys, 'cmdline_args_for_open', True)
|
setattr(sys, 'cmdline_args_for_open', True)
|
||||||
sys.argv = ['kitty'] + args[1:]
|
sys.argv = ['kitty'] + args[1:]
|
||||||
@ -144,6 +155,7 @@ entry_points = {
|
|||||||
}
|
}
|
||||||
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
|
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
|
||||||
namespaced_entry_points['hold'] = hold
|
namespaced_entry_points['hold'] = hold
|
||||||
|
namespaced_entry_points['complete'] = complete
|
||||||
namespaced_entry_points['runpy'] = runpy
|
namespaced_entry_points['runpy'] = runpy
|
||||||
namespaced_entry_points['launch'] = launch
|
namespaced_entry_points['launch'] = launch
|
||||||
namespaced_entry_points['open'] = open_urls
|
namespaced_entry_points['open'] = open_urls
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#compdef kitty
|
#compdef kitty
|
||||||
|
|
||||||
(( ${+commands[kitty]} )) || builtin return
|
(( ${+commands[kitten]} )) || builtin return
|
||||||
builtin local src cmd=${(F)words:0:$CURRENT}
|
builtin local src cmd=${(F)words:0:$CURRENT}
|
||||||
# Send all words up to the word the cursor is currently on.
|
# Send all words up to the word the cursor is currently on.
|
||||||
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
|
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
|
||||||
|
|||||||
@ -11,6 +11,26 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func bash_completion_script(commands []string) ([]byte, error) {
|
||||||
|
script := `_ksi_completions() {
|
||||||
|
builtin local src
|
||||||
|
builtin local limit
|
||||||
|
# Send all words up to the word the cursor is currently on
|
||||||
|
builtin let limit=1+$COMP_CWORD
|
||||||
|
src=$(builtin printf "%s\n" "${COMP_WORDS[@]:0:$limit}" | builtin command kitten __complete__ bash)
|
||||||
|
if [[ $? == 0 ]]; then
|
||||||
|
builtin eval "${src}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
builtin complete -F _ksi_completions kitty
|
||||||
|
builtin complete -F _ksi_completions edit-in-kitty
|
||||||
|
builtin complete -F _ksi_completions clone-in-kitty
|
||||||
|
builtin complete -F _ksi_completions kitten
|
||||||
|
`
|
||||||
|
return []byte(script), nil
|
||||||
|
}
|
||||||
|
|
||||||
func bash_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
func bash_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
||||||
output := strings.Builder{}
|
output := strings.Builder{}
|
||||||
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
||||||
@ -51,6 +71,7 @@ func bash_init_completions(completions *Completions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
completion_scripts["bash"] = bash_completion_script
|
||||||
input_parsers["bash"] = shell_input_parser
|
input_parsers["bash"] = shell_input_parser
|
||||||
output_serializers["bash"] = bash_output_serializer
|
output_serializers["bash"] = bash_output_serializer
|
||||||
init_completions["bash"] = bash_init_completions
|
init_completions["bash"] = bash_init_completions
|
||||||
|
|||||||
@ -30,9 +30,11 @@ func json_output_serializer(completions []*Completions, shell_state map[string]s
|
|||||||
return json.Marshal(completions)
|
return json.Marshal(completions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type completion_script_func func(commands []string) ([]byte, error)
|
||||||
type parser_func func(data []byte, shell_state map[string]string) ([][]string, error)
|
type parser_func func(data []byte, shell_state map[string]string) ([][]string, error)
|
||||||
type serializer_func func(completions []*Completions, shell_state map[string]string) ([]byte, error)
|
type serializer_func func(completions []*Completions, shell_state map[string]string) ([]byte, error)
|
||||||
|
|
||||||
|
var completion_scripts = make(map[string]completion_script_func, 4)
|
||||||
var input_parsers = make(map[string]parser_func, 4)
|
var input_parsers = make(map[string]parser_func, 4)
|
||||||
var output_serializers = make(map[string]serializer_func, 4)
|
var output_serializers = make(map[string]serializer_func, 4)
|
||||||
var init_completions = make(map[string]func(*Completions), 4)
|
var init_completions = make(map[string]func(*Completions), 4)
|
||||||
@ -61,6 +63,22 @@ func GenerateCompletions(args []string) error {
|
|||||||
if n < 1 {
|
if n < 1 {
|
||||||
n = 1
|
n = 1
|
||||||
}
|
}
|
||||||
|
if output_type == "setup" {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return fmt.Errorf("The shell needs to be specified")
|
||||||
|
}
|
||||||
|
shell_name := args[0]
|
||||||
|
args = args[1:]
|
||||||
|
completion_script := completion_scripts[shell_name]
|
||||||
|
if completion_script == nil {
|
||||||
|
return fmt.Errorf("Unsupported shell: %s", shell_name)
|
||||||
|
}
|
||||||
|
output, err := completion_script(args)
|
||||||
|
if err == nil {
|
||||||
|
_, err = os.Stdout.Write(output)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
shell_state := make(map[string]string, n)
|
shell_state := make(map[string]string, n)
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
k, v, found := utils.Cut(arg, "=")
|
k, v, found := utils.Cut(arg, "=")
|
||||||
|
|||||||
@ -12,12 +12,53 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func fish_completion_script(commands []string) ([]byte, error) {
|
||||||
|
// One command in fish requires one completion script.
|
||||||
|
// Usage: kitten __complete__ setup fish [kitty|kitten|clone-in-kitty]
|
||||||
|
all_commands := map[string]bool{
|
||||||
|
"kitty": true,
|
||||||
|
"clone-in-kitty": true,
|
||||||
|
"kitten": true,
|
||||||
|
}
|
||||||
|
if len(commands) == 0 {
|
||||||
|
for cmd, _ := range all_commands {
|
||||||
|
commands = append(commands, cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
script := strings.Builder{}
|
||||||
|
script.WriteString(`function __ksi_completions
|
||||||
|
set --local ct (commandline --current-token)
|
||||||
|
set --local tokens (commandline --tokenize --cut-at-cursor --current-process)
|
||||||
|
printf "%s\n" $tokens $ct | command kitten __complete__ fish | source -
|
||||||
|
end
|
||||||
|
|
||||||
|
`)
|
||||||
|
for _, cmd := range commands {
|
||||||
|
if all_commands[cmd] {
|
||||||
|
fmt.Fprintf(&script, "complete -f -c %s -a \"(__ksi_completions)\"\n", cmd)
|
||||||
|
} else if strings.Contains(cmd, "=") {
|
||||||
|
// Reserved for `setup SHELL [KEY=VALUE ...]`, not used now.
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("No fish completion script for command: %s", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []byte(script.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
func fish_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
func fish_output_serializer(completions []*Completions, shell_state map[string]string) ([]byte, error) {
|
||||||
output := strings.Builder{}
|
output := strings.Builder{}
|
||||||
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
f := func(format string, args ...any) { fmt.Fprintf(&output, format+"\n", args...) }
|
||||||
n := completions[0].Delegate.NumToRemove
|
n := completions[0].Delegate.NumToRemove
|
||||||
fm := markup.New(false) // fish freaks out if there are escape codes in the description strings
|
fm := markup.New(false) // fish freaks out if there are escape codes in the description strings
|
||||||
if n > 0 {
|
legacy_completion := shell_state["_legacy_completion"]
|
||||||
|
if legacy_completion == "fish2" {
|
||||||
|
for _, mg := range completions[0].Groups {
|
||||||
|
for _, m := range mg.Matches {
|
||||||
|
f("%s", strings.ReplaceAll(m.Word+"\t"+fm.Prettify(m.Description), "\n", " "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if n > 0 {
|
||||||
words := make([]string, len(completions[0].AllWords)-n+1)
|
words := make([]string, len(completions[0].AllWords)-n+1)
|
||||||
words[0] = completions[0].Delegate.Command
|
words[0] = completions[0].Delegate.Command
|
||||||
copy(words[1:], completions[0].AllWords[n:])
|
copy(words[1:], completions[0].AllWords[n:])
|
||||||
@ -40,6 +81,7 @@ func fish_output_serializer(completions []*Completions, shell_state map[string]s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
completion_scripts["fish"] = fish_completion_script
|
||||||
input_parsers["fish"] = shell_input_parser
|
input_parsers["fish"] = shell_input_parser
|
||||||
output_serializers["fish"] = fish_output_serializer
|
output_serializers["fish"] = fish_output_serializer
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,26 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func zsh_completion_script(commands []string) ([]byte, error) {
|
||||||
|
script := `#compdef kitty
|
||||||
|
|
||||||
|
_kitty() {
|
||||||
|
(( ${+commands[kitten]} )) || builtin return
|
||||||
|
builtin local src cmd=${(F)words:0:$CURRENT}
|
||||||
|
# Send all words up to the word the cursor is currently on.
|
||||||
|
src=$(builtin command kitten __complete__ zsh "_matcher=$_matcher" <<<$cmd) || builtin return
|
||||||
|
builtin eval "$src"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $+functions[compdef] )); then
|
||||||
|
compdef _kitty kitty
|
||||||
|
compdef _kitty clone-in-kitty
|
||||||
|
compdef _kitty kitten
|
||||||
|
fi
|
||||||
|
`
|
||||||
|
return []byte(script), nil
|
||||||
|
}
|
||||||
|
|
||||||
func shell_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {
|
func shell_input_parser(data []byte, shell_state map[string]string) ([][]string, error) {
|
||||||
raw := string(data)
|
raw := string(data)
|
||||||
new_word := strings.HasSuffix(raw, "\n\n")
|
new_word := strings.HasSuffix(raw, "\n\n")
|
||||||
@ -150,6 +170,7 @@ func zsh_output_serializer(completions []*Completions, shell_state map[string]st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
completion_scripts["zsh"] = zsh_completion_script
|
||||||
input_parsers["zsh"] = zsh_input_parser
|
input_parsers["zsh"] = zsh_input_parser
|
||||||
output_serializers["zsh"] = zsh_output_serializer
|
output_serializers["zsh"] = zsh_output_serializer
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ func EntryPoint(tool_root *cli.Command) {
|
|||||||
Name: "__complete__", Hidden: true,
|
Name: "__complete__", Hidden: true,
|
||||||
Usage: "output_type [shell state...]",
|
Usage: "output_type [shell state...]",
|
||||||
ShortDescription: "Generate completions for kitty commands",
|
ShortDescription: "Generate completions for kitty commands",
|
||||||
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells or :code:`json` for JSON output.",
|
HelpText: "Generate completion candidates for kitty commands. The command line is read from STDIN. output_type can be one of the supported shells: :code:`zsh`, :code:`fish`, :code:`bash`, or :code:`setup` for completion setup script following with the shell name, or :code:`json` for JSON output.",
|
||||||
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
|
Run: func(cmd *cli.Command, args []string) (ret int, err error) {
|
||||||
return ret, cli.GenerateCompletions(args)
|
return ret, cli.GenerateCompletions(args)
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user