1 | module.exports = completion
|
2 |
|
3 | completion.usage = 'source <(npm completion)'
|
4 |
|
5 | var npm = require('./npm.js')
|
6 | var npmconf = require('./config/core.js')
|
7 | var configDefs = npmconf.defs
|
8 | var configTypes = configDefs.types
|
9 | var shorthands = configDefs.shorthands
|
10 | var nopt = require('nopt')
|
11 | var configNames = Object.keys(configTypes)
|
12 | .filter(function (e) { return e.charAt(0) !== '_' })
|
13 | var shorthandNames = Object.keys(shorthands)
|
14 | var allConfs = configNames.concat(shorthandNames)
|
15 | var once = require('once')
|
16 | var isWindowsShell = require('./utils/is-windows-shell.js')
|
17 | var output = require('./utils/output.js')
|
18 |
|
19 | completion.completion = function (opts, cb) {
|
20 | if (opts.w > 3) return cb()
|
21 |
|
22 | var fs = require('graceful-fs')
|
23 | var path = require('path')
|
24 | var bashExists = null
|
25 | var zshExists = null
|
26 | fs.stat(path.resolve(process.env.HOME, '.bashrc'), function (er) {
|
27 | bashExists = !er
|
28 | next()
|
29 | })
|
30 | fs.stat(path.resolve(process.env.HOME, '.zshrc'), function (er) {
|
31 | zshExists = !er
|
32 | next()
|
33 | })
|
34 | function next () {
|
35 | if (zshExists === null || bashExists === null) return
|
36 | var out = []
|
37 | if (zshExists) out.push('~/.zshrc')
|
38 | if (bashExists) out.push('~/.bashrc')
|
39 | if (opts.w === 2) {
|
40 | out = out.map(function (m) {
|
41 | return ['>>', m]
|
42 | })
|
43 | }
|
44 | cb(null, out)
|
45 | }
|
46 | }
|
47 |
|
48 | function completion (args, cb) {
|
49 | if (isWindowsShell) {
|
50 | var e = new Error('npm completion supported only in MINGW / Git bash on Windows')
|
51 | e.code = 'ENOTSUP'
|
52 | e.errno = require('constants').ENOTSUP
|
53 | return cb(e)
|
54 | }
|
55 |
|
56 |
|
57 | if (process.env.COMP_CWORD === undefined ||
|
58 | process.env.COMP_LINE === undefined ||
|
59 | process.env.COMP_POINT === undefined) {
|
60 | return dumpScript(cb)
|
61 | }
|
62 |
|
63 | console.error(process.env.COMP_CWORD)
|
64 | console.error(process.env.COMP_LINE)
|
65 | console.error(process.env.COMP_POINT)
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | var w = +process.env.COMP_CWORD
|
71 | var words = args.map(unescape)
|
72 | var word = words[w]
|
73 | var line = process.env.COMP_LINE
|
74 | var point = +process.env.COMP_POINT
|
75 | var partialLine = line.substr(0, point)
|
76 | var partialWords = words.slice(0, w)
|
77 |
|
78 |
|
79 | var partialWord = args[w]
|
80 | var i = partialWord.length
|
81 | while (partialWord.substr(0, i) !== partialLine.substr(-1 * i) && i > 0) {
|
82 | i--
|
83 | }
|
84 | partialWord = unescape(partialWord.substr(0, i))
|
85 | partialWords.push(partialWord)
|
86 |
|
87 | var opts = {
|
88 | words: words,
|
89 | w: w,
|
90 | word: word,
|
91 | line: line,
|
92 | lineLength: line.length,
|
93 | point: point,
|
94 | partialLine: partialLine,
|
95 | partialWords: partialWords,
|
96 | partialWord: partialWord,
|
97 | raw: args
|
98 | }
|
99 |
|
100 | cb = wrapCb(cb, opts)
|
101 |
|
102 | console.error(opts)
|
103 |
|
104 | if (partialWords.slice(0, -1).indexOf('--') === -1) {
|
105 | if (word.charAt(0) === '-') return configCompl(opts, cb)
|
106 | if (words[w - 1] &&
|
107 | words[w - 1].charAt(0) === '-' &&
|
108 | !isFlag(words[w - 1])) {
|
109 |
|
110 |
|
111 | console.error('configValueCompl')
|
112 | return configValueCompl(opts, cb)
|
113 | }
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | var parsed = opts.conf =
|
122 | nopt(configTypes, shorthands, partialWords.slice(0, -1), 0)
|
123 |
|
124 | console.error(parsed)
|
125 | var cmd = parsed.argv.remain[1]
|
126 | if (!cmd) return cmdCompl(opts, cb)
|
127 |
|
128 | Object.keys(parsed).forEach(function (k) {
|
129 | npm.config.set(k, parsed[k])
|
130 | })
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | cmd = npm.commands[cmd]
|
136 | if (cmd && cmd.completion) return cmd.completion(opts, cb)
|
137 |
|
138 |
|
139 | cb()
|
140 | }
|
141 |
|
142 | function dumpScript (cb) {
|
143 | var fs = require('graceful-fs')
|
144 | var path = require('path')
|
145 | var p = path.resolve(__dirname, 'utils/completion.sh')
|
146 |
|
147 |
|
148 |
|
149 | cb = once(cb)
|
150 |
|
151 | fs.readFile(p, 'utf8', function (er, d) {
|
152 | if (er) return cb(er)
|
153 | d = d.replace(/^#!.*?\n/, '')
|
154 |
|
155 | process.stdout.write(d, function () { cb() })
|
156 | process.stdout.on('error', function (er) {
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | if (er.errno === 'EPIPE') er = null
|
168 | cb(er)
|
169 | })
|
170 | })
|
171 | }
|
172 |
|
173 | function unescape (w) {
|
174 | if (w.charAt(0) === '\'') return w.replace(/^'|'$/g, '')
|
175 | else return w.replace(/\\ /g, ' ')
|
176 | }
|
177 |
|
178 | function escape (w) {
|
179 | if (!w.match(/\s+/)) return w
|
180 | return '\'' + w + '\''
|
181 | }
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | function wrapCb (cb, opts) {
|
190 | return function (er, compls) {
|
191 | if (!Array.isArray(compls)) compls = compls ? [compls] : []
|
192 | compls = compls.map(function (c) {
|
193 | if (Array.isArray(c)) c = c.map(escape).join(' ')
|
194 | else c = escape(c)
|
195 | return c
|
196 | })
|
197 |
|
198 | if (opts.partialWord) {
|
199 | compls = compls.filter(function (c) {
|
200 | return c.indexOf(opts.partialWord) === 0
|
201 | })
|
202 | }
|
203 |
|
204 | console.error([er && er.stack, compls, opts.partialWord])
|
205 | if (er || compls.length === 0) return cb(er)
|
206 |
|
207 | output(compls.join('\n'))
|
208 | cb()
|
209 | }
|
210 | }
|
211 |
|
212 |
|
213 |
|
214 | function configCompl (opts, cb) {
|
215 | var word = opts.word
|
216 | var split = word.match(/^(-+)((?:no-)*)(.*)$/)
|
217 | var dashes = split[1]
|
218 | var no = split[2]
|
219 | var flags = configNames.filter(isFlag)
|
220 | console.error(flags)
|
221 |
|
222 | return cb(null, allConfs.map(function (c) {
|
223 | return dashes + c
|
224 | }).concat(flags.map(function (f) {
|
225 | return dashes + (no || 'no-') + f
|
226 | })))
|
227 | }
|
228 |
|
229 |
|
230 |
|
231 | function configValueCompl (opts, cb) {
|
232 | console.error('configValue', opts)
|
233 | return cb(null, [])
|
234 | }
|
235 |
|
236 |
|
237 | function isFlag (word) {
|
238 |
|
239 | var split = word.match(/^(-*)((?:no-)+)?(.*)$/)
|
240 | var no = split[2]
|
241 | var conf = split[3]
|
242 | return no || configTypes[conf] === Boolean || shorthands[conf]
|
243 | }
|
244 |
|
245 |
|
246 | function cmdCompl (opts, cb) {
|
247 | return cb(null, npm.fullList)
|
248 | }
|