From fd1ba46f77532b4bc9b6af00db9dc8ecdf6e2b3f Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 13 Apr 2024 14:00:16 +0900 Subject: [PATCH] Export $FZF_KEY environment variable to child processes It's the name of the last key pressed. Related #3412 --- CHANGELOG.md | 22 +++++++ man/man1/fzf.1 | 29 ++++----- src/options.go | 40 ++++++------ src/terminal.go | 36 +++++------ src/tui/eventtype_string.go | 120 ++++++++++++++++++++++++++++++++++++ src/tui/light.go | 65 +++++++++++-------- src/tui/tui.go | 112 +++++++++++++++++++++------------ src/util/util.go | 12 ++++ 8 files changed, 315 insertions(+), 121 deletions(-) create mode 100644 src/tui/eventtype_string.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d6c9fb..dffc16c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,28 @@ CHANGELOG ========= +0.50.0 +------ +- Added `jump` and `jump-cancel` events that are triggered when leaving `jump` mode + ```sh + # Default behavior + fzf --bind space:jump + + # Same as jump-accept action + fzf --bind space:jump,jump:accept + + # Accept on jump, abort on cancel + fzf --bind space:jump,jump:accept,jump-cancel:abort + + # Change header on jump-cancel + fzf --bind 'space:change-header(Type jump label)+jump,jump-cancel:change-header:Jump cancelled' + ``` +- Added a new environment variable `$FZF_KEY` exported to the child processes. It's the name of the last key pressed. + ```sh + fzf --bind 'space:jump,jump:accept,jump-cancel:transform:[[ $FZF_KEY =~ ctrl-c ]] && echo abort' + ``` +- Bug fixes + 0.49.0 ------ - Ingestion performance improved by around 40% (more or less depending on options) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 24048e7..fc621c3 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -191,7 +191,7 @@ actions are affected: \fBkill-word\fR .TP .BI "--jump-labels=" "CHARS" -Label characters for \fBjump\fR and \fBjump-accept\fR +Label characters for \fBjump\fR mode. .SS Layout .TP .BI "--height=" "[~]HEIGHT[%]" @@ -982,6 +982,8 @@ fzf exports the following environment variables to its child processes. .br .BR FZF_ACTION " The name of the last action performed" .br +.BR FZF_KEY " The name of the last key pressed" +.br .BR FZF_PORT " Port number when --listen option is used" .br @@ -1052,21 +1054,21 @@ e.g. .br \fIctrl-]\fR .br -\fIctrl-^\fR (\fIctrl-6\fR) +\fIctrl-^\fR (\fIctrl-6\fR) .br -\fIctrl-/\fR (\fIctrl-_\fR) +\fIctrl-/\fR (\fIctrl-_\fR) .br \fIctrl-alt-[a-z]\fR .br -\fIalt-[*]\fR (Any case-sensitive single character is allowed) +\fIalt-[*]\fR (Any case-sensitive single character is allowed) .br \fIf[1-12]\fR .br -\fIenter\fR (\fIreturn\fR \fIctrl-m\fR) +\fIenter\fR (\fIreturn\fR \fIctrl-m\fR) .br \fIspace\fR .br -\fIbspace\fR (\fIbs\fR) +\fIbackspace\fR (\fIbspace\fR \fIbs\fR) .br \fIalt-up\fR .br @@ -1080,15 +1082,15 @@ e.g. .br \fIalt-space\fR .br -\fIalt-bspace\fR (\fIalt-bs\fR) +\fIalt-backspace\fR (\fIalt-bspace\fR \fIalt-bs\fR) .br \fItab\fR .br -\fIbtab\fR (\fIshift-tab\fR) +\fIshift-tab\fR (\fIbtab\fR) .br \fIesc\fR .br -\fIdel\fR +\fIdelete\fR (\fIdel\fR) .br \fIup\fR .br @@ -1104,9 +1106,9 @@ e.g. .br \fIinsert\fR .br -\fIpgup\fR (\fIpage-up\fR) +\fIpage-up\fR (\fIpgup\fR) .br -\fIpgdn\fR (\fIpage-down\fR) +\fIpage-down\fR (\fIpgdn\fR) .br \fIshift-up\fR .br @@ -1248,7 +1250,7 @@ e.g. \fIjump\fR .RS -Triggered when successfully jumped to the target item in \fB--jump\fR mode. +Triggered when successfully jumped to the target item in \fBjump\fR mode. e.g. \fBfzf --bind space:jump,jump:accept\fR @@ -1256,7 +1258,7 @@ e.g. \fIjump-cancel\fR .RS -Triggered when \fB--jump\fR mode is cancelled. +Triggered when \fBjump\fR mode is cancelled. e.g. \fBfzf --bind space:jump,jump:accept,jump-cancel:abort\fR @@ -1304,7 +1306,6 @@ A key or an event can be bound to one or more of the following actions. \fBforward-word\fR \fIalt-f shift-right\fR \fBignore\fR \fBjump\fR (EasyMotion-like 2-keystroke movement) - \fBjump-accept\fR (jump and accept) \fBkill-line\fR \fBkill-word\fR \fIalt-d\fR \fBlast\fR (move to the last match; same as \fBpos(-1)\fR) diff --git a/src/options.go b/src/options.go index ffa3878..c4006df 100644 --- a/src/options.go +++ b/src/options.go @@ -52,7 +52,7 @@ const usage = `usage: fzf [options] --hscroll-off=COLS Number of screen columns to keep to the right of the highlighted substring (default: 10) --filepath-word Make word-wise movements respect path separators - --jump-labels=CHARS Label characters for jump and jump-accept + --jump-labels=CHARS Label characters for jump mode Layout --height=[~]HEIGHT[%] Display fzf window below the cursor with the given @@ -666,8 +666,8 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E add(tui.CtrlM) case "space": chords[tui.Key(' ')] = key - case "bspace", "bs": - add(tui.BSpace) + case "backspace", "bspace", "bs": + add(tui.Backspace) case "ctrl-space": add(tui.CtrlSpace) case "ctrl-delete": @@ -706,8 +706,8 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E chords[tui.CtrlAltKey('m')] = key case "alt-space": chords[tui.AltKey(' ')] = key - case "alt-bs", "alt-bspace": - add(tui.AltBS) + case "alt-bs", "alt-bspace", "alt-backspace": + add(tui.AltBackspace) case "alt-up": add(tui.AltUp) case "alt-down": @@ -719,11 +719,11 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E case "tab": add(tui.Tab) case "btab", "shift-tab": - add(tui.BTab) + add(tui.ShiftTab) case "esc": - add(tui.ESC) - case "del": - add(tui.Del) + add(tui.Esc) + case "delete", "del": + add(tui.Delete) case "home": add(tui.Home) case "end": @@ -731,27 +731,27 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E case "insert": add(tui.Insert) case "pgup", "page-up": - add(tui.PgUp) + add(tui.PageUp) case "pgdn", "page-down": - add(tui.PgDn) + add(tui.PageDown) case "alt-shift-up", "shift-alt-up": - add(tui.AltSUp) + add(tui.AltShiftUp) case "alt-shift-down", "shift-alt-down": - add(tui.AltSDown) + add(tui.AltShiftDown) case "alt-shift-left", "shift-alt-left": - add(tui.AltSLeft) + add(tui.AltShiftLeft) case "alt-shift-right", "shift-alt-right": - add(tui.AltSRight) + add(tui.AltShiftRight) case "shift-up": - add(tui.SUp) + add(tui.ShiftUp) case "shift-down": - add(tui.SDown) + add(tui.ShiftDown) case "shift-left": - add(tui.SLeft) + add(tui.ShiftLeft) case "shift-right": - add(tui.SRight) + add(tui.ShiftRight) case "shift-delete": - add(tui.SDelete) + add(tui.ShiftDelete) case "left-click": add(tui.LeftClick) case "right-click": diff --git a/src/terminal.go b/src/terminal.go index 5057e93..0519836 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -293,6 +293,7 @@ type Terminal struct { executing *util.AtomicBool termSize tui.TermSize lastAction actionType + lastKey string lastFocus int32 areaLines int areaColumns int @@ -408,7 +409,7 @@ const ( actOffsetUp actOffsetDown actJump - actJumpAccept + actJumpAccept // XXX Deprecated in favor of jump:accept binding actPrintQuery actRefreshPreview actReplaceQuery @@ -460,14 +461,7 @@ const ( ) func (a actionType) Name() string { - name := "" - for i, r := range a.String()[3:] { - if i > 0 && r >= 'A' && r <= 'Z' { - name += "-" - } - name += string(r) - } - return strings.ToLower(name) + return util.ToKebabCase(a.String()[3:]) } func processExecution(action actionType) bool { @@ -546,14 +540,14 @@ func defaultKeymap() map[tui.Event][]*action { add(tui.CtrlC, actAbort) add(tui.CtrlG, actAbort) add(tui.CtrlQ, actAbort) - add(tui.ESC, actAbort) + add(tui.Esc, actAbort) add(tui.CtrlD, actDeleteCharEof) add(tui.CtrlE, actEndOfLine) add(tui.CtrlF, actForwardChar) add(tui.CtrlH, actBackwardDeleteChar) - add(tui.BSpace, actBackwardDeleteChar) + add(tui.Backspace, actBackwardDeleteChar) add(tui.Tab, actToggleDown) - add(tui.BTab, actToggleUp) + add(tui.ShiftTab, actToggleUp) add(tui.CtrlJ, actDown) add(tui.CtrlK, actUp) add(tui.CtrlL, actClearScreen) @@ -568,11 +562,11 @@ func defaultKeymap() map[tui.Event][]*action { } addEvent(tui.AltKey('b'), actBackwardWord) - add(tui.SLeft, actBackwardWord) + add(tui.ShiftLeft, actBackwardWord) addEvent(tui.AltKey('f'), actForwardWord) - add(tui.SRight, actForwardWord) + add(tui.ShiftRight, actForwardWord) addEvent(tui.AltKey('d'), actKillWord) - add(tui.AltBS, actBackwardKillWord) + add(tui.AltBackspace, actBackwardKillWord) add(tui.Up, actUp) add(tui.Down, actDown) @@ -581,12 +575,12 @@ func defaultKeymap() map[tui.Event][]*action { add(tui.Home, actBeginningOfLine) add(tui.End, actEndOfLine) - add(tui.Del, actDeleteChar) - add(tui.PgUp, actPageUp) - add(tui.PgDn, actPageDown) + add(tui.Delete, actDeleteChar) + add(tui.PageUp, actPageUp) + add(tui.PageDown, actPageDown) - add(tui.SUp, actPreviewUp) - add(tui.SDown, actPreviewDown) + add(tui.ShiftUp, actPreviewUp) + add(tui.ShiftDown, actPreviewDown) add(tui.Mouse, actMouse) add(tui.LeftClick, actClick) @@ -851,6 +845,7 @@ func (t *Terminal) environ() []string { } env = append(env, "FZF_QUERY="+string(t.input)) env = append(env, "FZF_ACTION="+t.lastAction.Name()) + env = append(env, "FZF_KEY="+t.lastKey) env = append(env, "FZF_PROMPT="+string(t.promptString)) env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label) env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label) @@ -3290,6 +3285,7 @@ func (t *Terminal) Loop() { t.mutex.Lock() previousInput := t.input previousCx := t.cx + t.lastKey = event.KeyName() events := []util.EventType{} req := func(evts ...util.EventType) { for _, event := range evts { diff --git a/src/tui/eventtype_string.go b/src/tui/eventtype_string.go new file mode 100644 index 0000000..ce34d36 --- /dev/null +++ b/src/tui/eventtype_string.go @@ -0,0 +1,120 @@ +// Code generated by "stringer -type=EventType"; DO NOT EDIT. + +package tui + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Rune-0] + _ = x[CtrlA-1] + _ = x[CtrlB-2] + _ = x[CtrlC-3] + _ = x[CtrlD-4] + _ = x[CtrlE-5] + _ = x[CtrlF-6] + _ = x[CtrlG-7] + _ = x[CtrlH-8] + _ = x[Tab-9] + _ = x[CtrlJ-10] + _ = x[CtrlK-11] + _ = x[CtrlL-12] + _ = x[CtrlM-13] + _ = x[CtrlN-14] + _ = x[CtrlO-15] + _ = x[CtrlP-16] + _ = x[CtrlQ-17] + _ = x[CtrlR-18] + _ = x[CtrlS-19] + _ = x[CtrlT-20] + _ = x[CtrlU-21] + _ = x[CtrlV-22] + _ = x[CtrlW-23] + _ = x[CtrlX-24] + _ = x[CtrlY-25] + _ = x[CtrlZ-26] + _ = x[Esc-27] + _ = x[CtrlSpace-28] + _ = x[CtrlDelete-29] + _ = x[CtrlBackSlash-30] + _ = x[CtrlRightBracket-31] + _ = x[CtrlCaret-32] + _ = x[CtrlSlash-33] + _ = x[ShiftTab-34] + _ = x[Backspace-35] + _ = x[Delete-36] + _ = x[PageUp-37] + _ = x[PageDown-38] + _ = x[Up-39] + _ = x[Down-40] + _ = x[Left-41] + _ = x[Right-42] + _ = x[Home-43] + _ = x[End-44] + _ = x[Insert-45] + _ = x[ShiftUp-46] + _ = x[ShiftDown-47] + _ = x[ShiftLeft-48] + _ = x[ShiftRight-49] + _ = x[ShiftDelete-50] + _ = x[F1-51] + _ = x[F2-52] + _ = x[F3-53] + _ = x[F4-54] + _ = x[F5-55] + _ = x[F6-56] + _ = x[F7-57] + _ = x[F8-58] + _ = x[F9-59] + _ = x[F10-60] + _ = x[F11-61] + _ = x[F12-62] + _ = x[AltBackspace-63] + _ = x[AltUp-64] + _ = x[AltDown-65] + _ = x[AltLeft-66] + _ = x[AltRight-67] + _ = x[AltShiftUp-68] + _ = x[AltShiftDown-69] + _ = x[AltShiftLeft-70] + _ = x[AltShiftRight-71] + _ = x[Alt-72] + _ = x[CtrlAlt-73] + _ = x[Invalid-74] + _ = x[Mouse-75] + _ = x[DoubleClick-76] + _ = x[LeftClick-77] + _ = x[RightClick-78] + _ = x[SLeftClick-79] + _ = x[SRightClick-80] + _ = x[ScrollUp-81] + _ = x[ScrollDown-82] + _ = x[SScrollUp-83] + _ = x[SScrollDown-84] + _ = x[PreviewScrollUp-85] + _ = x[PreviewScrollDown-86] + _ = x[Resize-87] + _ = x[Change-88] + _ = x[BackwardEOF-89] + _ = x[Start-90] + _ = x[Load-91] + _ = x[Focus-92] + _ = x[One-93] + _ = x[Zero-94] + _ = x[Result-95] + _ = x[Jump-96] + _ = x[JumpCancel-97] +} + +const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLCtrlMCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancel" + +var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 458, 467, 477, 487, 498, 506, 516, 525, 536, 551, 568, 574, 580, 591, 596, 600, 605, 608, 612, 618, 622, 632} + +func (i EventType) String() string { + if i < 0 || i >= EventType(len(_EventType_index)-1) { + return "EventType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _EventType_name[_EventType_index[i]:_EventType_index[i+1]] +} diff --git a/src/tui/light.go b/src/tui/light.go index e42d398..244891a 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -245,7 +245,7 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte { } retries := 0 - if c == ESC.Int() || nonblock { + if c == Esc.Int() || nonblock { retries = r.escDelay / escPollInterval } buffer = append(buffer, byte(c)) @@ -260,7 +260,7 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte { continue } break - } else if c == ESC.Int() && pc != c { + } else if c == Esc.Int() && pc != c { retries = r.escDelay / escPollInterval } else { retries = 0 @@ -300,7 +300,7 @@ func (r *LightRenderer) GetChar() Event { case CtrlQ.Byte(): return Event{CtrlQ, 0, nil} case 127: - return Event{BSpace, 0, nil} + return Event{Backspace, 0, nil} case 0: return Event{CtrlSpace, 0, nil} case 28: @@ -311,7 +311,7 @@ func (r *LightRenderer) GetChar() Event { return Event{CtrlCaret, 0, nil} case 31: return Event{CtrlSlash, 0, nil} - case ESC.Byte(): + case Esc.Byte(): ev := r.escSequence(&sz) // Second chance if ev.Type == Invalid { @@ -327,7 +327,7 @@ func (r *LightRenderer) GetChar() Event { } char, rsz := utf8.DecodeRune(r.buffer) if char == utf8.RuneError { - return Event{ESC, 0, nil} + return Event{Esc, 0, nil} } sz = rsz return Event{Rune, char, nil} @@ -335,7 +335,7 @@ func (r *LightRenderer) GetChar() Event { func (r *LightRenderer) escSequence(sz *int) Event { if len(r.buffer) < 2 { - return Event{ESC, 0, nil} + return Event{Esc, 0, nil} } loc := offsetRegexpBegin.FindIndex(r.buffer) @@ -349,15 +349,15 @@ func (r *LightRenderer) escSequence(sz *int) Event { return CtrlAltKey(rune(r.buffer[1] + 'a' - 1)) } alt := false - if len(r.buffer) > 2 && r.buffer[1] == ESC.Byte() { + if len(r.buffer) > 2 && r.buffer[1] == Esc.Byte() { r.buffer = r.buffer[1:] alt = true } switch r.buffer[1] { - case ESC.Byte(): - return Event{ESC, 0, nil} + case Esc.Byte(): + return Event{Esc, 0, nil} case 127: - return Event{AltBS, 0, nil} + return Event{AltBackspace, 0, nil} case '[', 'O': if len(r.buffer) < 3 { return Event{Invalid, 0, nil} @@ -386,7 +386,7 @@ func (r *LightRenderer) escSequence(sz *int) Event { } return Event{Up, 0, nil} case 'Z': - return Event{BTab, 0, nil} + return Event{ShiftTab, 0, nil} case 'H': return Event{Home, 0, nil} case 'F': @@ -434,7 +434,7 @@ func (r *LightRenderer) escSequence(sz *int) Event { return Event{Invalid, 0, nil} // INS case '3': if r.buffer[3] == '~' { - return Event{Del, 0, nil} + return Event{Delete, 0, nil} } if len(r.buffer) == 6 && r.buffer[5] == '~' { *sz = 6 @@ -442,16 +442,16 @@ func (r *LightRenderer) escSequence(sz *int) Event { case '5': return Event{CtrlDelete, 0, nil} case '2': - return Event{SDelete, 0, nil} + return Event{ShiftDelete, 0, nil} } } return Event{Invalid, 0, nil} case '4': return Event{End, 0, nil} case '5': - return Event{PgUp, 0, nil} + return Event{PageUp, 0, nil} case '6': - return Event{PgDn, 0, nil} + return Event{PageDown, 0, nil} case '7': return Event{Home, 0, nil} case '8': @@ -489,16 +489,29 @@ func (r *LightRenderer) escSequence(sz *int) Event { } *sz = 6 switch r.buffer[4] { - case '1', '2', '3', '5': + case '1', '2', '3', '4', '5': + // Kitty iTerm2 WezTerm + // SHIFT-ARROW "\e[1;2D" + // ALT-SHIFT-ARROW "\e[1;4D" "\e[1;10D" "\e[1;4D" + // CTRL-SHIFT-ARROW "\e[1;6D" N/A + // CMD-SHIFT-ARROW "\e[1;10D" N/A N/A ("\e[1;2D") alt := r.buffer[4] == '3' - altShift := r.buffer[4] == '1' && r.buffer[5] == '0' char := r.buffer[5] - if altShift { + altShift := false + if r.buffer[4] == '1' && r.buffer[5] == '0' { + altShift = true if len(r.buffer) < 7 { return Event{Invalid, 0, nil} } *sz = 7 char = r.buffer[6] + } else if r.buffer[4] == '4' { + altShift = true + if len(r.buffer) < 6 { + return Event{Invalid, 0, nil} + } + *sz = 6 + char = r.buffer[5] } switch char { case 'A': @@ -506,33 +519,33 @@ func (r *LightRenderer) escSequence(sz *int) Event { return Event{AltUp, 0, nil} } if altShift { - return Event{AltSUp, 0, nil} + return Event{AltShiftUp, 0, nil} } - return Event{SUp, 0, nil} + return Event{ShiftUp, 0, nil} case 'B': if alt { return Event{AltDown, 0, nil} } if altShift { - return Event{AltSDown, 0, nil} + return Event{AltShiftDown, 0, nil} } - return Event{SDown, 0, nil} + return Event{ShiftDown, 0, nil} case 'C': if alt { return Event{AltRight, 0, nil} } if altShift { - return Event{AltSRight, 0, nil} + return Event{AltShiftRight, 0, nil} } - return Event{SRight, 0, nil} + return Event{ShiftRight, 0, nil} case 'D': if alt { return Event{AltLeft, 0, nil} } if altShift { - return Event{AltSLeft, 0, nil} + return Event{AltShiftLeft, 0, nil} } - return Event{SLeft, 0, nil} + return Event{ShiftLeft, 0, nil} } } // r.buffer[4] } // r.buffer[3] diff --git a/src/tui/tui.go b/src/tui/tui.go index 729146c..ad65e92 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -6,10 +6,13 @@ import ( "strconv" "time" + "github.com/junegunn/fzf/src/util" "github.com/rivo/uniseg" ) // Types of user action +// +//go:generate stringer -type=EventType type EventType int const ( @@ -41,7 +44,7 @@ const ( CtrlX CtrlY CtrlZ - ESC + Esc CtrlSpace CtrlDelete @@ -51,27 +54,12 @@ const ( CtrlCaret CtrlSlash - Invalid - Resize - Mouse - DoubleClick - LeftClick - RightClick - SLeftClick - SRightClick - ScrollUp - ScrollDown - SScrollUp - SScrollDown - PreviewScrollUp - PreviewScrollDown + ShiftTab + Backspace - BTab - BSpace - - Del - PgUp - PgDn + Delete + PageUp + PageDown Up Down @@ -81,11 +69,11 @@ const ( End Insert - SUp - SDown - SLeft - SRight - SDelete + ShiftUp + ShiftDown + ShiftLeft + ShiftRight + ShiftDelete F1 F2 @@ -100,6 +88,38 @@ const ( F11 F12 + AltBackspace + + AltUp + AltDown + AltLeft + AltRight + + AltShiftUp + AltShiftDown + AltShiftLeft + AltShiftRight + + Alt + CtrlAlt + + Invalid + + Mouse + DoubleClick + LeftClick + RightClick + SLeftClick + SRightClick + ScrollUp + ScrollDown + SScrollUp + SScrollDown + PreviewScrollUp + PreviewScrollDown + + // Events + Resize Change BackwardEOF Start @@ -110,21 +130,6 @@ const ( Result Jump JumpCancel - - AltBS - - AltUp - AltDown - AltLeft - AltRight - - AltSUp - AltSDown - AltSLeft - AltSRight - - Alt - CtrlAlt ) func (t EventType) AsEvent() Event { @@ -144,6 +149,31 @@ func (e Event) Comparable() Event { return Event{e.Type, e.Char, nil} } +func (e Event) KeyName() string { + if e.Type >= Invalid { + return "" + } + + switch e.Type { + case Rune: + return string(e.Char) + case Alt: + return "alt-" + string(e.Char) + case CtrlAlt: + return "ctrl-alt-" + string(e.Char) + case CtrlBackSlash: + return "ctrl-\\" + case CtrlRightBracket: + return "ctrl-]" + case CtrlCaret: + return "ctrl-^" + case CtrlSlash: + return "ctrl-/" + } + + return util.ToKebabCase(e.Type.String()) +} + func Key(r rune) Event { return Event{Rune, r, nil} } diff --git a/src/util/util.go b/src/util/util.go index 190d34d..f6e00e9 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -176,3 +176,15 @@ func RepeatToFill(str string, length int, limit int) string { } return output } + +// ToKebabCase converts the given CamelCase string to kebab-case +func ToKebabCase(s string) string { + name := "" + for i, r := range s { + if i > 0 && r >= 'A' && r <= 'Z' { + name += "-" + } + name += string(r) + } + return strings.ToLower(name) +}