mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-23 04:07:58 +01:00
Bunch more completion stuff. Add more/less indicators to completion listint.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
|
||||
//}}}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 () [
|
||||
|
||||
@@ -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))]]]
|
||||
});
|
||||
|
||||
|
||||
@@ -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;">⌃</span>
|
||||
<div/>
|
||||
<span style="display: block; text-align: center; height: .5ex; line-height: .5ex; padding-bottom: 1ex;">⌄</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";
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -343,7 +343,7 @@ const config = { //{{{
|
||||
},
|
||||
{
|
||||
argCount: "+",
|
||||
completer: function (context) completion.sidebar(context.filter),
|
||||
completer: function (context) completion.sidebar(context),
|
||||
literal: true
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user