-
Notifications
You must be signed in to change notification settings - Fork 0
/
interface.go
553 lines (474 loc) · 15.1 KB
/
interface.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
package peco
import (
"io"
"regexp"
"sync"
"time"
"golang.org/x/net/context"
"github.com/google/btree"
"github.com/nsf/termbox-go"
"github.com/peco/peco/hub"
"github.com/peco/peco/internal/keyseq"
"github.com/peco/peco/pipeline"
)
const invalidSelectionRange = -1
const (
ToLineAbove PagingRequestType = iota // ToLineAbove moves the selection to the line above
ToScrollPageDown // ToScrollPageDown moves the selection to the next page
ToLineBelow // ToLineBelow moves the selection to the line below
ToScrollPageUp // ToScrollPageUp moves the selection to the previous page
ToScrollLeft // ToScrollLeft scrolls screen to the left
ToScrollRight // ToScrollRight scrolls screen to the right
ToLineInPage // ToLineInPage jumps to a particular line on the page
)
const (
DefaultLayoutType = LayoutTypeTopDown // LayoutTypeTopDown makes the layout so the items read from top to bottom
LayoutTypeTopDown = "top-down" // LayoutTypeBottomUp changes the layout to read from bottom to up
LayoutTypeBottomUp = "bottom-up"
)
const (
AnchorTop VerticalAnchor = iota + 1 // AnchorTop anchors elements towards the top of the screen
AnchorBottom // AnchorBottom anchors elements towards the bottom of the screen
)
// These are used as keys in the config file
const (
IgnoreCaseMatch = "IgnoreCase"
CaseSensitiveMatch = "CaseSensitive"
SmartCaseMatch = "SmartCase"
RegexpMatch = "Regexp"
)
// lineIDGenerator defines an interface for things that generate
// unique IDs for lines used within peco.
type lineIDGenerator interface {
next() uint64
}
type idgen struct {
ch chan uint64
}
// Peco is the global object containing everything required to run peco.
// It also contains the global state of the program.
type Peco struct {
Argv []string
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
hub MessageHub
args []string
bufferSize int
caret Caret
// Config contains the values read in from config file
config Config
currentLineBuffer Buffer
enableSep bool // Enable parsing on separators
filters FilterSet
idgen *idgen
initialFilter string
initialQuery string // populated if --query is specified
inputseq Inputseq // current key sequence (just the names)
keymap Keymap
layoutType string
location Location
mutex sync.Mutex
prompt string
query Query
queryExecDelay time.Duration
queryExecMutex sync.Mutex
queryExecTimer *time.Timer
readyCh chan struct{}
resultCh chan Line
screen Screen
selection *Selection
selectionRangeStart RangeStart
selectOneAndExit bool // True if --select-1 is enabled
singleKeyJumpMode bool
singleKeyJumpPrefixes []rune
singleKeyJumpPrefixMap map[rune]uint
singleKeyJumpShowPrefix bool
skipReadConfig bool
styles StyleSet
// Source is where we buffer input. It gets reused when a new query is
// executed.
source *Source
// cancelFunc is called for Exit()
cancelFunc func()
// Errors are stored here
err error
}
// Line represents each of the line that peco uses to display
// and match against queries.
type Line interface {
btree.Item
ID() uint64
// Buffer returns the raw buffer
Buffer() string
// DisplayString returns the string to be displayed. This means if you have
// a null separator, the contents after the separator are not included
// in this string
DisplayString() string
// Indices return the matched portion(s) of a string after filtering.
// Note that while Indices may return nil, that just means that there are
// no substrings to be highlighted. It doesn't mean there were no matches
Indices() [][]int
// Output returns the string to be display as peco finishes up doing its
// thing. This means if you have null separator, the contents before the
// separator are not included in this string
Output() string
// IsDirty returns true if this line should be forcefully redrawn
IsDirty() bool
// SetDirty sets the dirty flag on or off
SetDirty(bool)
}
// RawLine is the input line as sent to peco, before filtering and what not.
type RawLine struct {
id uint64
buf string
sepLoc int
displayString string
dirty bool
}
// MatchedLine contains the indices to the matches
type MatchedLine struct {
Line
indices [][]int
}
type Keyseq interface {
Add(keyseq.KeyList, interface{})
AcceptKey(keyseq.Key) (interface{}, error)
CancelChain()
Clear()
Compile() error
InMiddleOfChain() bool
}
// PagingRequest can be sent to move the selection cursor
type PagingRequestType int
type PagingRequest interface {
Type() PagingRequestType
}
type JumpToLineRequest int
// Selection stores the line ids that were selected by the user.
// The contents of the Selection is always sorted from smallest to
// largest line ID
type Selection struct {
mutex sync.Mutex
tree *btree.BTree
}
// StatusMsgRequest specifies the string to be drawn
// on the status message bar and an optional delay that tells
// the view to clear that message
type StatusMsgRequest struct {
message string
clearDelay time.Duration
}
// Screen hides termbox from the consuming code so that
// it can be swapped out for testing
type Screen interface {
Init() error
Close() error
Flush() error
PollEvent() chan termbox.Event
Print(PrintArgs) int
SetCell(int, int, rune, termbox.Attribute, termbox.Attribute)
Size() (int, int)
SendEvent(termbox.Event)
}
// Termbox just hands out the processing to the termbox library
type Termbox struct {
mutex sync.Mutex
}
// View handles the drawing/updating the screen
type View struct {
layout Layout
state *Peco
}
// PageCrop filters out a new LineBuffer based on entries
// per page and the page number
type PageCrop struct {
perPage int
currentPage int
}
// LayoutType describes the types of layout that peco can take
type LayoutType string
// VerticalAnchor describes the direction to which elements in the
// layout are anchored to
type VerticalAnchor int
// Layout represents the component that controls where elements are placed on screen
type Layout interface {
PrintStatus(string, time.Duration)
DrawPrompt(*Peco)
DrawScreen(*Peco, *DrawOptions)
MovePage(*Peco, PagingRequest) (moved bool)
PurgeDisplayCache()
}
// AnchorSettings groups items that are required to control
// where an anchored item is actually placed
type AnchorSettings struct {
anchor VerticalAnchor // AnchorTop or AnchorBottom
anchorOffset int // offset this many lines from the anchor
screen Screen
}
// UserPrompt draws the prompt line
type UserPrompt struct {
*AnchorSettings
prompt string
promptLen int
styles *StyleSet
}
// StatusBar draws the status message bar
type StatusBar struct {
*AnchorSettings
clearTimer *time.Timer
styles *StyleSet
timerMutex sync.Mutex
}
// ListArea represents the area where the actual line buffer is
// displayed in the screen
type ListArea struct {
*AnchorSettings
sortTopDown bool
displayCache []Line
dirty bool
styles *StyleSet
}
// BasicLayout is... the basic layout :) At this point this is the
// only struct for layouts, which means that while the position
// of components may be configurable, the actual types of components
// that are used are set and static
type BasicLayout struct {
*StatusBar
prompt *UserPrompt
list *ListArea
}
// Keymap holds all the key sequence to action map
type Keymap struct {
Config map[string]string
Action map[string][]string // custom actions
seq Keyseq
state *Peco
}
// internal stuff
type regexpFlags interface {
flags(string) []string
}
type regexpFlagList []string
type regexpFlagFunc func(string) []string
// Filter is responsible for the actual "grep" part of peco
type Filter struct {
state *Peco
}
// Action describes an action that can be executed upon receiving user
// input. It's an interface so you can create any kind of Action you need,
// but most everything is implemented in terms of ActionFunc, which is
// callback based Action
type Action interface {
Register(string, ...termbox.Key)
RegisterKeySequence(string, keyseq.KeyList)
Execute(context.Context, *Peco, termbox.Event)
}
// ActionFunc is a type of Action that is basically just a callback.
type ActionFunc func(context.Context, *Peco, termbox.Event)
// FilteredBuffer holds a "filtered" buffer. It holds a reference to
// the source buffer (note: should be immutable) and a list of indices
// into the source buffer
type FilteredBuffer struct {
src Buffer
selection []int // maps from our index to src's index
}
// Config holds all the data that can be configured in the
// external configuran file
type Config struct {
Action map[string][]string `json:"Action"`
// Keymap used to be directly responsible for dispatching
// events against user input, but since then this has changed
// into something that just records the user's config input
Keymap map[string]string `json:"Keymap"`
Matcher string `json:"Matcher"` // Deprecated.
InitialMatcher string `json:"InitialMatcher"` // Use this instead of Matcher
InitialFilter string `json:"InitialFilter"`
Style StyleSet `json:"Style"`
Prompt string `json:"Prompt"`
Layout string `json:"Layout"`
CustomMatcher map[string][]string
CustomFilter map[string]CustomFilterConfig
Command []CommandConfig
QueryExecutionDelay int
StickySelection bool
// If this is true, then the prefix for single key jump mode
// is displayed by default.
SingleKeyJump SingleKeyJumpConfig `json:"SingleKeyJump"`
}
type SingleKeyJumpConfig struct {
ShowPrefix bool `json:"ShowPrefix"`
}
type CommandConfig struct {
// Name is the name of the command to execute
Name string
// TODO: need to check if how we use this is correct
Args []string
// Spawn mean the command should be executed asynchronous.
Spawn bool
}
// CustomFilterConfig is used to specify configuration parameters
// to CustomFilters
type CustomFilterConfig struct {
// Cmd is the name of the command to invoke
Cmd string
// TODO: need to check if how we use this is correct
Args []string
// BufferThreshold defines how many lines peco buffers before
// invoking the external command. If this value is big, we
// will execute the external command fewer times, but the
// results will not be generated for longer periods of time.
// If this value is small, we will execute the external command
// more often, but you pay the penalty of invoking that command
// more times.
BufferThreshold int
}
// StyleSet holds styles for various sections
type StyleSet struct {
Basic Style `json:"Basic"`
SavedSelection Style `json:"SavedSelection"`
Selected Style `json:"Selected"`
Query Style `json:"Query"`
Matched Style `json:"Matched"`
}
// Style describes termbox styles
type Style struct {
fg termbox.Attribute
bg termbox.Attribute
}
type Caret struct {
mutex sync.Mutex
pos int
}
type Location struct {
col int
lineno int
maxPage int
page int
perPage int
offset int
total int
}
type Query struct {
query []rune
savedQuery []rune
mutex sync.Mutex
}
type FilterQuery Query
type FilterSet struct {
current int
filters []LineFilter
mutex sync.Mutex
}
// Source implements pipeline.Source, and is the buffer for the input
type Source struct {
pipeline.OutputChannel
done chan struct{}
capacity int
enableSep bool
idgen lineIDGenerator
in io.Reader
lines []Line
mutex sync.RWMutex
ready chan struct{}
setupDone chan struct{}
setupOnce sync.Once
start chan struct{}
}
type State interface {
Keymap() *Keymap
Query() Query
Screen() Screen
SetCurrentCol(int)
CurrentCol() int
SetCurrentLine(int)
CurrentLine() int
SetSingleKeyJumpMode(bool)
SingleKeyJumpMode() bool
}
type CLIOptions struct {
OptHelp bool `short:"h" long:"help" description:"show this help message and exit"`
OptTTY string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
OptQuery string `long:"query" description:"initial value for query"`
OptRcfile string `long:"rcfile" description:"path to the settings file"`
OptVersion bool `long:"version" description:"print the version and exit"`
OptBufferSize int `long:"buffer-size" short:"b" description:"number of lines to keep in search buffer"`
OptEnableNullSep bool `long:"null" description:"expect NUL (\\0) as separator for target/output"`
OptInitialIndex int `long:"initial-index" description:"position of the initial index of the selection (0 base)"`
OptInitialMatcher string `long:"initial-matcher" description:"specify the default matcher (deprecated)"`
OptInitialFilter string `long:"initial-filter" description:"specify the default filter"`
OptPrompt string `long:"prompt" description:"specify the prompt string"`
OptLayout string `long:"layout" description:"layout to be used 'top-down' or 'bottom-up'. default is 'top-down'"`
OptSelect1 bool `long:"select-1" description:"select first item and immediately exit if the input contains only 1 item"`
}
type CLI struct {
}
type RangeStart struct {
val int
valid bool
}
// Buffer interface is used for containers for lines to be
// processed by peco.
type Buffer interface {
LineAt(int) (Line, error)
Size() int
}
// MemoryBuffer is an implementation of Buffer
type MemoryBuffer struct {
done chan struct{}
lines []Line
mutex sync.RWMutex
PeriodicFunc func()
}
type ActionMap interface {
ExecuteAction(context.Context, *Peco, termbox.Event) error
}
type Input struct {
actions ActionMap
evsrc chan termbox.Event
mod *time.Timer
mutex sync.Mutex
state *Peco
}
type LineFilter interface {
pipeline.Acceptor
SetQuery(string)
Clone() LineFilter
String() string
}
type RegexpFilter struct {
compiledQuery []*regexp.Regexp
flags regexpFlags
quotemeta bool
query string
mutex sync.Mutex
name string
onEnd func()
outCh pipeline.OutputChannel
}
type ExternalCmdFilter struct {
args []string
cmd string
enableSep bool
idgen lineIDGenerator
outCh pipeline.OutputChannel
name string
query string
thresholdBufsiz int
}
// MessageHub is the interface that must be satisfied by the
// message hub component. Unless we're in testing, github.com/peco/peco/hub.Hub
// is used.
type MessageHub interface {
Batch(func(), bool)
DrawCh() chan hub.Payload
PagingCh() chan hub.Payload
QueryCh() chan hub.Payload
SendDraw(interface{})
SendDrawPrompt()
SendPaging(interface{})
SendQuery(string)
SendStatusMsg(string)
SendStatusMsgAndClear(string, time.Duration)
StatusMsgCh() chan hub.Payload
}