doing 2.0.20 → 2.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/doing.rdoc +1 -1
- data/lib/doing/version.rb +1 -1
- data/lib/helpers/fzf/.goreleaser.yml +119 -0
- data/lib/helpers/fzf/.rubocop.yml +28 -0
- data/lib/helpers/fzf/ADVANCED.md +565 -0
- data/lib/helpers/fzf/BUILD.md +49 -0
- data/lib/helpers/fzf/CHANGELOG.md +1193 -0
- data/lib/helpers/fzf/Dockerfile +11 -0
- data/lib/helpers/fzf/LICENSE +21 -0
- data/lib/helpers/fzf/Makefile +166 -0
- data/lib/helpers/fzf/README-VIM.md +486 -0
- data/lib/helpers/fzf/README.md +712 -0
- data/lib/helpers/fzf/bin/fzf-tmux +233 -0
- data/lib/helpers/fzf/doc/fzf.txt +512 -0
- data/lib/helpers/fzf/go.mod +17 -0
- data/lib/helpers/fzf/go.sum +31 -0
- data/lib/helpers/fzf/install +382 -0
- data/lib/helpers/fzf/install.ps1 +65 -0
- data/lib/helpers/fzf/main.go +14 -0
- data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
- data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
- data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
- data/lib/helpers/fzf/shell/completion.bash +381 -0
- data/lib/helpers/fzf/shell/completion.zsh +329 -0
- data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
- data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
- data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
- data/lib/helpers/fzf/src/LICENSE +21 -0
- data/lib/helpers/fzf/src/algo/algo.go +884 -0
- data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
- data/lib/helpers/fzf/src/algo/normalize.go +492 -0
- data/lib/helpers/fzf/src/ansi.go +409 -0
- data/lib/helpers/fzf/src/ansi_test.go +427 -0
- data/lib/helpers/fzf/src/cache.go +81 -0
- data/lib/helpers/fzf/src/cache_test.go +39 -0
- data/lib/helpers/fzf/src/chunklist.go +89 -0
- data/lib/helpers/fzf/src/chunklist_test.go +80 -0
- data/lib/helpers/fzf/src/constants.go +85 -0
- data/lib/helpers/fzf/src/core.go +351 -0
- data/lib/helpers/fzf/src/history.go +96 -0
- data/lib/helpers/fzf/src/history_test.go +68 -0
- data/lib/helpers/fzf/src/item.go +44 -0
- data/lib/helpers/fzf/src/item_test.go +23 -0
- data/lib/helpers/fzf/src/matcher.go +235 -0
- data/lib/helpers/fzf/src/merger.go +120 -0
- data/lib/helpers/fzf/src/merger_test.go +88 -0
- data/lib/helpers/fzf/src/options.go +1691 -0
- data/lib/helpers/fzf/src/options_test.go +457 -0
- data/lib/helpers/fzf/src/pattern.go +425 -0
- data/lib/helpers/fzf/src/pattern_test.go +209 -0
- data/lib/helpers/fzf/src/protector/protector.go +8 -0
- data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
- data/lib/helpers/fzf/src/reader.go +201 -0
- data/lib/helpers/fzf/src/reader_test.go +63 -0
- data/lib/helpers/fzf/src/result.go +243 -0
- data/lib/helpers/fzf/src/result_others.go +16 -0
- data/lib/helpers/fzf/src/result_test.go +159 -0
- data/lib/helpers/fzf/src/result_x86.go +16 -0
- data/lib/helpers/fzf/src/terminal.go +2832 -0
- data/lib/helpers/fzf/src/terminal_test.go +638 -0
- data/lib/helpers/fzf/src/terminal_unix.go +26 -0
- data/lib/helpers/fzf/src/terminal_windows.go +45 -0
- data/lib/helpers/fzf/src/tokenizer.go +253 -0
- data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
- data/lib/helpers/fzf/src/tui/dummy.go +46 -0
- data/lib/helpers/fzf/src/tui/light.go +987 -0
- data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
- data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
- data/lib/helpers/fzf/src/tui/tcell.go +721 -0
- data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
- data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
- data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
- data/lib/helpers/fzf/src/tui/tui.go +625 -0
- data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
- data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
- data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
- data/lib/helpers/fzf/src/util/chars.go +198 -0
- data/lib/helpers/fzf/src/util/chars_test.go +46 -0
- data/lib/helpers/fzf/src/util/eventbox.go +96 -0
- data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
- data/lib/helpers/fzf/src/util/slab.go +12 -0
- data/lib/helpers/fzf/src/util/util.go +138 -0
- data/lib/helpers/fzf/src/util/util_test.go +40 -0
- data/lib/helpers/fzf/src/util/util_unix.go +47 -0
- data/lib/helpers/fzf/src/util/util_windows.go +83 -0
- data/lib/helpers/fzf/test/fzf.vader +175 -0
- data/lib/helpers/fzf/test/test_go.rb +2626 -0
- data/lib/helpers/fzf/uninstall +117 -0
- metadata +87 -1
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
// +build tcell windows
|
|
2
|
+
|
|
3
|
+
package tui
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"os"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"runtime"
|
|
10
|
+
|
|
11
|
+
"github.com/gdamore/tcell"
|
|
12
|
+
"github.com/gdamore/tcell/encoding"
|
|
13
|
+
|
|
14
|
+
"github.com/mattn/go-runewidth"
|
|
15
|
+
"github.com/rivo/uniseg"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
func HasFullscreenRenderer() bool {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func (p ColorPair) style() tcell.Style {
|
|
23
|
+
style := tcell.StyleDefault
|
|
24
|
+
return style.Foreground(tcell.Color(p.Fg())).Background(tcell.Color(p.Bg()))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Attr tcell.Style
|
|
28
|
+
|
|
29
|
+
type TcellWindow struct {
|
|
30
|
+
color bool
|
|
31
|
+
preview bool
|
|
32
|
+
top int
|
|
33
|
+
left int
|
|
34
|
+
width int
|
|
35
|
+
height int
|
|
36
|
+
normal ColorPair
|
|
37
|
+
lastX int
|
|
38
|
+
lastY int
|
|
39
|
+
moveCursor bool
|
|
40
|
+
borderStyle BorderStyle
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func (w *TcellWindow) Top() int {
|
|
44
|
+
return w.top
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (w *TcellWindow) Left() int {
|
|
48
|
+
return w.left
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (w *TcellWindow) Width() int {
|
|
52
|
+
return w.width
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func (w *TcellWindow) Height() int {
|
|
56
|
+
return w.height
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func (w *TcellWindow) Refresh() {
|
|
60
|
+
if w.moveCursor {
|
|
61
|
+
_screen.ShowCursor(w.left+w.lastX, w.top+w.lastY)
|
|
62
|
+
w.moveCursor = false
|
|
63
|
+
}
|
|
64
|
+
w.lastX = 0
|
|
65
|
+
w.lastY = 0
|
|
66
|
+
|
|
67
|
+
w.drawBorder()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func (w *TcellWindow) FinishFill() {
|
|
71
|
+
// NO-OP
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const (
|
|
75
|
+
Bold Attr = Attr(tcell.AttrBold)
|
|
76
|
+
Dim = Attr(tcell.AttrDim)
|
|
77
|
+
Blink = Attr(tcell.AttrBlink)
|
|
78
|
+
Reverse = Attr(tcell.AttrReverse)
|
|
79
|
+
Underline = Attr(tcell.AttrUnderline)
|
|
80
|
+
Italic = Attr(tcell.AttrItalic)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const (
|
|
84
|
+
AttrUndefined = Attr(0)
|
|
85
|
+
AttrRegular = Attr(1 << 7)
|
|
86
|
+
AttrClear = Attr(1 << 8)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
|
90
|
+
if _screen.Colors() >= 256 {
|
|
91
|
+
return Dark256
|
|
92
|
+
}
|
|
93
|
+
return Default16
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var (
|
|
97
|
+
_colorToAttribute = []tcell.Color{
|
|
98
|
+
tcell.ColorBlack,
|
|
99
|
+
tcell.ColorRed,
|
|
100
|
+
tcell.ColorGreen,
|
|
101
|
+
tcell.ColorYellow,
|
|
102
|
+
tcell.ColorBlue,
|
|
103
|
+
tcell.ColorDarkMagenta,
|
|
104
|
+
tcell.ColorLightCyan,
|
|
105
|
+
tcell.ColorWhite,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
func (c Color) Style() tcell.Color {
|
|
110
|
+
if c <= colDefault {
|
|
111
|
+
return tcell.ColorDefault
|
|
112
|
+
} else if c >= colBlack && c <= colWhite {
|
|
113
|
+
return _colorToAttribute[int(c)]
|
|
114
|
+
} else {
|
|
115
|
+
return tcell.Color(c)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func (a Attr) Merge(b Attr) Attr {
|
|
120
|
+
return a | b
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// handle the following as private members of FullscreenRenderer instance
|
|
124
|
+
// they are declared here to prevent introducing tcell library in non-windows builds
|
|
125
|
+
var (
|
|
126
|
+
_screen tcell.Screen
|
|
127
|
+
_prevMouseButton tcell.ButtonMask
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
func (r *FullscreenRenderer) initScreen() {
|
|
131
|
+
s, e := tcell.NewScreen()
|
|
132
|
+
if e != nil {
|
|
133
|
+
errorExit(e.Error())
|
|
134
|
+
}
|
|
135
|
+
if e = s.Init(); e != nil {
|
|
136
|
+
errorExit(e.Error())
|
|
137
|
+
}
|
|
138
|
+
if r.mouse {
|
|
139
|
+
s.EnableMouse()
|
|
140
|
+
} else {
|
|
141
|
+
s.DisableMouse()
|
|
142
|
+
}
|
|
143
|
+
_screen = s
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
func (r *FullscreenRenderer) Init() {
|
|
147
|
+
if os.Getenv("TERM") == "cygwin" {
|
|
148
|
+
os.Setenv("TERM", "")
|
|
149
|
+
}
|
|
150
|
+
encoding.Register()
|
|
151
|
+
|
|
152
|
+
r.initScreen()
|
|
153
|
+
initTheme(r.theme, r.defaultTheme(), r.forceBlack)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func (r *FullscreenRenderer) MaxX() int {
|
|
157
|
+
ncols, _ := _screen.Size()
|
|
158
|
+
return int(ncols)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func (r *FullscreenRenderer) MaxY() int {
|
|
162
|
+
_, nlines := _screen.Size()
|
|
163
|
+
return int(nlines)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func (w *TcellWindow) X() int {
|
|
167
|
+
return w.lastX
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
func (w *TcellWindow) Y() int {
|
|
171
|
+
return w.lastY
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
func (r *FullscreenRenderer) Clear() {
|
|
175
|
+
_screen.Sync()
|
|
176
|
+
_screen.Clear()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
func (r *FullscreenRenderer) Refresh() {
|
|
180
|
+
// noop
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func (r *FullscreenRenderer) GetChar() Event {
|
|
184
|
+
ev := _screen.PollEvent()
|
|
185
|
+
switch ev := ev.(type) {
|
|
186
|
+
case *tcell.EventResize:
|
|
187
|
+
return Event{Resize, 0, nil}
|
|
188
|
+
|
|
189
|
+
// process mouse events:
|
|
190
|
+
case *tcell.EventMouse:
|
|
191
|
+
// mouse down events have zeroed buttons, so we can't use them
|
|
192
|
+
// mouse up event consists of two events, 1. (main) event with modifier and other metadata, 2. event with zeroed buttons
|
|
193
|
+
// so mouse click is three consecutive events, but the first and last are indistinguishable from movement events (with released buttons)
|
|
194
|
+
// dragging has same structure, it only repeats the middle (main) event appropriately
|
|
195
|
+
x, y := ev.Position()
|
|
196
|
+
mod := ev.Modifiers() != 0
|
|
197
|
+
|
|
198
|
+
// since we dont have mouse down events (unlike LightRenderer), we need to track state in prevButton
|
|
199
|
+
prevButton, button := _prevMouseButton, ev.Buttons()
|
|
200
|
+
_prevMouseButton = button
|
|
201
|
+
drag := prevButton == button
|
|
202
|
+
|
|
203
|
+
switch {
|
|
204
|
+
case button&tcell.WheelDown != 0:
|
|
205
|
+
return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, mod}}
|
|
206
|
+
case button&tcell.WheelUp != 0:
|
|
207
|
+
return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, mod}}
|
|
208
|
+
case button&tcell.Button1 != 0 && !drag:
|
|
209
|
+
// all potential double click events put their 'line' coordinate in the clickY array
|
|
210
|
+
// double click event has two conditions, temporal and spatial, the first is checked here
|
|
211
|
+
now := time.Now()
|
|
212
|
+
if now.Sub(r.prevDownTime) < doubleClickDuration {
|
|
213
|
+
r.clickY = append(r.clickY, y)
|
|
214
|
+
} else {
|
|
215
|
+
r.clickY = []int{y}
|
|
216
|
+
}
|
|
217
|
+
r.prevDownTime = now
|
|
218
|
+
|
|
219
|
+
// detect double clicks (also check for spatial condition)
|
|
220
|
+
n := len(r.clickY)
|
|
221
|
+
double := n > 1 && r.clickY[n-2] == r.clickY[n-1]
|
|
222
|
+
if double {
|
|
223
|
+
// make sure two consecutive double clicks require four clicks
|
|
224
|
+
r.clickY = []int{}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// fire single or double click event
|
|
228
|
+
return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, mod}}
|
|
229
|
+
case button&tcell.Button2 != 0 && !drag:
|
|
230
|
+
return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, mod}}
|
|
231
|
+
case runtime.GOOS != "windows":
|
|
232
|
+
|
|
233
|
+
// double and single taps on Windows don't quite work due to
|
|
234
|
+
// the console acting on the events and not allowing us
|
|
235
|
+
// to consume them.
|
|
236
|
+
|
|
237
|
+
left := button&tcell.Button1 != 0
|
|
238
|
+
down := left || button&tcell.Button3 != 0
|
|
239
|
+
double := false
|
|
240
|
+
if down {
|
|
241
|
+
now := time.Now()
|
|
242
|
+
if !left {
|
|
243
|
+
r.clickY = []int{}
|
|
244
|
+
} else if now.Sub(r.prevDownTime) < doubleClickDuration {
|
|
245
|
+
r.clickY = append(r.clickY, x)
|
|
246
|
+
} else {
|
|
247
|
+
r.clickY = []int{x}
|
|
248
|
+
r.prevDownTime = now
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
if len(r.clickY) > 1 && r.clickY[0] == r.clickY[1] &&
|
|
252
|
+
time.Now().Sub(r.prevDownTime) < doubleClickDuration {
|
|
253
|
+
double = true
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// process keyboard:
|
|
261
|
+
case *tcell.EventKey:
|
|
262
|
+
mods := ev.Modifiers()
|
|
263
|
+
none := mods == tcell.ModNone
|
|
264
|
+
alt := (mods & tcell.ModAlt) > 0
|
|
265
|
+
ctrl := (mods & tcell.ModCtrl) > 0
|
|
266
|
+
shift := (mods & tcell.ModShift) > 0
|
|
267
|
+
ctrlAlt := ctrl && alt
|
|
268
|
+
altShift := alt && shift
|
|
269
|
+
|
|
270
|
+
keyfn := func(r rune) Event {
|
|
271
|
+
if alt {
|
|
272
|
+
return CtrlAltKey(r)
|
|
273
|
+
}
|
|
274
|
+
return EventType(CtrlA.Int() - 'a' + int(r)).AsEvent()
|
|
275
|
+
}
|
|
276
|
+
switch ev.Key() {
|
|
277
|
+
// section 1: Ctrl+(Alt)+[a-z]
|
|
278
|
+
case tcell.KeyCtrlA:
|
|
279
|
+
return keyfn('a')
|
|
280
|
+
case tcell.KeyCtrlB:
|
|
281
|
+
return keyfn('b')
|
|
282
|
+
case tcell.KeyCtrlC:
|
|
283
|
+
return keyfn('c')
|
|
284
|
+
case tcell.KeyCtrlD:
|
|
285
|
+
return keyfn('d')
|
|
286
|
+
case tcell.KeyCtrlE:
|
|
287
|
+
return keyfn('e')
|
|
288
|
+
case tcell.KeyCtrlF:
|
|
289
|
+
return keyfn('f')
|
|
290
|
+
case tcell.KeyCtrlG:
|
|
291
|
+
return keyfn('g')
|
|
292
|
+
case tcell.KeyCtrlH:
|
|
293
|
+
switch ev.Rune() {
|
|
294
|
+
case 0:
|
|
295
|
+
if ctrl {
|
|
296
|
+
return Event{BSpace, 0, nil}
|
|
297
|
+
}
|
|
298
|
+
case rune(tcell.KeyCtrlH):
|
|
299
|
+
switch {
|
|
300
|
+
case ctrl:
|
|
301
|
+
return keyfn('h')
|
|
302
|
+
case alt:
|
|
303
|
+
return Event{AltBS, 0, nil}
|
|
304
|
+
case none, shift:
|
|
305
|
+
return Event{BSpace, 0, nil}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
case tcell.KeyCtrlI:
|
|
309
|
+
return keyfn('i')
|
|
310
|
+
case tcell.KeyCtrlJ:
|
|
311
|
+
return keyfn('j')
|
|
312
|
+
case tcell.KeyCtrlK:
|
|
313
|
+
return keyfn('k')
|
|
314
|
+
case tcell.KeyCtrlL:
|
|
315
|
+
return keyfn('l')
|
|
316
|
+
case tcell.KeyCtrlM:
|
|
317
|
+
return keyfn('m')
|
|
318
|
+
case tcell.KeyCtrlN:
|
|
319
|
+
return keyfn('n')
|
|
320
|
+
case tcell.KeyCtrlO:
|
|
321
|
+
return keyfn('o')
|
|
322
|
+
case tcell.KeyCtrlP:
|
|
323
|
+
return keyfn('p')
|
|
324
|
+
case tcell.KeyCtrlQ:
|
|
325
|
+
return keyfn('q')
|
|
326
|
+
case tcell.KeyCtrlR:
|
|
327
|
+
return keyfn('r')
|
|
328
|
+
case tcell.KeyCtrlS:
|
|
329
|
+
return keyfn('s')
|
|
330
|
+
case tcell.KeyCtrlT:
|
|
331
|
+
return keyfn('t')
|
|
332
|
+
case tcell.KeyCtrlU:
|
|
333
|
+
return keyfn('u')
|
|
334
|
+
case tcell.KeyCtrlV:
|
|
335
|
+
return keyfn('v')
|
|
336
|
+
case tcell.KeyCtrlW:
|
|
337
|
+
return keyfn('w')
|
|
338
|
+
case tcell.KeyCtrlX:
|
|
339
|
+
return keyfn('x')
|
|
340
|
+
case tcell.KeyCtrlY:
|
|
341
|
+
return keyfn('y')
|
|
342
|
+
case tcell.KeyCtrlZ:
|
|
343
|
+
return keyfn('z')
|
|
344
|
+
// section 2: Ctrl+[ \]_]
|
|
345
|
+
case tcell.KeyCtrlSpace:
|
|
346
|
+
return Event{CtrlSpace, 0, nil}
|
|
347
|
+
case tcell.KeyCtrlBackslash:
|
|
348
|
+
return Event{CtrlBackSlash, 0, nil}
|
|
349
|
+
case tcell.KeyCtrlRightSq:
|
|
350
|
+
return Event{CtrlRightBracket, 0, nil}
|
|
351
|
+
case tcell.KeyCtrlCarat:
|
|
352
|
+
return Event{CtrlCaret, 0, nil}
|
|
353
|
+
case tcell.KeyCtrlUnderscore:
|
|
354
|
+
return Event{CtrlSlash, 0, nil}
|
|
355
|
+
// section 3: (Alt)+Backspace2
|
|
356
|
+
case tcell.KeyBackspace2:
|
|
357
|
+
if alt {
|
|
358
|
+
return Event{AltBS, 0, nil}
|
|
359
|
+
}
|
|
360
|
+
return Event{BSpace, 0, nil}
|
|
361
|
+
|
|
362
|
+
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
|
|
363
|
+
case tcell.KeyUp:
|
|
364
|
+
if altShift {
|
|
365
|
+
return Event{AltSUp, 0, nil}
|
|
366
|
+
}
|
|
367
|
+
if shift {
|
|
368
|
+
return Event{SUp, 0, nil}
|
|
369
|
+
}
|
|
370
|
+
if alt {
|
|
371
|
+
return Event{AltUp, 0, nil}
|
|
372
|
+
}
|
|
373
|
+
return Event{Up, 0, nil}
|
|
374
|
+
case tcell.KeyDown:
|
|
375
|
+
if altShift {
|
|
376
|
+
return Event{AltSDown, 0, nil}
|
|
377
|
+
}
|
|
378
|
+
if shift {
|
|
379
|
+
return Event{SDown, 0, nil}
|
|
380
|
+
}
|
|
381
|
+
if alt {
|
|
382
|
+
return Event{AltDown, 0, nil}
|
|
383
|
+
}
|
|
384
|
+
return Event{Down, 0, nil}
|
|
385
|
+
case tcell.KeyLeft:
|
|
386
|
+
if altShift {
|
|
387
|
+
return Event{AltSLeft, 0, nil}
|
|
388
|
+
}
|
|
389
|
+
if shift {
|
|
390
|
+
return Event{SLeft, 0, nil}
|
|
391
|
+
}
|
|
392
|
+
if alt {
|
|
393
|
+
return Event{AltLeft, 0, nil}
|
|
394
|
+
}
|
|
395
|
+
return Event{Left, 0, nil}
|
|
396
|
+
case tcell.KeyRight:
|
|
397
|
+
if altShift {
|
|
398
|
+
return Event{AltSRight, 0, nil}
|
|
399
|
+
}
|
|
400
|
+
if shift {
|
|
401
|
+
return Event{SRight, 0, nil}
|
|
402
|
+
}
|
|
403
|
+
if alt {
|
|
404
|
+
return Event{AltRight, 0, nil}
|
|
405
|
+
}
|
|
406
|
+
return Event{Right, 0, nil}
|
|
407
|
+
|
|
408
|
+
// section 5: (Insert|Home|Delete|End|PgUp|PgDn|BackTab|F1-F12)
|
|
409
|
+
case tcell.KeyInsert:
|
|
410
|
+
return Event{Insert, 0, nil}
|
|
411
|
+
case tcell.KeyHome:
|
|
412
|
+
return Event{Home, 0, nil}
|
|
413
|
+
case tcell.KeyDelete:
|
|
414
|
+
return Event{Del, 0, nil}
|
|
415
|
+
case tcell.KeyEnd:
|
|
416
|
+
return Event{End, 0, nil}
|
|
417
|
+
case tcell.KeyPgUp:
|
|
418
|
+
return Event{PgUp, 0, nil}
|
|
419
|
+
case tcell.KeyPgDn:
|
|
420
|
+
return Event{PgDn, 0, nil}
|
|
421
|
+
case tcell.KeyBacktab:
|
|
422
|
+
return Event{BTab, 0, nil}
|
|
423
|
+
case tcell.KeyF1:
|
|
424
|
+
return Event{F1, 0, nil}
|
|
425
|
+
case tcell.KeyF2:
|
|
426
|
+
return Event{F2, 0, nil}
|
|
427
|
+
case tcell.KeyF3:
|
|
428
|
+
return Event{F3, 0, nil}
|
|
429
|
+
case tcell.KeyF4:
|
|
430
|
+
return Event{F4, 0, nil}
|
|
431
|
+
case tcell.KeyF5:
|
|
432
|
+
return Event{F5, 0, nil}
|
|
433
|
+
case tcell.KeyF6:
|
|
434
|
+
return Event{F6, 0, nil}
|
|
435
|
+
case tcell.KeyF7:
|
|
436
|
+
return Event{F7, 0, nil}
|
|
437
|
+
case tcell.KeyF8:
|
|
438
|
+
return Event{F8, 0, nil}
|
|
439
|
+
case tcell.KeyF9:
|
|
440
|
+
return Event{F9, 0, nil}
|
|
441
|
+
case tcell.KeyF10:
|
|
442
|
+
return Event{F10, 0, nil}
|
|
443
|
+
case tcell.KeyF11:
|
|
444
|
+
return Event{F11, 0, nil}
|
|
445
|
+
case tcell.KeyF12:
|
|
446
|
+
return Event{F12, 0, nil}
|
|
447
|
+
|
|
448
|
+
// section 6: (Ctrl+Alt)+'rune'
|
|
449
|
+
case tcell.KeyRune:
|
|
450
|
+
r := ev.Rune()
|
|
451
|
+
|
|
452
|
+
switch {
|
|
453
|
+
// translate native key events to ascii control characters
|
|
454
|
+
case r == ' ' && ctrl:
|
|
455
|
+
return Event{CtrlSpace, 0, nil}
|
|
456
|
+
// handle AltGr characters
|
|
457
|
+
case ctrlAlt:
|
|
458
|
+
return Event{Rune, r, nil} // dropping modifiers
|
|
459
|
+
// simple characters (possibly with modifier)
|
|
460
|
+
case alt:
|
|
461
|
+
return AltKey(r)
|
|
462
|
+
default:
|
|
463
|
+
return Event{Rune, r, nil}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// section 7: Esc
|
|
467
|
+
case tcell.KeyEsc:
|
|
468
|
+
return Event{ESC, 0, nil}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// section 8: Invalid
|
|
473
|
+
return Event{Invalid, 0, nil}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
func (r *FullscreenRenderer) Pause(clear bool) {
|
|
477
|
+
if clear {
|
|
478
|
+
_screen.Fini()
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
func (r *FullscreenRenderer) Resume(clear bool, sigcont bool) {
|
|
483
|
+
if clear {
|
|
484
|
+
r.initScreen()
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
func (r *FullscreenRenderer) Close() {
|
|
489
|
+
_screen.Fini()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
|
|
493
|
+
// TODO
|
|
494
|
+
for _, w := range windows {
|
|
495
|
+
w.Refresh()
|
|
496
|
+
}
|
|
497
|
+
_screen.Show()
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
|
|
501
|
+
normal := ColNormal
|
|
502
|
+
if preview {
|
|
503
|
+
normal = ColPreview
|
|
504
|
+
}
|
|
505
|
+
return &TcellWindow{
|
|
506
|
+
color: r.theme.Colored,
|
|
507
|
+
preview: preview,
|
|
508
|
+
top: top,
|
|
509
|
+
left: left,
|
|
510
|
+
width: width,
|
|
511
|
+
height: height,
|
|
512
|
+
normal: normal,
|
|
513
|
+
borderStyle: borderStyle}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
func (w *TcellWindow) Close() {
|
|
517
|
+
// TODO
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
func fill(x, y, w, h int, n ColorPair, r rune) {
|
|
521
|
+
for ly := 0; ly <= h; ly++ {
|
|
522
|
+
for lx := 0; lx <= w; lx++ {
|
|
523
|
+
_screen.SetContent(x+lx, y+ly, r, nil, n.style())
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
func (w *TcellWindow) Erase() {
|
|
529
|
+
fill(w.left-1, w.top, w.width+1, w.height, w.normal, ' ')
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
func (w *TcellWindow) Enclose(y int, x int) bool {
|
|
533
|
+
return x >= w.left && x < (w.left+w.width) &&
|
|
534
|
+
y >= w.top && y < (w.top+w.height)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
func (w *TcellWindow) Move(y int, x int) {
|
|
538
|
+
w.lastX = x
|
|
539
|
+
w.lastY = y
|
|
540
|
+
w.moveCursor = true
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
func (w *TcellWindow) MoveAndClear(y int, x int) {
|
|
544
|
+
w.Move(y, x)
|
|
545
|
+
for i := w.lastX; i < w.width; i++ {
|
|
546
|
+
_screen.SetContent(i+w.left, w.lastY+w.top, rune(' '), nil, w.normal.style())
|
|
547
|
+
}
|
|
548
|
+
w.lastX = x
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
func (w *TcellWindow) Print(text string) {
|
|
552
|
+
w.printString(text, w.normal)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
func (w *TcellWindow) printString(text string, pair ColorPair) {
|
|
556
|
+
lx := 0
|
|
557
|
+
a := pair.Attr()
|
|
558
|
+
|
|
559
|
+
style := pair.style()
|
|
560
|
+
if a&AttrClear == 0 {
|
|
561
|
+
style = style.
|
|
562
|
+
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
|
563
|
+
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
|
564
|
+
Italic(a&Attr(tcell.AttrItalic) != 0).
|
|
565
|
+
Blink(a&Attr(tcell.AttrBlink) != 0).
|
|
566
|
+
Dim(a&Attr(tcell.AttrDim) != 0)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
gr := uniseg.NewGraphemes(text)
|
|
570
|
+
for gr.Next() {
|
|
571
|
+
rs := gr.Runes()
|
|
572
|
+
|
|
573
|
+
if len(rs) == 1 {
|
|
574
|
+
r := rs[0]
|
|
575
|
+
if r < rune(' ') { // ignore control characters
|
|
576
|
+
continue
|
|
577
|
+
} else if r == '\n' {
|
|
578
|
+
w.lastY++
|
|
579
|
+
lx = 0
|
|
580
|
+
continue
|
|
581
|
+
} else if r == '\u000D' { // skip carriage return
|
|
582
|
+
continue
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
var xPos = w.left + w.lastX + lx
|
|
586
|
+
var yPos = w.top + w.lastY
|
|
587
|
+
if xPos < (w.left+w.width) && yPos < (w.top+w.height) {
|
|
588
|
+
_screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
|
|
589
|
+
}
|
|
590
|
+
lx += runewidth.StringWidth(string(rs))
|
|
591
|
+
}
|
|
592
|
+
w.lastX += lx
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
func (w *TcellWindow) CPrint(pair ColorPair, text string) {
|
|
596
|
+
w.printString(text, pair)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
|
|
600
|
+
lx := 0
|
|
601
|
+
a := pair.Attr()
|
|
602
|
+
|
|
603
|
+
var style tcell.Style
|
|
604
|
+
if w.color {
|
|
605
|
+
style = pair.style()
|
|
606
|
+
} else {
|
|
607
|
+
style = w.normal.style()
|
|
608
|
+
}
|
|
609
|
+
style = style.
|
|
610
|
+
Blink(a&Attr(tcell.AttrBlink) != 0).
|
|
611
|
+
Bold(a&Attr(tcell.AttrBold) != 0).
|
|
612
|
+
Dim(a&Attr(tcell.AttrDim) != 0).
|
|
613
|
+
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
|
614
|
+
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
|
615
|
+
Italic(a&Attr(tcell.AttrItalic) != 0)
|
|
616
|
+
|
|
617
|
+
gr := uniseg.NewGraphemes(text)
|
|
618
|
+
for gr.Next() {
|
|
619
|
+
rs := gr.Runes()
|
|
620
|
+
if len(rs) == 1 && rs[0] == '\n' {
|
|
621
|
+
w.lastY++
|
|
622
|
+
w.lastX = 0
|
|
623
|
+
lx = 0
|
|
624
|
+
continue
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// word wrap:
|
|
628
|
+
xPos := w.left + w.lastX + lx
|
|
629
|
+
if xPos >= (w.left + w.width) {
|
|
630
|
+
w.lastY++
|
|
631
|
+
w.lastX = 0
|
|
632
|
+
lx = 0
|
|
633
|
+
xPos = w.left
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
yPos := w.top + w.lastY
|
|
637
|
+
if yPos >= (w.top + w.height) {
|
|
638
|
+
return FillSuspend
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
_screen.SetContent(xPos, yPos, rs[0], rs[1:], style)
|
|
642
|
+
lx += runewidth.StringWidth(string(rs))
|
|
643
|
+
}
|
|
644
|
+
w.lastX += lx
|
|
645
|
+
if w.lastX == w.width {
|
|
646
|
+
w.lastY++
|
|
647
|
+
w.lastX = 0
|
|
648
|
+
return FillNextLine
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return FillContinue
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
func (w *TcellWindow) Fill(str string) FillReturn {
|
|
655
|
+
return w.fillString(str, w.normal)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
|
659
|
+
if fg == colDefault {
|
|
660
|
+
fg = w.normal.Fg()
|
|
661
|
+
}
|
|
662
|
+
if bg == colDefault {
|
|
663
|
+
bg = w.normal.Bg()
|
|
664
|
+
}
|
|
665
|
+
return w.fillString(str, NewColorPair(fg, bg, a))
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
func (w *TcellWindow) drawBorder() {
|
|
669
|
+
shape := w.borderStyle.shape
|
|
670
|
+
if shape == BorderNone {
|
|
671
|
+
return
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
left := w.left
|
|
675
|
+
right := left + w.width
|
|
676
|
+
top := w.top
|
|
677
|
+
bot := top + w.height
|
|
678
|
+
|
|
679
|
+
var style tcell.Style
|
|
680
|
+
if w.color {
|
|
681
|
+
if w.preview {
|
|
682
|
+
style = ColPreviewBorder.style()
|
|
683
|
+
} else {
|
|
684
|
+
style = ColBorder.style()
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
style = w.normal.style()
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
switch shape {
|
|
691
|
+
case BorderRounded, BorderSharp, BorderHorizontal, BorderTop:
|
|
692
|
+
for x := left; x < right; x++ {
|
|
693
|
+
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
switch shape {
|
|
697
|
+
case BorderRounded, BorderSharp, BorderHorizontal, BorderBottom:
|
|
698
|
+
for x := left; x < right; x++ {
|
|
699
|
+
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
switch shape {
|
|
703
|
+
case BorderRounded, BorderSharp, BorderVertical, BorderLeft:
|
|
704
|
+
for y := top; y < bot; y++ {
|
|
705
|
+
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
switch shape {
|
|
709
|
+
case BorderRounded, BorderSharp, BorderVertical, BorderRight:
|
|
710
|
+
for y := top; y < bot; y++ {
|
|
711
|
+
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
switch shape {
|
|
715
|
+
case BorderRounded, BorderSharp:
|
|
716
|
+
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
|
717
|
+
_screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
|
|
718
|
+
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
|
719
|
+
_screen.SetContent(right-1, bot-1, w.borderStyle.bottomRight, nil, style)
|
|
720
|
+
}
|
|
721
|
+
}
|