1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-23 08:28:01 +01:00

Bunch more completion stuff. Add more/less indicators to completion listint.

This commit is contained in:
Kris Maglione
2008-11-26 03:22:13 +00:00
parent 2a3081f03f
commit f1a6bcc671
12 changed files with 290 additions and 317 deletions

View File

@@ -513,7 +513,7 @@ function Buffer() //{{{
stylesheetSwitchAll(window.content, args);
},
{
completer: function (context) completion.alternateStylesheet(context.filter),
completer: function (context) completion.alternateStylesheet(context),
literal: true
});
@@ -790,11 +790,9 @@ function Buffer() //{{{
{
var stylesheets = getAllStyleSheets(window.content);
stylesheets = stylesheets.filter(
return stylesheets.filter(
function (stylesheet) /^(screen|all|)$/i.test(stylesheet.media.mediaText) && !/^\s*$/.test(stylesheet.title)
);
return stylesheets;
},
get pageInfo() pageInfo,

View File

@@ -852,7 +852,7 @@ function Commands() //{{{
{
argCount: 2,
bang: true,
completer: function (context) completion.userCommand(context.filter),
completer: function (context) completion.userCommand(context),
options: [
[["-nargs"], commandManager.OPTION_STRING,
function (arg) /^[01*?+]$/.test(arg), ["0", "1", "*", "?", "+"]],
@@ -901,7 +901,7 @@ function Commands() //{{{
},
{
argCount: "1",
completer: function (context) completion.userCommand(context.filter)
completer: function (context) completion.userCommand(context)
});
//}}}

View File

@@ -46,23 +46,23 @@ function CompletionContext(editor, name, offset)
name = parent.name + "/" + name;
this.contexts = parent.contexts;
if (name in this.contexts)
{
self = this.contexts[name];
self.offset = parent.offset + (offset || 0);
return self;
}
this.contexts[name] = this;
this.anchored = parent.anchored;
this.parent = parent;
this.offset = parent.offset + (offset || 0);
this.keys = util.cloneObject(this.parent.keys);
else
self.contexts[name] = this;
self.anchored = parent.anchored;
self.filters = parent.filters.slice();
self.incomplete = false;
self.parent = parent;
self.offset = parent.offset + (offset || 0);
self.keys = util.cloneObject(parent.keys);
["compare", "editor", "filterFunc", "keys", "quote", "title", "top"].forEach(function (key)
self[key] = parent[key]);
["contextList", "onUpdate", "selectionTypes", "tabPressed", "updateAsync", "value"].forEach(function (key) {
self.__defineGetter__(key, function () this.top[key]);
self.__defineSetter__(key, function (val) this.top[key] = val);
});
this.incomplete = false;
if (self != this)
return self;
}
else
{
@@ -71,7 +71,26 @@ function CompletionContext(editor, name, offset)
else
this.editor = editor;
this.compare = function (a, b) String.localeCompare(a.text, b.text);
this.filterFunc = completion.filter;
this.filterFunc = function (items)
{
let self = this;
return this.filters.reduce(function (res, filter)
res.filter(function (item) filter.call(self, item)),
items);
}
this.filters = [function (item) {
let text = Array.concat(this.getKey(item, "text"));
let texts = this.ignoreCase ? text.map(String.toLowerCase) : text;
for (let [i, str] in Iterator(texts))
{
if (this.match(str))
{
item.text = text[i];
return true;
}
}
return false;
}];
this.keys = { text: 0, description: 1, icon: "icon" };
this.offset = offset || 0;
this.onUpdate = function () true;
@@ -85,9 +104,11 @@ function CompletionContext(editor, name, offset)
}
this.name = name || "";
this.cache = {};
this.key = "";
this.itemCache = {};
this.process = [];
this._completions = []; // FIXME
this.getKey = function (item, key) item.item[self.keys[key]];
this.getKey = function (item, key) (typeof self.keys[key] == "function") ? self.keys[key].call(this, item) : item.item[self.keys[key]];
}
CompletionContext.prototype = {
// Temporary
@@ -101,7 +122,30 @@ CompletionContext.prototype = {
let prefix = self.value.substring(minStart, context.offset);
return [{ text: prefix + item.text, item: item.item } for ([i, item] in Iterator(context.items))];
});
return { start: minStart, items: util.Array.flatten(items) }
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring }
},
get allSubstrings()
{
let self = this;
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.items.length && context.hasItems)]);
let items = this.contextList.map(function (context) {
if (!context.hasItems)
return [];
let prefix = self.value.substring(minStart, context.offset);
return context.substrings.map(function (str) prefix + str);
});
return util.Array.uniq(util.Array.flatten(items), true);
},
get longestAllSubstring()
{
let substrings = this.allSubstrings;
return substrings.reduce(function (res, str)
res.filter(function (s) {
let len = Math.min(s.length, str.length);
return str.substr(0, len) == s.substr(0, len)
}),
substrings)
.reduce(function (a, b) a.length > b.length ? a : b, "");
},
get caret() (this.editor ? this.editor.selection.getRangeAt(0).startOffset : this.value.length) - this.offset,
@@ -125,19 +169,17 @@ CompletionContext.prototype = {
get filterFunc() this._filterFunc || function (items) items,
set filterFunc(val) this._filterFunc = val,
get regenerate() this._generate && (!this.completions || this.cache.key != this.key || this.cache.offset != this.offset),
set regenerate(val) { if (val) delete this.cache.offset },
get regenerate() this._generate && (!this.completions || !this.itemCache[this.key] || this.cache.offset != this.offset),
set regenerate(val) { if (val) delete this.itemCache[this.key] },
get generate() !this._generate ? null : function ()
{
let updateAsync = this.updateAsync; // XXX
this.updateAsync = false;
this.completions = this._generate.call(this);
this.updateAsync = updateAsync;
if (this.offset != this.cache.offset)
this.itemCache = {};
this.cache.offset = this.offset;
this.cache.key = this.key;
return this.completions;
if (!this.itemCache[this.key])
this.itemCache[this.key] = this._generate.call(this);
return this.itemCache[this.key];
},
set generate(arg)
{
@@ -174,6 +216,9 @@ CompletionContext.prototype = {
this.process = format.process || this.process;
},
// XXX
get ignoreCase() this.filter == this.filter.toLowerCase(),
get items()
{
if (!this.hasItems)
@@ -182,18 +227,22 @@ CompletionContext.prototype = {
return this.cache.filtered;
this.cache.rows = [];
let items = this.completions;
if (this.regenerate)
items = this.generate();
if (this.generate)
{
// XXX
let updateAsync = this.updateAsync;
this.updateAsync = false;
this.completions = items = this.generate();
this.updateAsync = updateAsync;
}
this.cache.filter = this.filter;
if (items == null)
return items;
let self = this;
delete this._substrings;
completion.getKey = this.getKey; // XXX
let filtered = this.filterFunc(items.map(function (item) ({ text: item[self.keys["text"]], item: item })),
this.filter, this.anchored);
completion.getKey = null;
let filtered = this.filterFunc(items.map(function (item) ({ text: item[self.keys["text"]], item: item })));
if (self.quote)
filtered.forEach(function (item) item.text = self.quote(item.text));
@@ -218,6 +267,43 @@ CompletionContext.prototype = {
this._process = process;
},
get substrings()
{
let items = this.items;
if (items.length == 0)
return [];
if (this._substrings)
return this._substrings;
let fixCase = this.ignoreCase ? String.toLowerCase : function (str) str;
let text = fixCase(items[0].text);
let filter = fixCase(this.filter);
if (this.anchored)
{
function compare (text, s) text.substr(0, s.length) == s;
substrings = util.map(util.range(filter.length, text.length),
function (end) text.substring(0, end));
}
else
{
function compare (text, s) text.indexOf(s) >= 0;
substrings = [];
let start = 0;
let idx;
let length = filter.length;
while ((idx = text.indexOf(filter, start)) > -1 && idx < length)
{
for (let end in util.range(idx + length, text.length + 1))
substrings.push(text.substring(idx, end));
start = idx + 1;
}
}
substrings = items.reduce(function (res, {text: text})
res.filter(function (str) compare(fixCase(text), str)),
substrings);
return this._substrings = substrings;
},
advance: function advance(count)
{
this.offset += count;
@@ -243,7 +329,7 @@ CompletionContext.prototype = {
let cache = this.cache.rows;
let reverse = start > end;
start = Math.max(0, start || 0);
end = Math.min(items.length, end ? end : items.length);
end = Math.min(items.length, end != null ? end : items.length);
return util.map(util.range(start, end, reverse),
function (i) cache[i] = cache[i] || util.xmlToDom(self.createRow(items[i]), doc));
},
@@ -281,6 +367,19 @@ CompletionContext.prototype = {
catch (e) {}
},
match: function (str)
{
let filter = this.filter;
if (this.ignoreCase)
{
filter = filter.toLowerCase();
str = str.toLowerCase();
}
if (this.anchored)
return str.substr(0, filter.length) == filter;
return str.indexOf(filter) > -1;
},
reset: function reset()
{
let self = this;
@@ -390,17 +489,8 @@ function Completion() //{{{
* wrapped in @last after @offset characters are sliced
* off of it and it's quoted.
*/
this.objectKeys = function objectKeys(objects)
this.objectKeys = function objectKeys(obj)
{
if (!(objects instanceof Array))
objects = [objects];
let [obj, key] = objects;
let cache = this.context.cache.objects || {};
this.context.cache.objects = cache;
if (key in cache)
return cache[key];
// Things we can dereference
if (["object", "string", "function"].indexOf(typeof obj) == -1)
return [];
@@ -430,29 +520,7 @@ function Completion() //{{{
key = "";
item.key = key;
});
return cache[key] = compl;
}
this.filter = function filter(context, compl, name, anchored, key, last, offset)
{
context.title = [name];
context.anchored = anchored;
context.filter = key;
if (last != undefined) // Escaping the key (without adding quotes), so it matches the escaped completions.
key = util.escapeString(key.substr(offset), "");
if (last != undefined) // Prepend the quote delimiter to the substrings list, so it's not stripped on <Tab>
substrings = substrings.map(function (s) last + s);
let res;
if (last != undefined) // We're looking for a quoted string, so, strip whatever prefix we have and quote the rest
res = compl.map(function (a) [util.escapeString(a[0].substr(offset), last), a[1]]);
else // We're not looking for a quoted string, so filter out anything that's not a valid identifier
res = compl.filter(function isIdent(a) /^[\w$][\w\d$]*$/.test(a[0]));
if (!anchored)
res = res.filter(function ([k]) util.compareIgnoreCase(k.substr(0, key.length), key));
context.completions = res;
return compl;
}
this.eval = function eval(arg, key, tmp)
@@ -697,19 +765,46 @@ function Completion() //{{{
return [dot + 1 + space.length, obj, key];
}
function fill(context, obj, name, compl, anchored, key, last, offset)
{
context.title = [name];
context.key = name;
context.anchored = anchored;
context.filter = key;
context.itemCache = context.parent.itemCache;
if (compl)
context.completions = compl;
else
context.generate = function () self.objectKeys(obj);
if (last != undefined) // Escaping the key (without adding quotes), so it matches the escaped completions.
key = util.escapeString(key.substr(offset), "");
// FIXME
if (last != undefined) // Prepend the quote delimiter to the substrings list, so it's not stripped on <Tab>
substrings = substrings.map(function (s) last + s);
let res;
if (last != undefined) // We're looking for a quoted string, so, strip whatever prefix we have and quote the rest
context.quote = function (text) util.escapeString(text, last);
else // We're not looking for a quoted string, so filter out anything that's not a valid identifier
context.filters.push(function (item) /^[\w$][\w\d$]*$/.test(item.text));
if (!anchored)
context.filters.push(function (item) util.compareIgnoreCase(item.text.substr(0, key.length), key));
}
function complete(objects, key, compl, string, last)
{
for (let [,obj] in Iterator(objects))
{
obj[3] = compl || this.objectKeys(obj);
this.context.fork(obj[1], top[OFFSET], this, "filter",
obj[3], obj[1], true, key + (string || ""), last, key.length);
this.context.fork(obj[1], top[OFFSET], this, fill, obj[0], obj[1], compl,
true, key + (string || ""), last, key.length);
}
for (let [,obj] in Iterator(objects))
{
obj[1] += " (substrings)";
this.context.fork(obj[1], top[OFFSET], this, "filter",
obj[3], obj[1], false, key + (string || ""), last, key.length);
this.context.fork(obj[1], top[OFFSET], this, fill, obj[0], obj[1], compl,
false, key + (string || ""), last, key.length);
}
}
@@ -822,102 +917,6 @@ function Completion() //{{{
};
let javascript = new Javascript();
function buildSubstrings(str, filter)
{
if (substrings.length)
{
substrings = substrings.filter(function strIndex(s) str.indexOf(s) >= 0);
return;
}
if (filter == "")
return;
let length = filter.length;
let start = 0;
let idx;
while ((idx = str.indexOf(filter, start)) > -1)
{
for (let end in util.range(idx + length, str.length + 1))
substrings.push(str.substring(idx, end));
start = idx + 1;
}
}
// function uses smartcase
// list = [ [['com1', 'com2'], 'text'], [['com3', 'com4'], 'text'] ]
function buildLongestCommonSubstring(list, filter, favicon)
{
var filtered = [];
var ignorecase = false;
if (filter == filter.toLowerCase())
ignorecase = true;
var longest = false;
if (options["wildmode"].indexOf("longest") >= 0)
longest = true;
for (let [,item] in Iterator(list))
{
let text = completion.getKey(item, "text");
var complist = text instanceof Array ? text : [text];
for (let [,compitem] in Iterator(complist))
{
let str = !ignorecase ? compitem : String(compitem).toLowerCase();
if (str.indexOf(filter) == -1)
continue;
item.text = compitem;
filtered.push(item);
if (longest)
buildSubstrings(str, filter);
break;
}
}
return filtered;
}
// this function is case sensitive
function buildLongestStartingSubstring(list, filter, favicon)
{
var filtered = [];
var longest = false;
if (options["wildmode"].indexOf("longest") >= 0)
longest = true;
for (let [,item] in Iterator(list))
{
let text = completion.getKey(item, "text");
var complist = text instanceof Array ? text : [text];
for (let [,compitem] in Iterator(complist))
{
if (compitem.substr(0, filter.length) != filter)
continue;
item.text = compitem;
filtered.push(item);
if (longest)
{
if (substrings.length == 0)
{
var length = compitem.length;
for (let k = filter.length; k <= length; k++)
substrings.push(compitem.substring(0, k));
}
else
{
substrings = substrings.filter(function strIndex(s) compitem.indexOf(s) == 0);
}
}
break;
}
}
return filtered;
}
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
@@ -930,8 +929,7 @@ function Completion() //{{{
setFunctionCompleter: function setFunctionCompleter(funcs, completers)
{
if (!(funcs instanceof Array))
funcs = [funcs];
funcs = Array.concat(funcs);
for (let [,func] in Iterator(funcs))
{
func.liberatorCompleter = function liberatorCompleter(func, obj, string, args) {
@@ -958,15 +956,6 @@ function Completion() //{{{
return context.items.map(function (i) i.item);
},
// generic filter function, also builds substrings needed
// for :set wildmode=list:longest, if necessary
filter: function filter(array, filter, matchFromBeginning)
{
if (matchFromBeginning)
return buildLongestStartingSubstring(array, filter);
return buildLongestCommonSubstring(array, filter);
},
// cancel any ongoing search
cancel: function cancel()
{
@@ -1009,9 +998,9 @@ function Completion() //{{{
for (let [,elem] in Iterator(urls))
{
let item = elem.item || elem; // Kludge
var url = item.url || "";
var title = item.title || "";
var tags = item.tags || [];
let url = item.url || "";
let title = item.title || "";
let tags = item.tags || [];
if (ignorecase)
{
url = url.toLowerCase();
@@ -1032,13 +1021,6 @@ function Completion() //{{{
continue;
}
// TODO: refactor out? And just build if wildmode contains longest?
// Of course --Kris
if (substrings.length == 0) // Build the substrings
buildSubstrings(url, filter);
else
substrings = substrings.filter(function strIndex(s) url.indexOf(s) >= 0);
filtered.push(elem);
}
@@ -1083,20 +1065,19 @@ function Completion() //{{{
////////////////////// COMPLETION TYPES ////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
autocmdEvent: function autocmdEvent(filter) [0, this.filter(config.autocommands, filter)],
autocmdEvent: function autocmdEvent(context)
{
context.completions = config.autocommands;
},
bookmark: function bookmark(context, tags)
{
context.title = ["Bookmark", "Title"];
context.format = bookmarks.format;
context.completions = bookmarks.get(context.filter)
context.filters = [];
if (tags)
{
let filterFunc = context.filterFunc;
context.filterFunc = function (items, filter, anchored)
filterFunc.call(this, items, filter, anchored)
.filter(function ({item: item}) tags.every(function (tag) item.tags.indexOf(tag) > -1));
}
context.filters.push(function ({item: item}) tags.every(function (tag) item.tags.indexOf(tag) > -1));
},
buffer: function buffer(context)
@@ -1153,24 +1134,26 @@ function Completion() //{{{
context.completions = [k for (k in commands)];
},
dialog: function dialog(filter) [0, this.filter(config.dialogs, filter)],
dialog: function dialog(context)
{
context.title = ["Dialog"];
context.completions = config.dialogs;
},
directory: function directory(context, tail)
{
this.file(context, tail);
context.completions = context.completions.filter(function (i) i[1] == "Directory");
context.filters.push(function (item) this.getKey(item, "description") == "Directory");
},
environment: function environment(filter)
environment: function environment(context)
{
let command = liberator.has("Win32") ? "set" : "env";
let lines = io.system(command).split("\n");
lines.pop();
let vars = lines.map(function (line) (line.match(/([^=]+)=(.+)/) || []).slice(1));
return [0, this.filter(vars, filter)];
context.title = ["Environment Variable", "Value"];
context.generate = function () lines.map(function (line) (line.match(/([^=]+)=(.+)/) || []).slice(1));
},
// provides completions for ex commands, including their arguments
@@ -1238,7 +1221,6 @@ function Completion() //{{{
context.keys = { text: 0, description: 1, icon: 2 };
context.anchored = true;
context.key = dir;
context.quote = function (text) text.replace(" ", "\\ ", "g");
context.generate = function generate()
{
context.cache.dir = dir;
@@ -1312,7 +1294,6 @@ function Completion() //{{{
];
context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
let filter = context.filter;
context.completions.forEach(function ([item]) buildSubstrings(item, filter));
});
completionService.stopSearch();
completionService.startSearch(context.filter, "", context.result, {
@@ -1428,33 +1409,31 @@ function Completion() //{{{
}
},
sidebar: function sidebar(filter)
sidebar: function sidebar(context)
{
let menu = document.getElementById("viewSidebarMenu");
let panels = Array.map(menu.childNodes, function (n) [n.label, ""]);
return [0, this.filter(panels, filter)];
context.title = ["Sidebar Panel"];
context.completions = Array.map(menu.childNodes, function (n) [n.label, ""]);
},
alternateStylesheet: function alternateStylesheet(filter)
alternateStylesheet: function alternateStylesheet(context)
{
let completions = buffer.alternateStyleSheets.map(
function (stylesheet) [stylesheet.title, stylesheet.href || "inline"]
);
context.title = ["Stylesheet", "Location"];
context.keys = { text: "title", description: function (item) item.href };
// unify split style sheets
let completions = buffer.alternateStyleSheets;
completions.forEach(function (stylesheet) {
completions = completions.filter(function (completion) {
if (stylesheet[0] == completion[0] && stylesheet[1] != completion[1])
stylesheet.href = stylesheet.href || "inline";
completions = completions.filter(function (sheet) {
if (stylesheet.title == sheet.title && stylesheet != sheet)
{
stylesheet[1] += ", " + completion[1];
stylesheet.href += ", " + sheet.href;
return false;
}
return true;
});
});
return [0, this.filter(completions, filter)];
},
// filter a list of urls
@@ -1482,11 +1461,11 @@ function Completion() //{{{
this.urlCompleters[opt] = UrlCompleter.apply(null, Array.slice(arguments));
},
userCommand: function userCommand(filter)
userCommand: function userCommand(context)
{
let cmds = commands.getUserCommands();
cmds = cmds.map(function (cmd) [cmd.name, ""]);
return [0, this.filter(cmds, filter)];
context.title = ["User Command", "Definition"];
context.keys = { text: "name", description: "replacementText" };
context.completions = commands.getUserCommands();
},
userMapping: function userMapping(context, args, modes)
@@ -1494,7 +1473,7 @@ function Completion() //{{{
if (args.completeArg == 0)
{
let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))];
context.completions = this.filter(maps, args.arguments[0]);
context.completions = maps;
}
}
// }}}

