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 // the completions were already sorted by 'refresh', so we can
200 // get the common prefix by comparing the first and last items.
201 var a = this.get_string(narrowed[0]);
202 var b = this.get_string(narrowed[nnarrowed - 1]);
203 let i = common_prefix_length(a, b);
205 common_prefix = a.substring(0, i);
206 if (! default_completion) {
207 for (var j = 0; j < nnarrowed; ++j) {
208 if (this.get_string(narrowed[j]) == common_prefix) {
209 default_completion = j;
216 return new prefix_completions(this, narrowed, default_completion,
217 null, pos, input, common_prefix);
219 refresh: function () {
220 completer.prototype.refresh.call(this);
222 this.completions.sort(function (a, b) {
236 * Javascript Completer
239 function javascript_completer (scope) {
240 prefix_completer.call(this,
242 $get_description = second);
245 javascript_completer.prototype = {
246 constructor: javascript_completer,
247 __proto__: prefix_completer.prototype,
248 toString: function () "#<javascript_completer>",
250 complete: function (input, pos) {
251 var str = input.substr(0, pos);
252 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
253 var filter = matches[3] || "";
254 var start = matches[1].length - 1;
255 var offset = matches[1] ? matches[1].length : 0;
256 offset += matches[2] ? matches[2].length : 0;
259 let brackets = 0, parentheses = 0;
261 for (; start >= 0; start--) {
262 switch (matches[1][start]) {
279 if (brackets > 0 || parentheses > 0)
286 var common_prefix_len = null;
287 var common_prefix = null;
290 function add_completion (str, desc) {
291 if (common_prefix != null)
292 common_prefix_len = common_prefix_length(common_prefix, str, common_prefix_len);
295 narrowed.push([str, desc]);
297 if (matches[1].substr(start+1)) {
299 var source_obj = eval(matches[1].substr(start+1));
302 source_obj = this.scope;
304 if (source_obj != null) {
306 for (let i in source_obj) {
307 if (i.substring(0, filter.length) != filter)
310 var type = typeof source_obj[i];
312 type = "unknown type";
314 if (type == "number" || type == "string" || type == "boolean") {
315 var description = type + ": " + source_obj[i];
318 add_completion(i, description);
322 if (common_prefix != null && common_prefix_len > 0)
323 common_prefix = common_prefix.substr(0, common_prefix_len);
324 else if (common_prefix_len != null)
325 common_prefix = null;
326 return new prefix_completions(this, narrowed, null, offset, pos, input, common_prefix);
328 refresh: function () {}
333 * Merged Completer (combinator)
336 function merged_completions (results, count) {
337 completions.call(this, null);
338 this.results = results;
339 this.nresults = results.length;
342 merged_completions.prototype = {
343 constructor: merged_completions,
344 __proto__: completions.prototype,
345 toString: function () "#<merged_completions>",
348 forward: function (name, i) {
349 for (var j = 0; j < this.nresults; ++j) {
350 var r = this.results[j];
357 destroy: function () {
358 for (var j = 0; j < this.nresults; ++j) {
359 this.results[j].destroy();
362 //XXX: index_of: function (x) { },
363 get_string: function (i) {
364 return this.forward("get_string", i);
366 get_input_state: function (i) {
367 return this.forward("get_input_state", i);
369 get_description: function (i) {
370 return this.forward("get_description", i);
372 get_icon: function (i) {
373 return this.forward("get_icon", i);
375 get_value: function (i) {
376 return this.forward("get_value", i);
380 function merged_completer (completers) {
381 this.completers = completers;
382 this.ncompleters = completers.length;
383 completer.call(this);
385 merged_completer.prototype = {
386 constructor: merged_completer,
387 __proto__: completer.prototype,
388 toString: function () "#<merged_completer>",
391 complete: function (input, pos) {
392 var merged_results = [];
394 for (let i = 0; i < this.ncompleters; ++i) {
395 let r = yield this.completers[i].complete(input, pos);
396 if (r != null && r.count > 0) {
397 merged_results.push(r);
401 yield co_return(new merged_completions(merged_results, count));
403 refresh: function () {
404 for (var i = 0; i < this.ncompleters; ++i) {
405 this.completers[i].refresh();
408 get require_match () {
409 for (var i = 0; i < this.ncompleters; ++i) {
410 var r = this.completers[i];
412 return r.require_match;
422 * Adjust input_state of a prefix_completions object for prefix and/or
426 function nest_completions (completions_o, prefix, suffix) {
437 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
440 __proto__: completions_o,
441 get_input_state: function (i) {
442 var x = completions_o.get_input_state(i);
445 get common_prefix_input_state () {
446 let x = completions_o.common_prefix_input_state;
455 provide("completers");