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
13 require("minibuffer.js");
16 * Generic completer function factory.
19 * - $completions: Either a visit function or an array. If a function, the
20 * function's argument is a function which pushes argument into the
21 * completions array. Otherwise, it uses the provided array.
23 * - $get_string: TODO. Optional, default: identity function.
24 * - $get_description: TODO: Optional, default: function returning "".
25 * - $get_icon: optional. returns an icon for the completions line.
27 * TODO: Exactly what does this function return and how do you use it?
29 define_keywords("$completions", "$get_string", "$get_description",
30 "$get_value", "$get_icon");
31 function all_word_completer () {
33 $get_description = function (x) "",
34 $get_string = function (x) x,
36 var completions = arguments.$completions;
37 var get_string = arguments.$get_string;
38 var get_description = arguments.$get_description;
39 var get_value = arguments.$get_value;
40 var get_icon = arguments.$get_icon;
42 var completer = function (input, pos, conservative) {
43 if (input.length == 0 && conservative)
45 var words = input.toLowerCase().split(" ");
46 var data = arr.filter(function (x) {
47 var s = get_string(x);
48 var d = get_description(x);
49 for (var i = 0; i < words.length; ++i) {
50 if (s.toLowerCase().indexOf(words[i]) == -1 &&
51 d.toLowerCase().indexOf(words[i]) == -1)
58 return {count: data.length,
59 index_of: function (x) data.indexOf(x),
60 get_string: function (i) get_string(data[i]),
61 get_description : function (i) get_description(data[i]),
62 get_input_state: function (i) [get_string(data[i])],
63 get_value: function (i) (get_value ? get_value(data[i]) : data[i]),
64 get_icon: function (i) (get_icon ? get_icon(data[i]) : null)
67 completer.refresh = function () {
68 if (typeof completions == "function") {
70 completions(function (x) { arr.push(x); });
78 function get_common_prefix_length (a, b, len) {
80 if (len != null && len < a.length)
87 for (i = 0; i < lim && a[i] == b[i]; ++i);
91 function get_partial_completion_input_state (x, prefix_end, suffix_begin, orig_str) {
92 if (suffix_begin < orig_str.length) {
93 if (orig_str[suffix_begin] == " ")
95 let sel = x.length + prefix_end + 1;
96 return [orig_str.substring(0, prefix_end) + x + " " + orig_str.substring(suffix_begin),
99 let sel = x.length + prefix_end;
100 return [orig_str.substring(0, prefix_end) + x, sel, sel];
104 function prefix_completer () {
106 $get_description = function (x) "",
107 $get_string = function (x) x,
109 var completions = arguments.$completions;
110 var get_string = arguments.$get_string;
111 var get_description = arguments.$get_description;
112 var get_value = arguments.$get_value;
113 var get_icon = arguments.$get_icon;
115 if (typeof completions == "function") {
117 completions(function (x) { arr.push(x); });
119 arr = completions.slice();
120 arr.sort(function (a,b) {
129 return function (input, pos, conservative) {
130 var common_prefix = null;
131 if (pos == 0 && conservative)
133 var input_prefix = input.substring(0,pos);
134 var default_completion = null;
136 var data = arr.filter(function (x) {
137 var s = get_string(x);
139 default_completion = i;
142 retval = (s.length >= pos && s.substring(0,pos) == input_prefix);
147 if (data.length > 0) {
148 let a = get_string(data[0]);
149 let b = get_string(data[data.length - 1]);
150 let i = get_common_prefix_length(a, b);
152 common_prefix = a.substring(0,i);
153 if (!default_completion) {
154 for (let j = 0; j < data.length; ++j) {
155 if (get_string(data[j]) == common_prefix) {
156 default_completion = j;
163 return {count:data.length,
164 index_of: function (x) data.indexOf(x),
165 get_string: function (i) get_string(data[i]),
166 get_description: function (i) get_description(data[i]),
167 get_input_state: function (i) get_partial_completion_input_state(get_string(data[i]), 0, pos, input),
168 get_value: function (i) (get_value ? get_value(data[i]) : data[i]),
169 get_icon: function (i) (get_icon ? get_icon(data[i]) : null),
170 get common_prefix_input_state () {
171 return common_prefix && get_partial_completion_input_state(common_prefix, 0, pos, input);
173 default_completion: default_completion
178 function javascript_completer (buffer) {
179 var window = buffer.window;
181 return function (input, pos, conservative) {
182 // Derived from Vimperator JavaScript completion
183 if (pos == 0 && conservative)
185 var str = input.substr(0, pos);
186 var matches = str.match(/^(.*?)(\s*\.\s*)?(\w*)$/);
187 var filter = matches[3] || "";
188 var start = matches[1].length - 1;
189 var offset = matches[1] ? matches[1].length : 0;
190 offset += matches[2] ? matches[2].length : 0;
193 let brackets = 0, parentheses = 0;
195 for (; start >= 0; start--) {
196 switch (matches[1][start]) {
214 if (brackets > 0 || parentheses > 0)
222 var common_prefix_len = null;
223 var common_prefix = null;
225 function add_completion (str, desc) {
226 if (common_prefix != null)
227 common_prefix_len = get_common_prefix_length(common_prefix, str, common_prefix_len);
230 data.push([str,desc]);
232 if (matches[1].substr(start+1)) {
234 source_obj = eval(matches[1].substr(start+1));
237 source_obj = conkeror;
238 if ("window".substring(0,filter.length) == filter)
239 add_completion("window", "object");
240 if ("buffer".substring(0,filter.length) == filter)
241 add_completion("buffer", "object");
244 if (source_obj != null) {
246 for (let i in source_obj) {
247 if (i.substring(0,filter.length) != filter)
249 let type, description;
251 type = typeof(source_obj[i]);
252 } catch (e) { type = "unknown type"; }
253 if (type == "number" || type == "string" || type == "boolean") {
254 description = type + ": " + source_obj[i];
257 add_completion(i, description);
261 if (common_prefix != null && common_prefix_len > 0)
262 common_prefix = common_prefix.substr(0, common_prefix_len);
263 else if (common_prefix_len != null)
264 common_prefix = null;
265 return {count:data.length,
266 get_string: function (i) data[i][0],
267 get_description: function (i) data[i][1],
268 get_input_state: function (i) get_partial_completion_input_state(data[i][0], offset, pos, input),
269 get common_prefix_input_state () {
270 return common_prefix && get_partial_completion_input_state(common_prefix, offset, pos, input);
277 function merge_completers (completers) {
278 if(completers.length == 0)
280 return function (input, pos, conservative) {
283 for (let i = 0; i < completers.length; ++i) {
284 let r = yield completers[i](input, pos, conservative);
285 if (r != null && (r.count > 0 || "get_match_required" in r)) {
290 function forward (name) {
292 var args = Array.prototype.slice.call(arguments);
293 var i = args.shift();
294 for (var j=0; j < results.length; j++) {
297 if (name in r && r[name] != null) {
299 return r[name].apply(this, args);
309 function combine_or (name) {
312 for (var j=0; j < results.length; j++) {
314 if (name in r && r[name] != null) {
315 b = b || r[name].apply(this, arguments);
321 yield co_return({count: count,
322 get_string: forward('get_string'),
323 get_description: forward('get_description'),
324 get_input_state: forward('get_input_state'),
325 get_icon: forward('get_icon'),
326 destroy: forward('destroy'),
327 get_match_required: combine_or('get_match_required')
332 function nest_completions (completions, prefix, suffix) {
343 return [prefix + s + suffix, a + prefix.length, b + prefix.length];
346 __proto__: completions,
347 get_input_state: function (i) nest(completions.get_input_state(i)),
348 get common_prefix_input_state () {
349 let x = completions.common_prefix_input_state;
358 provide("minibuffer-completion");