fx-completion
Version:
fx completion for bash
128 lines (113 loc) • 2.62 kB
JavaScript
const os = require('os')
const fs = require('fs')
const path = require('path')
const reduce = require('fx/reduce')
void function main() {
if (process.argv.includes('--bash')) {
process.stdout.write(fs.readFileSync(path.join(__dirname, './complete.sh')))
return
}
process.stdout.write(
[
...globals(),
...fields(),
].join('\n')
)
}()
function globals() {
const defaultGlobals = [
'clearImmediate',
'clearTimeout',
'global',
'setImmediate',
'setTimeout',
'Buffer',
'clearInterval',
'process',
'setInterval',
]
try {
require(path.join(os.homedir(), '.fxrc'))
} catch (err) {
// Ignore
}
return [
'select',
...Object.keys(global)
.filter(x =>
!x.startsWith('DTRACE_')
&& !x.startsWith('FX_')
&& !defaultGlobals.includes(x)
),
]
}
function fields() {
const compIndex = parseInt(process.argv[2], 10) - 2
const compLine = process.argv[4] || ''
let args = compLine
.trim()
.split(' ')
.slice(1) // Remove fx name
if (/\.json$/.test(args[0])) {
try {
let filename = args[0]
filename = filename.replace(/^~\//, os.homedir() + '/')
const input = fs.readFileSync(filename).toString('utf8')
const json = JSON.parse(input)
args = args.slice(1) // Remove file name
if (args[compIndex]) {
args = args.slice(0, compIndex) // Remove current word
}
const output = args.reduce(reduce, json)
const fields = []
let i = 0
for (let p of paths(output)) {
fields.push(p)
if (i++ > 1000) {
break
}
}
return fields
} catch (e) {
// Ignore
}
}
return []
}
function* paths(json) {
const queue = []
if (Array.isArray(json)) {
let i = 0
for (let item of json) {
const path = 'this[' + (i++) + ']'
if (Array.isArray(item) || isObject(item)) {
yield path
}
}
} else {
if (isObject(json)) {
for (let [key, value] of Object.entries(json)) {
const path = /^\w+$/.test(key) ? `.${key}` : `this["${key}"]`
yield path
if (Array.isArray(value)) {
queue.push([value, path])
}
}
}
}
while (queue.length > 0) {
const [value, path] = queue.shift()
let i = 0
for (let item of value) {
const p = path + '[' + (i++) + ']'
if (Array.isArray(item) || isObject(item)) {
yield p
}
}
}
}
function isObject(v) {
return typeof v === 'object' && v.constructor === Object
}