2 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
5 * Portions of this file (the JavaScript completer) were derived from Vimperator,
6 * (C) Copyright 2006-2007 Martin Stubenschrott.
8 * Use, modification, and distribution are subject to the terms specified in the
12 require("minibuffer.js");
15 * Generic completer function factory.
18 * - $completions: Either a visit function or an array. If a function, the
19 * function's argument is a function which pushes argument into the
20 * completions array. Otherwise, it uses the provided array.
22 * - $get_string: TODO. Optional, default: identity function.
23 * - $get_description: TODO: Optional, default: function returning "".
25 * TODO: Exactly what does this function return and how do you use it?
27 define_keywords("$completions", "$get_string", "$get_description", "$get_value");
28 function all_word_completer () {
30 var completions = arguments.$completions;
31 var get_string = arguments.$get_string ? arguments.$get_string : function (x) x;
32 var get_description = arguments.$get_description ? arguments.$get_description : function (x) "";
33 var get_value = arguments.$get_value;
35 var completer = function (input, pos, conservative) {
36 if (input.length == 0 && conservative)
38 var words = input.toLowerCase().split(" ");
39 var data = arr.filter(function (x) {
40 var s = get_string(x);
41 var d = get_description(x);
42 for (var i = 0; i < words.length; ++i) {
43 if (s.toLowerCase().indexOf(words[i]) == -1 && d.toLowerCase().indexOf(words[i]) == -1)
48 return {count: data.length,
49 index_of: function (x) data.indexOf(x),
50 get_string: function (i) get_string(data[i]),
51 get_description : function (i) get_description(data[i]),
52 get_input_state: function (i) [get_string(data[i])],
53 get_value : function(i) (get_value ? get_value(data[i]) : data[i])
56 completer.refresh = function () {
57 if (typeof(completions) == "function") {
59 completions(function (x) { arr.push(x); });
67 function get_common_prefix_length (a, b, len) {
69 if (len != null && len < a.length)
76 for (i = 0; i < lim && a[i] == b[i]; ++i);
80 function get_partial_completion_input_state (x, prefix_end, suffix_begin, orig_str) {
81 if (suffix_begin < orig_str.length) {
82 if (orig_str[suffix_begin] == " ")
84 let sel = x.length + prefix_end + 1;
85 return [orig_str.substring(0, prefix_end) + x + " " + orig_str.substring(suffix_begin),
88 let sel = x.length + prefix_end;
89 return [orig_str.substring(0, prefix_end) + x, sel, sel];
93 function prefix_completer () {
95 var completions = arguments.$completions;
96 var get_string = arguments.$get_string ? arguments.$get_string : function (x) x;
97 var get_description = arguments.$get_description ? arguments.$get_description : function (x) "";
98 var get_value = arguments.$get_value;
100 if (typeof(completions) == "function") {
102 completions(function (x) { arr.push(x); });
104 arr = completions.slice();
105 arr.sort(function (a,b) {
114 return function (input, pos, conservative) {
115 var common_prefix = null;
116 if (pos == 0 && conservative)
118 var input_prefix = input.substring(0,pos);
119 var default_completion = null;
121 var data = arr.filter(function (x) {
122 var s = get_string(x);
126 default_completion = i;
129 retval = (s.length >= pos && s.substring(0,pos) == input_prefix);
134 if (data.length > 0) {
135 let a = get_string(data[0]);
136 let b = get_string(data[data.length - 1]);
137 let i = get_common_prefix_length(a, b);
139 common_prefix = a.substring(0,i);
140 if (!default_completion) {
141 for (let j = 0; j < data.length; ++j) {
142 if (get_string(data[j]) == common_prefix) {
143 default_completion = j;
150 return {count:data.length,
151 index_of: function (x) data.indexOf(x),
152 get_string: function (i) get_string(data[i]),
153 get_description : function (i) get_description(data[i]),
154 get_input_state : function (i) get_partial_completion_input_state(get_string(data[i]), 0, pos, input),
155 get_value : function(i) (get_value ? get_value(data[i]) : data[i]),
156 get common_prefix_input_state () {
157 return common_prefix && get_partial_completion_input_state(common_prefix, 0, pos, input);
159 default_completion: default_completion
164 function javascript_completer (buffer) {
165 var window = buffer.window;
167 return function (input, pos, conservative) {
168 // Derived from Vimperator JavaScript completion
169 if (pos == 0 && conservative)
171 var str = input.substr(0, pos);
172 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
173 var filter = matches[3] || "";
174 var start = matches[1].length - 1;
175 var offset = matches[1] ? matches[1].length : 0;
176 offset += matches[2] ? matches[2].length : 0;
179 let brackets = 0, parentheses = 0;
181 for (; start >= 0; start--) {
182 switch (matches[1][start]) {
200 if (brackets > 0 || parentheses > 0)
208 var common_prefix_len = null;
209 var common_prefix = null;
211 function add_completion (str, desc) {
212 if (common_prefix != null)
213 common_prefix_len = get_common_prefix_length(common_prefix, str, common_prefix_len);
216 data.push([str,desc]);
218 if (matches[1].substr(start+1)) {
220 source_obj = eval(matches[1].substr(start+1));
223 source_obj = conkeror;
224 if ("window".substring(0,filter.length) == filter)
225 add_completion("window", "object");
226 if ("buffer".substring(0,filter.length) == filter)
227 add_completion("buffer", "object");
230 if (source_obj != null) {
232 for (let i in source_obj) {
233 if (i.substring(0,filter.length) != filter)
235 let type, description;
237 type = typeof(source_obj[i]);
238 } catch (e) { type = "unknown type"; }
239 if (type == "number" || type == "string" || type == "boolean") {
240 description = type + ": " + source_obj[i];
243 add_completion(i, description);
247 if (common_prefix != null && common_prefix_len > 0)
248 common_prefix = common_prefix.substr(0, common_prefix_len);
249 else if (common_prefix_len != null)
250 common_prefix = null;
251 return {count:data.length,
252 get_string: function (i) data[i][0],
253 get_description: function (i) data[i][1],
254 get_input_state: function (i) get_partial_completion_input_state(data[i][0], offset, pos, input),
255 get common_prefix_input_state () {
256 return common_prefix && get_partial_completion_input_state(common_prefix, offset, pos, input);
263 function merge_completers (completers) {
264 if(completers.length == 0)
266 return function (input, pos, conservative) {
269 for (let i = 0; i < completers.length; ++i) {
270 let r = yield completers[i](input, pos, conservative);
271 if (r != null && (r.count > 0 || "get_match_required" in r)) {
276 function forward (name) {
278 var args = Array.prototype.slice.call(arguments);
279 var i = args.shift();
280 for (var j=0; j < results.length; j++) {
283 if (name in r && r[name] != null) {
285 return r[name].apply(this, args);
295 function combine_or(name) {
298 for (var j=0; j < results.length; j++) {
300 if (name in r && r[name] != null) {
301 b = b || r[name].apply(this, arguments);
307 yield co_return({count: count,
308 get_string: forward('get_string'),
309 get_description: forward('get_description'),
310 get_input_state: forward('get_input_state'),
311 destroy: forward('destroy'),
312 get_match_required: combine_or('get_match_required')
317 function nest_completions (completions, prefix, suffix) {
328 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
331 __proto__: completions,
332 get_input_state: function (i) nest(completions.get_input_state(i)),
333 get common_prefix_input_state () {
334 let x = completions.common_prefix_input_state;
344 * Generic simple completer for associative arrays.
346 * - `options' is an associative array, where the key represents a string that the
347 * user can complete to.
348 * - `query' is the string to show in the minibuffer.
350 function completer_with_mappings(options, query) {
351 let completer = all_word_completer(
352 $completions = function (push) {
353 for (let i in options)
359 yield get_recent_conkeror_window().minibuffer.read(
361 $completer = completer