mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 14:18:26 +02:00
more work on history search
This commit is contained in:
@@ -17,9 +17,9 @@ var _ = fmt.Print
|
|||||||
func (self *Readline) text_upto_cursor_pos() string {
|
func (self *Readline) text_upto_cursor_pos() string {
|
||||||
buf := strings.Builder{}
|
buf := strings.Builder{}
|
||||||
buf.Grow(1024)
|
buf.Grow(1024)
|
||||||
for i, line := range self.lines {
|
for i, line := range self.input_state.lines {
|
||||||
if i == self.cursor.Y {
|
if i == self.input_state.cursor.Y {
|
||||||
buf.WriteString(line[:utils.Min(len(line), self.cursor.X)])
|
buf.WriteString(line[:utils.Min(len(line), self.input_state.cursor.X)])
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
buf.WriteString(line)
|
buf.WriteString(line)
|
||||||
@@ -32,11 +32,11 @@ func (self *Readline) text_upto_cursor_pos() string {
|
|||||||
func (self *Readline) text_after_cursor_pos() string {
|
func (self *Readline) text_after_cursor_pos() string {
|
||||||
buf := strings.Builder{}
|
buf := strings.Builder{}
|
||||||
buf.Grow(1024)
|
buf.Grow(1024)
|
||||||
for i, line := range self.lines {
|
for i, line := range self.input_state.lines {
|
||||||
if i == self.cursor.Y {
|
if i == self.input_state.cursor.Y {
|
||||||
buf.WriteString(line[utils.Min(len(line), self.cursor.X):])
|
buf.WriteString(line[utils.Min(len(line), self.input_state.cursor.X):])
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
} else if i > self.cursor.Y {
|
} else if i > self.input_state.cursor.Y {
|
||||||
buf.WriteString(line)
|
buf.WriteString(line)
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
@@ -49,35 +49,35 @@ func (self *Readline) text_after_cursor_pos() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) all_text() string {
|
func (self *Readline) all_text() string {
|
||||||
return strings.Join(self.lines, "\n")
|
return strings.Join(self.input_state.lines, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) add_text(text string) {
|
func (self *Readline) add_text(text string) {
|
||||||
new_lines := make([]string, 0, len(self.lines)+4)
|
new_lines := make([]string, 0, len(self.input_state.lines)+4)
|
||||||
new_lines = append(new_lines, self.lines[:self.cursor.Y]...)
|
new_lines = append(new_lines, self.input_state.lines[:self.input_state.cursor.Y]...)
|
||||||
var lines_after []string
|
var lines_after []string
|
||||||
if len(self.lines) > self.cursor.Y+1 {
|
if len(self.input_state.lines) > self.input_state.cursor.Y+1 {
|
||||||
lines_after = self.lines[self.cursor.Y+1:]
|
lines_after = self.input_state.lines[self.input_state.cursor.Y+1:]
|
||||||
}
|
}
|
||||||
has_trailing_newline := strings.HasSuffix(text, "\n")
|
has_trailing_newline := strings.HasSuffix(text, "\n")
|
||||||
|
|
||||||
add_line_break := func(line string) {
|
add_line_break := func(line string) {
|
||||||
new_lines = append(new_lines, line)
|
new_lines = append(new_lines, line)
|
||||||
self.cursor.X = len(line)
|
self.input_state.cursor.X = len(line)
|
||||||
self.cursor.Y += 1
|
self.input_state.cursor.Y += 1
|
||||||
}
|
}
|
||||||
cline := self.lines[self.cursor.Y]
|
cline := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
before_first_line := cline[:self.cursor.X]
|
before_first_line := cline[:self.input_state.cursor.X]
|
||||||
after_first_line := ""
|
after_first_line := ""
|
||||||
if self.cursor.X < len(cline) {
|
if self.input_state.cursor.X < len(cline) {
|
||||||
after_first_line = cline[self.cursor.X:]
|
after_first_line = cline[self.input_state.cursor.X:]
|
||||||
}
|
}
|
||||||
for i, line := range utils.Splitlines(text) {
|
for i, line := range utils.Splitlines(text) {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
add_line_break(line)
|
add_line_break(line)
|
||||||
} else {
|
} else {
|
||||||
line := before_first_line + line
|
line := before_first_line + line
|
||||||
self.cursor.X = len(line)
|
self.input_state.cursor.X = len(line)
|
||||||
new_lines = append(new_lines, line)
|
new_lines = append(new_lines, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,23 +93,23 @@ func (self *Readline) add_text(text string) {
|
|||||||
if len(lines_after) > 0 {
|
if len(lines_after) > 0 {
|
||||||
new_lines = append(new_lines, lines_after...)
|
new_lines = append(new_lines, lines_after...)
|
||||||
}
|
}
|
||||||
self.lines = new_lines
|
self.input_state.lines = new_lines
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) move_cursor_left(amt uint, traverse_line_breaks bool) (amt_moved uint) {
|
func (self *Readline) move_cursor_left(amt uint, traverse_line_breaks bool) (amt_moved uint) {
|
||||||
for amt_moved < amt {
|
for amt_moved < amt {
|
||||||
if self.cursor.X == 0 {
|
if self.input_state.cursor.X == 0 {
|
||||||
if !traverse_line_breaks || self.cursor.Y == 0 {
|
if !traverse_line_breaks || self.input_state.cursor.Y == 0 {
|
||||||
return amt_moved
|
return amt_moved
|
||||||
}
|
}
|
||||||
self.cursor.Y -= 1
|
self.input_state.cursor.Y -= 1
|
||||||
self.cursor.X = len(self.lines[self.cursor.Y])
|
self.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])
|
||||||
amt_moved++
|
amt_moved++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
for ci := wcswidth.NewCellIterator(line[:self.cursor.X]).GotoEnd(); amt_moved < amt && ci.Backward(); amt_moved++ {
|
for ci := wcswidth.NewCellIterator(line[:self.input_state.cursor.X]).GotoEnd(); amt_moved < amt && ci.Backward(); amt_moved++ {
|
||||||
self.cursor.X -= len(ci.Current())
|
self.input_state.cursor.X -= len(ci.Current())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return amt_moved
|
return amt_moved
|
||||||
@@ -117,19 +117,19 @@ func (self *Readline) move_cursor_left(amt uint, traverse_line_breaks bool) (amt
|
|||||||
|
|
||||||
func (self *Readline) move_cursor_right(amt uint, traverse_line_breaks bool) (amt_moved uint) {
|
func (self *Readline) move_cursor_right(amt uint, traverse_line_breaks bool) (amt_moved uint) {
|
||||||
for amt_moved < amt {
|
for amt_moved < amt {
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if self.cursor.X >= len(line) {
|
if self.input_state.cursor.X >= len(line) {
|
||||||
if !traverse_line_breaks || self.cursor.Y == len(self.lines)-1 {
|
if !traverse_line_breaks || self.input_state.cursor.Y == len(self.input_state.lines)-1 {
|
||||||
return amt_moved
|
return amt_moved
|
||||||
}
|
}
|
||||||
self.cursor.Y += 1
|
self.input_state.cursor.Y += 1
|
||||||
self.cursor.X = 0
|
self.input_state.cursor.X = 0
|
||||||
amt_moved++
|
amt_moved++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for ci := wcswidth.NewCellIterator(line[self.cursor.X:]); amt_moved < amt && ci.Forward(); amt_moved++ {
|
for ci := wcswidth.NewCellIterator(line[self.input_state.cursor.X:]); amt_moved < amt && ci.Forward(); amt_moved++ {
|
||||||
self.cursor.X += len(ci.Current())
|
self.input_state.cursor.X += len(ci.Current())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return amt_moved
|
return amt_moved
|
||||||
@@ -138,9 +138,9 @@ func (self *Readline) move_cursor_right(amt uint, traverse_line_breaks bool) (am
|
|||||||
func (self *Readline) move_cursor_to_target_line(source_line, target_line *ScreenLine) {
|
func (self *Readline) move_cursor_to_target_line(source_line, target_line *ScreenLine) {
|
||||||
if source_line != target_line {
|
if source_line != target_line {
|
||||||
visual_distance_into_text := source_line.CursorCell - source_line.Prompt.Length
|
visual_distance_into_text := source_line.CursorCell - source_line.Prompt.Length
|
||||||
self.cursor.Y = target_line.ParentLineNumber
|
self.input_state.cursor.Y = target_line.ParentLineNumber
|
||||||
tp := wcswidth.TruncateToVisualLength(target_line.Text, visual_distance_into_text)
|
tp := wcswidth.TruncateToVisualLength(target_line.Text, visual_distance_into_text)
|
||||||
self.cursor.X = target_line.OffsetInParentLine + len(tp)
|
self.input_state.cursor.X = target_line.OffsetInParentLine + len(tp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,37 +173,37 @@ func (self *Readline) move_cursor_down(amt uint) uint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) move_to_start_of_line() bool {
|
func (self *Readline) move_to_start_of_line() bool {
|
||||||
if self.cursor.X > 0 {
|
if self.input_state.cursor.X > 0 {
|
||||||
self.cursor.X = 0
|
self.input_state.cursor.X = 0
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) move_to_end_of_line() bool {
|
func (self *Readline) move_to_end_of_line() bool {
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if self.cursor.X >= len(line) {
|
if self.input_state.cursor.X >= len(line) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.cursor.X = len(line)
|
self.input_state.cursor.X = len(line)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) move_to_start() bool {
|
func (self *Readline) move_to_start() bool {
|
||||||
if self.cursor.Y == 0 && self.cursor.X == 0 {
|
if self.input_state.cursor.Y == 0 && self.input_state.cursor.X == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.cursor.Y = 0
|
self.input_state.cursor.Y = 0
|
||||||
self.move_to_start_of_line()
|
self.move_to_start_of_line()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) move_to_end() bool {
|
func (self *Readline) move_to_end() bool {
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if self.cursor.Y == len(self.lines)-1 && self.cursor.X >= len(line) {
|
if self.input_state.cursor.Y == len(self.input_state.lines)-1 && self.input_state.cursor.X >= len(line) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.cursor.Y = len(self.lines) - 1
|
self.input_state.cursor.Y = len(self.input_state.lines) - 1
|
||||||
self.move_to_end_of_line()
|
self.move_to_end_of_line()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -214,68 +214,68 @@ func (self *Readline) erase_between(start, end Position) string {
|
|||||||
}
|
}
|
||||||
buf := strings.Builder{}
|
buf := strings.Builder{}
|
||||||
if start.Y == end.Y {
|
if start.Y == end.Y {
|
||||||
line := self.lines[start.Y]
|
line := self.input_state.lines[start.Y]
|
||||||
buf.WriteString(line[start.X:end.X])
|
buf.WriteString(line[start.X:end.X])
|
||||||
self.lines[start.Y] = line[:start.X] + line[end.X:]
|
self.input_state.lines[start.Y] = line[:start.X] + line[end.X:]
|
||||||
if self.cursor.Y == start.Y && self.cursor.X >= start.X {
|
if self.input_state.cursor.Y == start.Y && self.input_state.cursor.X >= start.X {
|
||||||
if self.cursor.X < end.X {
|
if self.input_state.cursor.X < end.X {
|
||||||
self.cursor.X = start.X
|
self.input_state.cursor.X = start.X
|
||||||
} else {
|
} else {
|
||||||
self.cursor.X -= end.X - start.X
|
self.input_state.cursor.X -= end.X - start.X
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
lines := make([]string, 0, len(self.lines))
|
lines := make([]string, 0, len(self.input_state.lines))
|
||||||
for i, line := range self.lines {
|
for i, line := range self.input_state.lines {
|
||||||
if i < start.Y || i > end.Y {
|
if i < start.Y || i > end.Y {
|
||||||
lines = append(lines, line)
|
lines = append(lines, line)
|
||||||
} else if i == start.Y {
|
} else if i == start.Y {
|
||||||
lines = append(lines, line[:start.X])
|
lines = append(lines, line[:start.X])
|
||||||
buf.WriteString(line[start.X:])
|
buf.WriteString(line[start.X:])
|
||||||
if self.cursor.Y == i && self.cursor.X > start.X {
|
if self.input_state.cursor.Y == i && self.input_state.cursor.X > start.X {
|
||||||
self.cursor.X = start.X
|
self.input_state.cursor.X = start.X
|
||||||
}
|
}
|
||||||
} else if i == end.Y {
|
} else if i == end.Y {
|
||||||
lines[len(lines)-1] += line[end.X:]
|
lines[len(lines)-1] += line[end.X:]
|
||||||
buf.WriteString(line[:end.X])
|
buf.WriteString(line[:end.X])
|
||||||
if i == self.cursor.Y {
|
if i == self.input_state.cursor.Y {
|
||||||
self.cursor.Y = start.Y
|
self.input_state.cursor.Y = start.Y
|
||||||
if self.cursor.X < end.X {
|
if self.input_state.cursor.X < end.X {
|
||||||
self.cursor.X = start.X
|
self.input_state.cursor.X = start.X
|
||||||
} else {
|
} else {
|
||||||
self.cursor.X -= end.X - start.X
|
self.input_state.cursor.X -= end.X - start.X
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if i == self.cursor.Y {
|
if i == self.input_state.cursor.Y {
|
||||||
self.cursor = start
|
self.input_state.cursor = start
|
||||||
}
|
}
|
||||||
buf.WriteString(line)
|
buf.WriteString(line)
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.lines = lines
|
self.input_state.lines = lines
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) erase_chars_before_cursor(amt uint, traverse_line_breaks bool) uint {
|
func (self *Readline) erase_chars_before_cursor(amt uint, traverse_line_breaks bool) uint {
|
||||||
pos := self.cursor
|
pos := self.input_state.cursor
|
||||||
num := self.move_cursor_left(amt, traverse_line_breaks)
|
num := self.move_cursor_left(amt, traverse_line_breaks)
|
||||||
if num == 0 {
|
if num == 0 {
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
self.erase_between(self.cursor, pos)
|
self.erase_between(self.input_state.cursor, pos)
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) erase_chars_after_cursor(amt uint, traverse_line_breaks bool) uint {
|
func (self *Readline) erase_chars_after_cursor(amt uint, traverse_line_breaks bool) uint {
|
||||||
pos := self.cursor
|
pos := self.input_state.cursor
|
||||||
num := self.move_cursor_right(amt, traverse_line_breaks)
|
num := self.move_cursor_right(amt, traverse_line_breaks)
|
||||||
if num == 0 {
|
if num == 0 {
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
self.erase_between(pos, self.cursor)
|
self.erase_between(pos, self.input_state.cursor)
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,9 +292,9 @@ func (self *Readline) move_to_end_of_word(amt uint, traverse_line_breaks bool, i
|
|||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
in_word := false
|
in_word := false
|
||||||
ci := wcswidth.NewCellIterator(line[self.cursor.X:])
|
ci := wcswidth.NewCellIterator(line[self.input_state.cursor.X:])
|
||||||
sz := 0
|
sz := 0
|
||||||
|
|
||||||
for ci.Forward() {
|
for ci.Forward() {
|
||||||
@@ -304,7 +304,7 @@ func (self *Readline) move_to_end_of_word(amt uint, traverse_line_breaks bool, i
|
|||||||
if current_is_word_char {
|
if current_is_word_char {
|
||||||
in_word = true
|
in_word = true
|
||||||
} else if in_word {
|
} else if in_word {
|
||||||
self.cursor.X += plen
|
self.input_state.cursor.X += plen
|
||||||
amt--
|
amt--
|
||||||
num_of_words_moved++
|
num_of_words_moved++
|
||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
@@ -318,9 +318,9 @@ func (self *Readline) move_to_end_of_word(amt uint, traverse_line_breaks bool, i
|
|||||||
num_of_words_moved++
|
num_of_words_moved++
|
||||||
}
|
}
|
||||||
if amt > 0 {
|
if amt > 0 {
|
||||||
if traverse_line_breaks && self.cursor.Y < len(self.lines)-1 {
|
if traverse_line_breaks && self.input_state.cursor.Y < len(self.input_state.lines)-1 {
|
||||||
self.cursor.Y++
|
self.input_state.cursor.Y++
|
||||||
self.cursor.X = 0
|
self.input_state.cursor.X = 0
|
||||||
num_of_words_moved += self.move_to_end_of_word(amt, traverse_line_breaks, is_part_of_word)
|
num_of_words_moved += self.move_to_end_of_word(amt, traverse_line_breaks, is_part_of_word)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,9 +331,9 @@ func (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool,
|
|||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
in_word := false
|
in_word := false
|
||||||
ci := wcswidth.NewCellIterator(line[:self.cursor.X]).GotoEnd()
|
ci := wcswidth.NewCellIterator(line[:self.input_state.cursor.X]).GotoEnd()
|
||||||
sz := 0
|
sz := 0
|
||||||
|
|
||||||
for ci.Backward() {
|
for ci.Backward() {
|
||||||
@@ -343,7 +343,7 @@ func (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool,
|
|||||||
if current_is_word_char {
|
if current_is_word_char {
|
||||||
in_word = true
|
in_word = true
|
||||||
} else if in_word {
|
} else if in_word {
|
||||||
self.cursor.X -= plen
|
self.input_state.cursor.X -= plen
|
||||||
amt--
|
amt--
|
||||||
num_of_words_moved++
|
num_of_words_moved++
|
||||||
if amt == 0 {
|
if amt == 0 {
|
||||||
@@ -357,9 +357,9 @@ func (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool,
|
|||||||
num_of_words_moved++
|
num_of_words_moved++
|
||||||
}
|
}
|
||||||
if amt > 0 {
|
if amt > 0 {
|
||||||
if traverse_line_breaks && self.cursor.Y > 0 {
|
if traverse_line_breaks && self.input_state.cursor.Y > 0 {
|
||||||
self.cursor.Y--
|
self.input_state.cursor.Y--
|
||||||
self.cursor.X = len(self.lines[self.cursor.Y])
|
self.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])
|
||||||
num_of_words_moved += self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)
|
num_of_words_moved += self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,40 +375,40 @@ func (self *Readline) kill_text(text string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) kill_to_end_of_line() bool {
|
func (self *Readline) kill_to_end_of_line() bool {
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if self.cursor.X >= len(line) {
|
if self.input_state.cursor.X >= len(line) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.lines[self.cursor.Y] = line[:self.cursor.X]
|
self.input_state.lines[self.input_state.cursor.Y] = line[:self.input_state.cursor.X]
|
||||||
self.kill_text(line[self.cursor.X:])
|
self.kill_text(line[self.input_state.cursor.X:])
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) kill_to_start_of_line() bool {
|
func (self *Readline) kill_to_start_of_line() bool {
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if self.cursor.X <= 0 {
|
if self.input_state.cursor.X <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.lines[self.cursor.Y] = line[self.cursor.X:]
|
self.input_state.lines[self.input_state.cursor.Y] = line[self.input_state.cursor.X:]
|
||||||
self.kill_text(line[:self.cursor.X])
|
self.kill_text(line[:self.input_state.cursor.X])
|
||||||
self.cursor.X = 0
|
self.input_state.cursor.X = 0
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) kill_next_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
func (self *Readline) kill_next_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
||||||
before := self.cursor
|
before := self.input_state.cursor
|
||||||
num_killed = self.move_to_end_of_word(amt, traverse_line_breaks, has_word_chars)
|
num_killed = self.move_to_end_of_word(amt, traverse_line_breaks, has_word_chars)
|
||||||
if num_killed > 0 {
|
if num_killed > 0 {
|
||||||
self.kill_text(self.erase_between(before, self.cursor))
|
self.kill_text(self.erase_between(before, self.input_state.cursor))
|
||||||
}
|
}
|
||||||
return num_killed
|
return num_killed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) kill_previous_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
func (self *Readline) kill_previous_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
||||||
before := self.cursor
|
before := self.input_state.cursor
|
||||||
num_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)
|
num_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_word_chars)
|
||||||
if num_killed > 0 {
|
if num_killed > 0 {
|
||||||
self.kill_text(self.erase_between(self.cursor, before))
|
self.kill_text(self.erase_between(self.input_state.cursor, before))
|
||||||
}
|
}
|
||||||
return num_killed
|
return num_killed
|
||||||
}
|
}
|
||||||
@@ -423,17 +423,17 @@ func has_no_space_chars(text string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) kill_previous_space_delimited_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
func (self *Readline) kill_previous_space_delimited_word(amt uint, traverse_line_breaks bool) (num_killed uint) {
|
||||||
before := self.cursor
|
before := self.input_state.cursor
|
||||||
num_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_no_space_chars)
|
num_killed = self.move_to_start_of_word(amt, traverse_line_breaks, has_no_space_chars)
|
||||||
if num_killed > 0 {
|
if num_killed > 0 {
|
||||||
self.kill_text(self.erase_between(self.cursor, before))
|
self.kill_text(self.erase_between(self.input_state.cursor, before))
|
||||||
}
|
}
|
||||||
return num_killed
|
return num_killed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) ensure_position_in_bounds(pos *Position) *Position {
|
func (self *Readline) ensure_position_in_bounds(pos *Position) *Position {
|
||||||
pos.Y = utils.Max(0, utils.Min(pos.Y, len(self.lines)-1))
|
pos.Y = utils.Max(0, utils.Min(pos.Y, len(self.input_state.lines)-1))
|
||||||
line := self.lines[pos.Y]
|
line := self.input_state.lines[pos.Y]
|
||||||
pos.X = utils.Max(0, utils.Min(pos.X, len(line)))
|
pos.X = utils.Max(0, utils.Min(pos.X, len(line)))
|
||||||
return pos
|
return pos
|
||||||
}
|
}
|
||||||
@@ -451,24 +451,24 @@ func (self *Readline) yank(repeat_count uint, pop bool) bool {
|
|||||||
if text == "" {
|
if text == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
before := self.cursor
|
before := self.input_state.cursor
|
||||||
if pop {
|
if pop {
|
||||||
self.ensure_position_in_bounds(&self.last_yank_extent.start)
|
self.ensure_position_in_bounds(&self.last_yank_extent.start)
|
||||||
self.ensure_position_in_bounds(&self.last_yank_extent.end)
|
self.ensure_position_in_bounds(&self.last_yank_extent.end)
|
||||||
self.erase_between(self.last_yank_extent.start, self.last_yank_extent.end)
|
self.erase_between(self.last_yank_extent.start, self.last_yank_extent.end)
|
||||||
self.cursor = self.last_yank_extent.start
|
self.input_state.cursor = self.last_yank_extent.start
|
||||||
before = self.cursor
|
before = self.input_state.cursor
|
||||||
}
|
}
|
||||||
self.add_text(text)
|
self.add_text(text)
|
||||||
self.last_yank_extent.start = before
|
self.last_yank_extent.start = before
|
||||||
self.last_yank_extent.end = self.cursor
|
self.last_yank_extent.end = self.input_state.cursor
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) apply_history_text(text string) {
|
func (self *Readline) apply_history_text(text string) {
|
||||||
self.lines = utils.Splitlines(text)
|
self.input_state.lines = utils.Splitlines(text)
|
||||||
if len(self.lines) == 0 {
|
if len(self.input_state.lines) == 0 {
|
||||||
self.lines = []string{""}
|
self.input_state.lines = []string{""}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,9 +528,15 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
|||||||
defer func() { self.last_action = ac }()
|
defer func() { self.last_action = ac }()
|
||||||
switch ac {
|
switch ac {
|
||||||
case ActionBackspace:
|
case ActionBackspace:
|
||||||
|
if self.history_search != nil {
|
||||||
|
if self.remove_text_from_history_search(repeat_count) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if self.erase_chars_before_cursor(repeat_count, true) > 0 {
|
if self.erase_chars_before_cursor(repeat_count, true) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case ActionDelete:
|
case ActionDelete:
|
||||||
if self.erase_chars_after_cursor(repeat_count, true) > 0 {
|
if self.erase_chars_after_cursor(repeat_count, true) > 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -568,7 +574,7 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case ActionEndInput:
|
case ActionEndInput:
|
||||||
line := self.lines[self.cursor.Y]
|
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||||
if line == "" {
|
if line == "" {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
@@ -672,6 +678,16 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
|||||||
self.add_text(text)
|
self.add_text(text)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case ActionTerminateHistorySearchAndRestore:
|
||||||
|
if self.history_search != nil {
|
||||||
|
self.end_history_search(false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case ActionTerminateHistorySearchAndApply:
|
||||||
|
if self.history_search != nil {
|
||||||
|
self.end_history_search(true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ErrCouldNotPerformAction
|
return ErrCouldNotPerformAction
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,15 +48,15 @@ func TestAddText(t *testing.T) {
|
|||||||
dt("test", nil, "test", "", "test")
|
dt("test", nil, "test", "", "test")
|
||||||
dt("1234\n", nil, "1234\n", "", "1234\n")
|
dt("1234\n", nil, "1234\n", "", "1234\n")
|
||||||
dt("abcd", func(rl *Readline) {
|
dt("abcd", func(rl *Readline) {
|
||||||
rl.cursor.X = 2
|
rl.input_state.cursor.X = 2
|
||||||
rl.add_text("12")
|
rl.add_text("12")
|
||||||
}, "ab12", "cd", "ab12cd")
|
}, "ab12", "cd", "ab12cd")
|
||||||
dt("abcd", func(rl *Readline) {
|
dt("abcd", func(rl *Readline) {
|
||||||
rl.cursor.X = 2
|
rl.input_state.cursor.X = 2
|
||||||
rl.add_text("12\n34")
|
rl.add_text("12\n34")
|
||||||
}, "ab12\n34", "cd", "ab12\n34cd")
|
}, "ab12\n34", "cd", "ab12\n34cd")
|
||||||
dt("abcd\nxyz", func(rl *Readline) {
|
dt("abcd\nxyz", func(rl *Readline) {
|
||||||
rl.cursor.X = 2
|
rl.input_state.cursor.X = 2
|
||||||
rl.add_text("12\n34")
|
rl.add_text("12\n34")
|
||||||
}, "abcd\nxy12\n34", "z", "abcd\nxy12\n34z")
|
}, "abcd\nxy12\n34", "z", "abcd\nxy12\n34z")
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ func TestGetScreenLines(t *testing.T) {
|
|||||||
actual[i] = *x
|
actual[i] = *x
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||||
t.Fatalf("Did not get expected screen lines for: %#v and cursor: %+v\n%s", rl.AllText(), rl.cursor, diff)
|
t.Fatalf("Did not get expected screen lines for: %#v and cursor: %+v\n%s", rl.AllText(), rl.input_state.cursor, diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tsl(ScreenLine{Prompt: p(true), CursorCell: 3})
|
tsl(ScreenLine{Prompt: p(true), CursorCell: 3})
|
||||||
@@ -105,19 +105,19 @@ func TestGetScreenLines(t *testing.T) {
|
|||||||
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||||
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 3, CursorTextPos: 3, Text: "XYZ"},
|
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 3, CursorTextPos: 3, Text: "XYZ"},
|
||||||
)
|
)
|
||||||
rl.cursor = Position{X: 2}
|
rl.input_state.cursor = Position{X: 2}
|
||||||
tsl(
|
tsl(
|
||||||
ScreenLine{Prompt: p(true), CursorCell: 5, Text: "123", CursorTextPos: 2, TextLengthInCells: 3},
|
ScreenLine{Prompt: p(true), CursorCell: 5, Text: "123", CursorTextPos: 2, TextLengthInCells: 3},
|
||||||
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||||
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
||||||
)
|
)
|
||||||
rl.cursor = Position{X: 2, Y: 1}
|
rl.input_state.cursor = Position{X: 2, Y: 1}
|
||||||
tsl(
|
tsl(
|
||||||
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
||||||
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: 4, CursorTextPos: 2},
|
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: 4, CursorTextPos: 2},
|
||||||
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
ScreenLine{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
||||||
)
|
)
|
||||||
rl.cursor = Position{X: 8, Y: 1}
|
rl.input_state.cursor = Position{X: 8, Y: 1}
|
||||||
tsl(
|
tsl(
|
||||||
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
||||||
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||||
@@ -125,7 +125,7 @@ func TestGetScreenLines(t *testing.T) {
|
|||||||
)
|
)
|
||||||
rl.ResetText()
|
rl.ResetText()
|
||||||
rl.add_text("1234567\nabc")
|
rl.add_text("1234567\nabc")
|
||||||
rl.cursor = Position{X: 7}
|
rl.input_state.cursor = Position{X: 7}
|
||||||
tsl(
|
tsl(
|
||||||
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||||
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "abc", CursorCell: 2, TextLengthInCells: 3, CursorTextPos: 0},
|
ScreenLine{ParentLineNumber: 1, Prompt: p(false), Text: "abc", CursorCell: 2, TextLengthInCells: 3, CursorTextPos: 0},
|
||||||
@@ -164,8 +164,8 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
}, "one", "à")
|
}, "one", "à")
|
||||||
|
|
||||||
right := func(rl *Readline, amt uint, moved_amt uint, traverse_line_breaks bool) {
|
right := func(rl *Readline, amt uint, moved_amt uint, traverse_line_breaks bool) {
|
||||||
rl.cursor.Y = 0
|
rl.input_state.cursor.Y = 0
|
||||||
rl.cursor.X = 0
|
rl.input_state.cursor.X = 0
|
||||||
actual := rl.move_cursor_right(amt, traverse_line_breaks)
|
actual := rl.move_cursor_right(amt, traverse_line_breaks)
|
||||||
if actual != moved_amt {
|
if actual != moved_amt {
|
||||||
t.Fatalf("Failed to move cursor by %d\nactual != expected: %d != %d", amt, actual, moved_amt)
|
t.Fatalf("Failed to move cursor by %d\nactual != expected: %d != %d", amt, actual, moved_amt)
|
||||||
@@ -196,7 +196,7 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
if len(initials) > 0 {
|
if len(initials) > 0 {
|
||||||
initial = initials[0]
|
initial = initials[0]
|
||||||
}
|
}
|
||||||
rl.cursor = initial
|
rl.input_state.cursor = initial
|
||||||
actual := rl.move_cursor_vertically(amt)
|
actual := rl.move_cursor_vertically(amt)
|
||||||
if actual != moved_amt {
|
if actual != moved_amt {
|
||||||
t.Fatalf("Failed to move cursor by %#v for: %#v \nactual != expected: %#v != %#v", amt, rl.AllText(), actual, moved_amt)
|
t.Fatalf("Failed to move cursor by %#v for: %#v \nactual != expected: %#v != %#v", amt, rl.AllText(), actual, moved_amt)
|
||||||
@@ -216,7 +216,7 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
rl.add_text("o\u0300ne two three\nfour five")
|
rl.add_text("o\u0300ne two three\nfour five")
|
||||||
|
|
||||||
wf := func(amt uint, expected_amt uint, text_before_cursor string) {
|
wf := func(amt uint, expected_amt uint, text_before_cursor string) {
|
||||||
pos := rl.cursor
|
pos := rl.input_state.cursor
|
||||||
actual_amt := rl.move_to_end_of_word(amt, true, has_word_chars)
|
actual_amt := rl.move_to_end_of_word(amt, true, has_word_chars)
|
||||||
if actual_amt != expected_amt {
|
if actual_amt != expected_amt {
|
||||||
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
||||||
@@ -225,20 +225,20 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
t.Fatalf("Did not get expected text before cursor for: %#v and cursor: %+v\n%s", rl.AllText(), pos, diff)
|
t.Fatalf("Did not get expected text before cursor for: %#v and cursor: %+v\n%s", rl.AllText(), pos, diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rl.cursor = Position{}
|
rl.input_state.cursor = Position{}
|
||||||
wf(1, 1, "òne")
|
wf(1, 1, "òne")
|
||||||
wf(1, 1, "òne two")
|
wf(1, 1, "òne two")
|
||||||
wf(1, 1, "òne two three")
|
wf(1, 1, "òne two three")
|
||||||
wf(1, 1, "òne two three\nfour")
|
wf(1, 1, "òne two three\nfour")
|
||||||
wf(1, 1, "òne two three\nfour five")
|
wf(1, 1, "òne two three\nfour five")
|
||||||
wf(1, 0, "òne two three\nfour five")
|
wf(1, 0, "òne two three\nfour five")
|
||||||
rl.cursor = Position{}
|
rl.input_state.cursor = Position{}
|
||||||
wf(5, 5, "òne two three\nfour five")
|
wf(5, 5, "òne two three\nfour five")
|
||||||
rl.cursor = Position{X: 5}
|
rl.input_state.cursor = Position{X: 5}
|
||||||
wf(1, 1, "òne two")
|
wf(1, 1, "òne two")
|
||||||
|
|
||||||
wb := func(amt uint, expected_amt uint, text_before_cursor string) {
|
wb := func(amt uint, expected_amt uint, text_before_cursor string) {
|
||||||
pos := rl.cursor
|
pos := rl.input_state.cursor
|
||||||
actual_amt := rl.move_to_start_of_word(amt, true, has_word_chars)
|
actual_amt := rl.move_to_start_of_word(amt, true, has_word_chars)
|
||||||
if actual_amt != expected_amt {
|
if actual_amt != expected_amt {
|
||||||
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
t.Fatalf("Failed to move to word end, expected amt (%d) != actual amt (%d)", expected_amt, actual_amt)
|
||||||
@@ -247,18 +247,18 @@ func TestCursorMovement(t *testing.T) {
|
|||||||
t.Fatalf("Did not get expected text before cursor for: %#v and cursor: %+v\n%s", rl.AllText(), pos, 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}
|
rl.input_state.cursor = Position{X: 2}
|
||||||
wb(1, 1, "")
|
wb(1, 1, "")
|
||||||
rl.cursor = Position{X: 8, Y: 1}
|
rl.input_state.cursor = Position{X: 8, Y: 1}
|
||||||
wb(1, 1, "òne two three\nfour ")
|
wb(1, 1, "òne two three\nfour ")
|
||||||
wb(1, 1, "òne two three\n")
|
wb(1, 1, "òne two three\n")
|
||||||
wb(1, 1, "òne two ")
|
wb(1, 1, "òne two ")
|
||||||
wb(1, 1, "òne ")
|
wb(1, 1, "òne ")
|
||||||
wb(1, 1, "")
|
wb(1, 1, "")
|
||||||
wb(1, 0, "")
|
wb(1, 0, "")
|
||||||
rl.cursor = Position{X: 8, Y: 1}
|
rl.input_state.cursor = Position{X: 8, Y: 1}
|
||||||
wb(5, 5, "")
|
wb(5, 5, "")
|
||||||
rl.cursor = Position{X: 5}
|
rl.input_state.cursor = Position{X: 5}
|
||||||
wb(1, 1, "")
|
wb(1, 1, "")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -330,11 +330,11 @@ func TestEraseChars(t *testing.T) {
|
|||||||
backspace(rl, 2, 2, false)
|
backspace(rl, 2, 2, false)
|
||||||
}, "one\nt", "")
|
}, "one\nt", "")
|
||||||
dt("one\ntwo", func(rl *Readline) {
|
dt("one\ntwo", func(rl *Readline) {
|
||||||
rl.cursor.X = 1
|
rl.input_state.cursor.X = 1
|
||||||
backspace(rl, 2, 1, false)
|
backspace(rl, 2, 1, false)
|
||||||
}, "one\n", "wo")
|
}, "one\n", "wo")
|
||||||
dt("one\ntwo", func(rl *Readline) {
|
dt("one\ntwo", func(rl *Readline) {
|
||||||
rl.cursor.X = 1
|
rl.input_state.cursor.X = 1
|
||||||
backspace(rl, 2, 2, true)
|
backspace(rl, 2, 2, true)
|
||||||
}, "one", "wo")
|
}, "one", "wo")
|
||||||
dt("a😀", func(rl *Readline) {
|
dt("a😀", func(rl *Readline) {
|
||||||
@@ -345,8 +345,8 @@ func TestEraseChars(t *testing.T) {
|
|||||||
}, "b", "")
|
}, "b", "")
|
||||||
|
|
||||||
del := func(rl *Readline, amt uint, erased_amt uint, traverse_line_breaks bool) {
|
del := func(rl *Readline, amt uint, erased_amt uint, traverse_line_breaks bool) {
|
||||||
rl.cursor.Y = 0
|
rl.input_state.cursor.Y = 0
|
||||||
rl.cursor.X = 0
|
rl.input_state.cursor.X = 0
|
||||||
actual := rl.erase_chars_after_cursor(amt, traverse_line_breaks)
|
actual := rl.erase_chars_after_cursor(amt, traverse_line_breaks)
|
||||||
if actual != erased_amt {
|
if actual != erased_amt {
|
||||||
t.Fatalf("Failed to move cursor by %#v\nactual != expected: %d != %d", amt, actual, erased_amt)
|
t.Fatalf("Failed to move cursor by %#v\nactual != expected: %d != %d", amt, actual, erased_amt)
|
||||||
@@ -366,19 +366,19 @@ func TestEraseChars(t *testing.T) {
|
|||||||
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
||||||
}, "oree", "")
|
}, "oree", "")
|
||||||
dt("one\ntwo\nthree", func(rl *Readline) {
|
dt("one\ntwo\nthree", func(rl *Readline) {
|
||||||
rl.cursor.X = 1
|
rl.input_state.cursor.X = 1
|
||||||
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
||||||
}, "o", "ree")
|
}, "o", "ree")
|
||||||
dt("one\ntwo\nthree", func(rl *Readline) {
|
dt("one\ntwo\nthree", func(rl *Readline) {
|
||||||
rl.cursor = Position{X: 1, Y: 1}
|
rl.input_state.cursor = Position{X: 1, Y: 1}
|
||||||
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
||||||
}, "o", "ree")
|
}, "o", "ree")
|
||||||
dt("one\ntwo\nthree", func(rl *Readline) {
|
dt("one\ntwo\nthree", func(rl *Readline) {
|
||||||
rl.cursor = Position{X: 1, Y: 0}
|
rl.input_state.cursor = Position{X: 1, Y: 0}
|
||||||
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
||||||
}, "o", "ree")
|
}, "o", "ree")
|
||||||
dt("one\ntwo\nthree", func(rl *Readline) {
|
dt("one\ntwo\nthree", func(rl *Readline) {
|
||||||
rl.cursor = Position{X: 0, Y: 0}
|
rl.input_state.cursor = Position{X: 0, Y: 0}
|
||||||
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
rl.erase_between(Position{X: 1}, Position{X: 2, Y: 2})
|
||||||
}, "", "oree")
|
}, "", "oree")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ var _ = fmt.Print
|
|||||||
const ST = "\x1b\\"
|
const ST = "\x1b\\"
|
||||||
const PROMPT_MARK = "\x1b]133;"
|
const PROMPT_MARK = "\x1b]133;"
|
||||||
|
|
||||||
|
type SyntaxHighlightFunction func(text string, x, y int) string
|
||||||
|
|
||||||
type RlInit struct {
|
type RlInit struct {
|
||||||
Prompt string
|
Prompt string
|
||||||
HistoryPath string
|
HistoryPath string
|
||||||
@@ -24,6 +26,7 @@ type RlInit struct {
|
|||||||
ContinuationPrompt string
|
ContinuationPrompt string
|
||||||
EmptyContinuationPrompt bool
|
EmptyContinuationPrompt bool
|
||||||
DontMarkPrompts bool
|
DontMarkPrompts bool
|
||||||
|
SyntaxHighlighter SyntaxHighlightFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
type Position struct {
|
type Position struct {
|
||||||
@@ -35,6 +38,7 @@ func (self Position) Less(other Position) bool {
|
|||||||
return self.Y < other.Y || (self.Y == other.Y && self.X < other.X)
|
return self.Y < other.Y || (self.Y == other.Y && self.X < other.X)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actions {{{
|
||||||
type Action uint
|
type Action uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -61,6 +65,8 @@ const (
|
|||||||
ActionHistoryLast
|
ActionHistoryLast
|
||||||
ActionHistoryIncrementalSearchBackwards
|
ActionHistoryIncrementalSearchBackwards
|
||||||
ActionHistoryIncrementalSearchForwards
|
ActionHistoryIncrementalSearchForwards
|
||||||
|
ActionTerminateHistorySearchAndApply
|
||||||
|
ActionTerminateHistorySearchAndRestore
|
||||||
ActionClearScreen
|
ActionClearScreen
|
||||||
ActionAddText
|
ActionAddText
|
||||||
ActionAbortCurrentLine
|
ActionAbortCurrentLine
|
||||||
@@ -89,6 +95,8 @@ const (
|
|||||||
ActionNumericArgumentDigitMinus
|
ActionNumericArgumentDigitMinus
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
type kill_ring struct {
|
type kill_ring struct {
|
||||||
items *list.List
|
items *list.List
|
||||||
}
|
}
|
||||||
@@ -133,6 +141,28 @@ type Prompt struct {
|
|||||||
Length int
|
Length int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InputState struct {
|
||||||
|
// Input lines
|
||||||
|
lines []string
|
||||||
|
// The cursor position in the text
|
||||||
|
cursor Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self InputState) copy() InputState {
|
||||||
|
ans := self
|
||||||
|
l := make([]string, len(self.lines))
|
||||||
|
copy(l, self.lines)
|
||||||
|
ans.lines = l
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
type syntax_highlighted struct {
|
||||||
|
lines []string
|
||||||
|
src_for_last_highlight string
|
||||||
|
highlighter SyntaxHighlightFunction
|
||||||
|
last_highlighter_name string
|
||||||
|
}
|
||||||
|
|
||||||
type Readline struct {
|
type Readline struct {
|
||||||
prompt, continuation_prompt Prompt
|
prompt, continuation_prompt Prompt
|
||||||
|
|
||||||
@@ -141,13 +171,10 @@ type Readline struct {
|
|||||||
history *History
|
history *History
|
||||||
kill_ring kill_ring
|
kill_ring kill_ring
|
||||||
|
|
||||||
|
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 int
|
||||||
// Input lines
|
|
||||||
lines []string
|
|
||||||
// The cursor position in the text
|
|
||||||
cursor Position
|
|
||||||
last_yank_extent struct {
|
last_yank_extent struct {
|
||||||
start, end Position
|
start, end Position
|
||||||
}
|
}
|
||||||
@@ -158,6 +185,7 @@ type Readline struct {
|
|||||||
keyboard_state KeyboardState
|
keyboard_state KeyboardState
|
||||||
fmt_ctx *markup.Context
|
fmt_ctx *markup.Context
|
||||||
text_to_be_added string
|
text_to_be_added string
|
||||||
|
syntax_highlighted syntax_highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) make_prompt(text string, is_secondary bool) Prompt {
|
func (self *Readline) make_prompt(text string, is_secondary bool) Prompt {
|
||||||
@@ -178,7 +206,9 @@ func New(loop *loop.Loop, r RlInit) *Readline {
|
|||||||
}
|
}
|
||||||
ans := &Readline{
|
ans := &Readline{
|
||||||
mark_prompts: !r.DontMarkPrompts, fmt_ctx: markup.New(true),
|
mark_prompts: !r.DontMarkPrompts, fmt_ctx: markup.New(true),
|
||||||
loop: loop, lines: []string{""}, history: NewHistory(r.HistoryPath, hc), kill_ring: kill_ring{items: list.New().Init()},
|
loop: loop, input_state: InputState{lines: []string{""}}, history: NewHistory(r.HistoryPath, hc),
|
||||||
|
syntax_highlighted: syntax_highlighted{highlighter: r.SyntaxHighlighter},
|
||||||
|
kill_ring: kill_ring{items: list.New().Init()},
|
||||||
}
|
}
|
||||||
ans.prompt = ans.make_prompt(r.Prompt, false)
|
ans.prompt = ans.make_prompt(r.Prompt, false)
|
||||||
t := ""
|
t := ""
|
||||||
@@ -201,11 +231,10 @@ func (self *Readline) AddHistoryItem(hi HistoryItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) ResetText() {
|
func (self *Readline) ResetText() {
|
||||||
self.lines = []string{""}
|
self.input_state = InputState{lines: []string{""}}
|
||||||
self.cursor = Position{}
|
|
||||||
self.cursor_y = 0
|
|
||||||
self.last_action = ActionNil
|
self.last_action = ActionNil
|
||||||
self.keyboard_state = KeyboardState{}
|
self.keyboard_state = KeyboardState{}
|
||||||
|
self.history_search = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) {
|
func (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) {
|
||||||
@@ -278,7 +307,7 @@ func (self *Readline) AllText() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) CursorAtEndOfLine() bool {
|
func (self *Readline) CursorAtEndOfLine() bool {
|
||||||
return self.cursor.X >= len(self.lines[self.cursor.Y])
|
return self.input_state.cursor.X >= len(self.input_state.lines[self.input_state.cursor.Y])
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ package readline
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"kitty/tools/utils"
|
||||||
"kitty/tools/wcswidth"
|
"kitty/tools/wcswidth"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@@ -36,7 +38,7 @@ func (self *Readline) format_arg_prompt(cna string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) prompt_for_line_number(i int) Prompt {
|
func (self *Readline) prompt_for_line_number(i int) Prompt {
|
||||||
is_line_with_cursor := i == self.cursor.Y
|
is_line_with_cursor := i == self.input_state.cursor.Y
|
||||||
if is_line_with_cursor && self.keyboard_state.current_numeric_argument != "" {
|
if is_line_with_cursor && self.keyboard_state.current_numeric_argument != "" {
|
||||||
return self.make_prompt(self.format_arg_prompt(self.keyboard_state.current_numeric_argument), i > 0)
|
return self.make_prompt(self.format_arg_prompt(self.keyboard_state.current_numeric_argument), i > 0)
|
||||||
}
|
}
|
||||||
@@ -49,17 +51,48 @@ func (self *Readline) prompt_for_line_number(i int) Prompt {
|
|||||||
return self.continuation_prompt
|
return self.continuation_prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Readline) apply_syntax_highlighting() (lines []string, cursor Position) {
|
||||||
|
highlighter := self.syntax_highlighted.highlighter
|
||||||
|
highlighter_name := "default"
|
||||||
|
if self.history_search != nil {
|
||||||
|
highlighter = self.history_search_highlighter
|
||||||
|
highlighter_name = "## history ##"
|
||||||
|
}
|
||||||
|
if highlighter == nil {
|
||||||
|
return self.input_state.lines, self.input_state.cursor
|
||||||
|
}
|
||||||
|
src := strings.Join(self.input_state.lines, "\n")
|
||||||
|
if len(self.syntax_highlighted.lines) > 0 && self.syntax_highlighted.last_highlighter_name == highlighter_name && self.syntax_highlighted.src_for_last_highlight == src {
|
||||||
|
lines = self.syntax_highlighted.lines
|
||||||
|
} else {
|
||||||
|
if src == "" {
|
||||||
|
lines = []string{""}
|
||||||
|
} else {
|
||||||
|
text := highlighter(src, self.input_state.cursor.X, self.input_state.cursor.Y)
|
||||||
|
lines = utils.Splitlines(text)
|
||||||
|
for len(lines) < len(self.input_state.lines) {
|
||||||
|
lines = append(lines, "syntax highlighter malfunctioned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line := lines[self.input_state.cursor.Y]
|
||||||
|
w := wcswidth.Stringwidth(self.input_state.lines[self.input_state.cursor.Y][:self.input_state.cursor.X])
|
||||||
|
x := len(wcswidth.TruncateToVisualLength(line, w))
|
||||||
|
return lines, Position{X: x, Y: self.input_state.cursor.Y}
|
||||||
|
}
|
||||||
|
|
||||||
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.update_current_screen_size()
|
self.update_current_screen_size()
|
||||||
}
|
}
|
||||||
ans := make([]*ScreenLine, 0, len(self.lines))
|
lines, cursor := self.apply_syntax_highlighting()
|
||||||
|
ans := make([]*ScreenLine, 0, len(lines))
|
||||||
found_cursor := false
|
found_cursor := false
|
||||||
cursor_at_start_of_next_line := false
|
cursor_at_start_of_next_line := false
|
||||||
for i, line := range self.lines {
|
for i, line := range lines {
|
||||||
prompt := self.prompt_for_line_number(i)
|
prompt := self.prompt_for_line_number(i)
|
||||||
offset := 0
|
offset := 0
|
||||||
has_cursor := i == self.cursor.Y
|
has_cursor := i == cursor.Y
|
||||||
for is_first := true; is_first || offset < len(line); is_first = false {
|
for is_first := true; is_first || offset < len(line); is_first = false {
|
||||||
l, width := wcswidth.TruncateToVisualLengthWithWidth(line[offset:], self.screen_width-prompt.Length)
|
l, width := wcswidth.TruncateToVisualLengthWithWidth(line[offset:], self.screen_width-prompt.Length)
|
||||||
sl := ScreenLine{
|
sl := ScreenLine{
|
||||||
@@ -73,12 +106,12 @@ func (self *Readline) get_screen_lines() []*ScreenLine {
|
|||||||
sl.CursorTextPos = 0
|
sl.CursorTextPos = 0
|
||||||
}
|
}
|
||||||
ans = append(ans, &sl)
|
ans = append(ans, &sl)
|
||||||
if has_cursor && !found_cursor && offset <= self.cursor.X && self.cursor.X <= offset+len(l) {
|
if has_cursor && !found_cursor && offset <= cursor.X && cursor.X <= offset+len(l) {
|
||||||
found_cursor = true
|
found_cursor = true
|
||||||
ctpos := self.cursor.X - offset
|
ctpos := cursor.X - offset
|
||||||
ccell := prompt.Length + wcswidth.Stringwidth(l[:ctpos])
|
ccell := prompt.Length + wcswidth.Stringwidth(l[:ctpos])
|
||||||
if ccell >= self.screen_width {
|
if ccell >= self.screen_width {
|
||||||
if offset+len(l) < len(line) || i < len(self.lines)-1 {
|
if offset+len(l) < len(line) || i < len(lines)-1 {
|
||||||
cursor_at_start_of_next_line = true
|
cursor_at_start_of_next_line = true
|
||||||
} else {
|
} else {
|
||||||
ans = append(ans, &ScreenLine{ParentLineNumber: i, OffsetInParentLine: len(line)})
|
ans = append(ans, &ScreenLine{ParentLineNumber: i, OffsetInParentLine: len(line)})
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
|
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"kitty/tools/wcswidth"
|
"kitty/tools/wcswidth"
|
||||||
|
|
||||||
|
"github.com/google/shlex"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@@ -36,8 +38,7 @@ type HistorySearch struct {
|
|||||||
items []*HistoryItem
|
items []*HistoryItem
|
||||||
current_idx int
|
current_idx int
|
||||||
backwards bool
|
backwards bool
|
||||||
original_lines []string
|
original_input_state InputState
|
||||||
original_cursor Position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type History struct {
|
type History struct {
|
||||||
@@ -211,31 +212,32 @@ func (self *HistoryMatches) next(num uint) (ans *HistoryItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) create_history_search(backwards bool, num uint) {
|
func (self *Readline) create_history_search(backwards bool, num uint) {
|
||||||
self.history_search = &HistorySearch{backwards: backwards, original_lines: self.lines, original_cursor: self.cursor}
|
self.history_search = &HistorySearch{backwards: backwards, original_input_state: self.input_state.copy()}
|
||||||
|
self.push_keyboard_map(history_search_shortcuts())
|
||||||
self.markup_history_search()
|
self.markup_history_search()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) end_history_search(accept bool) {
|
func (self *Readline) end_history_search(accept bool) {
|
||||||
self.cursor = Position{}
|
|
||||||
if accept && self.history_search.current_idx < len(self.history_search.items) {
|
if accept && self.history_search.current_idx < len(self.history_search.items) {
|
||||||
self.lines = utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)
|
self.input_state.lines = utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)
|
||||||
self.cursor.Y = len(self.lines) - 1
|
self.input_state.cursor.Y = len(self.input_state.lines) - 1
|
||||||
self.cursor.X = len(self.lines[self.cursor.Y])
|
self.input_state.cursor.X = len(self.input_state.lines[self.input_state.cursor.Y])
|
||||||
} else {
|
} else {
|
||||||
self.lines = self.history_search.original_lines
|
self.input_state = self.history_search.original_input_state
|
||||||
self.cursor = self.history_search.original_cursor
|
|
||||||
}
|
}
|
||||||
self.cursor = *self.ensure_position_in_bounds(&self.cursor)
|
self.input_state.cursor = *self.ensure_position_in_bounds(&self.input_state.cursor)
|
||||||
|
self.pop_keyboard_map()
|
||||||
|
self.history_search = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) markup_history_search() {
|
func (self *Readline) markup_history_search() {
|
||||||
if len(self.history_search.items) == 0 {
|
if len(self.history_search.items) == 0 {
|
||||||
if len(self.history_search.tokens) == 0 {
|
if len(self.history_search.tokens) == 0 {
|
||||||
self.lines = []string{""}
|
self.input_state.lines = []string{""}
|
||||||
} else {
|
} else {
|
||||||
self.lines = []string{"No matches for: " + self.fmt_ctx.BrightRed(self.history_search.query)}
|
self.input_state.lines = []string{"No matches for: " + self.history_search.query}
|
||||||
}
|
}
|
||||||
self.cursor = Position{X: wcswidth.Stringwidth(self.lines[0])}
|
self.input_state.cursor = Position{X: wcswidth.Stringwidth(self.input_state.lines[0])}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lines := utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)
|
lines := utils.Splitlines(self.history_search.items[self.history_search.current_idx].Cmd)
|
||||||
@@ -243,7 +245,6 @@ func (self *Readline) markup_history_search() {
|
|||||||
for _, tok := range self.history_search.tokens {
|
for _, tok := range self.history_search.tokens {
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
if idx := strings.Index(line, tok); idx > -1 {
|
if idx := strings.Index(line, tok); idx > -1 {
|
||||||
lines[i] = line[:idx] + self.fmt_ctx.Green(tok) + line[idx+len(tok):]
|
|
||||||
q := Position{Y: i, X: idx}
|
q := Position{Y: i, X: idx}
|
||||||
if q.Less(cursor) {
|
if q.Less(cursor) {
|
||||||
cursor = q
|
cursor = q
|
||||||
@@ -252,17 +253,49 @@ func (self *Readline) markup_history_search() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.lines = lines
|
self.input_state.lines = lines
|
||||||
self.cursor = *self.ensure_position_in_bounds(&cursor)
|
self.input_state.cursor = *self.ensure_position_in_bounds(&cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) remove_text_from_history_search(num uint) uint {
|
||||||
|
l := len(self.history_search.query)
|
||||||
|
nl := utils.Max(0, l-int(num))
|
||||||
|
self.history_search.query = self.history_search.query[:nl]
|
||||||
|
num_removed := uint(l - nl)
|
||||||
|
self.add_text_to_history_search("") // update the search results
|
||||||
|
return num_removed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) history_search_highlighter(text string, x, y int) string {
|
||||||
|
if len(self.history_search.items) == 0 {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
lines := utils.Splitlines(text)
|
||||||
|
for _, tok := range self.history_search.tokens {
|
||||||
|
for i, line := range lines {
|
||||||
|
if idx := strings.Index(line, tok); idx > -1 {
|
||||||
|
lines[i] = line[:idx] + self.fmt_ctx.Green(tok) + line[idx+len(tok):]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) add_text_to_history_search(text string) {
|
func (self *Readline) add_text_to_history_search(text string) {
|
||||||
self.history_search.query += text
|
self.history_search.query += text
|
||||||
self.history_search.tokens = strings.Split(self.history_search.query, " ")
|
tokens, err := shlex.Split(self.history_search.query)
|
||||||
|
if err != nil {
|
||||||
|
tokens = strings.Split(self.history_search.query, " ")
|
||||||
|
}
|
||||||
|
self.history_search.tokens = tokens
|
||||||
var current_item *HistoryItem
|
var current_item *HistoryItem
|
||||||
if len(self.history_search.items) > 0 {
|
if len(self.history_search.items) > 0 {
|
||||||
current_item = self.history_search.items[self.history_search.current_idx]
|
current_item = self.history_search.items[self.history_search.current_idx]
|
||||||
}
|
}
|
||||||
|
if len(self.history_search.tokens) == 0 {
|
||||||
|
self.history_search.items = []*HistoryItem{}
|
||||||
|
} else {
|
||||||
items := make([]*HistoryItem, len(self.history.items))
|
items := make([]*HistoryItem, len(self.history.items))
|
||||||
for i, x := range self.history.items {
|
for i, x := range self.history.items {
|
||||||
items[i] = &x
|
items[i] = &x
|
||||||
@@ -277,6 +310,7 @@ func (self *Readline) add_text_to_history_search(text string) {
|
|||||||
items = matches
|
items = matches
|
||||||
}
|
}
|
||||||
self.history_search.items = items
|
self.history_search.items = items
|
||||||
|
}
|
||||||
idx := -1
|
idx := -1
|
||||||
for i, item := range self.history_search.items {
|
for i, item := range self.history_search.items {
|
||||||
if item == current_item {
|
if item == current_item {
|
||||||
|
|||||||
@@ -121,18 +121,69 @@ func default_shortcuts() *ShortcutMap {
|
|||||||
return _default_shortcuts
|
return _default_shortcuts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Readline) action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action {
|
var _history_search_shortcuts *ShortcutMap
|
||||||
for sc, ac := range shortcuts {
|
|
||||||
if event.MatchesPressOrRepeat(sc) {
|
func history_search_shortcuts() *ShortcutMap {
|
||||||
return ac
|
if _history_search_shortcuts == nil {
|
||||||
|
sm := ShortcutMap{leaves: make(map[string]Action, 32), children: map[string]*ShortcutMap{}}
|
||||||
|
sm.add(ActionBackspace, "backspace")
|
||||||
|
sm.add(ActionBackspace, "ctrl+h")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "home")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+a")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "end")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+e")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+home")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+end")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "alt+f")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+right")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+left")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "alt+b")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "left")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+b")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "right")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+f")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "up")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "down")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+c")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "ctrl+g")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndRestore, "escape")
|
||||||
|
|
||||||
|
sm.add(ActionTerminateHistorySearchAndApply, "ctrl+d")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndApply, "enter")
|
||||||
|
sm.add(ActionTerminateHistorySearchAndApply, "ctrl+j")
|
||||||
|
|
||||||
|
_history_search_shortcuts = &sm
|
||||||
}
|
}
|
||||||
}
|
return _history_search_shortcuts
|
||||||
return ActionNil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrCouldNotPerformAction = errors.New("Could not perform the specified action")
|
var ErrCouldNotPerformAction = errors.New("Could not perform the specified action")
|
||||||
var ErrAcceptInput = errors.New("Accept input")
|
var ErrAcceptInput = errors.New("Accept input")
|
||||||
|
|
||||||
|
func (self *Readline) push_keyboard_map(m *ShortcutMap) {
|
||||||
|
maps := self.keyboard_state.active_shortcut_maps
|
||||||
|
self.keyboard_state = KeyboardState{}
|
||||||
|
if maps == nil {
|
||||||
|
maps = make([]*ShortcutMap, 0, 2)
|
||||||
|
}
|
||||||
|
self.keyboard_state.active_shortcut_maps = append(maps, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Readline) pop_keyboard_map() {
|
||||||
|
maps := self.keyboard_state.active_shortcut_maps
|
||||||
|
self.keyboard_state = KeyboardState{}
|
||||||
|
if len(maps) > 0 {
|
||||||
|
maps = maps[:len(maps)-1]
|
||||||
|
self.keyboard_state.active_shortcut_maps = maps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Readline) handle_numeric_arg(ac Action) {
|
func (self *Readline) handle_numeric_arg(ac Action) {
|
||||||
t := "-"
|
t := "-"
|
||||||
num := int(ac - ActionNumericArgumentDigit0)
|
num := int(ac - ActionNumericArgumentDigit0)
|
||||||
|
|||||||
Reference in New Issue
Block a user