More work on readline completion

This commit is contained in:
Kovid Goyal 2022-11-12 11:27:27 +05:30
parent 4974219e0f
commit 9e2c96653f
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 80 additions and 16 deletions

View File

@ -51,14 +51,6 @@ 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

@ -175,13 +175,19 @@ 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
func completions(before_cursor, after_cursor string) (ans *cli.Completions) {
const prefix = "kitty @ "
text := prefix + before_cursor
argv, position_of_last_arg := shlex.SplitForCompletion(text)
if len(argv) == 0 || position_of_last_arg < len(prefix) {
return
}
return cli.CompletionsForArgv(argv)
root := cli.NewRootCommand()
c := root.AddSubCommand(&cli.Command{Name: "kitty-tool"})
EntryPoint(c)
ans = root.GetCompletions(argv, nil)
ans.CurrentWordIdx = position_of_last_arg - len(prefix)
return
}
func shell_main(cmd *cli.Command, args []string) (int, error) {

View File

@ -5,7 +5,9 @@ package readline
import (
"container/list"
"fmt"
"kitty/tools/cli"
"kitty/tools/tui/loop"
"kitty/tools/utils/shlex"
"strconv"
"strings"
"testing"
@ -481,7 +483,7 @@ func TestHistory(t *testing.T) {
t.Fatalf("Text before cursor not as expected:\n%s", diff)
}
if diff := cmp.Diff(after_cursor, aa); diff != "" {
t.Fatalf("Text before cursor not as expected:\n%s", diff)
t.Fatalf("Text after cursor not as expected:\n%s", diff)
}
}
add_item("xyz1")
@ -512,3 +514,48 @@ func TestHistory(t *testing.T) {
rl.perform_action(ActionTerminateHistorySearchAndRestore, 1)
ah("a", "")
}
func TestReadlineCompletion(t *testing.T) {
lp, _ := loop.New()
completer := func(before_cursor, after_cursor string) (ans *cli.Completions) {
root := cli.NewRootCommand()
c := root.AddSubCommand(&cli.Command{Name: "test-completion"})
c.AddSubCommand(&cli.Command{Name: "a1"})
c.AddSubCommand(&cli.Command{Name: "a11"})
c.AddSubCommand(&cli.Command{Name: "a2"})
prefix := c.Name + " "
text := prefix + before_cursor
argv, position_of_last_arg := shlex.SplitForCompletion(text)
if len(argv) == 0 || position_of_last_arg < len(prefix) {
return
}
ans = root.GetCompletions(argv, nil)
ans.CurrentWordIdx = position_of_last_arg - len(prefix)
return
}
rl := New(lp, RlInit{Prompt: "$$ ", Completer: completer})
ah := func(before_cursor, after_cursor string) {
ab := rl.text_upto_cursor_pos()
aa := rl.text_after_cursor_pos()
if diff := cmp.Diff(before_cursor, ab); diff != "" {
t.Fatalf("Text before cursor not as expected:\n%s", diff)
}
if diff := cmp.Diff(after_cursor, aa); diff != "" {
t.Fatalf("Text after cursor not as expected:\n%s", diff)
}
}
rl.add_text("a")
rl.perform_action(ActionCompleteForward, 1)
ah("a", "")
rl.perform_action(ActionCompleteForward, 1)
ah("a1 ", "")
rl.perform_action(ActionCompleteForward, 1)
ah("a11 ", "")
rl.perform_action(ActionCompleteForward, 1)
ah("a2 ", "")
rl.perform_action(ActionCompleteBackward, 1)
ah("a11 ", "")
}

View File

@ -181,6 +181,7 @@ func (self *Readline) ResetText() {
self.last_action = ActionNil
self.keyboard_state = KeyboardState{}
self.history_search = nil
self.completions.current = completion{}
self.cursor_y = 0
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"kitty/tools/cli"
"kitty/tools/utils"
)
var _ = fmt.Print
@ -39,7 +40,11 @@ func (self *completion) current_match_text() string {
for _, g := range self.results.Groups {
for _, m := range g.Matches {
if i == self.current_match {
return m.Word
t := m.Word
if !g.NoTrailingSpace {
t += " "
}
return t
}
i++
}
@ -83,6 +88,19 @@ func (self *Readline) complete(forwards bool, repeat_count uint) bool {
}
ct := c.current.current_match_text()
if ct != "" {
before := c.current.before_cursor[:c.current.results.CurrentWordIdx] + ct
after := c.current.after_cursor
self.input_state.lines = utils.Splitlines(before)
if len(self.input_state.lines) == 0 {
self.input_state.lines = []string{""}
}
self.input_state.cursor.Y = len(self.input_state.lines) - 1
self.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])
al := utils.Splitlines(after)
if len(al) > 0 {
self.input_state.lines[self.input_state.cursor.Y] += al[0]
self.input_state.lines = append(self.input_state.lines, al[1:]...)
}
}
if repeat_count > 0 {
self.complete(forwards, repeat_count)