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("./config/core.js")
|
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 | fs.stat(path.resolve(process.env.HOME, ".bashrc"), function (er) {
|
30 | bashExists = !er
|
31 | next()
|
32 | })
|
33 | fs.stat(path.resolve(process.env.HOME, ".zshrc"), function (er) {
|
34 | zshExists = !er
|
35 | next()
|
36 | })
|
37 | function next () {
|
38 | if (zshExists === null || bashExists === null) return
|
39 | var out = []
|
40 | if (zshExists) out.push("~/.zshrc")
|
41 | if (bashExists) out.push("~/.bashrc")
|
42 | if (opts.w === 2) out = out.map(function (m) {
|
43 | return [">>", m]
|
44 | })
|
45 | cb(null, out)
|
46 | }
|
47 | }
|
48 |
|
49 | function completion (args, cb) {
|
50 | if (process.platform === "win32") {
|
51 | var e = new Error("npm completion not supported on windows")
|
52 | e.code = "ENOTSUP"
|
53 | e.errno = require("constants").ENOTSUP
|
54 | return cb(e)
|
55 | }
|
56 |
|
57 |
|
58 | if (process.env.COMP_CWORD === undefined
|
59 | ||process.env.COMP_LINE === undefined
|
60 | ||process.env.COMP_POINT === undefined
|
61 | ) return dumpScript(cb)
|
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 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | var w = +process.env.COMP_CWORD
|
77 | , words = args.map(unescape)
|
78 | , word = words[w]
|
79 | , line = process.env.COMP_LINE
|
80 | , point = +process.env.COMP_POINT
|
81 | , partialLine = line.substr(0, point)
|
82 | , partialWords = words.slice(0, w)
|
83 |
|
84 |
|
85 | var partialWord = args[w]
|
86 | , i = partialWord.length
|
87 | while (partialWord.substr(0, i) !== partialLine.substr(-1*i) && i > 0) {
|
88 | i --
|
89 | }
|
90 | partialWord = unescape(partialWord.substr(0, i))
|
91 | partialWords.push(partialWord)
|
92 |
|
93 | var opts = { words : words
|
94 | , w : w
|
95 | , word : word
|
96 | , line : line
|
97 | , lineLength : line.length
|
98 | , point : point
|
99 | , partialLine : partialLine
|
100 | , partialWords : partialWords
|
101 | , partialWord : partialWord
|
102 | , raw: args
|
103 | }
|
104 |
|
105 | cb = wrapCb(cb, opts)
|
106 |
|
107 | console.error(opts)
|
108 |
|
109 | if (partialWords.slice(0, -1).indexOf("--") === -1) {
|
110 | if (word.charAt(0) === "-") return configCompl(opts, cb)
|
111 | if (words[w - 1]
|
112 | && words[w - 1].charAt(0) === "-"
|
113 | && !isFlag(words[w - 1])) {
|
114 |
|
115 |
|
116 | console.error("configValueCompl")
|
117 | return configValueCompl(opts, cb)
|
118 | }
|
119 | }
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | var parsed = opts.conf =
|
127 | nopt(configTypes, shorthands, partialWords.slice(0, -1), 0)
|
128 |
|
129 | console.error(parsed)
|
130 | var cmd = parsed.argv.remain[1]
|
131 | if (!cmd) return cmdCompl(opts, cb)
|
132 |
|
133 | Object.keys(parsed).forEach(function (k) {
|
134 | npm.config.set(k, parsed[k])
|
135 | })
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | cmd = npm.commands[cmd]
|
141 | if (cmd && cmd.completion) return cmd.completion(opts, cb)
|
142 |
|
143 |
|
144 | cb()
|
145 | }
|
146 |
|
147 | function dumpScript (cb) {
|
148 | var fs = require("graceful-fs")
|
149 | , path = require("path")
|
150 | , p = path.resolve(__dirname, "utils/completion.sh")
|
151 |
|
152 |
|
153 |
|
154 | cb = once(cb)
|
155 |
|
156 | fs.readFile(p, "utf8", function (er, d) {
|
157 | if (er) return cb(er)
|
158 | d = d.replace(/^\#\!.*?\n/, "")
|
159 |
|
160 | process.stdout.write(d, function () { cb() })
|
161 | process.stdout.on("error", function (er) {
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 | if (er.errno === "EPIPE") er = null
|
173 | cb(er)
|
174 | })
|
175 |
|
176 | })
|
177 | }
|
178 |
|
179 | function unescape (w) {
|
180 | if (w.charAt(0) === "\"") return w.replace(/^"|"$/g, "")
|
181 | else return w.replace(/\\ /g, " ")
|
182 | }
|
183 |
|
184 | function escape (w) {
|
185 | if (!w.match(/\s+/)) return w
|
186 | return "\"" + w + "\""
|
187 | }
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 | function wrapCb (cb, opts) { return function (er, compls) {
|
196 | if (!Array.isArray(compls)) compls = compls ? [compls] : []
|
197 | compls = compls.map(function (c) {
|
198 | if (Array.isArray(c)) c = c.map(escape).join(" ")
|
199 | else c = escape(c)
|
200 | return c
|
201 | })
|
202 | if (opts.partialWord) compls = compls.filter(function (c) {
|
203 | return c.indexOf(opts.partialWord) === 0
|
204 | })
|
205 | console.error([er && er.stack, compls, opts.partialWord])
|
206 | if (er || compls.length === 0) return cb(er)
|
207 |
|
208 | console.log(compls.join("\n"))
|
209 | cb()
|
210 | }}
|
211 |
|
212 |
|
213 |
|
214 | function configCompl (opts, cb) {
|
215 | var word = opts.word
|
216 | , split = word.match(/^(-+)((?:no-)*)(.*)$/)
|
217 | , dashes = split[1]
|
218 | , no = split[2]
|
219 | , 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 | , no = split[2]
|
241 | , 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 | }
|