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
14 require("minibuffer.js");
17 * Generic completer function factory.
20 * - $completions: Either a visit function or an array. If a function, the
21 * function's argument is a function which pushes argument into the
22 * completions array. Otherwise, it uses the provided array.
24 * - $get_string: TODO. Optional, default: identity function.
25 * - $get_description: TODO: Optional, default: function returning "".
27 * TODO: Exactly what does this function return and how do you use it?
29 define_keywords("$completions", "$get_string", "$get_description", "$get_value");
30 function all_word_completer () {
32 var completions = arguments.$completions;
33 var get_string = arguments.$get_string ? arguments.$get_string : function (x) x;
34 var get_description = arguments.$get_description ? arguments.$get_description : function (x) "";
35 var get_value = arguments.$get_value;
37 var completer = function (input, pos, conservative) {
38 if (input.length == 0 && conservative)
40 var words = input.toLowerCase().split(" ");
41 var data = arr.filter(function (x) {
42 var s = get_string(x);
43 var d = get_description(x);
44 for (var i = 0; i < words.length; ++i) {
45 if (s.toLowerCase().indexOf(words[i]) == -1 && d.toLowerCase().indexOf(words[i]) == -1)
50 return {count: data.length,
51 index_of: function (x) data.indexOf(x),
52 get_string: function (i) get_string(data[i]),
53 get_description : function (i) get_description(data[i]),
54 get_input_state: function (i) [get_string(data[i])],
55 get_value : function(i) (get_value ? get_value(data[i]) : data[i])
58 completer.refresh = function () {
59 if (typeof(completions) == "function") {
61 completions(function (x) { arr.push(x); });
69 function get_common_prefix_length (a, b, len) {
71 if (len != null && len < a.length)
78 for (i = 0; i < lim && a[i] == b[i]; ++i);
82 function get_partial_completion_input_state (x, prefix_end, suffix_begin, orig_str) {
83 if (suffix_begin < orig_str.length) {
84 if (orig_str[suffix_begin] == " ")
86 let sel = x.length + prefix_end + 1;
87 return [orig_str.substring(0, prefix_end) + x + " " + orig_str.substring(suffix_begin),
90 let sel = x.length + prefix_end;
91 return [orig_str.substring(0, prefix_end) + x, sel, sel];
95 function prefix_completer () {
97 var completions = arguments.$completions;
98 var get_string = arguments.$get_string ? arguments.$get_string : function (x) x;
99 var get_description = arguments.$get_description ? arguments.$get_description : function (x) "";
100 var get_value = arguments.$get_value;
102 if (typeof(completions) == "function") {
104 completions(function (x) { arr.push(x); });
106 arr = completions.slice();
107 arr.sort(function (a,b) {
116 return function (input, pos, conservative) {
117 var common_prefix = null;
118 if (pos == 0 && conservative)
120 var input_prefix = input.substring(0,pos);
121 var default_completion = null;
123 var data = arr.filter(function (x) {
124 var s = get_string(x);
128 default_completion = i;
131 retval = (s.length >= pos && s.substring(0,pos) == input_prefix);
136 if (data.length > 0) {
137 let a = get_string(data[0]);
138 let b = get_string(data[data.length - 1]);
139 let i = get_common_prefix_length(a, b);
141 common_prefix = a.substring(0,i);
142 if (!default_completion) {
143 for (let j = 0; j < data.length; ++j) {
144 if (get_string(data[j]) == common_prefix) {
145 default_completion = j;
152 return {count:data.length,
153 index_of: function (x) data.indexOf(x),
154 get_string: function (i) get_string(data[i]),
155 get_description : function (i) get_description(data[i]),
156 get_input_state : function (i) get_partial_completion_input_state(get_string(data[i]), 0, pos, input),
157 get_value : function(i) (get_value ? get_value(data[i]) : data[i]),
158 get common_prefix_input_state () {
159 return common_prefix && get_partial_completion_input_state(common_prefix, 0, pos, input);
161 default_completion: default_completion
166 function javascript_completer (buffer) {
167 var window = buffer.window;
169 return function (input, pos, conservative) {
170 // Derived from Vimperator JavaScript completion
171 if (pos == 0 && conservative)
173 var str = input.substr(0, pos);
174 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
175 var filter = matches[3] || "";
176 var start = matches[1].length - 1;
177 var offset = matches[1] ? matches[1].length : 0;
178 offset += matches[2] ? matches[2].length : 0;
181 let brackets = 0, parentheses = 0;
183 for (; start >= 0; start--) {
184 switch (matches[1][start]) {
202 if (brackets > 0 || parentheses > 0)
210 var common_prefix_len = null;
211 var common_prefix = null;
213 function add_completion (str, desc) {
214 if (common_prefix != null)
215 common_prefix_len = get_common_prefix_length(common_prefix, str, common_prefix_len);
218 data.push([str,desc]);
220 if (matches[1].substr(start+1)) {
222 source_obj = eval(matches[1].substr(start+1));
225 source_obj = conkeror;
226 if ("window".substring(0,filter.length) == filter)
227 add_completion("window", "object");
228 if ("buffer".substring(0,filter.length) == filter)
229 add_completion("buffer", "object");
232 if (source_obj != null) {
234 for (let i in source_obj) {
235 if (i.substring(0,filter.length) != filter)
237 let type, description;
239 type = typeof(source_obj[i]);
240 } catch (e) { type = "unknown type"; }
241 if (type == "number" || type == "string" || type == "boolean") {
242 description = type + ": " + source_obj[i];
245 add_completion(i, description);
249 if (common_prefix != null && common_prefix_len > 0)
250 common_prefix = common_prefix.substr(0, common_prefix_len);
251 else if (common_prefix_len != null)
252 common_prefix = null;
253 return {count:data.length,
254 get_string: function (i) data[i][0],
255 get_description: function (i) data[i][1],
256 get_input_state: function (i) get_partial_completion_input_state(data[i][0], offset, pos, input),
257 get common_prefix_input_state () {
258 return common_prefix && get_partial_completion_input_state(common_prefix, offset, pos, input);
265 function merge_completers (completers) {
266 if(completers.length == 0)
268 return function (input, pos, conservative) {
271 for (let i = 0; i < completers.length; ++i) {
272 let r = yield completers[i](input, pos, conservative);
273 if (r != null && (r.count > 0 || "get_match_required" in r)) {
278 function forward (name) {
280 var args = Array.prototype.slice.call(arguments);
281 var i = args.shift();
282 for (var j=0; j < results.length; j++) {
285 if (name in r && r[name] != null) {
287 return r[name].apply(this, args);
297 function combine_or(name) {
300 for (var j=0; j < results.length; j++) {
302 if (name in r && r[name] != null) {
303 b = b || r[name].apply(this, arguments);
309 yield co_return({count: count,
310 get_string: forward('get_string'),
311 get_description: forward('get_description'),
312 get_input_state: forward('get_input_state'),
313 destroy: forward('destroy'),
314 get_match_required: combine_or('get_match_required')
319 function nest_completions (completions, prefix, suffix) {
330 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
333 __proto__: completions,
334 get_input_state: function (i) nest(completions.get_input_state(i)),
335 get common_prefix_input_state () {
336 let x = completions.common_prefix_input_state;
346 * Generic simple completer for associative arrays.
348 * - `options' is an associative array, where the key represents a string that the
349 * user can complete to.
350 * - `query' is the string to show in the minibuffer.
352 function completer_with_mappings(options, query) {
353 let completer = all_word_completer(
354 $completions = function (push) {
355 for (let i in options)
361 yield get_recent_conkeror_window().minibuffer.read(
363 $completer = completer
368 provide("minibuffer-completion");