Move cursor to end of input when traversing history list
This commit is contained in:
parent
1523fef000
commit
cfb6d93dc0
@ -465,209 +465,175 @@ func (self *Readline) yank(repeat_count uint, pop bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *Readline) apply_history_text(text string) {
|
||||
self.input_state.lines = utils.Splitlines(text)
|
||||
if len(self.input_state.lines) == 0 {
|
||||
self.input_state.lines = []string{""}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Readline) history_first() bool {
|
||||
prefix := self.text_upto_cursor_pos()
|
||||
if self.history_matches == nil || self.history_matches.prefix != prefix {
|
||||
return false
|
||||
}
|
||||
item := self.history_matches.first()
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
self.apply_history_text(item.Cmd)
|
||||
return true
|
||||
self.create_history_matches()
|
||||
return self.history_matches.first(self)
|
||||
}
|
||||
|
||||
func (self *Readline) history_last() bool {
|
||||
prefix := self.text_upto_cursor_pos()
|
||||
if self.history_matches == nil || self.history_matches.prefix != prefix {
|
||||
return false
|
||||
}
|
||||
item := self.history_matches.last()
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
self.apply_history_text(item.Cmd)
|
||||
return true
|
||||
self.create_history_matches()
|
||||
return self.history_matches.last(self)
|
||||
}
|
||||
|
||||
func (self *Readline) history_prev(repeat_count uint) bool {
|
||||
prefix := self.text_upto_cursor_pos()
|
||||
if self.history_matches == nil || self.history_matches.prefix != prefix {
|
||||
self.history_matches = self.history.FindPrefixMatches(prefix, self.AllText())
|
||||
}
|
||||
item := self.history_matches.previous(repeat_count)
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
self.apply_history_text(item.Cmd)
|
||||
return true
|
||||
self.create_history_matches()
|
||||
return self.history_matches.previous(repeat_count, self)
|
||||
}
|
||||
|
||||
func (self *Readline) history_next(repeat_count uint) bool {
|
||||
prefix := self.text_upto_cursor_pos()
|
||||
if self.history_matches == nil || self.history_matches.prefix != prefix {
|
||||
return false
|
||||
}
|
||||
item := self.history_matches.next(repeat_count)
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
self.apply_history_text(item.Cmd)
|
||||
return true
|
||||
self.create_history_matches()
|
||||
return self.history_matches.next(repeat_count, self)
|
||||
}
|
||||
|
||||
func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
defer func() { self.last_action = ac }()
|
||||
func (self *Readline) _perform_action(ac Action, repeat_count uint) (err error, dont_set_last_action bool) {
|
||||
switch ac {
|
||||
case ActionBackspace:
|
||||
if self.history_search != nil {
|
||||
if self.remove_text_from_history_search(repeat_count) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if self.erase_chars_before_cursor(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
}
|
||||
case ActionDelete:
|
||||
if self.erase_chars_after_cursor(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToStartOfLine:
|
||||
if self.move_to_start_of_line() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToEndOfLine:
|
||||
if self.move_to_end_of_line() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToEndOfWord:
|
||||
if self.move_to_end_of_word(repeat_count, true, has_word_chars) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToStartOfWord:
|
||||
if self.move_to_start_of_word(repeat_count, true, has_word_chars) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToStartOfDocument:
|
||||
if self.move_to_start() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionMoveToEndOfDocument:
|
||||
if self.move_to_end() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionCursorLeft:
|
||||
if self.move_cursor_left(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionCursorRight:
|
||||
if self.move_cursor_right(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionEndInput:
|
||||
line := self.input_state.lines[self.input_state.cursor.Y]
|
||||
if line == "" {
|
||||
return io.EOF
|
||||
err = io.EOF
|
||||
|
||||
} else {
|
||||
err = self.perform_action(ActionAcceptInput, 1)
|
||||
}
|
||||
return self.perform_action(ActionAcceptInput, 1)
|
||||
return
|
||||
case ActionAcceptInput:
|
||||
return ErrAcceptInput
|
||||
err = ErrAcceptInput
|
||||
return
|
||||
case ActionCursorUp:
|
||||
if self.move_cursor_vertically(-int(repeat_count)) != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionCursorDown:
|
||||
if self.move_cursor_vertically(int(repeat_count)) != 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionHistoryPreviousOrCursorUp:
|
||||
dont_set_last_action = true
|
||||
if self.perform_action(ActionCursorUp, repeat_count) == ErrCouldNotPerformAction {
|
||||
return self.perform_action(ActionHistoryPrevious, repeat_count)
|
||||
err = self.perform_action(ActionHistoryPrevious, repeat_count)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
case ActionHistoryNextOrCursorDown:
|
||||
dont_set_last_action = true
|
||||
if self.perform_action(ActionCursorDown, repeat_count) == ErrCouldNotPerformAction {
|
||||
return self.perform_action(ActionHistoryNext, repeat_count)
|
||||
err = self.perform_action(ActionHistoryNext, repeat_count)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
case ActionHistoryFirst:
|
||||
if self.history_first() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionHistoryPrevious:
|
||||
if self.history_prev(repeat_count) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionHistoryNext:
|
||||
if self.history_next(repeat_count) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionHistoryLast:
|
||||
if self.history_last() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionClearScreen:
|
||||
self.loop.StartAtomicUpdate()
|
||||
self.loop.ClearScreen()
|
||||
self.RedrawNonAtomic()
|
||||
self.loop.EndAtomicUpdate()
|
||||
return nil
|
||||
return
|
||||
case ActionKillToEndOfLine:
|
||||
if self.kill_to_end_of_line() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionKillToStartOfLine:
|
||||
if self.kill_to_start_of_line() {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionKillNextWord:
|
||||
if self.kill_next_word(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionKillPreviousWord:
|
||||
if self.kill_previous_word(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionKillPreviousSpaceDelimitedWord:
|
||||
if self.kill_previous_space_delimited_word(repeat_count, true) > 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionYank:
|
||||
if self.yank(repeat_count, false) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionPopYank:
|
||||
if self.yank(repeat_count, true) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionAbortCurrentLine:
|
||||
self.loop.QueueWriteString("\r\n")
|
||||
self.ResetText()
|
||||
return nil
|
||||
return
|
||||
case ActionHistoryIncrementalSearchForwards:
|
||||
if self.history_search == nil {
|
||||
self.create_history_search(false, repeat_count)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if self.next_history_search(false, repeat_count) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionHistoryIncrementalSearchBackwards:
|
||||
if self.history_search == nil {
|
||||
self.create_history_search(true, repeat_count)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if self.next_history_search(true, repeat_count) {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionAddText:
|
||||
text := strings.Repeat(self.text_to_be_added, int(repeat_count))
|
||||
@ -677,17 +643,26 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
} else {
|
||||
self.add_text(text)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
case ActionTerminateHistorySearchAndRestore:
|
||||
if self.history_search != nil {
|
||||
self.end_history_search(false)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
case ActionTerminateHistorySearchAndApply:
|
||||
if self.history_search != nil {
|
||||
self.end_history_search(true)
|
||||
return nil
|
||||
return
|
||||
}
|
||||
}
|
||||
return ErrCouldNotPerformAction
|
||||
err = ErrCouldNotPerformAction
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
err, dont_set_last_action := self._perform_action(ac, repeat_count)
|
||||
if err == nil && !dont_set_last_action {
|
||||
self.last_action = ac
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@ -450,27 +450,27 @@ func TestHistory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
test(ActionHistoryPrevious, "", "b four")
|
||||
test(ActionHistoryPrevious, "", "b three")
|
||||
test(ActionHistoryPrevious, "", "a two")
|
||||
test(ActionHistoryPrevious, "", "a one")
|
||||
test(ActionHistoryPrevious, "", "a one")
|
||||
test(ActionHistoryNext, "", "a two")
|
||||
test(ActionHistoryNext, "", "b three")
|
||||
test(ActionHistoryNext, "", "b four")
|
||||
test(ActionHistoryPreviousOrCursorUp, "b four", "")
|
||||
test(ActionHistoryPreviousOrCursorUp, "b three", "")
|
||||
test(ActionHistoryPrevious, "a two", "")
|
||||
test(ActionHistoryPrevious, "a one", "")
|
||||
test(ActionHistoryPrevious, "a one", "")
|
||||
test(ActionHistoryNext, "a two", "")
|
||||
test(ActionHistoryNext, "b three", "")
|
||||
test(ActionHistoryNext, "b four", "")
|
||||
test(ActionHistoryNext, "", "")
|
||||
test(ActionHistoryNext, "", "")
|
||||
|
||||
test(ActionHistoryPrevious, "", "b four")
|
||||
test(ActionHistoryPrevious, "", "b three")
|
||||
test(ActionHistoryNext, "", "b four")
|
||||
test(ActionHistoryPrevious, "b four", "")
|
||||
test(ActionHistoryPrevious, "b three", "")
|
||||
test(ActionHistoryNext, "b four", "")
|
||||
|
||||
rl.ResetText()
|
||||
rl.add_text("a")
|
||||
test(ActionHistoryPrevious, "a", " two")
|
||||
test(ActionHistoryPrevious, "a", " one")
|
||||
test(ActionHistoryPrevious, "a", " one")
|
||||
test(ActionHistoryNext, "a", " two")
|
||||
test(ActionHistoryPrevious, "a two", "")
|
||||
test(ActionHistoryPrevious, "a one", "")
|
||||
test(ActionHistoryPrevious, "a one", "")
|
||||
test(ActionHistoryNext, "a two", "")
|
||||
test(ActionHistoryNext, "a", "")
|
||||
test(ActionHistoryNext, "a", "")
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ type Action uint
|
||||
|
||||
const (
|
||||
ActionNil Action = iota
|
||||
ActionIgnored
|
||||
ActionBackspace
|
||||
ActionDelete
|
||||
ActionMoveToStartOfLine
|
||||
|
||||
@ -27,9 +27,10 @@ type HistoryItem struct {
|
||||
}
|
||||
|
||||
type HistoryMatches struct {
|
||||
items []HistoryItem
|
||||
prefix string
|
||||
current_idx int
|
||||
items []HistoryItem
|
||||
prefix string
|
||||
current_idx int
|
||||
original_input_state InputState
|
||||
}
|
||||
|
||||
type HistorySearch struct {
|
||||
@ -168,8 +169,8 @@ func NewHistory(path string, max_items int) *History {
|
||||
return &ans
|
||||
}
|
||||
|
||||
func (self *History) FindPrefixMatches(prefix, current_command string) *HistoryMatches {
|
||||
ans := HistoryMatches{items: make([]HistoryItem, 0, len(self.items)+1), prefix: prefix}
|
||||
func (self *History) find_prefix_matches(prefix, current_command string, input_state InputState) *HistoryMatches {
|
||||
ans := HistoryMatches{items: make([]HistoryItem, 0, len(self.items)+1), prefix: prefix, original_input_state: input_state}
|
||||
if prefix == "" {
|
||||
ans.items = ans.items[:len(self.items)]
|
||||
copy(ans.items, self.items)
|
||||
@ -185,30 +186,65 @@ func (self *History) FindPrefixMatches(prefix, current_command string) *HistoryM
|
||||
return &ans
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) first() (ans *HistoryItem) {
|
||||
func (self *Readline) create_history_matches() {
|
||||
if self.last_action_was_history_movement() && self.history_matches != nil {
|
||||
return
|
||||
}
|
||||
prefix := self.text_upto_cursor_pos()
|
||||
self.history_matches = self.history.find_prefix_matches(prefix, self.AllText(), self.input_state.copy())
|
||||
}
|
||||
|
||||
func (self *Readline) last_action_was_history_movement() bool {
|
||||
switch self.last_action {
|
||||
case ActionHistoryLast, ActionHistoryFirst, ActionHistoryNext, ActionHistoryPrevious:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) apply(rl *Readline) bool {
|
||||
if self.current_idx >= len(self.items) || self.current_idx < 0 {
|
||||
return false
|
||||
}
|
||||
if self.current_idx == len(self.items)-1 {
|
||||
rl.input_state = self.original_input_state.copy()
|
||||
} else {
|
||||
item := self.items[self.current_idx]
|
||||
rl.input_state.lines = utils.Splitlines(item.Cmd)
|
||||
if len(rl.input_state.lines) == 0 {
|
||||
rl.input_state.lines = []string{""}
|
||||
}
|
||||
idx := len(rl.input_state.lines) - 1
|
||||
rl.input_state.cursor = Position{Y: idx, X: len(rl.input_state.lines[idx])}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) first(rl *Readline) bool {
|
||||
self.current_idx = 0
|
||||
return &self.items[self.current_idx]
|
||||
return self.apply(rl)
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) last() (ans *HistoryItem) {
|
||||
self.current_idx = len(self.items) - 1
|
||||
return &self.items[self.current_idx]
|
||||
func (self *HistoryMatches) last(rl *Readline) bool {
|
||||
self.current_idx = utils.Max(0, len(self.items)-1)
|
||||
return self.apply(rl)
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) previous(num uint) (ans *HistoryItem) {
|
||||
func (self *HistoryMatches) previous(num uint, rl *Readline) bool {
|
||||
if self.current_idx > 0 {
|
||||
self.current_idx = utils.Max(0, self.current_idx-int(num))
|
||||
ans = &self.items[self.current_idx]
|
||||
return self.apply(rl)
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *HistoryMatches) next(num uint) (ans *HistoryItem) {
|
||||
func (self *HistoryMatches) next(num uint, rl *Readline) bool {
|
||||
if self.current_idx+1 < len(self.items) {
|
||||
self.current_idx = utils.Min(len(self.items)-1, self.current_idx+int(num))
|
||||
ans = &self.items[self.current_idx]
|
||||
return self.apply(rl)
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Readline) create_history_search(backwards bool, num uint) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user