Get completion basically working
This commit is contained in:
parent
9e2c96653f
commit
723a9c91b5
@ -61,6 +61,7 @@ func shell_loop(rl *readline.Readline, kill_if_signaled bool) (int, error) {
|
|||||||
rl.Redraw()
|
rl.Redraw()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
lp.ClearToEndOfScreen()
|
||||||
return ErrExec
|
return ErrExec
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -176,7 +177,7 @@ func exec_command(rl *readline.Readline, cmdline string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func completions(before_cursor, after_cursor string) (ans *cli.Completions) {
|
func completions(before_cursor, after_cursor string) (ans *cli.Completions) {
|
||||||
const prefix = "kitty @ "
|
const prefix = "kitty-tool @ "
|
||||||
text := prefix + before_cursor
|
text := prefix + before_cursor
|
||||||
argv, position_of_last_arg := shlex.SplitForCompletion(text)
|
argv, position_of_last_arg := shlex.SplitForCompletion(text)
|
||||||
if len(argv) == 0 || position_of_last_arg < len(prefix) {
|
if len(argv) == 0 || position_of_last_arg < len(prefix) {
|
||||||
|
|||||||
@ -17,10 +17,17 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func new_rl() *Readline {
|
||||||
|
lp, _ := loop.New()
|
||||||
|
rl := New(lp, RlInit{Prompt: "$$ "})
|
||||||
|
rl.screen_width = 10
|
||||||
|
rl.screen_height = 100
|
||||||
|
return rl
|
||||||
|
}
|
||||||
|
|
||||||
func test_func(t *testing.T) func(string, func(*Readline), ...string) *Readline {
|
func test_func(t *testing.T) func(string, func(*Readline), ...string) *Readline {
|
||||||
return func(initial string, prepare func(rl *Readline), expected ...string) *Readline {
|
return func(initial string, prepare func(rl *Readline), expected ...string) *Readline {
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{})
|
|
||||||
rl.add_text(initial)
|
rl.add_text(initial)
|
||||||
if prepare != nil {
|
if prepare != nil {
|
||||||
prepare(rl)
|
prepare(rl)
|
||||||
@ -64,9 +71,7 @@ func TestAddText(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetScreenLines(t *testing.T) {
|
func TestGetScreenLines(t *testing.T) {
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
|
||||||
rl.screen_width = 10
|
|
||||||
|
|
||||||
p := func(primary bool) Prompt {
|
p := func(primary bool) Prompt {
|
||||||
if primary {
|
if primary {
|
||||||
@ -189,9 +194,7 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
right(rl, 1, 1, false)
|
right(rl, 1, 1, false)
|
||||||
}, "à", "b")
|
}, "à", "b")
|
||||||
|
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
|
||||||
rl.screen_width = 10
|
|
||||||
|
|
||||||
vert := func(amt int, moved_amt int, text_upto_cursor_pos string, initials ...Position) {
|
vert := func(amt int, moved_amt int, text_upto_cursor_pos string, initials ...Position) {
|
||||||
initial := Position{}
|
initial := Position{}
|
||||||
@ -266,8 +269,7 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestYanking(t *testing.T) {
|
func TestYanking(t *testing.T) {
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
|
||||||
|
|
||||||
as_slice := func(l *list.List) []string {
|
as_slice := func(l *list.List) []string {
|
||||||
ans := make([]string, 0, l.Len())
|
ans := make([]string, 0, l.Len())
|
||||||
@ -386,8 +388,9 @@ func TestEraseChars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNumberArgument(t *testing.T) {
|
func TestNumberArgument(t *testing.T) {
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
rl.screen_width = 100
|
||||||
|
|
||||||
test := func(ac Action, before_cursor, after_cursor string) {
|
test := func(ac Action, before_cursor, after_cursor string) {
|
||||||
rl.dispatch_key_action(ac)
|
rl.dispatch_key_action(ac)
|
||||||
if diff := cmp.Diff(before_cursor, rl.text_upto_cursor_pos()); diff != "" {
|
if diff := cmp.Diff(before_cursor, rl.text_upto_cursor_pos()); diff != "" {
|
||||||
@ -431,8 +434,7 @@ func TestNumberArgument(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHistory(t *testing.T) {
|
func TestHistory(t *testing.T) {
|
||||||
lp, _ := loop.New()
|
rl := new_rl()
|
||||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
|
||||||
|
|
||||||
add_item := func(x string) {
|
add_item := func(x string) {
|
||||||
rl.history.AddItem(x, 0)
|
rl.history.AddItem(x, 0)
|
||||||
@ -516,8 +518,6 @@ func TestHistory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestReadlineCompletion(t *testing.T) {
|
func TestReadlineCompletion(t *testing.T) {
|
||||||
lp, _ := loop.New()
|
|
||||||
|
|
||||||
completer := func(before_cursor, after_cursor string) (ans *cli.Completions) {
|
completer := func(before_cursor, after_cursor string) (ans *cli.Completions) {
|
||||||
root := cli.NewRootCommand()
|
root := cli.NewRootCommand()
|
||||||
c := root.AddSubCommand(&cli.Command{Name: "test-completion"})
|
c := root.AddSubCommand(&cli.Command{Name: "test-completion"})
|
||||||
@ -535,7 +535,8 @@ func TestReadlineCompletion(t *testing.T) {
|
|||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
rl := New(lp, RlInit{Prompt: "$$ ", Completer: completer})
|
rl := new_rl()
|
||||||
|
rl.completions.completer = completer
|
||||||
|
|
||||||
ah := func(before_cursor, after_cursor string) {
|
ah := func(before_cursor, after_cursor string) {
|
||||||
ab := rl.text_upto_cursor_pos()
|
ab := rl.text_upto_cursor_pos()
|
||||||
|
|||||||
@ -117,9 +117,9 @@ type Readline struct {
|
|||||||
|
|
||||||
input_state InputState
|
input_state InputState
|
||||||
// The number of lines after the initial line on the screen
|
// The number of lines after the initial line on the screen
|
||||||
cursor_y int
|
cursor_y int
|
||||||
screen_width int
|
screen_width, screen_height int
|
||||||
last_yank_extent struct {
|
last_yank_extent struct {
|
||||||
start, end Position
|
start, end Position
|
||||||
}
|
}
|
||||||
bracketed_paste_buffer strings.Builder
|
bracketed_paste_buffer strings.Builder
|
||||||
@ -259,10 +259,7 @@ func (self *Readline) CursorAtEndOfLine() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) OnResize(old_size loop.ScreenSize, new_size loop.ScreenSize) error {
|
func (self *Readline) OnResize(old_size loop.ScreenSize, new_size loop.ScreenSize) error {
|
||||||
self.screen_width = int(new_size.CellWidth)
|
self.screen_width, self.screen_height = 0, 0
|
||||||
if self.screen_width < 1 {
|
|
||||||
self.screen_width = 1
|
|
||||||
}
|
|
||||||
self.Redraw()
|
self.Redraw()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,11 @@ package readline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
|
"kitty/tools/wcswidth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@ -16,6 +18,9 @@ type completion struct {
|
|||||||
results *cli.Completions
|
results *cli.Completions
|
||||||
results_displayed, forwards bool
|
results_displayed, forwards bool
|
||||||
num_of_matches, current_match int
|
num_of_matches, current_match int
|
||||||
|
rendered_at_screen_width int
|
||||||
|
rendered_lines []string
|
||||||
|
last_rendered_above bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *completion) initialize() {
|
func (self *completion) initialize() {
|
||||||
@ -64,23 +69,29 @@ func (self *Readline) complete(forwards bool, repeat_count uint) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if self.last_action == ActionCompleteForward || self.last_action == ActionCompleteBackward {
|
if self.last_action == ActionCompleteForward || self.last_action == ActionCompleteBackward {
|
||||||
|
if c.current.num_of_matches == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
delta := -1
|
delta := -1
|
||||||
if forwards {
|
if forwards {
|
||||||
delta = 1
|
delta = 1
|
||||||
}
|
}
|
||||||
|
repeat_count %= uint(c.current.num_of_matches)
|
||||||
delta *= int(repeat_count)
|
delta *= int(repeat_count)
|
||||||
c.current.current_match = (c.current.current_match + delta + c.current.num_of_matches) % c.current.num_of_matches
|
c.current.current_match = (c.current.current_match + delta + c.current.num_of_matches) % c.current.num_of_matches
|
||||||
repeat_count = 0
|
repeat_count = 0
|
||||||
} else {
|
} else {
|
||||||
before, after := self.text_upto_cursor_pos(), self.text_after_cursor_pos()
|
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 = completion{before_cursor: before, after_cursor: after, forwards: forwards, results: c.completer(before, after)}
|
||||||
c.current.initialize()
|
c.current.initialize()
|
||||||
if repeat_count > 0 {
|
if repeat_count > 0 {
|
||||||
repeat_count--
|
repeat_count--
|
||||||
}
|
}
|
||||||
|
if c.current.current_match != 0 {
|
||||||
|
if self.loop != nil {
|
||||||
|
self.loop.Beep()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.current.forwards = forwards
|
c.current.forwards = forwards
|
||||||
if c.current.results == nil {
|
if c.current.results == nil {
|
||||||
@ -107,3 +118,158 @@ func (self *Readline) complete(forwards bool, repeat_count uint) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Readline) screen_lines_for_match_group_with_descriptions(g *cli.MatchGroup, lines []string) []string {
|
||||||
|
maxw := 0
|
||||||
|
lengths := make(map[string]int)
|
||||||
|
for _, m := range g.Matches {
|
||||||
|
l := wcswidth.Stringwidth(m.Word)
|
||||||
|
lengths[m.Word] = l
|
||||||
|
if l > maxw {
|
||||||
|
maxw = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range g.Matches {
|
||||||
|
p := m.Word + strings.Repeat(" ", maxw-lengths[m.Word])
|
||||||
|
line, _, _ := utils.Cut(strings.TrimSpace(m.Description), "\n")
|
||||||
|
line = p + " - " + self.fmt_ctx.Prettify(line)
|
||||||
|
truncated := wcswidth.TruncateToVisualLength(line, self.screen_width-1) + "\x1b[m"
|
||||||
|
if len(truncated) < len(line) {
|
||||||
|
line = truncated + "…"
|
||||||
|
}
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
type cell struct {
|
||||||
|
text string
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self cell) whitespace(desired_length int) string {
|
||||||
|
return strings.Repeat(" ", desired_length-self.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
type column struct {
|
||||||
|
cells []cell
|
||||||
|
length int
|
||||||
|
is_last bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *column) update_length() int {
|
||||||
|
self.length = 0
|
||||||
|
for _, c := range self.cells {
|
||||||
|
if c.length > self.length {
|
||||||
|
self.length = c.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.is_last {
|
||||||
|
self.length++
|
||||||
|
}
|
||||||
|
return self.length
|
||||||
|
}
|
||||||
|
|
||||||
|
func layout_words_in_table(words []string, lengths map[string]int, num_cols int) ([]column, int) {
|
||||||
|
cols := make([]column, num_cols)
|
||||||
|
for i, col := range cols {
|
||||||
|
col.cells = make([]cell, 0, len(words))
|
||||||
|
if i == len(cols)-1 {
|
||||||
|
col.is_last = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r, c := 0, 0
|
||||||
|
for _, word := range words {
|
||||||
|
cols[r].cells = append(cols[r].cells, cell{word, lengths[word]})
|
||||||
|
c++
|
||||||
|
if c > num_cols {
|
||||||
|
c = 0
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_length := 0
|
||||||
|
for i, col := range cols {
|
||||||
|
total_length += col.update_length()
|
||||||
|
for i > 0 && len(col.cells) < len(cols[i-1].cells) {
|
||||||
|
col.cells = append(col.cells, cell{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cols, total_length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) screen_lines_for_match_group_without_descriptions(g *cli.MatchGroup, lines []string) []string {
|
||||||
|
words := make([]string, len(g.Matches))
|
||||||
|
lengths := make(map[string]int, len(words))
|
||||||
|
max_length := 0
|
||||||
|
for i, m := range g.Matches {
|
||||||
|
words[i] = m.Word
|
||||||
|
l := wcswidth.Stringwidth(words[i])
|
||||||
|
lengths[words[i]] = l
|
||||||
|
if l > max_length {
|
||||||
|
max_length = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var ans []column
|
||||||
|
ncols := utils.Max(1, self.screen_width/(max_length+1))
|
||||||
|
for {
|
||||||
|
cols, total_length := layout_words_in_table(words, lengths, ncols)
|
||||||
|
if total_length > self.screen_width {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ans = cols
|
||||||
|
ncols++
|
||||||
|
}
|
||||||
|
if ans == nil {
|
||||||
|
for _, w := range words {
|
||||||
|
if lengths[w] > self.screen_width {
|
||||||
|
lines = append(lines, wcswidth.TruncateToVisualLength(w, self.screen_width))
|
||||||
|
} else {
|
||||||
|
lines = append(lines, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for r := 0; r < len(ans[0].cells); r++ {
|
||||||
|
w := strings.Builder{}
|
||||||
|
w.Grow(self.screen_width)
|
||||||
|
for c := 0; c < len(ans); c++ {
|
||||||
|
cell := ans[c].cells[r]
|
||||||
|
w.WriteString(cell.text)
|
||||||
|
if !ans[c].is_last {
|
||||||
|
w.WriteString(cell.whitespace(ans[r].length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines = append(lines, w.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) completion_screen_lines() ([]string, bool) {
|
||||||
|
if self.completions.current.results == nil || self.completions.current.num_of_matches < 2 {
|
||||||
|
return []string{}, false
|
||||||
|
}
|
||||||
|
if len(self.completions.current.rendered_lines) > 0 && self.completions.current.rendered_at_screen_width == self.screen_width {
|
||||||
|
return self.completions.current.rendered_lines, true
|
||||||
|
}
|
||||||
|
lines := make([]string, 0, self.completions.current.num_of_matches)
|
||||||
|
for _, g := range self.completions.current.results.Groups {
|
||||||
|
if g.Title != "" {
|
||||||
|
lines = append(lines, self.fmt_ctx.Title(g.Title))
|
||||||
|
}
|
||||||
|
has_descriptions := false
|
||||||
|
for _, m := range g.Matches {
|
||||||
|
if m.Description != "" {
|
||||||
|
has_descriptions = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_descriptions {
|
||||||
|
lines = self.screen_lines_for_match_group_with_descriptions(g, lines)
|
||||||
|
} else {
|
||||||
|
lines = self.screen_lines_for_match_group_without_descriptions(g, lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.completions.current.rendered_lines = lines
|
||||||
|
self.completions.current.rendered_at_screen_width = self.screen_width
|
||||||
|
return lines, false
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ package readline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"kitty/tools/wcswidth"
|
"kitty/tools/wcswidth"
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,18 +13,20 @@ import (
|
|||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
func (self *Readline) update_current_screen_size() {
|
func (self *Readline) update_current_screen_size() {
|
||||||
screen_size, err := self.loop.ScreenSize()
|
var screen_size loop.ScreenSize
|
||||||
if err != nil {
|
var err error
|
||||||
|
if self.loop != nil {
|
||||||
|
screen_size, err = self.loop.ScreenSize()
|
||||||
|
if err != nil {
|
||||||
|
screen_size.WidthCells = 80
|
||||||
|
screen_size.HeightCells = 24
|
||||||
|
}
|
||||||
|
} else {
|
||||||
screen_size.WidthCells = 80
|
screen_size.WidthCells = 80
|
||||||
screen_size.HeightCells = 24
|
screen_size.HeightCells = 24
|
||||||
}
|
}
|
||||||
if screen_size.WidthCells < 1 {
|
self.screen_width = utils.Max(1, int(screen_size.WidthCells))
|
||||||
screen_size.WidthCells = 1
|
self.screen_height = utils.Max(1, int(screen_size.HeightCells))
|
||||||
}
|
|
||||||
if screen_size.HeightCells < 1 {
|
|
||||||
screen_size.HeightCells = 1
|
|
||||||
}
|
|
||||||
self.screen_width = int(screen_size.WidthCells)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScreenLine struct {
|
type ScreenLine struct {
|
||||||
@ -82,7 +85,7 @@ func (self *Readline) apply_syntax_highlighting() (lines []string, cursor Positi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) get_screen_lines() []*ScreenLine {
|
func (self *Readline) get_screen_lines() []*ScreenLine {
|
||||||
if self.screen_width == 0 {
|
if self.screen_width == 0 || self.screen_height == 0 {
|
||||||
self.update_current_screen_size()
|
self.update_current_screen_size()
|
||||||
}
|
}
|
||||||
lines, cursor := self.apply_syntax_highlighting()
|
lines, cursor := self.apply_syntax_highlighting()
|
||||||
@ -129,7 +132,7 @@ func (self *Readline) get_screen_lines() []*ScreenLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) redraw() {
|
func (self *Readline) redraw() {
|
||||||
if self.screen_width == 0 {
|
if self.screen_width == 0 || self.screen_height == 0 {
|
||||||
self.update_current_screen_size()
|
self.update_current_screen_size()
|
||||||
}
|
}
|
||||||
if self.screen_width < 4 {
|
if self.screen_width < 4 {
|
||||||
@ -140,11 +143,38 @@ func (self *Readline) redraw() {
|
|||||||
}
|
}
|
||||||
self.loop.QueueWriteString("\r")
|
self.loop.QueueWriteString("\r")
|
||||||
self.loop.ClearToEndOfScreen()
|
self.loop.ClearToEndOfScreen()
|
||||||
|
prompt_lines := self.get_screen_lines()
|
||||||
|
csl, csl_cached := self.completion_screen_lines()
|
||||||
|
render_completion_above := len(csl)+len(prompt_lines) > self.screen_height
|
||||||
|
completion_needs_render := len(csl) > 0 && (!render_completion_above || !self.completions.current.last_rendered_above || !csl_cached)
|
||||||
cursor_x := -1
|
cursor_x := -1
|
||||||
cursor_y := 0
|
cursor_y := 0
|
||||||
move_cursor_up_by := 0
|
move_cursor_up_by := 0
|
||||||
|
|
||||||
|
render_completion_lines := func() int {
|
||||||
|
if completion_needs_render {
|
||||||
|
if render_completion_above {
|
||||||
|
self.loop.QueueWriteString("\r")
|
||||||
|
} else {
|
||||||
|
self.loop.QueueWriteString("\r\n")
|
||||||
|
}
|
||||||
|
for i, cl := range csl {
|
||||||
|
self.loop.QueueWriteString(cl)
|
||||||
|
if i < len(csl)-1 || render_completion_above {
|
||||||
|
self.loop.QueueWriteString("\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return len(csl)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
self.loop.AllowLineWrapping(false)
|
self.loop.AllowLineWrapping(false)
|
||||||
for i, sl := range self.get_screen_lines() {
|
if render_completion_above {
|
||||||
|
render_completion_lines()
|
||||||
|
}
|
||||||
|
for i, sl := range prompt_lines {
|
||||||
self.loop.QueueWriteString("\r")
|
self.loop.QueueWriteString("\r")
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
self.loop.QueueWriteString("\n")
|
self.loop.QueueWriteString("\n")
|
||||||
@ -160,6 +190,9 @@ func (self *Readline) redraw() {
|
|||||||
}
|
}
|
||||||
cursor_y++
|
cursor_y++
|
||||||
}
|
}
|
||||||
|
if !render_completion_above {
|
||||||
|
move_cursor_up_by += render_completion_lines()
|
||||||
|
}
|
||||||
self.loop.AllowLineWrapping(true)
|
self.loop.AllowLineWrapping(true)
|
||||||
self.loop.MoveCursorVertically(-move_cursor_up_by)
|
self.loop.MoveCursorVertically(-move_cursor_up_by)
|
||||||
self.loop.QueueWriteString("\r")
|
self.loop.QueueWriteString("\r")
|
||||||
|
|||||||
@ -88,6 +88,8 @@ func default_shortcuts() *ShortcutMap {
|
|||||||
sm.AddOrPanic(ActionNumericArgumentDigit9, "alt+9")
|
sm.AddOrPanic(ActionNumericArgumentDigit9, "alt+9")
|
||||||
sm.AddOrPanic(ActionNumericArgumentDigitMinus, "alt+-")
|
sm.AddOrPanic(ActionNumericArgumentDigitMinus, "alt+-")
|
||||||
|
|
||||||
|
sm.AddOrPanic(ActionCompleteForward, "Tab")
|
||||||
|
sm.AddOrPanic(ActionCompleteBackward, "Shift+Tab")
|
||||||
_default_shortcuts = sm
|
_default_shortcuts = sm
|
||||||
}
|
}
|
||||||
return _default_shortcuts
|
return _default_shortcuts
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user