2 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
4 * (C) Copyright 2010,2012 John J. Foerch
6 * Portions of this file (the JavaScript completer) were derived from Vimperator,
7 * (C) Copyright 2006-2007 Martin Stubenschrott.
9 * Use, modification, and distribution are subject to the terms specified in the
13 function completions (completer, data) {
14 this.completer = completer;
17 this.count = data.length;
20 completions.prototype = {
21 constructor: completions,
22 toString: function () "#<completions>",
26 destroy: function () {},
27 index_of: function (x) {
28 return this.data.indexOf(x);
30 get_string: function (i) {
31 return this.completer.get_string(this.data[i]);
33 get_input_state: function (i) {
34 return [this.get_string(i)];
36 get_description: function (i) {
37 return this.completer.get_description(this.data[i]);
39 get_icon: function (i) {
40 if (this.completer.get_icon)
41 return this.completer.get_icon(this.data[i]);
45 get_value: function (i) {
46 if (this.completer.get_value)
47 return this.completer.get_value(this.data[i]);
54 define_keywords("$completions", "$get_string", "$get_description",
55 "$get_icon", "$get_value");
56 function completer () {
59 $get_string = identity,
60 $get_description = constantly(""),
63 this.completions_src = arguments.$completions;
64 this.get_string = arguments.$get_string;
65 this.get_description = arguments.$get_description;
66 this.get_icon = arguments.$get_icon;
67 this.get_value = arguments.$get_value;
70 completer.prototype = {
71 constructor: completer,
72 toString: function () "#<completer>",
73 completions_src: null,
76 get_description: null,
79 complete: function (input, pos) {},
80 refresh: function () {
81 if (typeof this.completions_src == "function") {
83 this.completions_src(function (x) { completions.push(x); });
84 this.completions = completions;
85 } else if (this.completions_src)
86 this.completions = this.completions_src.slice();
95 function all_word_completer () {
97 completer.call(this, forward_keywords(arguments));
99 all_word_completer.prototype = {
100 constructor: all_word_completer,
101 __proto__: completer.prototype,
102 toString: function () "#<all_word_completer>",
103 complete: function (input, pos) {
104 var words = input.toLowerCase().split(" ");
105 var nwords = words.length;
107 var narrowed = this.completions.filter(function (x) {
108 var s = c.get_string(x);
109 var d = c.get_description(x);
110 for (var i = 0; i < nwords; ++i) {
111 if (s.toLowerCase().indexOf(words[i]) == -1 &&
112 d.toLowerCase().indexOf(words[i]) == -1)
119 return new completions(this, narrowed);
128 function prefix_completions (completer, data, default_completion,
129 offset, pos, input, common_prefix)
131 completions.call(this, completer, data);
132 this.default_completion = default_completion;
133 this.common_prefix = common_prefix;
134 this.offset = offset || 0;
138 prefix_completions.prototype = {
139 constructor: prefix_completions,
140 __proto__: completions.prototype,
141 toString: function () "#<prefix_completions>",
142 default_completion: null,
147 get_partial_completion_input_state: function (x, prefix_end, suffix_begin, orig_str) {
148 if (suffix_begin < orig_str.length) {
149 if (orig_str[suffix_begin] == " ")
151 var sel = x.length + prefix_end + 1;
152 return [orig_str.substring(0, prefix_end) + x + " " +
153 orig_str.substring(suffix_begin),
156 sel = x.length + prefix_end;
157 return [orig_str.substring(0, prefix_end) + x, sel, sel];
160 get_input_state: function (i) {
161 return this.get_partial_completion_input_state(
162 this.get_string(i), 0, this.pos, this.input)
164 get common_prefix_input_state () { //used by minibuffer-read
165 return (this.common_prefix &&
166 this.get_partial_completion_input_state(this.common_prefix,
167 this.offset, this.pos,
172 function prefix_completer () {
174 completer.call(this, forward_keywords(arguments));
176 prefix_completer.prototype = {
177 constructor: prefix_completer,
178 __proto__: completer.prototype,
179 toString: function () "#<prefix_completer>",
180 complete: function (input, pos) {
181 var common_prefix = null;
182 var input_prefix = input.substring(0, pos);
183 var default_completion = null;
186 var narrowed = this.completions.filter(function (x) {
187 var s = c.get_string(x);
189 default_completion = i;
192 retval = (s.substring(0, pos) == input_prefix);
197 var nnarrowed = narrowed.length;
199 var a = this.get_string(narrowed[0]);
200 var b = this.get_string(narrowed[nnarrowed - 1]);
201 let i = common_prefix_length(a, b);
203 common_prefix = a.substring(0, i);
204 if (! default_completion) {
205 for (var j = 0; j < nnarrowed; ++j) {
206 if (this.get_string(narrowed[j]) == common_prefix) {
207 default_completion = j;
214 return new prefix_completions(this, narrowed, default_completion,
215 null, pos, input, common_prefix);
217 refresh: function () {
218 completer.prototype.refresh.call(this);
220 this.completions.sort(function (a, b) {
234 * Javascript Completer
237 function javascript_completer (scope) {
238 prefix_completer.call(this,
240 $get_description = second);
243 javascript_completer.prototype = {
244 constructor: javascript_completer,
245 __proto__: prefix_completer.prototype,
246 toString: function () "#<javascript_completer>",
248 complete: function (input, pos) {
249 var str = input.substr(0, pos);
250 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
251 var filter = matches[3] || "";
252 var start = matches[1].length - 1;
253 var offset = matches[1] ? matches[1].length : 0;
254 offset += matches[2] ? matches[2].length : 0;
257 let brackets = 0, parentheses = 0;
259 for (; start >= 0; start--) {
260 switch (matches[1][start]) {
277 if (brackets > 0 || parentheses > 0)
284 var common_prefix_len = null;
285 var common_prefix = null;
288 function add_completion (str, desc) {
289 if (common_prefix != null)
290 common_prefix_len = common_prefix_length(common_prefix, str, common_prefix_len);
293 narrowed.push([str, desc]);
295 if (matches[1].substr(start+1)) {
297 var source_obj = eval(matches[1].substr(start+1));
300 source_obj = this.scope;
302 if (source_obj != null) {
304 for (let i in source_obj) {
305 if (i.substring(0, filter.length) != filter)
308 var type = typeof source_obj[i];
310 type = "unknown type";
312 if (type == "number" || type == "string" || type == "boolean") {
313 var description = type + ": " + source_obj[i];
316 add_completion(i, description);
320 if (common_prefix != null && common_prefix_len > 0)
321 common_prefix = common_prefix.substr(0, common_prefix_len);
322 else if (common_prefix_len != null)
323 common_prefix = null;
324 return new prefix_completions(this, narrowed, null, offset, pos, input, common_prefix);
326 refresh: function () {}
331 * Merged Completer (combinator)
334 function merged_completions (results, count) {
335 completions.call(this, null);
336 this.results = results;
337 this.nresults = results.length;
340 merged_completions.prototype = {
341 constructor: merged_completions,
342 __proto__: completions.prototype,
343 toString: function () "#<merged_completions>",
346 forward: function (name, i) {
347 for (var j = 0; j < this.nresults; ++j) {
348 var r = this.results[j];
355 destroy: function () {
356 for (var j = 0; j < this.nresults; ++j) {
357 this.results[j].destroy();
360 //XXX: index_of: function (x) { },
361 get_string: function (i) {
362 return this.forward("get_string", i);
364 get_input_state: function (i) {
365 return this.forward("get_input_state", i);
367 get_description: function (i) {
368 return this.forward("get_description", i);
370 get_icon: function (i) {
371 return this.forward("get_icon", i);
373 get_value: function (i) {
374 return this.forward("get_value", i);
378 function merged_completer (completers) {
379 this.completers = completers;
380 this.ncompleters = completers.length;
381 completer.call(this);
383 merged_completer.prototype = {
384 constructor: merged_completer,
385 __proto__: completer.prototype,
386 toString: function () "#<merged_completer>",
389 complete: function (input, pos) {
390 var merged_results = [];
392 for (let i = 0; i < this.ncompleters; ++i) {
393 let r = yield this.completers[i].complete(input, pos);
394 if (r != null && r.count > 0) {
395 merged_results.push(r);
399 yield co_return(new merged_completions(merged_results, count));
401 refresh: function () {
402 for (var i = 0; i < this.ncompleters; ++i) {
403 this.completers[i].refresh();
406 get require_match () {
407 for (var i = 0; i < this.ncompleters; ++i) {
408 var r = this.completers[i];
410 return r.require_match;
420 * Adjust input_state of a prefix_completions object for prefix and/or
424 function nest_completions (completions_o, prefix, suffix) {
435 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
438 __proto__: completions_o,
439 get_input_state: function (i) {
440 var x = completions_o.get_input_state(i);
443 get common_prefix_input_state () {
444 let x = completions_o.common_prefix_input_state;
453 provide("completers");