View File

@@ -113,7 +113,7 @@ function AutoCommands() //{{{
{
argCount: 3,
bang: true,
completer: function (context) completion.autocmdEvent(context.filter),
completer: function (context) completion.autocmdEvent(context),
literal: true
});
@@ -125,7 +125,7 @@ function AutoCommands() //{{{
commands.get("doautocmd").action.call(this, args);
},
{
completer: function (context) completion.autocmdEvent(context.filter),
completer: function (context) completion.autocmdEvent(context),
literal: true
}
);
@@ -163,7 +163,7 @@ function AutoCommands() //{{{
}
},
{
completer: function (context) completion.autocmdEvent(context.filter),
completer: function (context) completion.autocmdEvent(context),
literal: true
}
);

View File

@@ -7,7 +7,7 @@
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
function load(script)
function load(script, i)
{
try
{
@@ -18,6 +18,8 @@
if (Components.utils.reportError)
Components.utils.reportError(e);
dump("liberator: Loading script " + script + ": " + e + "\n");
if (!i || i < 3)
return load(script, i + 1); // Sometimes loading (seemingly randomly) fails
}
}

View File

@@ -203,7 +203,7 @@ const liberator = (function () //{{{
{
argCount: "1",
bang: true,
completer: function (context) completion.dialog(context.filter)
completer: function (context) completion.dialog(context)
});
// TODO: move this

