Add basic history operations

This commit is contained in:
Kovid Goyal 2022-11-04 20:13:10 +05:30
parent f77d07259a
commit e1ab2383b3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 136 additions and 13 deletions

View File

@ -121,7 +121,7 @@ func exec_command(rl *readline.Readline, cmdline string) bool {
return true
}
cwd, _ := os.Getwd()
hi := readline.HistoryItem{Timestamp: time.Now(), Cmd: cmdline, ExitCode: -1, Cwd: cwd}
hi := readline.HistoryItem{Timestamp: time.Now(), Cmd: rl.AllText(), ExitCode: -1, Cwd: cwd}
switch parsed_cmdline[0] {
case "exit":
hi.ExitCode = 0

View File

@ -463,6 +463,62 @@ func (self *Readline) yank(repeat_count uint, pop bool) bool {
return true
}
func (self *Readline) apply_history_text(text string) {
self.lines = utils.Splitlines(text)
}
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
}
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
}
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
}
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
}
func (self *Readline) perform_action(ac Action, repeat_count uint) error {
defer func() { self.last_action = ac }()
switch ac {
@ -523,21 +579,31 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
return nil
}
case ActionHistoryPreviousOrCursorUp:
if self.cursor.Y == 0 {
r := self.perform_action(ActionHistoryPrevious, repeat_count)
if r == nil {
return nil
}
if self.perform_action(ActionCursorUp, repeat_count) != nil {
return self.perform_action(ActionHistoryPrevious, repeat_count)
}
return self.perform_action(ActionCursorUp, repeat_count)
return nil
case ActionHistoryNextOrCursorDown:
if self.cursor.Y == 0 {
r := self.perform_action(ActionHistoryNext, repeat_count)
if r == nil {
return nil
}
if self.perform_action(ActionCursorDown, repeat_count) != nil {
return self.perform_action(ActionHistoryNext, repeat_count)
}
return nil
case ActionHistoryFirst:
if self.history_first() {
return nil
}
case ActionHistoryPrevious:
if self.history_prev(repeat_count) {
return nil
}
case ActionHistoryNext:
if self.history_next(repeat_count) {
return nil
}
case ActionHistoryLast:
if self.history_last() {
return nil
}
return self.perform_action(ActionCursorDown, repeat_count)
case ActionClearScreen:
self.loop.StartAtomicUpdate()
self.loop.ClearScreen()

View File

@ -56,6 +56,8 @@ const (
ActionHistoryNextOrCursorDown
ActionHistoryNext
ActionHistoryPrevious
ActionHistoryFirst
ActionHistoryLast
ActionClearScreen
ActionAddText
@ -132,6 +134,7 @@ type Readline struct {
}
bracketed_paste_buffer strings.Builder
last_action Action
history_matches *HistoryMatches
}
func New(loop *loop.Loop, r RlInit) *Readline {

View File

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"os"
"strings"
"time"
"kitty/tools/utils"
@ -22,6 +23,12 @@ type HistoryItem struct {
ExitCode int `json:"exit_code"`
}
type HistoryMatches struct {
items []HistoryItem
prefix string
current_idx int
}
type History struct {
file_path string
file *os.File
@ -148,3 +155,45 @@ func NewHistory(path string, max_items int) *History {
ans.Read()
return &ans
}
func (self *History) FindPrefixMatches(prefix, current_command string) *HistoryMatches {
ans := HistoryMatches{items: make([]HistoryItem, 0, len(self.items)+1), prefix: prefix}
if prefix == "" {
copy(ans.items, self.items)
} else {
for _, x := range self.items {
if strings.HasPrefix(x.Cmd, prefix) {
ans.items = append(ans.items, x)
}
}
}
ans.items = append(ans.items, HistoryItem{Cmd: current_command})
ans.current_idx = len(ans.items) - 1
return &ans
}
func (self *HistoryMatches) first() (ans *HistoryItem) {
self.current_idx = 0
return &self.items[self.current_idx]
}
func (self *HistoryMatches) last() (ans *HistoryItem) {
self.current_idx = len(self.items) - 1
return &self.items[self.current_idx]
}
func (self *HistoryMatches) previous(num uint) (ans *HistoryItem) {
if self.current_idx > 0 {
self.current_idx = utils.Max(0, self.current_idx-int(num))
ans = &self.items[self.current_idx]
}
return
}
func (self *HistoryMatches) next(num uint) (ans *HistoryItem) {
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
}

View File

@ -48,6 +48,11 @@ var default_shortcuts = map[string]Action{
"ctrl+w": ActionKillPreviousSpaceDelimitedWord,
"ctrl+y": ActionYank,
"alt+y": ActionPopYank,
"ctrl+p": ActionHistoryPrevious,
"ctrl+n": ActionHistoryNext,
"alt+<": ActionHistoryFirst,
"alt+>": ActionHistoryLast,
}
func action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action {