More completion work

This commit is contained in:
Kovid Goyal 2022-09-16 17:29:41 +05:30
parent 946d44c43f
commit 0ff2446a1a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 46 additions and 5 deletions

View File

@ -61,6 +61,7 @@ def generate_completions_for_kitty() -> None:
print('k := root.add_command("kitty", "")') print('k := root.add_command("kitty", "")')
print('k.First_arg_may_not_be_subcommand = true') print('k.First_arg_may_not_be_subcommand = true')
print('k.Completion_for_arg = complete_kitty') print('k.Completion_for_arg = complete_kitty')
print('k.Subcommand_must_be_first = true')
for opt in go_options_for_seq(parse_option_spec()[0]): for opt in go_options_for_seq(parse_option_spec()[0]):
print(opt.as_completion_option('k')) print(opt.as_completion_option('k'))
print('at := k.add_command("@", "Remote control")') print('at := k.add_command("@", "Remote control")')

View File

@ -8,6 +8,7 @@ import socket
import sys import sys
from collections import deque from collections import deque
from enum import Enum, auto from enum import Enum, auto
from dataclasses import dataclass
from typing import ( from typing import (
Any, Callable, Dict, FrozenSet, Iterator, List, Match, Optional, Sequence, Any, Callable, Dict, FrozenSet, Iterator, List, Match, Optional, Sequence,
Tuple, Type, TypeVar, Union, cast Tuple, Type, TypeVar, Union, cast
@ -37,6 +38,7 @@ class CompletionRelativeTo(Enum):
config_dir = auto() config_dir = auto()
@dataclass
class CompletionSpec: class CompletionSpec:
type: CompletionType = CompletionType.none type: CompletionType = CompletionType.none
@ -70,7 +72,7 @@ class CompletionSpec:
raise KeyError(f'Unknown completion property: {ck}') raise KeyError(f'Unknown completion property: {ck}')
return self return self
def as_go_code(self, go_name: str) -> Iterator[str]: def as_go_code(self, go_name: str, sep: str = ' = ') -> Iterator[str]:
completers = [] completers = []
if self.kwds: if self.kwds:
kwds = (f'"{serialize_as_go_string(x)}"' for x in self.kwds) kwds = (f'"{serialize_as_go_string(x)}"' for x in self.kwds)
@ -84,10 +86,15 @@ class CompletionSpec:
completers.append(f'fnmatch_completer("{g}", {relative_to}, ' + ', '.join(pats) + ')') completers.append(f'fnmatch_completer("{g}", {relative_to}, ' + ', '.join(pats) + ')')
if self.mime_patterns: if self.mime_patterns:
completers.append(f'mimepat_completer("{g}", {relative_to}, ' + ', '.join(f'"{p}"' for p in self.mime_patterns) + ')') completers.append(f'mimepat_completer("{g}", {relative_to}, ' + ', '.join(f'"{p}"' for p in self.mime_patterns) + ')')
if self.type is CompletionType.directory:
g = serialize_as_go_string(self.group or 'Directories')
completers.append(f'directory_completer("{g}", {relative_to})')
if go_name:
go_name += '.'
if len(completers) > 1: if len(completers) > 1:
yield f'{go_name}.Completion_for_arg = chain_completers(' + ', '.join(completers) + ')' yield f'{go_name}Completion_for_arg{sep}chain_completers(' + ', '.join(completers) + ')'
elif completers: elif completers:
yield f'{go_name}.Completion_for_arg = {completers[0]}' yield f'{go_name}Completion_for_arg{sep}{completers[0]}'
class OptionDict(TypedDict): class OptionDict(TypedDict):
@ -191,6 +198,8 @@ class GoOption:
if self.type == 'choices': if self.type == 'choices':
cx = ', '.join(f'"{serialize_as_go_string(x)}"' for x in self.sorted_choices) cx = ', '.join(f'"{serialize_as_go_string(x)}"' for x in self.sorted_choices)
ans += f'Completion_for_arg: names_completer("Choices for {self.long}", {cx}),' ans += f'Completion_for_arg: names_completer("Choices for {self.long}", {cx}),'
elif self.obj_dict['completion'].type is not CompletionType.none:
ans += ''.join(self.obj_dict['completion'].as_go_code('', ': ')) + ','
return ans + '})' return ans + '})'

View File

@ -118,6 +118,9 @@ def completion(self: TestCompletion, tdir: str):
add('kitty --s', has_words('--session', '--start-as')) add('kitty --s', has_words('--session', '--start-as'))
add('kitty --start-as', all_words('--start-as')) add('kitty --start-as', all_words('--start-as'))
add('kitty --start-as ', all_words('minimized', 'maximized', 'fullscreen', 'normal')) add('kitty --start-as ', all_words('minimized', 'maximized', 'fullscreen', 'normal'))
add('kitty -1 ', does_not_have_words('@ls', '@'))
add('kitty --directory ', all_words('bin/', 'sub/'))
add('kitty -1d ', all_words('bin/', 'sub/'))
for cmd, tests, result in zip(all_cmds, all_tests, run_tool()): for cmd, tests, result in zip(all_cmds, all_tests, run_tool()):
self.current_cmd = cmd self.current_cmd = cmd

View File

@ -250,3 +250,20 @@ func fnmatch_completer(title string, relative_to relative_to, patterns ...string
func mimepat_completer(title string, relative_to relative_to, patterns ...string) completion_func { func mimepat_completer(title string, relative_to relative_to, patterns ...string) completion_func {
return make_completer(title, relative_to, patterns, complete_by_mimepat) return make_completer(title, relative_to, patterns, complete_by_mimepat)
} }
func directory_completer(title string, relative_to relative_to) completion_func {
if title == "" {
title = "Directories"
}
cwd := get_cwd_for_completion(relative_to)
return func(completions *Completions, word string, arg_num int) {
mg := completions.add_match_group(title)
mg.IsFiles = true
complete_files(word, func(entry *FileEntry) {
if entry.mode.IsDir() {
mg.add_match(entry.completion_candidate)
}
}, cwd)
}
}

View File

@ -14,6 +14,9 @@ import (
var _ = fmt.Print var _ = fmt.Print
func complete_kitty(completions *Completions, word string, arg_num int) { func complete_kitty(completions *Completions, word string, arg_num int) {
if arg_num > 1 {
return
}
exes := complete_executables_in_path(word) exes := complete_executables_in_path(word)
if len(exes) > 0 { if len(exes) > 0 {
mg := completions.add_match_group("Executables in PATH") mg := completions.add_match_group("Executables in PATH")

View File

@ -100,7 +100,7 @@ func complete_word(word string, completions *Completions, only_args_allowed bool
} }
return return
} }
if arg_num == 1 && cmd.has_subcommands() { if cmd.has_subcommands() && cmd.sub_command_allowed_at(completions, arg_num) {
for _, cg := range cmd.Groups { for _, cg := range cmd.Groups {
group := completions.add_match_group(cg.Title) group := completions.add_match_group(cg.Title)
if group.Title == "" { if group.Title == "" {
@ -163,7 +163,7 @@ func (cmd *Command) parse_args(words []string, completions *Completions) {
} }
continue continue
} }
if cmd.has_subcommands() && arg_num == 1 { if cmd.has_subcommands() && cmd.sub_command_allowed_at(completions, arg_num) {
sc := cmd.find_subcommand_with_name(word) sc := cmd.find_subcommand_with_name(word)
if sc == nil { if sc == nil {
only_args_allowed = true only_args_allowed = true

View File

@ -68,6 +68,7 @@ type Command struct {
Completion_for_arg completion_func Completion_for_arg completion_func
Stop_processing_at_arg int Stop_processing_at_arg int
First_arg_may_not_be_subcommand bool First_arg_may_not_be_subcommand bool
Subcommand_must_be_first bool
} }
func (self *Command) add_group(name string) *CommandGroup { func (self *Command) add_group(name string) *CommandGroup {
@ -122,6 +123,13 @@ func (self *Command) has_subcommands() bool {
return false return false
} }
func (self *Command) sub_command_allowed_at(completions *Completions, arg_num int) bool {
if self.Subcommand_must_be_first {
return arg_num == 1 && completions.current_word_idx == 0
}
return arg_num == 1
}
func (self *Command) add_option(opt *Option) { func (self *Command) add_option(opt *Option) {
self.Options = append(self.Options, opt) self.Options = append(self.Options, opt)
} }