View File

@@ -91,18 +91,10 @@ function Mail() //{{{
function getFolderCompletions(filter)
{
var completions = [];
var folders = mail.getFolders(filter);
for (let folder = 0; folder < folders.length; folder++)
{
completions.push([folders[folder].server.prettyName + ": "
+ folders[folder].name,
"Unread: " + folders[folder].getNumUnread(false)]);
}
//return [0, completion.filter(completions, filter)];
return [0, completions];
let folders = mail.getFolders(filter);
context.completions = folders.map(function (folder)
[folder.server.prettyName + ": " + folder.name,
"Unread: " + folder.getNumUnread(false)]);
}
function getCurrentFolderIndex()
@@ -684,7 +676,7 @@ function Mail() //{{{
SelectFolder(folder.URI);
},
{
completer: function (context) getFolderCompletions(context.filter),
completer: function (context) getFolderCompletions(context),
count: true
});
@@ -726,12 +718,12 @@ function Mail() //{{{
commands.add(["copy[to]"],
"Copy selected messages",
function (args) { moveOrCopy(true, args.string); },
{ completer: function (context) getFolderCompletions(context.filter) });
{ completer: function (context) getFolderCompletions(context) });
commands.add(["move[to]"],
"Move selected messages",
function (args) { moveOrCopy(false, args.string); },
{ completer: function (context) getFolderCompletions(context.filter) });
{ completer: function (context) getFolderCompletions(context) });
commands.add(["empty[trash]"],
"Empty trash of the current account",

View File

@@ -52,6 +52,7 @@ function Option(names, description, type, defaultValue, extraInfo) //{{{
this.getter = extraInfo.getter || null;
this.completer = extraInfo.completer || null;
this.validator = extraInfo.validator || null;
this.checkHas = extraInfo.checkHas || null;
// this property is set to true whenever the option is first set
// useful to see whether it was changed by some rc file
@@ -73,6 +74,18 @@ function Option(names, description, type, defaultValue, extraInfo) //{{{
if (this.globalvalue == undefined)
this.globalvalue = this.defaultValue;
this.__defineGetter__("values", function () this.getValues(this.scope));
this.getValues = function (scope)
{
let value = this.get(scope);
if (this.type == "stringlist")
return value.split(",");
if (this.type == "charlist")
return Array.slice(value);
return value;
};
this.get = function (scope)
{
if (scope)
@@ -124,18 +137,20 @@ function Option(names, description, type, defaultValue, extraInfo) //{{{
this.hasChanged = true;
};
this.has = function ()
{
let value = this.value;
if (this.type == "stringlist")
value = this.value.split(",");
/* Return whether some argument matches */
return Array.some(arguments, function (val) value.indexOf(val) >= 0);
};
this.__defineGetter__("value", this.get);
this.__defineSetter__("value", this.set);
this.has = function ()
{
let self = this;
let test = function (val) values.indexOf(val) >= 0;
if (this.checkHas)
test = function (val) values.some(function (value) self.checkHas(value, val));
let values = this.values;
/* Return whether some argument matches */
return Array.some(arguments, function (val) test(val))
};
this.hasName = function (name)
{
return this.names.indexOf(name) >= 0;
@@ -695,28 +710,18 @@ function Options() //{{{
if (special) // list completions for about:config entries
{
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var prefArray = prefs.getChildList("", { value: 0 });
prefArray.sort();
if (filter.length > 0 && filter.lastIndexOf("=") == filter.length - 1)
if (filter[filter.length - 1] == "=")
{
for (let [,name] in Iterator(prefArray))
{
if (name.match("^" + filter.substr(0, filter.length - 1) + "$" ))
{
let value = options.getPref(name) + "";
return [filter.length + 1, [[value, ""]]];
}
}
return [0, []];
context.advance(filter.length);
context.completions = [options.getPref(filter.substr(0, filter.length - 1)), "Current Value"];
return;
}
optionCompletions = prefArray.map(function (pref)
[pref, options.getPref(pref)]);
return [0, completion.filter(optionCompletions, filter)];
let prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
context.keys = [function (pref) pref, function (pref) options.getPref(pref)];
context.completions = prefs.getChildList("", { value: 0 });
return;
}
let prefix = (filter.match(/^(no|inv)/) || [""])[0];
@@ -728,19 +733,13 @@ function Options() //{{{
let opts = (opt for (opt in options)
if ((opt.scope & scope) && (!prefix || opt.type == "boolean" || prefix == "inv" && /list$/.test(opt.type))));
if (!filter)
if (filter.indexOf("=") == -1)
{
return [0, [[prefix + option.name, option.description] for (option in opts)]];
}
else if (filter.indexOf("=") == -1)
{
for (let option in opts)
optionCompletions.push([[prefix + name, option.description]
for each (name in option.names)
if (name.indexOf(filter) == 0)]);
optionCompletions = util.Array.flatten(optionCompletions);
return [0, completion.filter(optionCompletions, prefix + filter, true)];
context.title = ["Option"];
context.quote = function (name) prefix + name;
context.keys = { text: "names", description: "description" };
context.completions = [opt for (opt in opts)];
return;
}
else if (prefix == "no")
return;
@@ -754,7 +753,7 @@ function Options() //{{{
context.highlight(0, name.length, "SPELLCHECK");
if (opt.get || opt.reset || !option || prefix)
return [0, []];
return;
let completer = option.completer;
@@ -773,8 +772,8 @@ function Options() //{{{
break;
}
len = filter.length - len;
filter = filter.substr(len);
context.advance(filter.length - len);
filter = context.filter;
/* Not vim compatible, but is a significant enough improvement
* that it's worth breaking compatibility.
@@ -800,7 +799,9 @@ function Options() //{{{
}
}
}
return [len, completion.filter(completions, filter, true)];
context.compare = function (a, b) 0;
context.title = ["Option Value"];
context.completions = completions;
},
literal: true,
serial: function () [

View File

@@ -463,15 +463,9 @@ liberator.registerObserver("load_commands", function ()
},
{
argCount: 2,
// FIXME: Ugly.
completer: function (context) [0, completion.filter(
[[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}</>]
for ([i, s] in styles.userSheets)
]
.concat([[s, ""] for each (s in styles.sites)])
, context.filter)],
completer: function (context) { context.completions = styles.sites.map(function (site) [site, ""]); },
literal: true,
options: [[["-index", "-i"], commands.OPTION_INT, null, function () [[k, v.name || v.sites.join(",") + " " + v.css] for ([k, v] in Iterator(styles.userNames))]],
options: [[["-index", "-i"], commands.OPTION_INT, null, function () [[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}</>] for ([i, s] in styles.userSheets)]],
[["-name", "-n"], commands.OPTION_STRING, null, function () [[k, v.css] for ([k, v] in Iterator(styles.userNames))]]]
});

View File

@@ -128,12 +128,12 @@ function CommandLine() //{{{
historyIndex = UNINITIALIZED;
// TODO: call just once, and not on each <Tab>
let wildmode = options["wildmode"].split(",");
let wildType = wildmode[Math.min(wildIndex++, wildmode.length - 1)];
let wildmode = options.get("wildmode");
let wildType = wildmode.values[Math.min(wildIndex++, wildmode.values.length - 1)];
let hasList = /^list(:|$)/.test(wildType);
let longest = /(^|:)longest$/.test(wildType);
let full = !longest && /(^|:)full/.test(wildType);
let hasList = wildmode.checkHas(wildType, "list");
let longest = wildmode.checkHas(wildType, "longest");
let full = !longest && wildmode.checkHas(wildType, "full");
// we need to build our completion list first
if (completionIndex == UNINITIALIZED)
@@ -196,7 +196,7 @@ function CommandLine() //{{{
{
var compl = null;
if (longest && completions.items.length > 1)
compl = completion.longestSubstring;
compl = completions.longestSubstring;
else if (full)
compl = completions.items[completionIndex].text;
else if (completions.items.length == 1)
@@ -207,7 +207,7 @@ function CommandLine() //{{{
setCommand(command.substring(0, completions.start) + compl + completionPostfix);
commandWidget.selectionStart = commandWidget.selectionEnd = completions.start + compl.length;
if (longest)
liberator.triggerCallback("change", currentExtendedMode, this.getCommand());
liberator.triggerCallback("change", currentExtendedMode, commandline.getCommand());
// Start a new completion in the next iteration. Useful for commands like :source
// RFC: perhaps the command can indicate whether the completion should be restarted
@@ -497,9 +497,14 @@ function CommandLine() //{{{
},
validator: function validator(value)
{
return value.split(",").every(
function (item) /^(full|longest|list|list:full|list:longest|)$/.test(item)
);
let self = this;
return value.split(",").every(function (opt)
self.completer().some(function ([key]) key == opt))
},
checkHas: function (value, val)
{
let [first, second] = value.split(":", 2);
return first == val || second == val;
}
});
@@ -1335,8 +1340,11 @@ function ItemList(id) //{{{
<div class="hl-Completions">
{context.createRow(context.title || [], "hl-CompTitle")}
</div>
<span style="display: block; text-align: center; height: .5ex; line-height: .5ex;">&#x2303;</span>
<div/>
<span style="display: block; text-align: center; height: .5ex; line-height: .5ex; padding-bottom: 1ex;">&#x2304;</span>
</div>);
context.cache.arrows = context.cache.dom.getElementsByTagName("span");
completionBody.appendChild(context.cache.dom);
});
}
@@ -1362,20 +1370,24 @@ function ItemList(id) //{{{
let off = 0;
function getRows(context)
{
function fix(n) Math.max(0, Math.min(len, n));
let len = context.items.length;
let start = off;
off += len;
return context.getRows(offset - start, endIndex - start, doc);
return [fix(offset - start), fix(endIndex - start)];
}
items.contextList.forEach(function fill_eachContext(context) {
let dom = context.cache.dom;
if (!dom)
return;
let [start, end] = getRows(context);
context.cache.arrows[0].style.display = (start == 0) ? "none" : "block";
context.cache.arrows[1].style.display = (end == context.items.length) ? "none" : "block";
let d = stuff.cloneNode(true);
for (let [,row] in Iterator(getRows(context)))
for (let [,row] in Iterator(context.getRows(start, end, doc)))
d.appendChild(row);
dom.replaceChild(d, dom.childNodes[3] || dom.childNodes[1]);
dom.replaceChild(d, dom.getElementsByTagName("div")[1]);
});
noCompletions.style.display = off > 0 ? "none" : "block";

View File

@@ -38,13 +38,8 @@ const util = { //{{{
return obj;
},
// flatten an array: [["foo", "bar"], ["baz"]] -> ["foo", "bar", "baz"]
flatten: function (ary)
{
if (ary.length == 0)
return [];
return Array.concat.apply(Array, ary);
},
// flatten an array: [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
flatten: function (ary) Array.concat.apply([], ary),
iterator: function (ary)
{

View File

@@ -343,7 +343,7 @@ const config = { //{{{
},
{
argCount: "+",
completer: function (context) completion.sidebar(context.filter),
completer: function (context) completion.sidebar(context),
literal: true
});