2 * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
3 * (C) Copyright 2008 Nelson Elhage
4 * (C) Copyright 2010 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
15 require("minibuffer.js");
18 * Generic completer function factory.
21 * - $completions: Either a visit function or an array. If a function, the
22 * function's argument is a function which pushes argument into the
23 * completions array. Otherwise, it uses the provided array.
25 * - $get_string: TODO. Optional, default: identity function.
26 * - $get_description: TODO: Optional, default: function returning "".
27 * - $get_icon: optional. returns an icon for the completions line.
29 * TODO: Exactly what does this function return and how do you use it?
31 define_keywords("$completions", "$get_string", "$get_description",
32 "$get_value", "$get_icon");
33 function all_word_completer () {
35 $get_description = function (x) "",
36 $get_string = function (x) x,
38 var completions = arguments.$completions;
39 var get_string = arguments.$get_string;
40 var get_description = arguments.$get_description;
41 var get_value = arguments.$get_value;
42 var get_icon = arguments.$get_icon;
44 var completer = function (input, pos, conservative) {
45 if (input.length == 0 && conservative)
47 var words = input.toLowerCase().split(" ");
48 var data = arr.filter(function (x) {
49 var s = get_string(x);
50 var d = get_description(x);
51 for (var i = 0; i < words.length; ++i) {
52 if (s.toLowerCase().indexOf(words[i]) == -1 &&
53 d.toLowerCase().indexOf(words[i]) == -1)
60 return {count: data.length,
61 index_of: function (x) data.indexOf(x),
62 get_string: function (i) get_string(data[i]),
63 get_description : function (i) get_description(data[i]),
64 get_input_state: function (i) [get_string(data[i])],
65 get_value: function (i) (get_value ? get_value(data[i]) : data[i]),
66 get_icon: function (i) (get_icon ? get_icon(data[i]) : null)
69 completer.refresh = function () {
70 if (typeof completions == "function") {
72 completions(function (x) { arr.push(x); });
80 function get_common_prefix_length (a, b, len) {
82 if (len != null && len < a.length)
89 for (i = 0; i < lim && a[i] == b[i]; ++i);
93 function get_partial_completion_input_state (x, prefix_end, suffix_begin, orig_str) {
94 if (suffix_begin < orig_str.length) {
95 if (orig_str[suffix_begin] == " ")
97 let sel = x.length + prefix_end + 1;
98 return [orig_str.substring(0, prefix_end) + x + " " + orig_str.substring(suffix_begin),
101 let sel = x.length + prefix_end;
102 return [orig_str.substring(0, prefix_end) + x, sel, sel];
106 function prefix_completer () {
108 $get_description = function (x) "",
109 $get_string = function (x) x,
111 var completions = arguments.$completions;
112 var get_string = arguments.$get_string;
113 var get_description = arguments.$get_description;
114 var get_value = arguments.$get_value;
115 var get_icon = arguments.$get_icon;
117 if (typeof completions == "function") {
119 completions(function (x) { arr.push(x); });
121 arr = completions.slice();
122 arr.sort(function (a,b) {
131 return function (input, pos, conservative) {
132 var common_prefix = null;
133 if (pos == 0 && conservative)
135 var input_prefix = input.substring(0,pos);
136 var default_completion = null;
138 var data = arr.filter(function (x) {
139 var s = get_string(x);
141 default_completion = i;
144 retval = (s.length >= pos && s.substring(0,pos) == input_prefix);
149 if (data.length > 0) {
150 let a = get_string(data[0]);
151 let b = get_string(data[data.length - 1]);
152 let i = get_common_prefix_length(a, b);
154 common_prefix = a.substring(0,i);
155 if (!default_completion) {
156 for (let j = 0; j < data.length; ++j) {
157 if (get_string(data[j]) == common_prefix) {
158 default_completion = j;
165 return {count:data.length,
166 index_of: function (x) data.indexOf(x),
167 get_string: function (i) get_string(data[i]),
168 get_description: function (i) get_description(data[i]),
169 get_input_state: function (i) get_partial_completion_input_state(get_string(data[i]), 0, pos, input),
170 get_value: function (i) (get_value ? get_value(data[i]) : data[i]),
171 get_icon: function (i) (get_icon ? get_icon(data[i]) : null),
172 get common_prefix_input_state () {
173 return common_prefix && get_partial_completion_input_state(common_prefix, 0, pos, input);
175 default_completion: default_completion
180 function javascript_completer (buffer) {
181 var window = buffer.window;
183 return function (input, pos, conservative) {
184 // Derived from Vimperator JavaScript completion
185 if (pos == 0 && conservative)
187 var str = input.substr(0, pos);
188 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
189 var filter = matches[3] || "";
190 var start = matches[1].length - 1;
191 var offset = matches[1] ? matches[1].length : 0;
192 offset += matches[2] ? matches[2].length : 0;
195 let brackets = 0, parentheses = 0;
197 for (; start >= 0; start--) {
198 switch (matches[1][start]) {
216 if (brackets > 0 || parentheses > 0)
224 var common_prefix_len = null;
225 var common_prefix = null;
227 function add_completion (str, desc) {
228 if (common_prefix != null)
229 common_prefix_len = get_common_prefix_length(common_prefix, str, common_prefix_len);
232 data.push([str,desc]);
234 if (matches[1].substr(start+1)) {
236 source_obj = eval(matches[1].substr(start+1));
239 source_obj = conkeror;
240 if ("window".substring(0,filter.length) == filter)
241 add_completion("window", "object");
242 if ("buffer".substring(0,filter.length) == filter)
243 add_completion("buffer", "object");
246 if (source_obj != null) {
248 for (let i in source_obj) {
249 if (i.substring(0,filter.length) != filter)
251 let type, description;
253 type = typeof(source_obj[i]);
254 } catch (e) { type = "unknown type"; }
255 if (type == "number" || type == "string" || type == "boolean") {
256 description = type + ": " + source_obj[i];
259 add_completion(i, description);
263 if (common_prefix != null && common_prefix_len > 0)
264 common_prefix = common_prefix.substr(0, common_prefix_len);
265 else if (common_prefix_len != null)
266 common_prefix = null;
267 return {count:data.length,
268 get_string: function (i) data[i][0],
269 get_description: function (i) data[i][1],
270 get_input_state: function (i) get_partial_completion_input_state(data[i][0], offset, pos, input),
271 get common_prefix_input_state () {
272 return common_prefix && get_partial_completion_input_state(common_prefix, offset, pos, input);
279 function merge_completers (completers) {
280 if(completers.length == 0)
282 return function (input, pos, conservative) {
285 for (let i = 0; i < completers.length; ++i) {
286 let r = yield completers[i](input, pos, conservative);
287 if (r != null && (r.count > 0 || "get_match_required" in r)) {
292 function forward (name) {
294 var args = Array.prototype.slice.call(arguments);
295 var i = args.shift();
296 for (var j=0; j < results.length; j++) {
299 if (name in r && r[name] != null) {
301 return r[name].apply(this, args);
311 function combine_or (name) {
314 for (var j=0; j < results.length; j++) {
316 if (name in r && r[name] != null) {
317 b = b || r[name].apply(this, arguments);
323 yield co_return({count: count,
324 get_string: forward('get_string'),
325 get_description: forward('get_description'),
326 get_input_state: forward('get_input_state'),
327 get_icon: forward('get_icon'),
328 destroy: forward('destroy'),
329 get_match_required: combine_or('get_match_required')
334 function nest_completions (completions, prefix, suffix) {
345 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
348 __proto__: completions,
349 get_input_state: function (i) nest(completions.get_input_state(i)),
350 get common_prefix_input_state () {
351 let x = completions.common_prefix_input_state;
361 * Generic simple completer for associative arrays.
363 * - `options' is an associative array, where the key represents a string that the
364 * user can complete to.
365 * - `query' is the string to show in the minibuffer.
367 function completer_with_mappings (options, query) {
368 var completer = all_word_completer(
369 $completions = function (push) {
370 for (var i in options)
374 yield get_recent_conkeror_window().minibuffer.read(
376 $completer = completer
381 provide("minibuffer-completion");