1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var fs = require('fs'),
|
7 | pth = require('path');
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | exports.complete = function complete(name, completer, cb) {
|
13 |
|
14 |
|
15 |
|
16 | if(!cb) {
|
17 | cb = completer;
|
18 | completer = name;
|
19 | }
|
20 |
|
21 | var env = parseEnv();
|
22 |
|
23 |
|
24 | if(!env.complete) return cb();
|
25 |
|
26 |
|
27 | if(env.install) return install(name, completer, function(err, state) {
|
28 | console.log(state || err.message);
|
29 | if(err) return cb(err);
|
30 | cb(null, null, state);
|
31 | });
|
32 |
|
33 |
|
34 | if(env.uninstall) return uninstall(name, completer, function(err, state) {
|
35 | console.log(state || err.message);
|
36 | if(err) return cb(err);
|
37 | cb(null, null, state);
|
38 | });
|
39 |
|
40 |
|
41 | if(!env.words || !env.point || !env.line) return script(name, completer, function(err, content) {
|
42 | if(err) return cb(err);
|
43 | process.stdout.write(content, function (n) { cb(null, null, content); });
|
44 | process.stdout.on("error", function (er) {
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | if (er.errno === "EPIPE") er = null
|
58 | cb(er, null, content);
|
59 | });
|
60 | cb(null, null, content);
|
61 | });
|
62 |
|
63 | var partial = env.line.substr(0, env.point),
|
64 | last = env.line.split(' ').slice(-1).join(''),
|
65 | lastPartial = partial.split(' ').slice(-1).join(''),
|
66 | prev = env.line.split(' ').slice(0, -1).slice(-1)[0];
|
67 |
|
68 | cb(null, {
|
69 | line: env.line,
|
70 | words: env.words,
|
71 | point: env.point,
|
72 | partial: partial,
|
73 | last: last,
|
74 | prev: prev,
|
75 | lastPartial: lastPartial
|
76 | });
|
77 | };
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | exports.isComplete = function isComplete() {
|
83 | var env = parseEnv();
|
84 | return env.complete || (env.words && env.point && env.line);
|
85 | };
|
86 |
|
87 | exports.parseOut = function parseOut(str) {
|
88 | var shorts = str.match(/\s-\w+/g);
|
89 | var longs = str.match(/\s--\w+/g);
|
90 |
|
91 | return {
|
92 | shorts: shorts.map(trim).map(cleanPrefix),
|
93 | longs: longs.map(trim).map(cleanPrefix)
|
94 | };
|
95 | };
|
96 |
|
97 |
|
98 | exports.parseTasks = function(str, prefix, reg) {
|
99 | var tasks = str.match(reg || new RegExp('^' + prefix + '\\s[^#]+', 'gm')) || [];
|
100 | return tasks.map(trim).map(function(s) {
|
101 | return s.replace(prefix + ' ', '');
|
102 | });
|
103 | };
|
104 |
|
105 | exports.log = function log(arr, o, prefix) {
|
106 | prefix = prefix || '';
|
107 | arr = Array.isArray(arr) ? arr : [arr];
|
108 | arr.filter(abbrev(o)).forEach(function(v) {
|
109 | console.log(prefix + v);
|
110 | });
|
111 | }
|
112 |
|
113 | function trim (s) {
|
114 | return s.trim();
|
115 | }
|
116 |
|
117 | function cleanPrefix(s) {
|
118 | return s.replace(/-/g, '');
|
119 | }
|
120 |
|
121 | function abbrev(o) { return function(it) {
|
122 | return new RegExp('^' + o.last.replace(/^--?/g, '')).test(it);
|
123 | }}
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | function script(name, completer, cb) {
|
129 | var p = pth.join(__dirname, 'completion.sh');
|
130 |
|
131 | fs.readFile(p, 'utf8', function (er, d) {
|
132 | if (er) return cb(er);
|
133 | cb(null, d);
|
134 | });
|
135 | }
|
136 |
|
137 | function install(name, completer, cb) {
|
138 | var markerIn = '###-begin-' + name + '-completion-###',
|
139 | markerOut = '###-end-' + name + '-completion-###';
|
140 |
|
141 | var rc, scriptOutput;
|
142 |
|
143 | readRc(completer, function(err, file) {
|
144 | if(err) return cb(err);
|
145 |
|
146 | var part = file.split(markerIn)[1];
|
147 | if(part) {
|
148 | return cb(null, ' ✗ ' + completer + ' tab-completion has been already installed. Do nothing.');
|
149 | }
|
150 |
|
151 | rc = file;
|
152 | next();
|
153 | });
|
154 |
|
155 | script(name, completer, function(err, file) {
|
156 | scriptOutput = file;
|
157 | next();
|
158 | });
|
159 |
|
160 | function next() {
|
161 | if(!rc || !scriptOutput) return;
|
162 |
|
163 | writeRc(rc + scriptOutput, function(err) {
|
164 | if(err) return cb(err);
|
165 | return cb(null, ' ✓ ' + completer + ' tab-completion installed.');
|
166 | });
|
167 | }
|
168 | }
|
169 |
|
170 | function uninstall(name, completer, cb) {
|
171 | var markerIn = '\n\n###-begin-' + name + '-completion-###',
|
172 | markerOut = '###-end-' + name + '-completion-###\n';
|
173 |
|
174 | readRc(completer, function(err, file) {
|
175 | if(err) return cb(err);
|
176 |
|
177 | var part = file.split(markerIn)[1];
|
178 | if(!part) {
|
179 | return cb(null, ' ✗ ' + completer + ' tab-completion has been already uninstalled. Do nothing.');
|
180 | }
|
181 |
|
182 | part = markerIn + part.split(markerOut)[0] + markerOut;
|
183 | writeRc(file.replace(part, ''), function(err) {
|
184 | if(err) return cb(err);
|
185 | return cb(null, ' ✓ ' + completer + ' tab-completion uninstalled.');
|
186 | });
|
187 | });
|
188 | }
|
189 |
|
190 | function readRc(completer, cb) {
|
191 | var file = '.' + process.env.SHELL.match(/\/bin\/(\w+)/)[1] + 'rc',
|
192 | filepath = pth.join(process.env.HOME, file);
|
193 | fs.lstat(filepath, function (err, stats) {
|
194 | if(err) return cb(new Error("No " + file + " file. You'll have to run instead: " + completer + " completion >> ~/" + file));
|
195 | fs.readFile(filepath, 'utf8', cb);
|
196 | });
|
197 | }
|
198 |
|
199 | function writeRc(content, cb) {
|
200 | var file = '.' + process.env.SHELL.match(/\/bin\/(\w+)/)[1] + 'rc',
|
201 | filepath = pth.join(process.env.HOME, file);
|
202 | fs.lstat(filepath, function (err, stats) {
|
203 | if(err) return cb(new Error("No " + file + " file. You'll have to run instead: " + completer + " completion >> ~/" + file));
|
204 | fs.writeFile(filepath, content, cb);
|
205 | });
|
206 | }
|
207 |
|
208 | function installed (marker, completer, cb) {
|
209 | readRc(completer, function(err, file) {
|
210 | if(err) return cb(err);
|
211 | var installed = file.match(marker);
|
212 | return cb(!!installed);
|
213 | });
|
214 | }
|
215 |
|
216 | function parseEnv() {
|
217 | var args = process.argv.slice(2),
|
218 | complete = args[0] === 'completion';
|
219 |
|
220 | return {
|
221 | args: args,
|
222 | complete: complete,
|
223 | install: complete && args[1] === 'install',
|
224 | uninstall: complete && args[1] === 'uninstall',
|
225 | words: +process.env.COMP_CWORD,
|
226 | point: +process.env.COMP_POINT,
|
227 | line: process.env.COMP_LINE
|
228 | }
|
229 | };
|