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