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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/doing.rdoc +1 -1
  6. data/lib/doing/version.rb +1 -1
  7. data/lib/helpers/fzf/.goreleaser.yml +119 -0
  8. data/lib/helpers/fzf/.rubocop.yml +28 -0
  9. data/lib/helpers/fzf/ADVANCED.md +565 -0
  10. data/lib/helpers/fzf/BUILD.md +49 -0
  11. data/lib/helpers/fzf/CHANGELOG.md +1193 -0
  12. data/lib/helpers/fzf/Dockerfile +11 -0
  13. data/lib/helpers/fzf/LICENSE +21 -0
  14. data/lib/helpers/fzf/Makefile +166 -0
  15. data/lib/helpers/fzf/README-VIM.md +486 -0
  16. data/lib/helpers/fzf/README.md +712 -0
  17. data/lib/helpers/fzf/bin/fzf-tmux +233 -0
  18. data/lib/helpers/fzf/doc/fzf.txt +512 -0
  19. data/lib/helpers/fzf/go.mod +17 -0
  20. data/lib/helpers/fzf/go.sum +31 -0
  21. data/lib/helpers/fzf/install +382 -0
  22. data/lib/helpers/fzf/install.ps1 +65 -0
  23. data/lib/helpers/fzf/main.go +14 -0
  24. data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
  25. data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
  26. data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
  27. data/lib/helpers/fzf/shell/completion.bash +381 -0
  28. data/lib/helpers/fzf/shell/completion.zsh +329 -0
  29. data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
  30. data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
  31. data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
  32. data/lib/helpers/fzf/src/LICENSE +21 -0
  33. data/lib/helpers/fzf/src/algo/algo.go +884 -0
  34. data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
  35. data/lib/helpers/fzf/src/algo/normalize.go +492 -0
  36. data/lib/helpers/fzf/src/ansi.go +409 -0
  37. data/lib/helpers/fzf/src/ansi_test.go +427 -0
  38. data/lib/helpers/fzf/src/cache.go +81 -0
  39. data/lib/helpers/fzf/src/cache_test.go +39 -0
  40. data/lib/helpers/fzf/src/chunklist.go +89 -0
  41. data/lib/helpers/fzf/src/chunklist_test.go +80 -0
  42. data/lib/helpers/fzf/src/constants.go +85 -0
  43. data/lib/helpers/fzf/src/core.go +351 -0
  44. data/lib/helpers/fzf/src/history.go +96 -0
  45. data/lib/helpers/fzf/src/history_test.go +68 -0
  46. data/lib/helpers/fzf/src/item.go +44 -0
  47. data/lib/helpers/fzf/src/item_test.go +23 -0
  48. data/lib/helpers/fzf/src/matcher.go +235 -0
  49. data/lib/helpers/fzf/src/merger.go +120 -0
  50. data/lib/helpers/fzf/src/merger_test.go +88 -0
  51. data/lib/helpers/fzf/src/options.go +1691 -0
  52. data/lib/helpers/fzf/src/options_test.go +457 -0
  53. data/lib/helpers/fzf/src/pattern.go +425 -0
  54. data/lib/helpers/fzf/src/pattern_test.go +209 -0
  55. data/lib/helpers/fzf/src/protector/protector.go +8 -0
  56. data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
  57. data/lib/helpers/fzf/src/reader.go +201 -0
  58. data/lib/helpers/fzf/src/reader_test.go +63 -0
  59. data/lib/helpers/fzf/src/result.go +243 -0
  60. data/lib/helpers/fzf/src/result_others.go +16 -0
  61. data/lib/helpers/fzf/src/result_test.go +159 -0
  62. data/lib/helpers/fzf/src/result_x86.go +16 -0
  63. data/lib/helpers/fzf/src/terminal.go +2832 -0
  64. data/lib/helpers/fzf/src/terminal_test.go +638 -0
  65. data/lib/helpers/fzf/src/terminal_unix.go +26 -0
  66. data/lib/helpers/fzf/src/terminal_windows.go +45 -0
  67. data/lib/helpers/fzf/src/tokenizer.go +253 -0
  68. data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
  69. data/lib/helpers/fzf/src/tui/dummy.go +46 -0
  70. data/lib/helpers/fzf/src/tui/light.go +987 -0
  71. data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
  72. data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
  73. data/lib/helpers/fzf/src/tui/tcell.go +721 -0
  74. data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
  75. data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
  76. data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
  77. data/lib/helpers/fzf/src/tui/tui.go +625 -0
  78. data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
  79. data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
  80. data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
  81. data/lib/helpers/fzf/src/util/chars.go +198 -0
  82. data/lib/helpers/fzf/src/util/chars_test.go +46 -0
  83. data/lib/helpers/fzf/src/util/eventbox.go +96 -0
  84. data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
  85. data/lib/helpers/fzf/src/util/slab.go +12 -0
  86. data/lib/helpers/fzf/src/util/util.go +138 -0
  87. data/lib/helpers/fzf/src/util/util_test.go +40 -0
  88. data/lib/helpers/fzf/src/util/util_unix.go +47 -0
  89. data/lib/helpers/fzf/src/util/util_windows.go +83 -0
  90. data/lib/helpers/fzf/test/fzf.vader +175 -0
  91. data/lib/helpers/fzf/test/test_go.rb +2626 -0
  92. data/lib/helpers/fzf/uninstall +117 -0
  93. 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
+ }