-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
112 lines (96 loc) · 3.05 KB
/
index.js
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
const ansi = require('ansi-escapes')
const chalk = require('chalk')
module.exports = ({
forceLowerCase = false,
start = '',
suggestions = [],
suggestionColor = 'gray'
} = {}) => {
return new Promise((resolve, reject) => {
const { stdout, stdin } = process
const { isRaw } = stdin
const abortChars = new Set(['\u0003'])
const resolveChars = new Set(['\r'])
const autoCompleteChars = new Set([
'\t' /* tab */,
'\r' /* return */,
'\u001B[C' /* right arrow */,
' ' /* Spacebar */
])
// Some environments (e.g., cygwin) don't provide a tty
if (!stdin.setRawMode) {
return reject(new TypeError('stdin lacks setRawMode support'))
}
stdout.write(start)
stdin.setRawMode(true)
stdin.resume()
const restore = () => {
stdout.write('')
stdin.setRawMode(isRaw)
stdin.pause()
stdin.removeListener('data', onData)
}
let val = ''
let suggestion = ''
let caretOffset = 0
// To make `for..of` work with buble
const _suggestions = [...suggestions]
const onData = buffer => {
const data = buffer.toString()
// Abort upon ctrl+C
if (abortChars.has(data)) {
restore()
return reject(new TypeError('User abort'))
}
let completion = ''
// If we have a suggestion *and*
// the user is at the end of the line *and*
// the user pressed one of the keys to trigger completion
if (suggestion !== '' && !caretOffset && autoCompleteChars.has(data)) {
val += suggestion
suggestion = ''
} else {
if (data === '\u001B[D') {
if (val.length > Math.abs(caretOffset)) {
caretOffset--
}
} else if (data === '\u001B[C') {
if (caretOffset < 0) {
caretOffset++
}
} else if (data === '\u0008' || data === '\u007F') {
// Delete key needs splicing according to caret position
val = val.slice(0, val.length + caretOffset - 1) + val.slice(val.length + caretOffset)
} else {
if (resolveChars.has(data)) {
restore()
return resolve(val)
}
const add = forceLowerCase ? data.toLowerCase() : data
val = val.slice(0, val.length + caretOffset) + add + val.slice(val.length + caretOffset)
}
if (val.length > 0) {
for (const sugestion of _suggestions) {
if (val === sugestion) {
break
}
if (val === sugestion.slice(0, val.length)) {
suggestion = sugestion.slice(val.length)
completion = chalk[suggestionColor](suggestion)
completion += ansi.cursorBackward(sugestion.length - val.length)
break
}
}
}
if (completion.length < 0) {
suggestion = ''
}
}
stdout.write(ansi.eraseLines(1) + start + val + completion)
if (caretOffset) {
stdout.write(ansi.cursorBackward(Math.abs(caretOffset)))
}
}
stdin.on('data', onData)
})
}