Search feature

js-version
Anton Medvedev 6 years ago
parent 83bed4a4ee
commit 4986be32b5

@ -10,14 +10,15 @@ const list = {
}
module.exports = {
space: global.FX_STYLE_SPACE || 2,
null: global.FX_STYLE_NULL || chalk.grey.bold,
number: global.FX_STYLE_NUMBER || chalk.cyan.bold,
boolean: global.FX_STYLE_BOOLEAN || chalk.yellow.bold,
string: global.FX_STYLE_STRING || chalk.green.bold,
key: global.FX_STYLE_KEY || chalk.blue.bold,
bracket: global.FX_STYLE_BRACKET || noop,
comma: global.FX_STYLE_COMMA || noop,
colon: global.FX_STYLE_COLON || noop,
list: global.FX_STYLE_LIST || list,
space: global.FX_STYLE_SPACE || 2,
null: global.FX_STYLE_NULL || chalk.grey.bold,
number: global.FX_STYLE_NUMBER || chalk.cyan.bold,
boolean: global.FX_STYLE_BOOLEAN || chalk.yellow.bold,
string: global.FX_STYLE_STRING || chalk.green.bold,
key: global.FX_STYLE_KEY || chalk.blue.bold,
bracket: global.FX_STYLE_BRACKET || noop,
comma: global.FX_STYLE_COMMA || noop,
colon: global.FX_STYLE_COLON || noop,
highlight: global.FX_STYLE_HIGHLIGHT || chalk.black.bgYellow,
list: global.FX_STYLE_LIST || list,
}

@ -0,0 +1,34 @@
'use strict'
function* find(v, regex, path = []) {
if (regex.test(path.join(''))) {
yield path
return
}
if (typeof v === 'undefined' || v === null) {
return
}
if (Array.isArray(v)) {
let i = 0
for (let value of v) {
yield* find(value, regex, path.concat(['[' + i++ + ']']))
}
return
}
if (typeof v === 'object' && v.constructor === Object) {
const entries = Object.entries(v)
for (let [key, value] of entries) {
yield* find(value, regex, path.concat(['.' + key]))
}
return
}
if (regex.test(v)) {
yield path
}
}
module.exports = find

97
fx.js

@ -5,6 +5,7 @@ const blessed = require('@medv/blessed')
const stringWidth = require('string-width')
const reduce = require('./reduce')
const print = require('./print')
const find = require('./find')
const config = require('./config')
module.exports = function start(filename, source) {
@ -20,6 +21,10 @@ module.exports = function start(filename, source) {
const expanded = new Set()
expanded.add('')
// Current search regexp and generator.
let highlight = null
let findGen = null
const ttyFd = fs.openSync('/dev/tty', 'r+')
const program = blessed.program({
input: tty.ReadStream(ttyFd),
@ -55,6 +60,14 @@ module.exports = function start(filename, source) {
width: '100%',
})
const search = blessed.textbox({
parent: screen,
bottom: 0,
left: 0,
height: 1,
width: '100%',
})
const autocomplete = blessed.list({
parent: screen,
width: 6,
@ -67,6 +80,7 @@ module.exports = function start(filename, source) {
screen.title = filename
box.focus()
input.hide()
search.hide()
autocomplete.hide()
screen.key(['escape', 'q', 'C-c'], function () {
@ -147,6 +161,48 @@ module.exports = function start(filename, source) {
render()
})
search.on('submit', function (pattern) {
let regex
const m = pattern.match(/^\/(.*)\/?([gimuy]*)$/)
if (m) {
try {
regex = new RegExp(m[1], m[2])
} catch (e) {
// Wrong regexp.
}
}
highlight = regex
if (highlight) {
findGen = find(json, highlight)
findNext()
} else {
findGen = null
}
search.hide()
search.setValue('')
box.height = '100%'
box.focus()
program.cursorPos(0, 0)
render()
})
search.on('cancel', function () {
highlight = null
search.hide()
search.setValue('')
box.height = '100%'
box.focus()
program.cursorPos(0, 0)
render()
})
box.key('.', function () {
box.height = '100%-1'
input.show()
@ -155,7 +211,15 @@ module.exports = function start(filename, source) {
complete('.')
}
input.readInput()
render()
screen.render()
})
box.key('/', function () {
box.height = '100%-1'
search.show()
search.setValue('/')
search.readInput()
screen.render()
})
box.key('e', function () {
@ -186,6 +250,10 @@ module.exports = function start(filename, source) {
}
})
box.key('n', function () {
findNext()
})
box.key(['up', 'k'], function () {
program.showCursor()
let rest = [...index.keys()]
@ -362,12 +430,37 @@ module.exports = function start(filename, source) {
if (code === '') {
json = source
}
findGen = find(json, highlight)
render()
}
function findNext() {
if (!findGen) {
return
}
const {value: path, done} = findGen.next()
if (!done) {
let value = ''
for (let p of path) {
expanded.add(value += p)
}
console.error(value)
render()
for (let [k, v] of index) {
if (v === value) {
const y = box.getScreenNumber(k)
box.scrollTo(y)
screen.render()
}
}
}
}
function render() {
let content
[content, index] = print(json, {expanded})
[content, index] = print(json, {expanded, highlight})
if (typeof content === 'undefined') {
content = 'undefined'

@ -3,10 +3,21 @@ const indent = require('indent-string')
const config = require('./config')
function print(input, options = {}) {
const {expanded} = options
const {expanded, highlight} = options
const index = new Map()
let row = 0
function format(text, style) {
if (!highlight) {
return style(text)
}
return text
.replace(highlight, s => '<fx>' + s + '<fx>')
.split(/<fx>/g)
.map((s, i) => i % 2 !== 0 ? config.highlight(s) : style(s))
.join('')
}
function doPrint(v, path = '') {
index.set(row, path)
@ -20,20 +31,20 @@ function print(input, options = {}) {
}
if (v === null) {
return config.null(v)
return format('null', config.null)
}
if (typeof v === 'number' && Number.isFinite(v)) {
return config.number(v)
return format(v.toString(), config.number)
}
if (typeof v === 'boolean') {
return config.boolean(v)
return format(v.toString(), config.boolean)
}
if (typeof v === 'string') {
return config.string(JSON.stringify(v))
return format(JSON.stringify(v), config.string)
}
if (Array.isArray(v)) {
@ -71,7 +82,7 @@ function print(input, options = {}) {
output += eol()
let i = 0
for (let [key, value] of entries) {
const part = config.key(JSON.stringify(key)) + config.colon(':') + ' ' + doPrint(value, path + '.' + key)
const part = format(JSON.stringify(key), config.key) + config.colon(':') + ' ' + doPrint(value, path + '.' + key)
output += indent(part, config.space)
output += i++ < len - 1 ? config.comma(',') : ''
output += eol()

Loading…
Cancel
Save