Implement bindings for moving by simple word
This commit is contained in:
parent
0068ae8f66
commit
a008c627e3
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"kitty/tools/utils"
|
||||
"kitty/tools/wcswidth"
|
||||
@ -18,7 +19,7 @@ func (self *Readline) text_upto_cursor_pos() string {
|
||||
buf.Grow(1024)
|
||||
for i, line := range self.lines {
|
||||
if i == self.cursor.Y {
|
||||
buf.WriteString(line[:self.cursor.X])
|
||||
buf.WriteString(line[:utils.Min(len(line), self.cursor.X)])
|
||||
break
|
||||
} else {
|
||||
buf.WriteString(line)
|
||||
@ -297,8 +298,91 @@ func (self *Readline) erase_chars_after_cursor(amt uint, traverse_line_breaks bo
|
||||
return num
|
||||
}
|
||||
|
||||
func (self *Readline) next_word_char_pos(traverse_line_breaks bool) int {
|
||||
return 0
|
||||
func has_word_chars(text string) bool {
|
||||
for _, ch := range text {
|
||||
if unicode.IsLetter(ch) || unicode.IsDigit(ch) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Readline) move_to_end_of_word(amt uint, traverse_line_breaks bool) (num_of_words_moved uint) {
|
||||
if amt == 0 {
|
||||
return 0
|
||||
}
|
||||
line := self.lines[self.cursor.Y]
|
||||
in_word := false
|
||||
ci := wcswidth.NewCellIterator(line[self.cursor.X:])
|
||||
sz := 0
|
||||
|
||||
for ci.Forward() {
|
||||
current_is_word_char := has_word_chars(ci.Current())
|
||||
plen := sz
|
||||
sz += len(ci.Current())
|
||||
if current_is_word_char {
|
||||
in_word = true
|
||||
} else if in_word {
|
||||
self.cursor.X += plen
|
||||
amt--
|
||||
num_of_words_moved++
|
||||
if amt == 0 {
|
||||
return
|
||||
}
|
||||
in_word = false
|
||||
}
|
||||
}
|
||||
if self.move_to_end_of_line() {
|
||||
amt--
|
||||
num_of_words_moved++
|
||||
}
|
||||
if amt > 0 {
|
||||
if traverse_line_breaks && self.cursor.Y < len(self.lines)-1 {
|
||||
self.cursor.Y++
|
||||
self.cursor.X = 0
|
||||
num_of_words_moved += self.move_to_end_of_word(amt, traverse_line_breaks)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool) (num_of_words_moved uint) {
|
||||
if amt == 0 {
|
||||
return 0
|
||||
}
|
||||
line := self.lines[self.cursor.Y]
|
||||
in_word := false
|
||||
ci := wcswidth.NewCellIterator(line[:self.cursor.X]).GotoEnd()
|
||||
sz := 0
|
||||
|
||||
for ci.Backward() {
|
||||
current_is_word_char := has_word_chars(ci.Current())
|
||||
plen := sz
|
||||
sz += len(ci.Current())
|
||||
if current_is_word_char {
|
||||
in_word = true
|
||||
} else if in_word {
|
||||
self.cursor.X -= plen
|
||||
amt--
|
||||
num_of_words_moved++
|
||||
if amt == 0 {
|
||||
return
|
||||
}
|
||||
in_word = false
|
||||
}
|
||||
}
|
||||
if self.move_to_start_of_line() {
|
||||
amt--
|
||||
num_of_words_moved++
|
||||
}
|
||||
if amt > 0 {
|
||||
if traverse_line_breaks && self.cursor.Y > 0 {
|
||||
self.cursor.Y--
|
||||
self.cursor.X = len(self.lines[self.cursor.Y])
|
||||
num_of_words_moved += self.move_to_start_of_word(amt, traverse_line_breaks)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
@ -319,6 +403,14 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
if self.move_to_end_of_line() {
|
||||
return nil
|
||||
}
|
||||
case ActionMoveToEndOfWord:
|
||||
if self.move_to_end_of_word(repeat_count, true) > 0 {
|
||||
return nil
|
||||
}
|
||||
case ActionMoveToStartOfWord:
|
||||
if self.move_to_start_of_word(repeat_count, true) > 0 {
|
||||
return nil
|
||||
}
|
||||
case ActionMoveToStartOfDocument:
|
||||
if self.move_to_start() {
|
||||
return nil
|
||||
|
||||
@ -201,6 +201,56 @@ func TestCursorMovement(t *testing.T) {
|
||||
vert(-1, -1, "1234567xy\nabc", Position{X: 3, Y: 2})
|
||||
vert(-2, -2, "1234567xy", Position{X: 3, Y: 2})
|
||||
vert(-30, -3, "123", Position{X: 3, Y: 2})
|
||||
|
||||
rl.ResetText()
|
||||
rl.add_text("o\u0300ne two three\nfour five")
|
||||
|
||||
wf := func(amt uint, expected_amt uint, text_before_cursor string) {
|
||||
pos := rl.cursor
|
||||
actual_amt := rl.move_to_end_of_word(amt, true)
|
||||
if actual_amt != expected_amt {
|
||||
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
||||
}
|
||||
if diff := cmp.Diff(text_before_cursor, rl.TextBeforeCursor()); diff != "" {
|
||||
t.Fatalf("Did not get expected text before cursor for: %#v and cursor: %+v\n%s", rl.AllText(), pos, diff)
|
||||
}
|
||||
}
|
||||
rl.cursor = Position{}
|
||||
wf(1, 1, "òne")
|
||||
wf(1, 1, "òne two")
|
||||
wf(1, 1, "òne two three")
|
||||
wf(1, 1, "òne two three\nfour")
|
||||
wf(1, 1, "òne two three\nfour five")
|
||||
wf(1, 0, "òne two three\nfour five")
|
||||
rl.cursor = Position{}
|
||||
wf(5, 5, "òne two three\nfour five")
|
||||
rl.cursor = Position{X: 5}
|
||||
wf(1, 1, "òne two")
|
||||
|
||||
wb := func(amt uint, expected_amt uint, text_before_cursor string) {
|
||||
pos := rl.cursor
|
||||
actual_amt := rl.move_to_start_of_word(amt, true)
|
||||
if actual_amt != expected_amt {
|
||||
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
||||
}
|
||||
if diff := cmp.Diff(text_before_cursor, rl.TextBeforeCursor()); diff != "" {
|
||||
t.Fatalf("Did not get expected text before cursor for: %#v and cursor: %+v\n%s", rl.AllText(), pos, diff)
|
||||
}
|
||||
}
|
||||
rl.cursor = Position{X: 2}
|
||||
wb(1, 1, "")
|
||||
rl.cursor = Position{X: 8, Y: 1}
|
||||
wb(1, 1, "òne two three\nfour ")
|
||||
wb(1, 1, "òne two three\n")
|
||||
wb(1, 1, "òne two ")
|
||||
wb(1, 1, "òne ")
|
||||
wb(1, 1, "")
|
||||
wb(1, 0, "")
|
||||
rl.cursor = Position{X: 8, Y: 1}
|
||||
wb(5, 5, "")
|
||||
rl.cursor = Position{X: 5}
|
||||
wb(1, 1, "")
|
||||
|
||||
}
|
||||
|
||||
func TestEraseChars(t *testing.T) {
|
||||
|
||||
@ -43,6 +43,8 @@ const (
|
||||
ActionMoveToEndOfLine
|
||||
ActionMoveToStartOfDocument
|
||||
ActionMoveToEndOfDocument
|
||||
ActionMoveToEndOfWord
|
||||
ActionMoveToStartOfWord
|
||||
ActionCursorLeft
|
||||
ActionCursorRight
|
||||
ActionEndInput
|
||||
|
||||
@ -25,6 +25,11 @@ var default_shortcuts = map[string]Action{
|
||||
"ctrl+home": ActionMoveToStartOfDocument,
|
||||
"ctrl+end": ActionMoveToEndOfDocument,
|
||||
|
||||
"alt+f": ActionMoveToEndOfWord,
|
||||
"ctrl+right": ActionMoveToEndOfWord,
|
||||
"ctrl+left": ActionMoveToStartOfWord,
|
||||
"alt+b": ActionMoveToStartOfWord,
|
||||
|
||||
"left": ActionCursorLeft,
|
||||
"ctrl+b": ActionCursorLeft,
|
||||
"right": ActionCursorRight,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user