Start work on kill ring

This commit is contained in:
Kovid Goyal 2022-10-25 12:18:25 +05:30
parent b7816d26be
commit ea583f60b3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 89 additions and 1 deletions

View File

@ -51,6 +51,7 @@ func (self *Readline) all_text() string {
} }
func (self *Readline) add_text(text string) { func (self *Readline) add_text(text string) {
defer func() { self.last_action = ActionAddText }()
new_lines := make([]string, 0, len(self.lines)+4) new_lines := make([]string, 0, len(self.lines)+4)
new_lines = append(new_lines, self.lines[:self.cursor.Y]...) new_lines = append(new_lines, self.lines[:self.cursor.Y]...)
var lines_after []string var lines_after []string
@ -355,7 +356,37 @@ func (self *Readline) move_to_start_of_word(amt uint, traverse_line_breaks bool)
return return
} }
func (self *Readline) kill_text(text string) {
if ActionStartKillActions < self.last_action && self.last_action < ActionEndKillActions {
self.kill_ring.append_to_existing_item(text)
} else {
self.kill_ring.add_new_item(text)
}
}
func (self *Readline) kill_to_end_of_line() bool {
line := self.lines[self.cursor.Y]
if self.cursor.X >= len(line) {
return false
}
self.lines[self.cursor.Y] = line[:self.cursor.X]
self.kill_text(line[self.cursor.X:])
return true
}
func (self *Readline) kill_to_start_of_line() bool {
line := self.lines[self.cursor.Y]
if self.cursor.X <= 0 {
return false
}
self.lines[self.cursor.Y] = line[self.cursor.X:]
self.kill_text(line[:self.cursor.X])
self.cursor.X = 0
return true
}
func (self *Readline) perform_action(ac Action, repeat_count uint) error { func (self *Readline) perform_action(ac Action, repeat_count uint) error {
defer func() { self.last_action = ac }()
switch ac { switch ac {
case ActionBackspace: case ActionBackspace:
if self.erase_chars_before_cursor(repeat_count, true) > 0 { if self.erase_chars_before_cursor(repeat_count, true) > 0 {
@ -435,6 +466,14 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
self.RedrawNonAtomic() self.RedrawNonAtomic()
self.loop.EndAtomicUpdate() self.loop.EndAtomicUpdate()
return nil return nil
case ActionKillToEndOfLine:
if self.kill_to_end_of_line() {
return nil
}
case ActionKillToStartOfLine:
if self.kill_to_start_of_line() {
return nil
}
} }
return ErrCouldNotPerformAction return ErrCouldNotPerformAction
} }

View File

@ -3,6 +3,7 @@
package readline package readline
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
@ -56,8 +57,49 @@ const (
ActionHistoryNext ActionHistoryNext
ActionHistoryPrevious ActionHistoryPrevious
ActionClearScreen ActionClearScreen
ActionAddText
ActionStartKillActions
ActionKillToEndOfLine
ActionKillToStartOfLine
ActionEndKillActions
) )
type kill_ring struct {
items *list.List
}
func (self *kill_ring) append_to_existing_item(text string) {
e := self.items.Front()
if e == nil {
self.add_new_item(text)
}
e.Value = e.Value.(string) + text
}
func (self *kill_ring) add_new_item(text string) {
if text != "" {
self.items.PushFront(text)
}
}
func (self *kill_ring) yank() string {
e := self.items.Front()
if e == nil {
return ""
}
return e.Value.(string)
}
func (self *kill_ring) pop_yank() string {
e := self.items.Front()
if e == nil {
return ""
}
self.items.MoveToBack(e)
return self.yank()
}
type Readline struct { type Readline struct {
prompt string prompt string
prompt_len int prompt_len int
@ -66,6 +108,7 @@ type Readline struct {
mark_prompts bool mark_prompts bool
loop *loop.Loop loop *loop.Loop
history *History history *History
kill_ring kill_ring
// 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
@ -75,6 +118,7 @@ type Readline struct {
// The cursor position in the text // The cursor position in the text
cursor Position cursor Position
bracketed_paste_buffer strings.Builder bracketed_paste_buffer strings.Builder
last_action Action
} }
func New(loop *loop.Loop, r RlInit) *Readline { func New(loop *loop.Loop, r RlInit) *Readline {
@ -84,7 +128,7 @@ func New(loop *loop.Loop, r RlInit) *Readline {
} }
ans := &Readline{ ans := &Readline{
prompt: r.Prompt, prompt_len: wcswidth.Stringwidth(r.Prompt), mark_prompts: !r.DontMarkPrompts, prompt: r.Prompt, prompt_len: wcswidth.Stringwidth(r.Prompt), mark_prompts: !r.DontMarkPrompts,
loop: loop, lines: []string{""}, history: NewHistory(r.HistoryPath, hc), loop: loop, lines: []string{""}, history: NewHistory(r.HistoryPath, hc), kill_ring: kill_ring{items: list.New().Init()},
} }
if r.ContinuationPrompt != "" || !r.EmptyContinuationPrompt { if r.ContinuationPrompt != "" || !r.EmptyContinuationPrompt {
ans.continuation_prompt = r.ContinuationPrompt ans.continuation_prompt = r.ContinuationPrompt
@ -112,6 +156,7 @@ func (self *Readline) ResetText() {
self.lines = []string{""} self.lines = []string{""}
self.cursor = Position{} self.cursor = Position{}
self.cursor_y = 0 self.cursor_y = 0
self.last_action = ActionNil
} }
func (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) { func (self *Readline) ChangeLoopAndResetText(lp *loop.Loop) {

View File

@ -39,6 +39,10 @@ var default_shortcuts = map[string]Action{
"ctrl+d": ActionEndInput, "ctrl+d": ActionEndInput,
"enter": ActionAcceptInput, "enter": ActionAcceptInput,
"ctrl+k": ActionKillToEndOfLine,
"ctrl+x": ActionKillToStartOfLine,
"ctrl+u": ActionKillToStartOfLine,
} }
func action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action { func action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action {