summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlastimil Ovčáčík <vovcacik@github.ovcacik.org>2021-09-19 21:37:37 +0200
committerJunegunn Choi <junegunn.c@gmail.com>2021-10-03 01:39:30 +0900
commit00fb486f6a5f90e92c60600103398553297fcd1c (patch)
tree716cd242274cae5e79ac5964d544411aaef27aaa
parent4173e94c6f260a64888928eead9aab8fd0bb1f97 (diff)
[tests] Add testing of keyboard events in FullscreenRenderer.GetChar()
This contains one test case of each tcell.Key* event type that can be sent to and subsequently processed in fzf's GetChar(). The test cases describe status quo, and all of them PASS. Small function util.ToTty() was added. It is similar to util.IsTty(), but for stdout (hence the To preposition).
-rw-r--r--src/tui/tcell.go11
-rw-r--r--src/tui/tcell_test.go386
-rw-r--r--src/util/util.go7
3 files changed, 400 insertions, 4 deletions
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 52bb6b68..f6bd05d8 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -233,6 +233,7 @@ func (r *FullscreenRenderer) GetChar() Event {
return EventType(CtrlA.Int() - 'a' + int(r)).AsEvent()
}
switch ev.Key() {
+ // section 1: Ctrl+(Alt)+[a-z]
case tcell.KeyCtrlA:
return keyfn('a')
case tcell.KeyCtrlB:
@@ -285,6 +286,7 @@ func (r *FullscreenRenderer) GetChar() Event {
return keyfn('y')
case tcell.KeyCtrlZ:
return keyfn('z')
+ // section 2: Ctrl+[ \]_]
case tcell.KeyCtrlSpace:
return Event{CtrlSpace, 0, nil}
case tcell.KeyCtrlBackslash:
@@ -293,12 +295,14 @@ func (r *FullscreenRenderer) GetChar() Event {
return Event{CtrlRightBracket, 0, nil}
case tcell.KeyCtrlUnderscore:
return Event{CtrlSlash, 0, nil}
+ // section 3: (Alt)+Backspace2
case tcell.KeyBackspace2:
if alt {
return Event{AltBS, 0, nil}
}
return Event{BSpace, 0, nil}
+ // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
case tcell.KeyUp:
if altShift {
return Event{AltSUp, 0, nil}
@@ -344,6 +348,7 @@ func (r *FullscreenRenderer) GetChar() Event {
}
return Event{Right, 0, nil}
+ // section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
case tcell.KeyInsert:
return Event{Insert, 0, nil}
case tcell.KeyHome:
@@ -356,10 +361,8 @@ func (r *FullscreenRenderer) GetChar() Event {
return Event{PgUp, 0, nil}
case tcell.KeyPgDn:
return Event{PgDn, 0, nil}
-
case tcell.KeyBacktab:
return Event{BTab, 0, nil}
-
case tcell.KeyF1:
return Event{F1, 0, nil}
case tcell.KeyF2:
@@ -385,6 +388,7 @@ func (r *FullscreenRenderer) GetChar() Event {
case tcell.KeyF12:
return Event{F12, 0, nil}
+ // section 6: (Alt)+'rune'
case tcell.KeyRune:
r := ev.Rune()
if alt {
@@ -392,12 +396,13 @@ func (r *FullscreenRenderer) GetChar() Event {
}
return Event{Rune, r, nil}
+ // section 7: Esc
case tcell.KeyEsc:
return Event{ESC, 0, nil}
-
}
}
+ // section 8: Invalid
return Event{Invalid, 0, nil}
}
diff --git a/src/tui/tcell_test.go b/src/tui/tcell_test.go
new file mode 100644
index 00000000..84501b87
--- /dev/null
+++ b/src/tui/tcell_test.go
@@ -0,0 +1,386 @@
+// +build tcell windows
+
+package tui
+
+import (
+ "testing"
+
+ "github.com/gdamore/tcell"
+ "github.com/junegunn/fzf/src/util"
+)
+
+func assert(t *testing.T, context string, got interface{}, want interface{}) bool {
+ if got == want {
+ return true
+ } else {
+ t.Errorf("%s = (%T)%v, want (%T)%v", context, got, got, want, want)
+ return false
+ }
+}
+
+// Test the handling of the tcell keyboard events.
+func TestGetCharEventKey(t *testing.T) {
+ if util.ToTty() {
+ // This test is skipped when output goes to terminal, because it causes
+ // some glitches:
+ // - output lines may not start at the beginning of a row which makes
+ // the output unreadable
+ // - terminal may get cleared which prevents you from seeing results of
+ // previous tests
+ // Good ways to prevent the glitches are piping the output to a pager
+ // or redirecting to a file. I've found `less +G` to be trouble-free.
+ t.Skip("Skipped because this test misbehaves in terminal, pipe to a pager or redirect output to a file to run it safely.")
+ } else if testing.Verbose() {
+ // I have observed a behaviour when this test outputted more than 8192
+ // bytes (32*256) into the 'less' pager, both the go's test executable
+ // and the pager hanged. The go's executable was blocking on printing.
+ // I was able to create minimal working example of that behaviour, but
+ // that example hanged after 12256 bytes (32*(256+127)).
+ t.Log("If you are piping this test to a pager and it hangs, make the pager greedy for input, e.g. 'less +G'.")
+ }
+
+ if !HasFullscreenRenderer() {
+ t.Skip("Can't test FullscreenRenderer.")
+ }
+
+ // construct test cases
+ type giveKey struct {
+ Type tcell.Key
+ Char rune
+ Mods tcell.ModMask
+ }
+ type wantKey = Event
+ type testCase struct {
+ giveKey
+ wantKey
+ }
+ /*
+ Some test cases are marked "fabricated". It means that giveKey value
+ is valid, but it is not what you get when you press the keys. For
+ example Ctrl+C will NOT give you tcell.KeyCtrlC, but tcell.KeyETX
+ (End-Of-Text character, causing SIGINT).
+ I was trying to accompany the fabricated test cases with real ones.
+
+ Some test cases are marked "unhandled". It means that giveKey.Type
+ is not present in tcell.go source code. It can still be handled via
+ implicit or explicit alias.
+
+ If not said otherwise, test cases are for US keyboard.
+
+ (tabstop=44)
+ */
+ tests := []testCase{
+
+ // section 1: Ctrl+(Alt)+[a-z]
+ {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl}, wantKey{CtrlA, 0, nil}},
+ {giveKey{tcell.KeyCtrlC, rune(tcell.KeyCtrlC), tcell.ModCtrl}, wantKey{CtrlC, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyETX, rune(tcell.KeyETX), tcell.ModCtrl}, wantKey{CtrlC, 0, nil}}, // this is SIGINT (Ctrl+C)
+ {giveKey{tcell.KeyCtrlZ, rune(tcell.KeyCtrlZ), tcell.ModCtrl}, wantKey{CtrlZ, 0, nil}}, // fabricated
+ // KeyTab is alias for KeyTAB
+ {giveKey{tcell.KeyCtrlI, rune(tcell.KeyCtrlI), tcell.ModCtrl}, wantKey{Tab, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyTab, rune(tcell.KeyTab), tcell.ModNone}, wantKey{Tab, 0, nil}}, // unhandled, actual "Tab" keystroke
+ {giveKey{tcell.KeyTAB, rune(tcell.KeyTAB), tcell.ModNone}, wantKey{Tab, 0, nil}}, // fabricated, unhandled
+ // KeyEnter is alias for KeyCR
+ {giveKey{tcell.KeyCtrlM, rune(tcell.KeyCtrlM), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // actual "Enter" keystroke
+ {giveKey{tcell.KeyCR, rune(tcell.KeyCR), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled
+ // Ctrl+Alt keys
+ {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'a', nil}}, // fabricated
+ {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'a', nil}}, // fabricated
+
+ /*
+ "Input method" in Windows Language options:
+ US: "US Keyboard" does not generate any characters (and thus any events) in Ctrl+Alt+[a-z] range
+ CS: "Czech keyboard"
+ DE: "German keyboard"
+
+ Note that right Alt is not just `tcell.ModAlt` on foreign language keyboards, but it is the AltGr `tcell.ModCtrl|tcell.ModAlt`.
+ */
+ {giveKey{tcell.KeyRune, '{', tcell.ModCtrl | tcell.ModAlt}, wantKey{Alt, '{', nil}}, // CS: Ctrl+Alt+b = "{"
+ {giveKey{tcell.KeyRune, '{', tcell.ModCtrl | tcell.ModAlt}, wantKey{Alt, '{', nil}}, // DE: Ctrl+Alt+7 = "{"
+ {giveKey{tcell.KeyRune, '@', tcell.ModCtrl | tcell.ModAlt}, wantKey{Alt, '@', nil}}, // DE: Ctrl+Alt+q = "@"
+ {giveKey{tcell.KeyRune, 'µ', tcell.ModCtrl | tcell.ModAlt}, wantKey{Alt, 'µ', nil}}, // DE: Ctrl+Alt+m = "µ"
+
+ // section 2: Ctrl+[ \]_]
+ {giveKey{tcell.KeyCtrlSpace, rune(tcell.KeyCtrlSpace), tcell.ModCtrl}, wantKey{CtrlSpace, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyNUL, rune(tcell.KeyNUL), tcell.ModNone}, wantKey{CtrlSpace, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyRune, ' ', tcell.ModCtrl}, wantKey{Rune, ' ', nil}}, // actual Ctrl+' '
+ {giveKey{tcell.KeyCtrlBackslash, rune(tcell.KeyCtrlBackslash), tcell.ModCtrl}, wantKey{CtrlBackSlash, 0, nil}},
+ {giveKey{tcell.KeyCtrlRightSq, rune(tcell.KeyCtrlRightSq), tcell.ModCtrl}, wantKey{CtrlRightBracket, 0, nil}},
+ {giveKey{tcell.KeyCtrlUnderscore, rune(tcell.KeyCtrlUnderscore), tcell.ModShift | tcell.ModCtrl}, wantKey{CtrlSlash, 0, nil}},
+
+ // section 3: (Alt)+Backspace2
+ // KeyBackspace2 is alias for KeyDEL = 0x7F (ASCII) (allegedly unused by Windows)
+ // KeyDelete = 0x2E (VK_DELETE constant in Windows)
+ // KeyBackspace is alias for KeyBS = 0x08 (ASCII) (implicit alias with KeyCtrlH)
+ {giveKey{tcell.KeyBackspace2, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyBackspace2, 0, tcell.ModAlt}, wantKey{AltBS, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyDEL, 0, tcell.ModNone}, wantKey{BSpace, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyDelete, 0, tcell.ModNone}, wantKey{Del, 0, nil}},
+ {giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{Del, 0, nil}},
+ {giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{CtrlH, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{CtrlH, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{CtrlH, 0, nil}}, // actual "Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // actual "Alt+Backspace" keystroke
+ {giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{BSpace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Shift+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // actual "Ctrl+Alt+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // actual "Shift+Alt+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'h', nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
+ {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke
+
+ // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
+ {giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},
+ {giveKey{tcell.KeyDown, 0, tcell.ModAlt}, wantKey{AltDown, 0, nil}},
+ {giveKey{tcell.KeyLeft, 0, tcell.ModShift}, wantKey{SLeft, 0, nil}},
+ {giveKey{tcell.KeyRight, 0, tcell.ModShift | tcell.ModAlt}, wantKey{AltSRight, 0, nil}},
+ {giveKey{tcell.KeyUpLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyUpRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyDownLeft, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyDownRight, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyCenter, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ // section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
+ {giveKey{tcell.KeyInsert, 0, tcell.ModNone}, wantKey{Insert, 0, nil}},
+ {giveKey{tcell.KeyF1, 0, tcell.ModNone}, wantKey{F1, 0, nil}},
+ // section 6: (Alt)+'rune'
+ {giveKey{tcell.KeyRune, 'a', tcell.ModNone}, wantKey{Rune, 'a', nil}},
+ {giveKey{tcell.KeyRune, 'a', tcell.ModAlt}, wantKey{Alt, 'a', nil}},
+ {giveKey{tcell.KeyRune, 'A', tcell.ModAlt}, wantKey{Alt, 'A', nil}},
+ {giveKey{tcell.KeyRune, '`', tcell.ModAlt}, wantKey{Alt, '`', nil}},
+ // section 7: Esc
+ // KeyEsc and KeyEscape are aliases for KeyESC
+ {giveKey{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated
+ {giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModNone}, wantKey{ESC, 0, nil}}, // unhandled
+ {giveKey{tcell.KeyEscape, rune(tcell.KeyEscape), tcell.ModNone}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyESC, rune(tcell.KeyESC), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // actual Ctrl+[ keystroke
+ {giveKey{tcell.KeyCtrlLeftSq, rune(tcell.KeyCtrlLeftSq), tcell.ModCtrl}, wantKey{ESC, 0, nil}}, // fabricated, unhandled
+
+ // section 8: Invalid
+ {giveKey{tcell.KeyRune, 'a', tcell.ModMeta}, wantKey{Rune, 'a', nil}}, // fabricated
+ {giveKey{tcell.KeyF24, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}},
+ {giveKey{tcell.KeyCtrlCarat, rune(tcell.KeyCtrlCarat), tcell.ModShift | tcell.ModCtrl}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyRS, rune(tcell.KeyRS), tcell.ModShift | tcell.ModCtrl}, wantKey{Invalid, 0, nil}}, // actual Ctrl+Shift+6 (i.e. Ctrl+^) keystroke
+ {giveKey{tcell.KeyHelp, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyExit, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyClear, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // unhandled, actual keystroke Numpad_5 with Numlock OFF
+ {giveKey{tcell.KeyCancel, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyPrint, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
+ {giveKey{tcell.KeyPause, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // unhandled
+
+ }
+ r := NewFullscreenRenderer(&ColorTheme{}, false, false)
+ r.Init()
+
+ // run and evaluate the tests
+ for _, test := range tests {
+ // generate key event
+ giveEvent := tcell.NewEventKey(test.giveKey.Type, test.giveKey.Char, test.giveKey.Mods)
+ _screen.PostEventWait(giveEvent)
+ t.Logf("giveEvent = %T{key: %v, ch: %q (%[3]v), mod: %#04b}\n", giveEvent, giveEvent.Key(), giveEvent.Rune(), giveEvent.Modifiers())
+
+ // process the event in fzf and evaluate the test
+ gotEvent := r.GetChar()
+ // skip Resize events, those are sometimes put in the buffer outside of this test
+ for gotEvent.Type == Resize {
+ t.Logf("Resize swallowed")
+ gotEvent = r.GetChar()
+ }
+ t.Logf("wantEvent = %T{Type: %v, Char: %q (%[3]v)}\n", test.wantKey, test.wantKey.Type, test.wantKey.Char)
+ t.Logf("gotEvent = %T{Type: %v, Char: %q (%[3]v)}\n", gotEvent, gotEvent.Type, gotEvent.Char)
+
+ assert(t, "r.GetChar().Type", gotEvent.Type, test.wantKey.Type)
+ assert(t, "r.GetChar().Char", gotEvent.Char, test.wantKey.Char)
+ }
+
+ r.Close()
+}
+
+/*
+Quick reference
+---------------
+
+(tabstop=18)
+(this is not mapping table, it merely puts multiple constants ranges in one table)
+
+¹) the two columns are each other implicit alias
+²) explicit aliases here
+
+%v section # tcell ctrl key¹ tcell ctrl char¹ tcell alias² tui constants tcell named keys tcell mods
+-- --------- -------------- --------------- ----------- ------------- ---------------- ----------
+0 2 KeyCtrlSpace KeyNUL = ^@ Rune ModNone
+1 1 KeyCtrlA KeySOH = ^A CtrlA ModShift
+2 1 KeyCtrlB KeySTX = ^B CtrlB ModCtrl
+3 1 KeyCtrlC KeyETX = ^C CtrlC
+4 1 KeyCtrlD KeyEOT = ^D CtrlD ModAlt
+5 1 KeyCtrlE KeyENQ = ^E CtrlE
+6 1 KeyCtrlF KeyACK = ^F CtrlF
+7 1 KeyCtrlG KeyBEL = ^G CtrlG
+8 1 KeyCtrlH KeyBS = ^H KeyBackspace CtrlH ModMeta
+9 1 KeyCtrlI KeyTAB = ^I KeyTab Tab
+10 1 KeyCtrlJ KeyLF = ^J CtrlJ
+11 1 KeyCtrlK KeyVT = ^K CtrlK
+12 1 KeyCtrlL KeyFF = ^L CtrlL
+13 1 KeyCtrlM KeyCR = ^M KeyEnter CtrlM
+14 1 KeyCtrlN KeySO = ^N CtrlN
+15 1 KeyCtrlO KeySI = ^O CtrlO
+16 1 KeyCtrlP KeyDLE = ^P CtrlP
+17 1 KeyCtrlQ KeyDC1 = ^Q CtrlQ
+18 1 KeyCtrlR KeyDC2 = ^R CtrlR
+19 1 KeyCtrlS KeyDC3 = ^S CtrlS
+20 1 KeyCtrlT KeyDC4 = ^T CtrlT
+21 1 KeyCtrlU KeyNAK = ^U CtrlU
+22 1 KeyCtrlV KeySYN = ^V CtrlV
+23 1 KeyCtrlW KeyETB = ^W CtrlW
+24 1 KeyCtrlX KeyCAN = ^X CtrlX
+25 1 KeyCtrlY KeyEM = ^Y CtrlY
+26 1 KeyCtrlZ KeySUB = ^Z CtrlZ
+27 7 KeyCtrlLeftSq KeyESC = ^[ KeyEsc, KeyEscape ESC
+28 2 KeyCtrlBackslash KeyFS = ^\ CtrlSpace
+29 2 KeyCtrlRightSq KeyGS = ^] CtrlBackSlash
+30 8 KeyCtrlCarat KeyRS = ^^ CtrlRightBracket
+31 2 KeyCtrlUnderscore KeyUS = ^_ CtrlCaret
+32 CtrlSlash
+33 Invalid
+34 Resize
+35 Mouse
+36 DoubleClick
+37 LeftClick
+38 RightClick
+39 BTab
+40 BSpace
+41 Del
+42 PgUp
+43 PgDn
+44 Up
+45 Down
+46 Left
+47 Right
+48 Home
+49 End
+50 Insert
+51 SUp
+52 SDown
+53 SLeft
+54 SRight
+55 F1
+56 F2
+57 F3
+58 F4
+59 F5
+60 F6
+61 F7
+62 F8
+63 F9
+64 F10
+65 F11
+66 F12
+67 Change
+68 BackwardEOF
+69 AltBS
+70 AltUp
+71 AltDown
+72 AltLeft
+73 AltRight
+74 AltSUp
+75 AltSDown
+76 AltSLeft
+77 AltSRight
+78 Alt
+79 CtrlAlt
+..
+127 3 KeyDEL KeyBackspace2
+..
+256 6 KeyRune
+257 4 KeyUp
+258 4 KeyDown
+259 4 KeyRight
+260 4 KeyLeft
+261 8 KeyUpLeft
+262 8 KeyUpRight
+263 8 KeyDownLeft
+264 8 KeyDownRight
+265 8 KeyCenter
+266 5 KeyPgUp
+267 5 KeyPgDn
+268 5 KeyHome
+269 5 KeyEnd
+270 5 KeyInsert
+271 5 KeyDelete
+272 8 KeyHelp
+273 8 KeyExit
+274 8 KeyClear
+275 8 KeyCancel
+276 8 KeyPrint
+277 8 KeyPause
+278 5 KeyBacktab
+279 5 KeyF1
+280 5 KeyF2
+281 5 KeyF3
+282 5 KeyF4
+283 5 KeyF5
+284 5 KeyF6
+285 5 KeyF7
+286 5 KeyF8
+287 5 KeyF9
+288 5 KeyF10
+289 5 KeyF11
+290 5 KeyF12
+291 8 KeyF13
+292 8 KeyF14
+293 8 KeyF15
+294 8 KeyF16
+295 8 KeyF17
+296 8 KeyF18
+297 8 KeyF19
+298 8 KeyF20
+299 8 KeyF21
+300 8 KeyF22
+301 8 KeyF23
+302 8 KeyF24
+303 8 KeyF25
+304 8 KeyF26
+305 8 KeyF27
+306 8 KeyF28
+307 8 KeyF29
+308 8 KeyF30
+309 8 KeyF31
+310 8 KeyF32
+311 8 KeyF33
+312 8 KeyF34
+313 8 KeyF35
+314 8 KeyF36
+315 8 KeyF37
+316 8 KeyF38
+317 8 KeyF39
+318 8 KeyF40
+319 8 KeyF41
+320 8 KeyF42
+321 8 KeyF43
+322 8 KeyF44
+323 8 KeyF45
+324 8 KeyF46
+325 8 KeyF47
+326 8 KeyF48
+327 8 KeyF49
+328 8 KeyF50
+329 8 KeyF51
+330 8 KeyF52
+331 8 KeyF53
+332 8 KeyF54
+333 8 KeyF55
+334 8 KeyF56
+335 8 KeyF57
+336 8 KeyF58
+337 8 KeyF59
+338 8 KeyF60
+339 8 KeyF61
+340 8 KeyF62
+341 8 KeyF63
+342 8 KeyF64
+-- --------- -------------- --------------- ----------- ------------- ---------------- ----------
+%v section # tcell ctrl key tcell ctrl char tcell alias tui constants tcell named keys tcell mods
+*/
diff --git a/src/util/util.go b/src/util/util.go
index b3c32970..c16f1af0 100644
--- a/src/util/util.go
+++ b/src/util/util.go
@@ -117,11 +117,16 @@ func DurWithin(
return val
}
-// IsTty returns true is stdin is a terminal
+// IsTty returns true if stdin is a terminal
func IsTty() bool {
return isatty.IsTerminal(os.Stdin.Fd())
}
+// ToTty returns true if stdout is a terminal
+func ToTty() bool {
+ return isatty.IsTerminal(os.Stdout.Fd())
+}
+
// Once returns a function that returns the specified boolean value only once
func Once(nextResponse bool) func() bool {
state := nextResponse