fx 20.0.2 → 25.0.0
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.
- package/README.md +46 -76
- package/index.js +94 -155
- package/package.json +8 -43
- package/LICENSE +0 -21
- package/bang.js +0 -79
- package/config.js +0 -26
- package/find.js +0 -35
- package/fx.js +0 -717
- package/print.js +0 -117
- package/reduce.js +0 -51
- package/std.js +0 -33
- package/stream.js +0 -84
- package/test.js +0 -85
package/fx.js
DELETED
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const tty = require('tty')
|
|
4
|
-
const blessed = require('@medv/blessed')
|
|
5
|
-
const stringWidth = require('string-width')
|
|
6
|
-
const reduce = require('./reduce')
|
|
7
|
-
const print = require('./print')
|
|
8
|
-
const find = require('./find')
|
|
9
|
-
const config = require('./config')
|
|
10
|
-
|
|
11
|
-
module.exports = function start(filename, source, prev = {}) {
|
|
12
|
-
// Current rendered object on a screen.
|
|
13
|
-
let json = prev.json || source
|
|
14
|
-
|
|
15
|
-
// Contains map from row number to expand path.
|
|
16
|
-
// Example: {0: '', 1: '.foo', 2: '.foo[0]'}
|
|
17
|
-
let index = new Map()
|
|
18
|
-
|
|
19
|
-
// Contains expanded paths. Example: ['', '.foo']
|
|
20
|
-
// Empty string represents root path.
|
|
21
|
-
const expanded = prev.expanded || new Set()
|
|
22
|
-
expanded.add('')
|
|
23
|
-
|
|
24
|
-
// Current filter code.
|
|
25
|
-
let currentCode = null
|
|
26
|
-
|
|
27
|
-
// Current search regexp and generator.
|
|
28
|
-
let highlight = null
|
|
29
|
-
let findGen = null
|
|
30
|
-
let currentPath = null
|
|
31
|
-
|
|
32
|
-
let ttyReadStream, ttyWriteStream
|
|
33
|
-
|
|
34
|
-
// Reopen tty
|
|
35
|
-
if (process.platform === 'win32') {
|
|
36
|
-
const cfs = process.binding('fs')
|
|
37
|
-
ttyReadStream = tty.ReadStream(cfs.open('conin$', fs.constants.O_RDWR | fs.constants.O_EXCL, 0o666))
|
|
38
|
-
ttyWriteStream = tty.WriteStream(cfs.open('conout$', fs.constants.O_RDWR | fs.constants.O_EXCL, 0o666))
|
|
39
|
-
} else {
|
|
40
|
-
const ttyFd = fs.openSync('/dev/tty', 'r+')
|
|
41
|
-
ttyReadStream = tty.ReadStream(ttyFd)
|
|
42
|
-
ttyWriteStream = tty.WriteStream(ttyFd)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const program = blessed.program({
|
|
46
|
-
input: ttyReadStream,
|
|
47
|
-
output: ttyWriteStream,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const screen = blessed.screen({
|
|
51
|
-
program: program,
|
|
52
|
-
smartCSR: true,
|
|
53
|
-
fullUnicode: true,
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const box = blessed.box({
|
|
57
|
-
parent: screen,
|
|
58
|
-
tags: false,
|
|
59
|
-
left: 0,
|
|
60
|
-
top: 0,
|
|
61
|
-
width: '100%',
|
|
62
|
-
height: '100%',
|
|
63
|
-
mouse: true,
|
|
64
|
-
keys: true,
|
|
65
|
-
vi: true,
|
|
66
|
-
ignoreArrows: true,
|
|
67
|
-
alwaysScroll: true,
|
|
68
|
-
scrollable: true,
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const input = blessed.textbox({
|
|
72
|
-
parent: screen,
|
|
73
|
-
bottom: 0,
|
|
74
|
-
left: 0,
|
|
75
|
-
height: 1,
|
|
76
|
-
width: '100%',
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
const search = blessed.textbox({
|
|
80
|
-
parent: screen,
|
|
81
|
-
bottom: 0,
|
|
82
|
-
left: 0,
|
|
83
|
-
height: 1,
|
|
84
|
-
width: '100%',
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const statusBar = blessed.box({
|
|
88
|
-
parent: screen,
|
|
89
|
-
tags: false,
|
|
90
|
-
bottom: 0,
|
|
91
|
-
left: 0,
|
|
92
|
-
height: 1,
|
|
93
|
-
width: '100%',
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const autocomplete = blessed.list({
|
|
97
|
-
parent: screen,
|
|
98
|
-
width: 6,
|
|
99
|
-
height: 7,
|
|
100
|
-
left: 1,
|
|
101
|
-
bottom: 1,
|
|
102
|
-
style: config.list,
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
screen.title = filename
|
|
106
|
-
box.focus()
|
|
107
|
-
input.hide()
|
|
108
|
-
search.hide()
|
|
109
|
-
statusBar.hide()
|
|
110
|
-
autocomplete.hide()
|
|
111
|
-
|
|
112
|
-
process.stdout.on('resize', () => {
|
|
113
|
-
// Blessed has a bug with resizing the terminal. I tried my best to fix it but was not succeeded.
|
|
114
|
-
// For now exit and print seem like a reasonable alternative, as it not usable after resize.
|
|
115
|
-
// If anyone can fix this bug it will be cool.
|
|
116
|
-
printJson({expanded})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
screen.key(['escape', 'q', 'C-c'], function () {
|
|
120
|
-
exit()
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
input.on('submit', function () {
|
|
124
|
-
if (autocomplete.hidden) {
|
|
125
|
-
const code = input.getValue()
|
|
126
|
-
if (/^\//.test(code)) {
|
|
127
|
-
// Forgive a mistake to the user. This looks like user wanted to search something.
|
|
128
|
-
apply('')
|
|
129
|
-
applyPattern(code)
|
|
130
|
-
} else {
|
|
131
|
-
apply(code)
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
// Autocomplete selected
|
|
135
|
-
let code = input.getValue()
|
|
136
|
-
let replace = autocomplete.getSelected()
|
|
137
|
-
if (/^[a-z]\w*$/i.test(replace)) {
|
|
138
|
-
replace = '.' + replace
|
|
139
|
-
} else {
|
|
140
|
-
replace = `["${replace}"]`
|
|
141
|
-
}
|
|
142
|
-
code = code.replace(/\.\w*$/, replace)
|
|
143
|
-
|
|
144
|
-
input.setValue(code)
|
|
145
|
-
autocomplete.hide()
|
|
146
|
-
update(code)
|
|
147
|
-
|
|
148
|
-
// Keep editing code
|
|
149
|
-
input.readInput()
|
|
150
|
-
}
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
input.on('cancel', function () {
|
|
154
|
-
if (autocomplete.hidden) {
|
|
155
|
-
const code = input.getValue()
|
|
156
|
-
apply(code)
|
|
157
|
-
} else {
|
|
158
|
-
// Autocomplete not selected
|
|
159
|
-
autocomplete.hide()
|
|
160
|
-
screen.render()
|
|
161
|
-
|
|
162
|
-
// Keep editing code
|
|
163
|
-
input.readInput()
|
|
164
|
-
}
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
input.on('update', function (code) {
|
|
168
|
-
if (currentCode === code) {
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
currentCode = code
|
|
172
|
-
if (index.size < 10000) { // Don't live update in we have a big JSON file.
|
|
173
|
-
update(code)
|
|
174
|
-
}
|
|
175
|
-
complete(code)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
input.key('up', function () {
|
|
179
|
-
if (!autocomplete.hidden) {
|
|
180
|
-
autocomplete.up()
|
|
181
|
-
screen.render()
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
input.key('down', function () {
|
|
186
|
-
if (!autocomplete.hidden) {
|
|
187
|
-
autocomplete.down()
|
|
188
|
-
screen.render()
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
input.key('C-u', function () {
|
|
193
|
-
input.setValue('')
|
|
194
|
-
update('')
|
|
195
|
-
render()
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
input.key('C-w', function () {
|
|
199
|
-
let code = input.getValue()
|
|
200
|
-
code = code.replace(/[\.\[][^\.\[]*$/, '')
|
|
201
|
-
input.setValue(code)
|
|
202
|
-
update(code)
|
|
203
|
-
render()
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
search.on('submit', function (pattern) {
|
|
207
|
-
applyPattern(pattern)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
search.on('cancel', function () {
|
|
211
|
-
highlight = null
|
|
212
|
-
currentPath = null
|
|
213
|
-
|
|
214
|
-
search.hide()
|
|
215
|
-
search.setValue('')
|
|
216
|
-
|
|
217
|
-
box.height = '100%'
|
|
218
|
-
box.focus()
|
|
219
|
-
|
|
220
|
-
program.cursorPos(0, 0)
|
|
221
|
-
render()
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
box.key('.', function () {
|
|
225
|
-
hideStatusBar()
|
|
226
|
-
box.height = '100%-1'
|
|
227
|
-
input.show()
|
|
228
|
-
if (input.getValue() === '') {
|
|
229
|
-
input.setValue('.')
|
|
230
|
-
complete('.')
|
|
231
|
-
}
|
|
232
|
-
input.readInput()
|
|
233
|
-
screen.render()
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
box.key('/', function () {
|
|
237
|
-
hideStatusBar()
|
|
238
|
-
box.height = '100%-1'
|
|
239
|
-
search.show()
|
|
240
|
-
search.setValue('/')
|
|
241
|
-
search.readInput()
|
|
242
|
-
screen.render()
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
box.key('e', function () {
|
|
246
|
-
hideStatusBar()
|
|
247
|
-
expanded.clear()
|
|
248
|
-
for (let path of dfs(json)) {
|
|
249
|
-
if (expanded.size < 1000) {
|
|
250
|
-
expanded.add(path)
|
|
251
|
-
} else {
|
|
252
|
-
break
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
render()
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
box.key('S-e', function () {
|
|
259
|
-
hideStatusBar()
|
|
260
|
-
expanded.clear()
|
|
261
|
-
expanded.add('')
|
|
262
|
-
render()
|
|
263
|
-
|
|
264
|
-
// Make sure cursor stay on JSON object.
|
|
265
|
-
const [n] = getLine(program.y)
|
|
266
|
-
if (typeof n === 'undefined' || !index.has(n)) {
|
|
267
|
-
// No line under cursor
|
|
268
|
-
let rest = [...index.keys()]
|
|
269
|
-
if (rest.length > 0) {
|
|
270
|
-
const next = Math.max(...rest)
|
|
271
|
-
let y = box.getScreenNumber(next) - box.childBase
|
|
272
|
-
if (y <= 0) {
|
|
273
|
-
y = 0
|
|
274
|
-
}
|
|
275
|
-
const line = box.getScreenLine(y + box.childBase)
|
|
276
|
-
program.cursorPos(y, line.search(/\S/))
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
box.key('n', function () {
|
|
282
|
-
hideStatusBar()
|
|
283
|
-
findNext()
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
box.key(['up', 'k'], function () {
|
|
287
|
-
hideStatusBar()
|
|
288
|
-
program.showCursor()
|
|
289
|
-
const [n] = getLine(program.y)
|
|
290
|
-
|
|
291
|
-
let next
|
|
292
|
-
for (let [i,] of index) {
|
|
293
|
-
if (i < n && (typeof next === 'undefined' || i > next)) {
|
|
294
|
-
next = i
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (typeof next !== 'undefined') {
|
|
299
|
-
let y = box.getScreenNumber(next) - box.childBase
|
|
300
|
-
if (y <= 0) {
|
|
301
|
-
box.scroll(-1)
|
|
302
|
-
screen.render()
|
|
303
|
-
y = 0
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const line = box.getScreenLine(y + box.childBase)
|
|
307
|
-
program.cursorPos(y, line.search(/\S/))
|
|
308
|
-
}
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
box.key(['down', 'j'], function () {
|
|
312
|
-
hideStatusBar()
|
|
313
|
-
program.showCursor()
|
|
314
|
-
const [n] = getLine(program.y)
|
|
315
|
-
|
|
316
|
-
let next
|
|
317
|
-
for (let [i,] of index) {
|
|
318
|
-
if (i > n && (typeof next === 'undefined' || i < next)) {
|
|
319
|
-
next = i
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (typeof next !== 'undefined') {
|
|
324
|
-
let y = box.getScreenNumber(next) - box.childBase
|
|
325
|
-
if (y >= box.height) {
|
|
326
|
-
box.scroll(1)
|
|
327
|
-
screen.render()
|
|
328
|
-
y = box.height - 1
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const line = box.getScreenLine(y + box.childBase)
|
|
332
|
-
program.cursorPos(y, line.search(/\S/))
|
|
333
|
-
}
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
box.key(['right', 'l'], function () {
|
|
337
|
-
hideStatusBar()
|
|
338
|
-
const [n, line] = getLine(program.y)
|
|
339
|
-
program.showCursor()
|
|
340
|
-
program.cursorPos(program.y, line.search(/\S/))
|
|
341
|
-
const path = index.get(n)
|
|
342
|
-
if (!expanded.has(path)) {
|
|
343
|
-
expanded.add(path)
|
|
344
|
-
render()
|
|
345
|
-
}
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
// Expand everything under cursor.
|
|
349
|
-
box.key(['S-right', 'S-l'], function () {
|
|
350
|
-
hideStatusBar()
|
|
351
|
-
const [n, line] = getLine(program.y)
|
|
352
|
-
program.showCursor()
|
|
353
|
-
program.cursorPos(program.y, line.search(/\S/))
|
|
354
|
-
const path = index.get(n)
|
|
355
|
-
const subJson = reduce(json, 'this' + path)
|
|
356
|
-
for (let p of dfs(subJson, path)) {
|
|
357
|
-
if (expanded.size < 1000) {
|
|
358
|
-
expanded.add(p)
|
|
359
|
-
} else {
|
|
360
|
-
break
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
render()
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
box.key(['left', 'h'], function () {
|
|
367
|
-
hideStatusBar()
|
|
368
|
-
const [n, line] = getLine(program.y)
|
|
369
|
-
program.showCursor()
|
|
370
|
-
program.cursorPos(program.y, line.search(/\S/))
|
|
371
|
-
|
|
372
|
-
// Find path at current cursor position.
|
|
373
|
-
const path = index.get(n)
|
|
374
|
-
|
|
375
|
-
if (expanded.has(path)) {
|
|
376
|
-
// Collapse current path.
|
|
377
|
-
expanded.delete(path)
|
|
378
|
-
render()
|
|
379
|
-
} else {
|
|
380
|
-
// If there is no expanded paths on current line,
|
|
381
|
-
// collapse parent path of current location.
|
|
382
|
-
if (typeof path === 'string') {
|
|
383
|
-
// Trip last part (".foo", "[0]") to get parent path.
|
|
384
|
-
const parentPath = path.replace(/(\.[^\[\].]+|\[\d+\])$/, '')
|
|
385
|
-
if (expanded.has(parentPath)) {
|
|
386
|
-
expanded.delete(parentPath)
|
|
387
|
-
render()
|
|
388
|
-
|
|
389
|
-
// Find line number of parent path, and if we able to find it,
|
|
390
|
-
// move cursor to this position of just collapsed parent path.
|
|
391
|
-
for (let y = program.y; y >= 0; --y) {
|
|
392
|
-
const [n, line] = getLine(y)
|
|
393
|
-
if (index.get(n) === parentPath) {
|
|
394
|
-
program.cursorPos(y, line.search(/\S/))
|
|
395
|
-
break
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
box.on('click', function (mouse) {
|
|
404
|
-
hideStatusBar()
|
|
405
|
-
const [n, line] = getLine(mouse.y)
|
|
406
|
-
if (mouse.x >= stringWidth(line)) {
|
|
407
|
-
return
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
program.hideCursor()
|
|
411
|
-
program.cursorPos(mouse.y, line.search(/\S/))
|
|
412
|
-
autocomplete.hide()
|
|
413
|
-
|
|
414
|
-
const path = index.get(n)
|
|
415
|
-
if (expanded.has(path)) {
|
|
416
|
-
expanded.delete(path)
|
|
417
|
-
} else {
|
|
418
|
-
expanded.add(path)
|
|
419
|
-
}
|
|
420
|
-
render()
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
box.on('scroll', function () {
|
|
424
|
-
hideStatusBar()
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
box.key('p', function () {
|
|
428
|
-
printJson({expanded})
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
box.key('S-p', function () {
|
|
432
|
-
printJson()
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
function printJson(options = {}) {
|
|
436
|
-
screen.destroy()
|
|
437
|
-
program.disableMouse()
|
|
438
|
-
program.destroy()
|
|
439
|
-
setTimeout(() => {
|
|
440
|
-
const [text] = print(json, options)
|
|
441
|
-
console.log(text)
|
|
442
|
-
process.exit(0)
|
|
443
|
-
}, 10)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function getLine(y) {
|
|
447
|
-
const dy = box.childBase + y
|
|
448
|
-
const n = box.getNumber(dy)
|
|
449
|
-
const line = box.getScreenLine(dy)
|
|
450
|
-
if (typeof line === 'undefined') {
|
|
451
|
-
return [n, '']
|
|
452
|
-
}
|
|
453
|
-
return [n, line]
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function apply(code) {
|
|
457
|
-
if (code && code.length !== 0) {
|
|
458
|
-
try {
|
|
459
|
-
json = reduce(source, code)
|
|
460
|
-
} catch (e) {
|
|
461
|
-
// pass
|
|
462
|
-
}
|
|
463
|
-
} else {
|
|
464
|
-
box.height = '100%'
|
|
465
|
-
input.hide()
|
|
466
|
-
json = source
|
|
467
|
-
}
|
|
468
|
-
box.focus()
|
|
469
|
-
program.cursorPos(0, 0)
|
|
470
|
-
render()
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function complete(inputCode) {
|
|
474
|
-
const match = inputCode.match(/\.(\w*)$/)
|
|
475
|
-
const code = /^\.\w*$/.test(inputCode) ? '.' : inputCode.replace(/\.\w*$/, '')
|
|
476
|
-
|
|
477
|
-
let json
|
|
478
|
-
try {
|
|
479
|
-
json = reduce(source, code)
|
|
480
|
-
} catch (e) {
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (match) {
|
|
484
|
-
if (typeof json === 'object' && json.constructor === Object) {
|
|
485
|
-
const keys = Object.keys(json)
|
|
486
|
-
.filter(key => key.startsWith(match[1]))
|
|
487
|
-
.slice(0, 1000) // With lots of items, list takes forever to render.
|
|
488
|
-
|
|
489
|
-
// Hide if there is nothing to show or
|
|
490
|
-
// don't show if there is complete match.
|
|
491
|
-
if (keys.length === 0 || (keys.length === 1 && keys[0] === match[1])) {
|
|
492
|
-
autocomplete.hide()
|
|
493
|
-
return
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
autocomplete.width = Math.max(...keys.map(key => key.length)) + 1
|
|
497
|
-
autocomplete.height = Math.min(7, keys.length)
|
|
498
|
-
autocomplete.left = Math.min(
|
|
499
|
-
screen.width - autocomplete.width,
|
|
500
|
-
code.length === 1 ? 1 : code.length + 1
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
let selectFirst = autocomplete.items.length !== keys.length
|
|
504
|
-
autocomplete.setItems(keys)
|
|
505
|
-
|
|
506
|
-
if (selectFirst) {
|
|
507
|
-
autocomplete.select(autocomplete.items.length - 1)
|
|
508
|
-
}
|
|
509
|
-
if (autocomplete.hidden) {
|
|
510
|
-
autocomplete.show()
|
|
511
|
-
}
|
|
512
|
-
} else {
|
|
513
|
-
autocomplete.clearItems()
|
|
514
|
-
autocomplete.hide()
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function update(code) {
|
|
520
|
-
if (code && code.length !== 0) {
|
|
521
|
-
try {
|
|
522
|
-
const pretender = reduce(source, code)
|
|
523
|
-
if (
|
|
524
|
-
typeof pretender !== 'undefined'
|
|
525
|
-
&& typeof pretender !== 'function'
|
|
526
|
-
&& !(pretender instanceof RegExp)
|
|
527
|
-
) {
|
|
528
|
-
json = pretender
|
|
529
|
-
}
|
|
530
|
-
} catch (e) {
|
|
531
|
-
// pass
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
if (code === '') {
|
|
535
|
-
json = source
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (highlight) {
|
|
539
|
-
findGen = find(json, highlight)
|
|
540
|
-
}
|
|
541
|
-
render()
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function applyPattern(pattern) {
|
|
545
|
-
let regex
|
|
546
|
-
let m = pattern.match(/^\/(.*)\/([gimuy]*)$/)
|
|
547
|
-
if (m) {
|
|
548
|
-
try {
|
|
549
|
-
regex = new RegExp(m[1], m[2])
|
|
550
|
-
} catch (e) {
|
|
551
|
-
// Wrong regexp.
|
|
552
|
-
}
|
|
553
|
-
} else {
|
|
554
|
-
m = pattern.match(/^\/(.*)$/)
|
|
555
|
-
if (m) {
|
|
556
|
-
try {
|
|
557
|
-
regex = new RegExp(m[1], 'gi')
|
|
558
|
-
} catch (e) {
|
|
559
|
-
// Wrong regexp.
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
highlight = regex
|
|
564
|
-
|
|
565
|
-
search.hide()
|
|
566
|
-
|
|
567
|
-
if (highlight) {
|
|
568
|
-
findGen = find(json, highlight)
|
|
569
|
-
findNext()
|
|
570
|
-
} else {
|
|
571
|
-
findGen = null
|
|
572
|
-
currentPath = null
|
|
573
|
-
}
|
|
574
|
-
search.setValue('')
|
|
575
|
-
|
|
576
|
-
box.height = '100%'
|
|
577
|
-
box.focus()
|
|
578
|
-
|
|
579
|
-
program.cursorPos(0, 0)
|
|
580
|
-
render()
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function findNext() {
|
|
584
|
-
if (!findGen) {
|
|
585
|
-
return
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const {value: path, done} = findGen.next()
|
|
589
|
-
|
|
590
|
-
if (done) {
|
|
591
|
-
showStatusBar('Pattern not found')
|
|
592
|
-
} else {
|
|
593
|
-
|
|
594
|
-
currentPath = ''
|
|
595
|
-
for (let p of path) {
|
|
596
|
-
expanded.add(currentPath += p)
|
|
597
|
-
}
|
|
598
|
-
render()
|
|
599
|
-
|
|
600
|
-
for (let [k, v] of index) {
|
|
601
|
-
if (v === currentPath) {
|
|
602
|
-
let y = box.getScreenNumber(k)
|
|
603
|
-
|
|
604
|
-
// Scroll one line up for better view and make sure it's not negative.
|
|
605
|
-
if (--y < 0) {
|
|
606
|
-
y = 0
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
box.scrollTo(y)
|
|
610
|
-
screen.render()
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Set cursor to current path.
|
|
615
|
-
// We need timeout here to give our terminal some time.
|
|
616
|
-
// Without timeout first cursorPos call does not working,
|
|
617
|
-
// it looks like an ugly hack and it is an ugly hack.
|
|
618
|
-
setTimeout(() => {
|
|
619
|
-
for (let [k, v] of index) {
|
|
620
|
-
if (v === currentPath) {
|
|
621
|
-
let y = box.getScreenNumber(k) - box.childBase
|
|
622
|
-
if (y <= 0) {
|
|
623
|
-
y = 0
|
|
624
|
-
}
|
|
625
|
-
const line = box.getScreenLine(y + box.childBase)
|
|
626
|
-
program.cursorPos(y, line.search(/\S/))
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}, 100)
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
function showStatusBar(status) {
|
|
634
|
-
statusBar.show()
|
|
635
|
-
statusBar.setContent(config.statusBar(` ${status} `))
|
|
636
|
-
screen.render()
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
function hideStatusBar() {
|
|
640
|
-
if (!statusBar.hidden) {
|
|
641
|
-
statusBar.hide()
|
|
642
|
-
statusBar.setContent('')
|
|
643
|
-
screen.render()
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
function render() {
|
|
648
|
-
let content
|
|
649
|
-
[content, index] = print(json, {expanded, highlight, currentPath})
|
|
650
|
-
|
|
651
|
-
if (typeof content === 'undefined') {
|
|
652
|
-
content = 'undefined'
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
box.setContent(content)
|
|
656
|
-
screen.render()
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function exit() {
|
|
660
|
-
// If exit program immediately, stdin may still receive
|
|
661
|
-
// mouse events which will be printed in stdout.
|
|
662
|
-
program.disableMouse()
|
|
663
|
-
setTimeout(() => process.exit(0), 10)
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
render()
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
function* bfs(json) {
|
|
670
|
-
const queue = [[json, '']]
|
|
671
|
-
|
|
672
|
-
while (queue.length > 0) {
|
|
673
|
-
const [v, path] = queue.shift()
|
|
674
|
-
|
|
675
|
-
if (!v) {
|
|
676
|
-
continue
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
if (Array.isArray(v)) {
|
|
680
|
-
yield path
|
|
681
|
-
let i = 0
|
|
682
|
-
for (let item of v) {
|
|
683
|
-
const p = path + '[' + (i++) + ']'
|
|
684
|
-
queue.push([item, p])
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
if (typeof v === 'object' && v.constructor === Object) {
|
|
689
|
-
yield path
|
|
690
|
-
for (let [key, value] of Object.entries(v)) {
|
|
691
|
-
const p = path + '.' + key
|
|
692
|
-
queue.push([value, p])
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
function* dfs(v, path = '') {
|
|
699
|
-
if (!v) {
|
|
700
|
-
return
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
if (Array.isArray(v)) {
|
|
704
|
-
yield path
|
|
705
|
-
let i = 0
|
|
706
|
-
for (let item of v) {
|
|
707
|
-
yield* dfs(item, path + '[' + (i++) + ']')
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (typeof v === 'object' && v.constructor === Object) {
|
|
712
|
-
yield path
|
|
713
|
-
for (let [key, value] of Object.entries(v)) {
|
|
714
|
-
yield* dfs(value, path + '.' + key)
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|