Start work on readline completions

This commit is contained in:
Kovid Goyal 2022-11-10 15:35:36 +05:30
parent f919efcd42
commit 7c23536bfe
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 125 additions and 1 deletions

View File

@ -434,6 +434,9 @@ def generate_readline_actions() -> str:
ActionNumericArgumentDigit8
ActionNumericArgumentDigit9
ActionNumericArgumentDigitMinus
ActionCompleteForward
ActionCompleteBackward
''')

View File

@ -51,6 +51,14 @@ func RegisterExeForCompletion(x func(root *Command)) {
registered_exes = append(registered_exes, x)
}
func CompletionsForArgv(argv []string) *Completions {
var root = NewRootCommand()
for _, re := range registered_exes {
re(root)
}
return root.GetCompletions(argv, init_completions["json"])
}
func GenerateCompletions(args []string) error {
output_type := "json"
if len(args) > 0 {

View File

@ -176,6 +176,15 @@ func exec_command(rl *readline.Readline, cmdline string) bool {
return true
}
func completions(before_cursor, after_cursor string) *cli.Completions {
text := "kitty @ " + before_cursor
argv, err := shlex.Split(text)
if err != nil {
return nil
}
return cli.CompletionsForArgv(argv)
}
func shell_main(cmd *cli.Command, args []string) (int, error) {
formatter = markup.New(true)
fmt.Println("Welcome to the kitty shell!")
@ -189,7 +198,7 @@ func shell_main(cmd *cli.Command, args []string) (int, error) {
}
fmt.Println(amsg)
}
rl := readline.New(nil, readline.RlInit{Prompt: prompt, HistoryPath: filepath.Join(utils.CacheDir(), "shell.history.json")})
rl := readline.New(nil, readline.RlInit{Prompt: prompt, Completer: completions, HistoryPath: filepath.Join(utils.CacheDir(), "shell.history.json")})
defer func() {
rl.Shutdown()
}()

View File

@ -654,6 +654,14 @@ func (self *Readline) _perform_action(ac Action, repeat_count uint) (err error,
self.end_history_search(true)
return
}
case ActionCompleteForward:
if self.complete(true, repeat_count) {
return
}
case ActionCompleteBackward:
if self.complete(false, repeat_count) {
return
}
}
err = ErrCouldNotPerformAction
return

View File

@ -7,6 +7,7 @@ import (
"fmt"
"strings"
"kitty/tools/cli"
"kitty/tools/cli/markup"
"kitty/tools/tui/loop"
"kitty/tools/wcswidth"
@ -18,6 +19,7 @@ const ST = "\x1b\\"
const PROMPT_MARK = "\x1b]133;"
type SyntaxHighlightFunction = func(text string, x, y int) string
type CompleterFunction = func(before_cursor, after_cursor string) *cli.Completions
type RlInit struct {
Prompt string
@ -27,6 +29,7 @@ type RlInit struct {
EmptyContinuationPrompt bool
DontMarkPrompts bool
SyntaxHighlighter SyntaxHighlightFunction
Completer CompleterFunction
}
type Position struct {
@ -127,6 +130,7 @@ type Readline struct {
fmt_ctx *markup.Context
text_to_be_added string
syntax_highlighted syntax_highlighted
completions completions
}
func (self *Readline) make_prompt(text string, is_secondary bool) Prompt {
@ -149,6 +153,7 @@ func New(loop *loop.Loop, r RlInit) *Readline {
mark_prompts: !r.DontMarkPrompts, fmt_ctx: markup.New(true),
loop: loop, input_state: InputState{lines: []string{""}}, history: NewHistory(r.HistoryPath, hc),
syntax_highlighted: syntax_highlighted{highlighter: r.SyntaxHighlighter},
completions: completions{completer: r.Completer},
kill_ring: kill_ring{items: list.New().Init()},
}
ans.prompt = ans.make_prompt(r.Prompt, false)

View File

@ -0,0 +1,91 @@
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
package readline
import (
"fmt"
"kitty/tools/cli"
)
var _ = fmt.Print
type completion struct {
before_cursor, after_cursor string
results *cli.Completions
results_displayed, forwards bool
num_of_matches, current_match int
}
func (self *completion) initialize() {
self.num_of_matches = 0
if self.results != nil {
for _, g := range self.results.Groups {
self.num_of_matches += len(g.Matches)
}
}
self.current_match = -1
if !self.forwards {
self.current_match = self.num_of_matches
}
if self.num_of_matches == 1 {
self.current_match = 0
}
}
func (self *completion) current_match_text() string {
if self.results != nil {
i := 0
for _, g := range self.results.Groups {
for _, m := range g.Matches {
if i == self.current_match {
return m.Word
}
i++
}
}
}
return ""
}
type completions struct {
completer CompleterFunction
current completion
}
func (self *Readline) complete(forwards bool, repeat_count uint) bool {
c := &self.completions
if c.completer == nil {
return false
}
if self.last_action == ActionCompleteForward || self.last_action == ActionCompleteBackward {
delta := -1
if forwards {
delta = 1
}
delta *= int(repeat_count)
c.current.current_match = (c.current.current_match + delta + c.current.num_of_matches) % c.current.num_of_matches
repeat_count = 0
} else {
before, after := self.text_upto_cursor_pos(), self.text_after_cursor_pos()
if before == "" {
return false
}
c.current = completion{before_cursor: before, after_cursor: after, forwards: forwards, results: c.completer(before, after)}
c.current.initialize()
if repeat_count > 0 {
repeat_count--
}
}
c.current.forwards = forwards
if c.current.results == nil {
return false
}
ct := c.current.current_match_text()
if ct != "" {
}
if repeat_count > 0 {
self.complete(forwards, repeat_count)
}
return true
}