Implement number args for repeats
This commit is contained in:
parent
2f2dbfb45f
commit
5a425ccaad
@ -137,7 +137,7 @@ 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) {
|
||||
if source_line != target_line {
|
||||
visual_distance_into_text := source_line.CursorCell - source_line.PromptLen
|
||||
visual_distance_into_text := source_line.CursorCell - source_line.Prompt.Length
|
||||
self.cursor.Y = target_line.ParentLineNumber
|
||||
tp := wcswidth.TruncateToVisualLength(target_line.Text, visual_distance_into_text)
|
||||
self.cursor.X = target_line.OffsetInParentLine + len(tp)
|
||||
@ -647,6 +647,11 @@ func (self *Readline) perform_action(ac Action, repeat_count uint) error {
|
||||
self.loop.QueueWriteString("\r\n")
|
||||
self.ResetText()
|
||||
return nil
|
||||
case ActionAddText:
|
||||
text := strings.Repeat(self.text_to_be_added, int(repeat_count))
|
||||
self.text_to_be_added = ""
|
||||
self.add_text(text)
|
||||
return nil
|
||||
}
|
||||
return ErrCouldNotPerformAction
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"kitty/tools/tui/loop"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -64,6 +66,13 @@ func TestGetScreenLines(t *testing.T) {
|
||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
||||
rl.screen_width = 10
|
||||
|
||||
p := func(primary bool) Prompt {
|
||||
if primary {
|
||||
return rl.prompt
|
||||
}
|
||||
return rl.continuation_prompt
|
||||
}
|
||||
|
||||
tsl := func(expected ...ScreenLine) {
|
||||
q := rl.get_screen_lines()
|
||||
actual := make([]ScreenLine, len(q))
|
||||
@ -74,52 +83,52 @@ func TestGetScreenLines(t *testing.T) {
|
||||
t.Fatalf("Did not get expected screen lines for: %#v and cursor: %+v\n%s", rl.AllText(), rl.cursor, diff)
|
||||
}
|
||||
}
|
||||
tsl(ScreenLine{PromptLen: 3, CursorCell: 3})
|
||||
tsl(ScreenLine{Prompt: p(true), CursorCell: 3})
|
||||
rl.add_text("123")
|
||||
tsl(ScreenLine{PromptLen: 3, CursorCell: 6, Text: "123", CursorTextPos: 3, TextLengthInCells: 3})
|
||||
tsl(ScreenLine{Prompt: p(true), CursorCell: 6, Text: "123", CursorTextPos: 3, TextLengthInCells: 3})
|
||||
rl.add_text("456")
|
||||
tsl(ScreenLine{PromptLen: 3, CursorCell: 9, Text: "123456", CursorTextPos: 6, TextLengthInCells: 6})
|
||||
tsl(ScreenLine{Prompt: p(true), CursorCell: 9, Text: "123456", CursorTextPos: 6, TextLengthInCells: 6})
|
||||
rl.add_text("7")
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||
ScreenLine{OffsetInParentLine: 7},
|
||||
)
|
||||
rl.add_text("89")
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||
ScreenLine{Prompt: p(true), CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||
ScreenLine{OffsetInParentLine: 7, Text: "89", CursorCell: 2, TextLengthInCells: 2, CursorTextPos: 2},
|
||||
)
|
||||
rl.ResetText()
|
||||
rl.add_text("123\n456abcdeXYZ")
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
||||
ScreenLine{ParentLineNumber: 1, PromptLen: 2, Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||
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{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 3, CursorTextPos: 3, Text: "XYZ"},
|
||||
)
|
||||
rl.cursor = Position{X: 2}
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: 5, Text: "123", CursorTextPos: 2, TextLengthInCells: 3},
|
||||
ScreenLine{ParentLineNumber: 1, PromptLen: 2, Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||
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{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
||||
)
|
||||
rl.cursor = Position{X: 2, Y: 1}
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
||||
ScreenLine{ParentLineNumber: 1, PromptLen: 2, Text: "456abcde", TextLengthInCells: 8, CursorCell: 4, CursorTextPos: 2},
|
||||
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{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: -1, CursorTextPos: -1, Text: "XYZ"},
|
||||
)
|
||||
rl.cursor = Position{X: 8, Y: 1}
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "123", CursorTextPos: -1, TextLengthInCells: 3},
|
||||
ScreenLine{ParentLineNumber: 1, PromptLen: 2, Text: "456abcde", TextLengthInCells: 8, CursorCell: -1, CursorTextPos: -1},
|
||||
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{OffsetInParentLine: 8, ParentLineNumber: 1, TextLengthInCells: 3, CursorCell: 0, CursorTextPos: 0, Text: "XYZ"},
|
||||
)
|
||||
rl.ResetText()
|
||||
rl.add_text("1234567\nabc")
|
||||
rl.cursor = Position{X: 7}
|
||||
tsl(
|
||||
ScreenLine{PromptLen: 3, CursorCell: -1, Text: "1234567", CursorTextPos: -1, TextLengthInCells: 7},
|
||||
ScreenLine{ParentLineNumber: 1, PromptLen: 2, Text: "abc", CursorCell: 2, TextLengthInCells: 3, CursorTextPos: 0},
|
||||
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},
|
||||
)
|
||||
}
|
||||
|
||||
@ -374,6 +383,51 @@ func TestEraseChars(t *testing.T) {
|
||||
}, "", "oree")
|
||||
}
|
||||
|
||||
func TestNumberArgument(t *testing.T) {
|
||||
lp, _ := loop.New()
|
||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
||||
test := func(ac Action, before_cursor, after_cursor string) {
|
||||
rl.dispatch_key_action(ac)
|
||||
if diff := cmp.Diff(before_cursor, rl.text_upto_cursor_pos()); diff != "" {
|
||||
t.Fatalf("The text before the cursor was not as expected for action: %#v\n%s", ac, diff)
|
||||
}
|
||||
if diff := cmp.Diff(after_cursor, rl.text_after_cursor_pos()); diff != "" {
|
||||
t.Fatalf("The text after the cursor was not as expected for action: %#v\n%s", ac, diff)
|
||||
}
|
||||
}
|
||||
sw := func(num int) {
|
||||
q := rl.format_arg_prompt(strconv.Itoa(num))
|
||||
for _, sl := range rl.get_screen_lines() {
|
||||
if num <= 0 && !strings.Contains(sl.Prompt.Text, "$$") {
|
||||
t.Fatalf("arg prompt unexpectedly present for: %#v", rl.AllText())
|
||||
}
|
||||
if num > 0 && !strings.Contains(sl.Prompt.Text, q) {
|
||||
t.Fatalf("arg prompt unexpectedly not present for: %#v prompt: %#v", rl.AllText(), sl.Prompt.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sw(0)
|
||||
rl.dispatch_key_action(ActionNumericArgumentDigit1)
|
||||
sw(1)
|
||||
rl.dispatch_key_action(ActionNumericArgumentDigit0)
|
||||
sw(10)
|
||||
rl.text_to_be_added = "x"
|
||||
test(ActionAddText, "xxxxxxxxxx", "")
|
||||
sw(0)
|
||||
test(ActionNumericArgumentDigit0, "xxxxxxxxxx0", "")
|
||||
sw(0)
|
||||
rl.dispatch_key_action(ActionNumericArgumentDigit1)
|
||||
test(ActionNumericArgumentDigitMinus, "xxxxxxxxxx0-", "")
|
||||
sw(0)
|
||||
rl.dispatch_key_action(ActionNumericArgumentDigit1)
|
||||
sw(1)
|
||||
rl.dispatch_key_action(ActionNumericArgumentDigit1)
|
||||
sw(11)
|
||||
test(ActionCursorLeft, "x", "xxxxxxxxx0-")
|
||||
sw(0)
|
||||
}
|
||||
|
||||
func TestHistory(t *testing.T) {
|
||||
lp, _ := loop.New()
|
||||
rl := New(lp, RlInit{Prompt: "$$ "})
|
||||
|
||||
@ -73,6 +73,18 @@ const (
|
||||
|
||||
ActionYank
|
||||
ActionPopYank
|
||||
|
||||
ActionNumericArgumentDigit0
|
||||
ActionNumericArgumentDigit1
|
||||
ActionNumericArgumentDigit2
|
||||
ActionNumericArgumentDigit3
|
||||
ActionNumericArgumentDigit4
|
||||
ActionNumericArgumentDigit5
|
||||
ActionNumericArgumentDigit6
|
||||
ActionNumericArgumentDigit7
|
||||
ActionNumericArgumentDigit8
|
||||
ActionNumericArgumentDigit9
|
||||
ActionNumericArgumentDigitMinus
|
||||
)
|
||||
|
||||
type kill_ring struct {
|
||||
@ -115,8 +127,8 @@ func (self *kill_ring) clear() {
|
||||
}
|
||||
|
||||
type Prompt struct {
|
||||
text string
|
||||
length int
|
||||
Text string
|
||||
Length int
|
||||
}
|
||||
|
||||
type Readline struct {
|
||||
@ -140,6 +152,9 @@ type Readline struct {
|
||||
bracketed_paste_buffer strings.Builder
|
||||
last_action Action
|
||||
history_matches *HistoryMatches
|
||||
keyboard_state KeyboardState
|
||||
fmt_ctx *markup.Context
|
||||
text_to_be_added string
|
||||
}
|
||||
|
||||
func New(loop *loop.Loop, r RlInit) *Readline {
|
||||
@ -147,10 +162,9 @@ func New(loop *loop.Loop, r RlInit) *Readline {
|
||||
if hc == 0 {
|
||||
hc = 8192
|
||||
}
|
||||
c := markup.New(true)
|
||||
ans := &Readline{
|
||||
mark_prompts: !r.DontMarkPrompts,
|
||||
loop: loop, lines: []string{""}, history: NewHistory(r.HistoryPath, hc), kill_ring: kill_ring{items: list.New().Init()},
|
||||
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()},
|
||||
}
|
||||
make_prompt := func(text string, is_secondary bool) Prompt {
|
||||
if ans.mark_prompts {
|
||||
@ -160,19 +174,19 @@ func New(loop *loop.Loop, r RlInit) *Readline {
|
||||
}
|
||||
text = m + ST + text
|
||||
}
|
||||
return Prompt{text: text, length: wcswidth.Stringwidth(text)}
|
||||
return Prompt{Text: text, Length: wcswidth.Stringwidth(text)}
|
||||
}
|
||||
ans.prompt = make_prompt(r.Prompt, false)
|
||||
t := ""
|
||||
if r.ContinuationPrompt != "" || !r.EmptyContinuationPrompt {
|
||||
t = r.ContinuationPrompt
|
||||
if t == "" {
|
||||
t = c.Yellow(">") + " "
|
||||
t = ans.fmt_ctx.Yellow(">") + " "
|
||||
}
|
||||
}
|
||||
ans.continuation_prompt = make_prompt(t, true)
|
||||
ans.reverse_search_prompt = make_prompt(c.Blue("?")+": ", false)
|
||||
ans.forward_search_prompt = make_prompt(c.Cyan("/")+": ", false)
|
||||
ans.reverse_search_prompt = make_prompt(ans.fmt_ctx.Blue("?")+": ", false)
|
||||
ans.forward_search_prompt = make_prompt(ans.fmt_ctx.Cyan("/")+": ", false)
|
||||
return ans
|
||||
}
|
||||
|
||||
@ -244,9 +258,8 @@ func (self *Readline) OnText(text string, from_key_event bool, in_bracketed_past
|
||||
text = self.bracketed_paste_buffer.String()
|
||||
self.bracketed_paste_buffer.Reset()
|
||||
}
|
||||
self.add_text(text)
|
||||
self.last_action = ActionAddText
|
||||
return nil
|
||||
self.text_to_be_added = text
|
||||
return self.dispatch_key_action(ActionAddText)
|
||||
}
|
||||
|
||||
func (self *Readline) TextBeforeCursor() string {
|
||||
|
||||
@ -25,12 +25,22 @@ func (self *Readline) update_current_screen_size() {
|
||||
}
|
||||
|
||||
type ScreenLine struct {
|
||||
ParentLineNumber, OffsetInParentLine, PromptLen int
|
||||
TextLengthInCells, CursorCell, CursorTextPos int
|
||||
Text string
|
||||
ParentLineNumber, OffsetInParentLine int
|
||||
Prompt Prompt
|
||||
TextLengthInCells, CursorCell, CursorTextPos int
|
||||
Text string
|
||||
}
|
||||
|
||||
func (self *Readline) format_arg_prompt(cna string) string {
|
||||
return fmt.Sprintf("(arg: %s) ", self.fmt_ctx.Yellow(cna))
|
||||
}
|
||||
|
||||
func (self *Readline) prompt_for_line_number(i int) Prompt {
|
||||
is_line_with_cursor := i == self.cursor.Y
|
||||
if is_line_with_cursor && self.keyboard_state.current_numeric_argument != "" {
|
||||
text := self.format_arg_prompt(self.keyboard_state.current_numeric_argument)
|
||||
return Prompt{Text: text, Length: wcswidth.Stringwidth(text)}
|
||||
}
|
||||
if i == 0 {
|
||||
return self.prompt
|
||||
}
|
||||
@ -45,26 +55,26 @@ func (self *Readline) get_screen_lines() []*ScreenLine {
|
||||
found_cursor := false
|
||||
cursor_at_start_of_next_line := false
|
||||
for i, line := range self.lines {
|
||||
plen := self.prompt_for_line_number(i).length
|
||||
prompt := self.prompt_for_line_number(i)
|
||||
offset := 0
|
||||
has_cursor := i == self.cursor.Y
|
||||
for is_first := true; is_first || offset < len(line); is_first = false {
|
||||
l, width := wcswidth.TruncateToVisualLengthWithWidth(line[offset:], self.screen_width-plen)
|
||||
l, width := wcswidth.TruncateToVisualLengthWithWidth(line[offset:], self.screen_width-prompt.Length)
|
||||
sl := ScreenLine{
|
||||
ParentLineNumber: i, OffsetInParentLine: offset,
|
||||
PromptLen: plen, TextLengthInCells: width,
|
||||
Prompt: prompt, TextLengthInCells: width,
|
||||
CursorCell: -1, Text: l, CursorTextPos: -1,
|
||||
}
|
||||
if cursor_at_start_of_next_line {
|
||||
cursor_at_start_of_next_line = false
|
||||
sl.CursorCell = plen
|
||||
sl.CursorCell = prompt.Length
|
||||
sl.CursorTextPos = 0
|
||||
}
|
||||
ans = append(ans, &sl)
|
||||
if has_cursor && !found_cursor && offset <= self.cursor.X && self.cursor.X <= offset+len(l) {
|
||||
found_cursor = true
|
||||
ctpos := self.cursor.X - offset
|
||||
ccell := plen + wcswidth.Stringwidth(l[:ctpos])
|
||||
ccell := prompt.Length + wcswidth.Stringwidth(l[:ctpos])
|
||||
if ccell >= self.screen_width {
|
||||
if offset+len(l) < len(line) || i < len(self.lines)-1 {
|
||||
cursor_at_start_of_next_line = true
|
||||
@ -76,7 +86,7 @@ func (self *Readline) get_screen_lines() []*ScreenLine {
|
||||
sl.CursorCell = ccell
|
||||
}
|
||||
}
|
||||
plen = 0
|
||||
prompt = Prompt{}
|
||||
offset += len(l)
|
||||
}
|
||||
}
|
||||
@ -104,8 +114,8 @@ func (self *Readline) redraw() {
|
||||
if i > 0 {
|
||||
self.loop.QueueWriteString("\n")
|
||||
}
|
||||
if sl.PromptLen > 0 {
|
||||
self.loop.QueueWriteString(self.prompt_for_line_number(i).text)
|
||||
if sl.Prompt.Length > 0 {
|
||||
self.loop.QueueWriteString(self.prompt_for_line_number(i).Text)
|
||||
}
|
||||
self.loop.QueueWriteString(sl.Text)
|
||||
if sl.CursorCell > -1 {
|
||||
|
||||
@ -5,61 +5,118 @@ package readline
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"kitty/tools/tui/loop"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
var default_shortcuts = map[string]Action{
|
||||
"backspace": ActionBackspace,
|
||||
"ctrl+h": ActionBackspace,
|
||||
"delete": ActionDelete,
|
||||
|
||||
"home": ActionMoveToStartOfLine,
|
||||
"ctrl+a": ActionMoveToStartOfLine,
|
||||
|
||||
"end": ActionMoveToEndOfLine,
|
||||
"ctrl+e": ActionMoveToEndOfLine,
|
||||
|
||||
"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,
|
||||
"ctrl+f": ActionCursorRight,
|
||||
|
||||
"ctrl+l": ActionClearScreen,
|
||||
|
||||
"ctrl+d": ActionEndInput,
|
||||
"enter": ActionAcceptInput,
|
||||
|
||||
"ctrl+k": ActionKillToEndOfLine,
|
||||
"ctrl+x": ActionKillToStartOfLine,
|
||||
"ctrl+u": ActionKillToStartOfLine,
|
||||
"alt+d": ActionKillNextWord,
|
||||
"alt+backspace": ActionKillPreviousWord,
|
||||
"ctrl+w": ActionKillPreviousSpaceDelimitedWord,
|
||||
"ctrl+y": ActionYank,
|
||||
"alt+y": ActionPopYank,
|
||||
|
||||
"up": ActionHistoryPreviousOrCursorUp,
|
||||
"down": ActionHistoryNextOrCursorDown,
|
||||
"ctrl+p": ActionHistoryPrevious,
|
||||
"ctrl+n": ActionHistoryNext,
|
||||
"alt+<": ActionHistoryFirst,
|
||||
"alt+>": ActionHistoryLast,
|
||||
|
||||
"ctrl+c": ActionAbortCurrentLine,
|
||||
type ShortcutMap struct {
|
||||
leaves map[string]Action
|
||||
children map[string]*ShortcutMap
|
||||
}
|
||||
|
||||
func action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action {
|
||||
type KeyboardState struct {
|
||||
shortcut_maps []*ShortcutMap
|
||||
current_pending_keys []string
|
||||
current_numeric_argument string
|
||||
}
|
||||
|
||||
func (self *ShortcutMap) add(ac Action, base string, keys ...string) error {
|
||||
items := []string{base}
|
||||
items = append(items, keys...)
|
||||
sm := self
|
||||
for i, key := range items {
|
||||
if i == len(items)-1 {
|
||||
if sm.children[key] != nil {
|
||||
return fmt.Errorf("The shortcut %s conflicts with another multi-key shortcut", strings.Join(items, " > "))
|
||||
}
|
||||
sm.leaves[key] = ac
|
||||
} else {
|
||||
if _, found := sm.leaves[key]; found {
|
||||
return fmt.Errorf("The shortcut %s conflicts with another multi-key shortcut", strings.Join(items, " > "))
|
||||
}
|
||||
q := sm.children[key]
|
||||
if q == nil {
|
||||
q = &ShortcutMap{leaves: map[string]Action{}, children: map[string]*ShortcutMap{}}
|
||||
sm.children[key] = q
|
||||
}
|
||||
sm = q
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _default_shortcuts *ShortcutMap
|
||||
|
||||
func default_shortcuts() *ShortcutMap {
|
||||
if _default_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(ActionDelete, "delete")
|
||||
|
||||
sm.add(ActionMoveToStartOfLine, "home")
|
||||
sm.add(ActionMoveToStartOfLine, "ctrl+a")
|
||||
|
||||
sm.add(ActionMoveToEndOfLine, "end")
|
||||
sm.add(ActionMoveToEndOfLine, "ctrl+e")
|
||||
|
||||
sm.add(ActionMoveToStartOfDocument, "ctrl+home")
|
||||
sm.add(ActionMoveToEndOfDocument, "ctrl+end")
|
||||
|
||||
sm.add(ActionMoveToEndOfWord, "alt+f")
|
||||
sm.add(ActionMoveToEndOfWord, "ctrl+right")
|
||||
sm.add(ActionMoveToStartOfWord, "ctrl+left")
|
||||
sm.add(ActionMoveToStartOfWord, "alt+b")
|
||||
|
||||
sm.add(ActionCursorLeft, "left")
|
||||
sm.add(ActionCursorLeft, "ctrl+b")
|
||||
sm.add(ActionCursorRight, "right")
|
||||
sm.add(ActionCursorRight, "ctrl+f")
|
||||
|
||||
sm.add(ActionClearScreen, "ctrl+l")
|
||||
sm.add(ActionAbortCurrentLine, "ctrl+c")
|
||||
|
||||
sm.add(ActionEndInput, "ctrl+d")
|
||||
sm.add(ActionAcceptInput, "enter")
|
||||
|
||||
sm.add(ActionKillToEndOfLine, "ctrl+k")
|
||||
sm.add(ActionKillToStartOfLine, "ctrl+x")
|
||||
sm.add(ActionKillToStartOfLine, "ctrl+u")
|
||||
sm.add(ActionKillNextWord, "alt+d")
|
||||
sm.add(ActionKillPreviousWord, "alt+backspace")
|
||||
sm.add(ActionKillPreviousSpaceDelimitedWord, "ctrl+w")
|
||||
sm.add(ActionYank, "ctrl+y")
|
||||
sm.add(ActionPopYank, "alt+y")
|
||||
|
||||
sm.add(ActionHistoryPreviousOrCursorUp, "up")
|
||||
sm.add(ActionHistoryNextOrCursorDown, "down")
|
||||
sm.add(ActionHistoryPrevious, "ctrl+p")
|
||||
sm.add(ActionHistoryNext, "ctrl+n")
|
||||
sm.add(ActionHistoryFirst, "alt+<")
|
||||
sm.add(ActionHistoryLast, "alt+>")
|
||||
|
||||
sm.add(ActionNumericArgumentDigit0, "alt+0")
|
||||
sm.add(ActionNumericArgumentDigit1, "alt+1")
|
||||
sm.add(ActionNumericArgumentDigit2, "alt+2")
|
||||
sm.add(ActionNumericArgumentDigit3, "alt+3")
|
||||
sm.add(ActionNumericArgumentDigit4, "alt+4")
|
||||
sm.add(ActionNumericArgumentDigit5, "alt+5")
|
||||
sm.add(ActionNumericArgumentDigit6, "alt+6")
|
||||
sm.add(ActionNumericArgumentDigit7, "alt+7")
|
||||
sm.add(ActionNumericArgumentDigit8, "alt+8")
|
||||
sm.add(ActionNumericArgumentDigit9, "alt+9")
|
||||
sm.add(ActionNumericArgumentDigitMinus, "alt+-")
|
||||
|
||||
_default_shortcuts = &sm
|
||||
}
|
||||
return _default_shortcuts
|
||||
}
|
||||
|
||||
func (self *Readline) action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Action {
|
||||
for sc, ac := range shortcuts {
|
||||
if event.MatchesPressOrRepeat(sc) {
|
||||
return ac
|
||||
@ -71,14 +128,67 @@ func action_for_key_event(event *loop.KeyEvent, shortcuts map[string]Action) Act
|
||||
var ErrCouldNotPerformAction = errors.New("Could not perform the specified action")
|
||||
var ErrAcceptInput = errors.New("Accept input")
|
||||
|
||||
func (self *Readline) handle_numeric_arg(ac Action) {
|
||||
t := "-"
|
||||
num := int(ac - ActionNumericArgumentDigit0)
|
||||
if num < 10 {
|
||||
t = strconv.Itoa(num)
|
||||
}
|
||||
cna := self.keyboard_state.current_numeric_argument
|
||||
if (cna == "" && t == "0") || (cna != "" && t == "-") {
|
||||
self.add_text(t)
|
||||
self.keyboard_state.current_numeric_argument = ""
|
||||
self.last_action = ActionAddText
|
||||
} else {
|
||||
self.keyboard_state.current_numeric_argument += t
|
||||
self.last_action = ac
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Readline) dispatch_key_action(ac Action) error {
|
||||
self.keyboard_state.current_pending_keys = nil
|
||||
if ActionNumericArgumentDigit0 <= ac && ac <= ActionNumericArgumentDigitMinus {
|
||||
self.handle_numeric_arg(ac)
|
||||
return nil
|
||||
}
|
||||
cna := self.keyboard_state.current_numeric_argument
|
||||
self.keyboard_state.current_numeric_argument = ""
|
||||
if cna == "" {
|
||||
cna = "1"
|
||||
}
|
||||
repeat_count, err := strconv.Atoi(cna)
|
||||
if err != nil || repeat_count <= 0 {
|
||||
repeat_count = 1
|
||||
}
|
||||
return self.perform_action(ac, uint(repeat_count))
|
||||
}
|
||||
|
||||
func (self *Readline) handle_key_event(event *loop.KeyEvent) error {
|
||||
if len(self.keyboard_state.shortcut_maps) == 0 {
|
||||
self.keyboard_state.shortcut_maps = []*ShortcutMap{default_shortcuts()}
|
||||
}
|
||||
if event.Text != "" {
|
||||
return nil
|
||||
}
|
||||
ac := action_for_key_event(event, default_shortcuts)
|
||||
if ac != ActionNil {
|
||||
event.Handled = true
|
||||
return self.perform_action(ac, 1)
|
||||
sm := self.keyboard_state.shortcut_maps[len(self.keyboard_state.shortcut_maps)-1]
|
||||
for _, pk := range self.keyboard_state.current_pending_keys {
|
||||
sm = sm.children[pk]
|
||||
}
|
||||
for k := range sm.children {
|
||||
if event.MatchesPressOrRepeat(k) {
|
||||
event.Handled = true
|
||||
if self.keyboard_state.current_pending_keys == nil {
|
||||
self.keyboard_state.current_pending_keys = []string{}
|
||||
}
|
||||
self.keyboard_state.current_pending_keys = append(self.keyboard_state.current_pending_keys, k)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for k, ac := range sm.leaves {
|
||||
if event.MatchesPressOrRepeat(k) {
|
||||
event.Handled = true
|
||||
return self.dispatch_key_action(ac)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user