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

More completion stuff.

This commit is contained in:
Kris Maglione
2008-11-22 06:53:44 +00:00
parent 4f1c195aa4
commit fcc799aa6a
12 changed files with 292 additions and 266 deletions

View File

@@ -240,10 +240,10 @@ function Bookmarks() //{{{
"Set the default search engine",
"string", "google",
{
completer: function (filter) completion.url("", "s")[1],
completer: function (filter) completion._url(filter, "s").items,
validator: function (value)
{
return completion.url("", "s")[1].some(function (s) s[0] == value);
return completion._url("", "s").items.some(function (s) s[0] == value);
}
});
@@ -350,7 +350,6 @@ function Bookmarks() //{{{
{
if (bypassCache) // Is this really necessary anymore?
cache.load();
return completion.cached("bookmarks", filter, function () cache.bookmarks, "filterURLArray", tags);
},

View File

@@ -564,7 +564,7 @@ function Buffer() //{{{
{
argCount: "?",
bang: true,
completer: function (filter) completion.file(filter)
completer: function (filter, bang, args, context) completion.file(context)
});
commands.add(["st[op]"],
@@ -578,7 +578,7 @@ function Buffer() //{{{
{
argCount: "?",
bang: true,
completer: function (filter) completion.url(filter, "bhf")
completer: function (filter, bang, args, context) completion.url(context, "bhf")
});
commands.add(["zo[om]"],

View File

@@ -112,8 +112,7 @@ Command.prototype = {
commandline.inputMultiline(new RegExp("^" + matches[2] + "$", "m"),
function (args)
{
args = this.parseArgs(matches[1] + "\n" + args);
args = self.parseArgs(matches[1] + "\n" + args);
if (args)
self.action.call(self, args, special, count, modifiers);
});
@@ -122,7 +121,6 @@ Command.prototype = {
}
args = this.parseArgs(args);
if (args)
this.action.call(this, args, special, count, modifiers);
},

View File

@@ -32,31 +32,78 @@ modules._cleanEval = function (__liberator_eval_arg, __liberator_eval_tmp)
return window.eval(__liberator_eval_arg);
}
function CompletionContext(editor, offset)
function CompletionContext(editor, name, offset)
{
if (!name)
name = "";
if (editor instanceof arguments.callee)
{
let parent = editor;
name = parent.name + "/" + name;
this.contexts = parent.contexts;
if (name in this.contexts)
return this.contexts[name];
this.contexts[name] = this;
this.parent = parent;
this.editor = parent.editor;
this.offset = parent.offset + (offset || 0);
this.__defineGetter__("tabPressed", function () this.parent.tabPressed);
this.contexts = this.parent.contexts;
this.__defineGetter__("onUpdate", function () this.parent.onUpdate);
this.incomplete = false;
}
else
{
this.editor = editor;
this.offset = offset || 0;
this.tabPressed = false;
this.contexts = {};
this.onUpdate = function () true;
this.contexts = { name: this };
this.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
this.reset();
}
this.name = name || "";
this._items = []; // FIXME
this.selectionTypes = {};
}
CompletionContext.prototype = {
// Temporary
get allItems()
{
let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.items.length && context.hasItems)]);
let items = [];
for each (let [k, context] in Iterator(this.contexts))
{
let prefix = this.value.substring(minStart, context.offset);
if (context.hasItems)
{
items.push(context.items.map(function (item) {
if (!("text" in item))
item = { icon: item[2], text: item[0], description: item[1] };
else // FIXME
item = util.Array.assocToObj([x for (x in Iterator(item))]);
item.text = prefix + item.text;
return item;
}));
}
}
return { start: minStart, items: util.Array.flatten(items) }
},
get caret() this.editor.selection.getRangeAt(0).startOffset - this.offset,
get contextList() [v for ([k, v] in Iterator(this.contexts))],
get filter() this.value.substr(this.offset, this.caret),
get items() this._items,
set items(items)
{
this.hasItems = items.length > 0;
this._items = items;
this.onUpdate.call(this);
},
get value() this.editor.rootElement.textContent,
advance: function (count)
@@ -64,12 +111,12 @@ CompletionContext.prototype = {
this.offset += count;
},
fork: function (name, offset)
fork: function (name, offset, completer, self)
{
if (!(name in this.contexts))
this.contexts[name] = new CompletionContext(this);
this.contexts[name].offset = this.offset + offset;
return this.contexts[name];
let context = new CompletionContext(this, name, offset);
if (completer)
return completer.apply(self, [context].concat(Array.slice(arguments, 4)));
return context;
},
highlight: function (start, length, type)
@@ -98,14 +145,23 @@ CompletionContext.prototype = {
reset: function ()
{
let self = this;
if (this.parent)
throw Error();
// Not ideal.
for (let type in this.selectionTypes)
for each (let type in this.selectionTypes)
this.highlight(0, 0, type);
this.selectionTypes = {};
this.tabPressed = false;
this.offset = 0;
//for (let key in (k for ([k, v] in Iterator(self.contexts)) if (v.offset > this.caret)))
// delete this.contexts[key];
for each (let context in this.contexts)
{
context.hasItems = false;
context.incomplete = false;
}
},
}
function Completion() //{{{
@@ -130,23 +186,6 @@ function Completion() //{{{
var cacheResults = {}
var substrings = [];
var historyCache = [];
var historyResult = null;
var completionCache = [];
var historyTimer = new util.Timer(50, 100, function histTimer() {
let comp = [];
for (let i in util.range(0, historyResult.matchCount))
comp.push([historyResult.getValueAt(i),
historyResult.getCommentAt(i),
historyResult.getImageAt(i)]);
//let foo = ["", "IGNORED", "FAILURE", "NOMATCH", "SUCCESS", "NOMATCH_ONGOING", "SUCCESS_ONGOING"];
// TODO: we need to have a "completionCacheAfter" to allow cpt=slf
historyCache = comp;
commandline.setCompletions({ get items() completionCache.concat(historyCache),
incompleteResult: historyResult.searchResult >= historyResult.RESULT_NOMATCH_ONGOING ? true : false });
});
function Javascript()
{
@@ -228,6 +267,8 @@ function Completion() //{{{
// Things we can dereference
if (["object", "string", "function"].indexOf(typeof obj) == -1)
continue;
if (!obj)
continue;
// XPCNativeWrappers, etc, don't show all accessible
// members until they're accessed, so, we look at
@@ -431,7 +472,7 @@ function Completion() //{{{
lastIdx = i;
}
this.complete = function complete(context)
this.complete = function (context)
{
this.context = context;
let string = context.filter;
@@ -446,10 +487,10 @@ function Completion() //{{{
catch (e)
{
if (e.message != "Invalid JS")
Components.utils.reportError(e);
liberator.reportError(e);
// liberator.dump(util.escapeString(string) + ": " + e + "\n" + e.stack);
lastIdx = 0;
return [0, []];
return;
}
/* Okay, have parse stack. Figure out what we're completing. */
@@ -488,7 +529,7 @@ function Completion() //{{{
cacheKey = str.substring(statement, dot);
obj = self.eval(s, cacheKey, obj);
}
return obj;
return [[obj], str.substring(statement, stop + 1)];
}
function getObjKey(frame)
@@ -498,7 +539,7 @@ function Completion() //{{{
let end = (frame == -1 ? lastIdx : get(frame + 1)[OFFSET]);
cacheKey = null;
let obj = [modules, window]; // Default objects;
let obj = [[modules, "modules"], [window, "window"]]; // Default objects;
/* Is this an object dereference? */
if (dot < statement) // No.
dot = statement - 1;
@@ -509,6 +550,16 @@ function Completion() //{{{
return [dot + 1 + space.length, obj, key];
}
function complete(objects, key, compl, string, last)
{
for (let [,obj] in Iterator(objects))
{
let ctxt = this.context.fork(obj[1], top[OFFSET]);
ctxt.title = [obj[1]];
ctxt.items = this.filter(compl || this.objectKeys(obj[0]), key + (string || ""), last, key.length);
}
}
// In a string. Check if we're dereferencing an object.
// Otherwise, do nothing.
if (last == "'" || last == '"')
@@ -537,7 +588,7 @@ function Completion() //{{{
// Yes. If the [ starts at the begining of a logical
// statement, we're in an array literal, and we're done.
if (get(-3, 0, STATEMENTS) == get(-2)[OFFSET])
return [0, []];
return;
// Begining of the statement upto the opening [
let obj = getObj(-3, get(-2)[OFFSET]);
@@ -546,8 +597,7 @@ function Completion() //{{{
// Now eval the key, to process any referenced variables.
key = this.eval(key);
let compl = this.objectKeys(obj);
return [top[OFFSET], this.filter(compl, key + string, last, key.length)];
return complete.call(this, obj, key, null, string, last);
}
// Is this a function call?
@@ -561,7 +611,7 @@ function Completion() //{{{
// Does the opening "(" mark a function call?
if (get(-3, 0, FUNCTIONS) != get(-2)[OFFSET])
return [0, []]; // No. We're done.
return; // No. We're done.
let [offset, obj, func] = getObjKey(-3);
let key = str.substring(get(-2, 0, STATEMENTS), top[OFFSET]) + "''";
@@ -574,7 +624,7 @@ function Completion() //{{{
if (!completer)
completer = this.completers[func];
if (!completer)
return [0, []];
return;
// Split up the arguments
let prev = get(-2)[OFFSET];
@@ -590,11 +640,11 @@ function Completion() //{{{
if (!(compl instanceof Array))
compl = [v for (v in compl)];
key = this.eval(key);
return [top[OFFSET], this.filter(compl, key + string, last, key.length)];
return complete.call(this, obj, key, compl, string, last);
}
// Nothing to do.
return [0, []];
return;
}
/*
@@ -603,23 +653,28 @@ function Completion() //{{{
* key = "baz"
*
* str = "foo"
* obj = [liberator, window]
* obj = [modules, window]
* key = "foo"
*/
let [offset, obj, key] = getObjKey(-1);
if (!/^(?:\w[\w\d]*)?$/.test(key))
return [0, []]; /* Not a word. Forget it. Can this even happen? */
return; /* Not a word. Forget it. Can this even happen? */
let compl = this.objectKeys(obj);
return [offset, this.filter(compl, key)];
top[OFFSET] = offset;
return complete.call(this, obj, key);
}
};
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;
@@ -662,12 +717,7 @@ function Completion() //{{{
filtered.push([compitem, item[1], favicon ? item[2] : null]);
if (longest)
{
if (substrings.length == 0)
buildSubstrings(str, filter);
else
substrings = substrings.filter(function strIndex(s) str.indexOf(s) >= 0);
}
buildSubstrings(str, filter);
break;
}
}
@@ -740,19 +790,9 @@ function Completion() //{{{
// returns the longest common substring
// used for the 'longest' setting for wildmode
getLongestSubstring: function getLongestSubstring()
{
if (substrings.length == 0)
return "";
get longestSubstring () substrings.reduce(function (a, b) a.length > b.length ? a : b, ""),
var longest = substrings[0];
for (let i = 1; i < substrings.length; i++)
{
if (substrings[i].length > longest.length)
longest = substrings[i];
}
return longest;
},
get substrings() substrings.slice(),
// generic filter function, also builds substrings needed
// for :set wildmode=list:longest, if necessary
@@ -853,10 +893,7 @@ function Completion() //{{{
filtered.push(elem);
}
filtered = filtered.concat(additionalCompletions);
if (options.get("wildoptions").has("sort"))
filtered = filtered.sort(function (a, b) util.compareIgnoreCase(a[0], b[0]));;
return filtered;
return filtered.concat(additionalCompletions);
},
// generic helper function which checks if the given "items" array pass "filter"
@@ -963,6 +1000,7 @@ function Completion() //{{{
let rtp = options["runtimepath"].split(",");
rtp.forEach(function (path) {
// FIXME: Now! Very, very broken.
schemes = schemes.concat([[c[0].replace(/\.vimp$/, ""), ""]
for each (c in completion.file(path + "/colors/", true)[1])]);
});
@@ -972,15 +1010,20 @@ function Completion() //{{{
command: function command(context)
{
context.title = ["Command"];
if (!context.filter)
return { start: 0, items: [[c.name, c.description] for (c in commands)] };
context.items = [[c.name, c.description] for (c in commands)];
else
return { start: 0, items: this.filter([[c.longNames, c.description] for (c in commands)], context.filter, true) };
context.items = this.filter([[c.longNames, c.description] for (c in commands)], context.filter, true);
},
dialog: function dialog(filter) [0, this.filter(config.dialogs, filter)],
directory: function (filter, tail) [0, this.file(filter, tail)[1].filter(function (f) f[1] == "Directory")],
directory: function (context, tail)
{
this.file(context, tail);
context.items = context.items.filter(function (i) i[1] == "Directory");
},
environment: function environment(filter)
{
@@ -1014,7 +1057,8 @@ function Completion() //{{{
// then get completions of the command name
let [count, cmd, special, args] = commands.parseCommand(context.filter);
let [, prefix, junk] = context.filter.match(/^(:*\d*)\w*(.?)/) || [];
context.advance(junk.length)
context.advance(prefix.length)
context.items = []; // XXX
if (!junk)
return this.command(context);
@@ -1023,40 +1067,38 @@ function Completion() //{{{
let compObject = { start: 0, items: [] };
if (command && command.completer)
{
[prefix] = context.filter.match(/^(?:\w+[\s!]|!)\s*/);
context.advance((prefix || "").length);
[prefix] = context.filter.match(/^(?:\w*[\s!]|!)\s*/);
context = context.fork(cmd, prefix.length);
this.filterString = context.filter;
args = command.parseArgs(context.filter, true);
liberator.dump(args);
if (args)
{
// XXX, XXX, XXX
compObject = command.completer.call(command, args.string, special, args, context);
liberator.dump(compObject);
if (compObject instanceof Array) // for now at least, let completion functions return arrays instead of objects
compObject = { start: compObject[0], items: compObject[1] };
if (compObject == null)
compObject = { start: context.offset, items: context.items };
else
compObject.start += context.offset;
if (args.completions)
{
if (!compObject.items.length)
compObject.start = args.completeStart + context.offset;
if (args.completeStart + context.offset == compObject.start)
compObject.items = args.completions.concat(compObject.items);
let argContext = context.fork("args", args.completionStart);
argContext.title = [args.completeOpt || "Options"];
argContext.items = args.completions;
}
if (compObject != null)
{
context.advance(compObject.start);
context.title = ["Completions"];
context.items = compObject.items;
}
liberator.dump(compObject);
liberator.dump("\n");
}
//liberator.dump([[v.name, v.offset, v.items.length, v.hasItems] for each (v in context.contexts)]);
}
return compObject;
},
// TODO: support file:// and \ or / path separators on both platforms
// if "tail" is true, only return names without any directory components
file: function file(filter, tail)
file: function file(context, tail)
{
let [, dir, compl] = filter.match(/^((?:.*[\/\\])?)(.*?)$/);
let [dir] = context.filter.match(/^(?:.*[\/\\])?/);
// dir == "" is expanded inside readDirectory to the current dir
let generate = function ()
@@ -1085,37 +1127,35 @@ function Completion() //{{{
return mapped;
};
context.title = ["Path", "Type"];
if (tail)
return [dir.length, this.cached("file-" + dir, compl, generate, "filter", [true])];
else
return [0, this.cached("file-" + dir, filter, generate, "filter", [true])];
context.advance(dir.length);
context.items = this.cached("file-" + dir, context.filter, generate, "filter", true);
},
help: function help(filter)
{
var files = config.helpFiles;
var res = [];
let res = [];
for (let i = 0; i < files.length; i++)
for (let [, file] in Iterator(config.helpFiles))
{
try
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "chrome://liberator/locale/" + files[i], false);
xmlhttp.open("GET", "chrome://liberator/locale/" + file, false);
xmlhttp.send(null);
}
catch (e)
{
liberator.log("Error opening chrome://liberator/locale/" + files[i], 1);
liberator.log("Error opening chrome://liberator/locale/" + file, 1);
continue;
}
var doc = xmlhttp.responseXML;
var elems = doc.getElementsByClassName("tag");
for (let j = 0; j < elems.length; j++)
res.push([elems[j].textContent, files[i]]);
let doc = xmlhttp.responseXML;
res.push(Array.map(doc.getElementsByClassName("tag"),
function (elem) [elem.textContent, file]));
}
return [0, this.filter(res, filter)];
return [0, this.filter(util.Array.flatten(res), filter)];
},
highlightGroup: function highlightGroup(filter) commands.get("highlight").completer(filter), // XXX
@@ -1140,11 +1180,14 @@ function Completion() //{{{
preference: function preference(filter) commands.get("set").completer(filter, true), // XXX
search: function search(filter)
search: function search(context)
{
let [, keyword, args] = filter.match(/^\s*(\S*)\s*(.*)/);
let [, keyword, args] = context.filter.match(/^\s*(\S*)\s*(.*)/);
let keywords = bookmarks.getKeywords();
let engines = this.filter(keywords.concat(bookmarks.getSearchEngines()), filter, false, true);
let engines = this.filter(keywords.concat(bookmarks.getSearchEngines()), context.filter, false, true);
context.title = ["Search Keywords"];
context.items = engines;
// NOTE: While i like the result of the code, due to the History simplification
// that code is too slow to be here. We might use a direct Places History query instead for better speed
@@ -1172,58 +1215,50 @@ function Completion() //{{{
// let searches = this.cached("searches-" + keyword, args, generate, "filter", [false, true]);
// searches = searches.map(function (a) (a = a.concat(), a[0] = keyword + " " + a[0], a));
// return [0, searches.concat(engines)];
return [0, engines];
},
// XXX: Move to bookmarks.js?
searchEngineSuggest: function searchEngineSuggest(filter, engineAliases)
searchEngineSuggest: function (context, engineAliases)
{
this.filterString = filter;
this.filterString = context.filter;
if (!filter)
return [0, []];
var engineList = (engineAliases || options["suggestengines"]).split(",");
var responseType = "application/x-suggestions+json";
var ss = Components.classes["@mozilla.org/browser/search-service;1"]
let engineList = (engineAliases || options["suggestengines"] || "google").split(",");
let responseType = "application/x-suggestions+json";
let ss = Components.classes["@mozilla.org/browser/search-service;1"]
.getService(Components.interfaces.nsIBrowserSearchService);
let matches = query.match(RegExp("^\s*(" + name + "\\s+)(.*)$")) || [];
if (matches[1])
context.advance(matches[1].length);
query = context.filter;
var completions = [];
let completions = [];
engineList.forEach(function (name) {
var query = filter;
var queryURI;
var engine = ss.getEngineByAlias(name);
var reg = new RegExp("^\s*(" + name + "\\s+)(.*)$");
var matches = query.match(reg);
if (matches)
query = matches[2];
let engine = ss.getEngineByAlias(name);
if (engine && engine.supportsResponseType(responseType))
queryURI = engine.getSubmission(query, responseType).uri.asciiSpec;
var queryURI = engine.getSubmission(query, responseType).uri.asciiSpec;
else
return [0, []];
return;
var xhr = new XMLHttpRequest();
let xhr = new XMLHttpRequest();
xhr.open("GET", queryURI, false);
xhr.send(null);
var json = Components.classes["@mozilla.org/dom/json;1"]
let json = Components.classes["@mozilla.org/dom/json;1"]
.createInstance(Components.interfaces.nsIJSON);
var results = json.decode(xhr.responseText)[1];
let results = json.decode(xhr.responseText)[1];
if (!results)
return [0, []];
return;
results.forEach(function (item) {
// make sure we receive strings, otherwise a man-in-the-middle attack
// could return objects which toString() method could be called to
// execute untrusted code
if (typeof item != "string")
return [0, []];
completions.push([(matches ? matches[1] : "") + item, engine.name + " suggestion"]);
});
let ctxt = context.fork(engine.name, (matches[1] || "").length);
ctxt.title = [engine.name + " Suggestions"];
// make sure we receive strings, otherwise a man-in-the-middle attack
// could return objects which toString() method could be called to
// execute untrusted code
ctxt.items = [[item, ""] for ([k, item] in results) if (typeof item == "string")];
});
return [0, completions];
},
shellCommand: function shellCommand(filter)
@@ -1288,84 +1323,67 @@ function Completion() //{{{
// may consist of search engines, filenames, bookmarks and history,
// depending on the 'complete' option
// if the 'complete' argument is passed like "h", it temporarily overrides the complete option
url: function url(filter, complete)
url: function url(context, complete)
{
function getMoreItems(count, maxTime)
{
maxTime = maxTime || 5000; // maximum time to wait, default 5 sec
count = count || 10;
var completions = [];
historyResult = null;
let then = new Date().getTime();
for (let now = then; now - then < maxTime; now = new Date().getTime())
{
liberator.threadYield();
if (!historyResult)
continue;
if (historyResult.searchResult == historyResult.RESULT_SUCCESS ||
historyResult.searchResult == historyResult.RESULT_NOMATCH ||
(historyResult.searchResult == historyResult.RESULT_SUCCESS_ONGOING &&
historyResult.matchCount >= count + numLocationCompletions))
{
//liberator.dump("Got " + historyResult.matchCount + " more results after " + (now - then) + " ms with result: " + historyResult.searchResult);
//completionService.stopSearch();
for (let i in util.range(numLocationCompletions, historyResult.matchCount))
completions.push([historyResult.getValueAt(i),
historyResult.getCommentAt(i),
historyResult.getImageAt(i)]);
numLocationCompletions = historyResult.matchCount;
break;
}
}
return completions;
}
this.filterString = filter;
var completions = [];
this.filterString = context.filter;
var numLocationCompletions = 0; // how many async completions did we already return to the caller?
var start = 0;
var skip = filter.match("^(.*" + options["urlseparator"] + ")(.*)"); // start after the last 'urlseparator'
var skip = context.filter.match("^.*" + options["urlseparator"]); // start after the last 'urlseparator'
if (skip)
{
start += skip[1].length;
filter = skip[2];
}
context.advance(skip[0].length);
var cpt = complete || options["complete"];
var suggestEngineAlias = options["suggestengines"] || "google";
// join all completion arrays together
for (let c in util.Array.iterator(cpt))
{
if (c == "s")
completions = completions.concat(this.search(filter)[1]);
else if (c == "f")
completions = completions.concat(this.file(filter, false)[1]);
else if (c == "S")
completions = completions.concat(this.searchEngineSuggest(filter, suggestEngineAlias)[1]);
else if (c == "b")
completions = completions.concat(bookmarks.get(filter));
else if (c == "l" && completionService) // add completions like Firefox's smart location bar
let opts = {
s: this.search,
f: this.file,
S: this.searchEngineSuggest,
b: function (context)
{
historyCache = [];
completionCache = completions.slice(); // make copy of current results
context.title = ["Bookmark", "Title"];
context.items = bookmarks.get(context.filter)
},
l: function (context)
{
if (!completionService)
return
context.title = ["Smart Completions"];
context.incomplete = true;
if (context.items.length)
context.hasItems = true; // XXX
let timer = new util.Timer(50, 100, function () {
let result = context.result;
context.items = [
[result.getValueAt(i), result.getCommentAt(i), result.getImageAt(i)]
for (i in util.range(0, result.matchCount))
];
context.incomplete = result.searchResult >= result.RESULT_NOMATCH_ONGOING;
let filter = context.filter;
context.items.forEach(function ([item]) buildSubstrings(item, filter));
});
completionService.stopSearch();
completionService.startSearch(filter, "", historyResult, {
completionService.startSearch(context.filter, "", context.result, {
onSearchResult: function onSearchResult(search, result) {
historyResult = result;
//liberator.dump("Search result in " + historyResult.matchCount + " results with retval: " + historyResult.searchResult);
historyTimer.tell();
context.result = result;
timer.tell();
if (result.searchResult <= result.RESULT_SUCCESS)
historyTimer.flush();
timer.flush();
}
});
}
}
};
Array.forEach(complete || options["complete"],
function (c) context.fork(c, 0, opts[c], completion));
},
// TODO: incomplete result should be set conditionally
return { start: start, items: completions, getMoreItems: getMoreItems, incompleteResult: true };
// FIXME: Temporary
_url: function (filter, complete)
{
let context = new CompletionContext({
selection: { getRangeAt: function () ({ startOffset: filter.length }) },
rootElement: { textContent: filter }
});
this.url(context, complete);
return context.allItems;
},
userCommand: function userCommand(filter)

View File

@@ -218,13 +218,13 @@ function AutoCommands() //{{{
var list = template.generic(
<table>
<tr>
<td class="hl-Title" colspan="2">----- Auto Commands -----</td>
<tr class="hl-Title">
<td colspan="2">----- Auto Commands -----</td>
</tr>
{
template.map(cmds, function ([event, items])
<tr>
<td class="hl-Title" colspan="2">{event}</td>
<tr class="hl-Title">
<td colspan="2">{event}</td>
</tr>
+
template.map(items, function (item)
@@ -562,9 +562,7 @@ function Events() //{{{
liberator.echoerr("Interrupted");
else
liberator.echoerr("Processing " + event.type + " event: " + (e.echoerr || e));
liberator.dump(e);
if (Components.utils.reportError)
Components.utils.reportError(e);
liberator.reportError(e);
}
};
}
@@ -1128,7 +1126,7 @@ function Events() //{{{
// if (events.wantsModeReset)
// {
// events.wantsModeReset = false;
modes.reset();
modes.reset();
// }
// }, 0);
}

View File

@@ -208,7 +208,7 @@ function IO() //{{{
},
{
argCount: "?",
completer: function (filter) completion.file(filter, true),
completer: function (filter, bang, args, context) completion.file(context, true),
literal: true
});
@@ -268,7 +268,7 @@ function IO() //{{{
{
argCount: "?",
bang: true,
completer: function (filter) completion.file(filter, true)
completer: function (filter, bang, args, context) completion.file(context, true)
});
commands.add(["runt[ime]"],
@@ -301,7 +301,7 @@ function IO() //{{{
{
argCount: "1",
bang: true,
completer: function (filter) completion.file(filter, true)
completer: function (filter, bang, args, context) completion.file(context, true)
});
commands.add(["!", "run"],
@@ -893,8 +893,7 @@ lookup:
catch (e)
{
let message = "Sourcing file: " + (e.echoerr || file.path + ": " + e);
if (Components.utils.reportError)
Components.utils.reportError(e);
liberator.reportError(e);
if (!silent)
liberator.echoerr(message);
}

View File

@@ -57,9 +57,7 @@ const liberator = (function () //{{{
catch (e)
{
toJavaScriptConsole();
if (Components.utils.reportError)
Components.utils.reportError(e);
liberator.dump(e);
liberator.reportError(e);
}
}
@@ -1087,6 +1085,23 @@ const liberator = (function () //{{{
goQuitApplication();
},
reportError: function (error)
{
if (Components.utils.reportError)
Components.utils.reportError(error);
let obj = {
toString: function () error.toString(),
stack: { toString: function () "\n" + error.stack.replace(/^/mg, "\t") }
};
for (let [k, v] in Iterator(error))
{
if (!(k in obj))
obj[k] = v;
}
liberator.dump(obj);
liberator.dump("");
},
restart: function ()
{
const nsIAppStartup = Components.interfaces.nsIAppStartup;

View File

@@ -548,7 +548,7 @@ function Tabs() //{{{
},
{
bang: true,
completer: function (filter) completion.url(filter)
completer: function (filter, bang, args, context) completion.url(context)
});
commands.add(["tabde[tach]"],

View File

@@ -62,7 +62,7 @@ const template = {
// that we cannot even try/catch it
if (/^\[JavaPackage.*\]$/.test(arg))
return <>[JavaPackage]</>;
if (processStrings)
if (processStrings && false)
arg = String(arg).replace("\n", "\\n", "g");
return <span class="hl-Object">{arg}</span>;
default:

View File

@@ -117,7 +117,8 @@ function CommandLine() //{{{
if (events.feedingKeys)
return;
completionContext.reset();
commandline.setCompletions(completion.ex(completionContext));
completionContext.fork("ex", 0, completion.ex, completion);
commandline.setCompletions(completionContext.allItems);
});
// the containing box for the promptWidget and commandWidget
@@ -156,7 +157,8 @@ function CommandLine() //{{{
liberator.registerCallback("complete", modes.EX, function (str) {
completionContext.reset();
completionContext.tabPressed = true;
return completion.ex(completionContext);
completionContext.fork("ex", 0, completion.ex, completion);
return completionContext.allItems;
});
liberator.registerCallback("change", modes.EX, function (command) {
completion.cancel(); // cancel any previous completion function
@@ -584,6 +586,10 @@ function CommandLine() //{{{
commandWidget.focus();
completionContext = new CompletionContext(commandWidget.inputField.editor);
completionContext.onUpdate = function ()
{
commandline.setCompletions(this.allItems);
};
// open the completion list automatically if wanted
if (/\s/.test(cmd) &&
options.get("wildoptions").has("auto") &&
@@ -815,17 +821,12 @@ function CommandLine() //{{{
historyIndex = UNINITIALIZED;
// TODO: call just once, and not on each <Tab>
var wim = options["wildmode"].split(",");
var hasList = false;
var longest = false;
var full = false;
var wildType = wim[wildIndex++] || wim[wim.length - 1];
if (wildType == "list" || wildType == "list:full" || wildType == "list:longest")
hasList = true;
if (wildType == "longest" || wildType == "list:longest")
longest = true;
else if (wildType == "full" || wildType == "list:full")
full = true;
let wildmode = options["wildmode"].split(",");
let wildType = wildmode[Math.min(wildIndex++, wildmode.length - 1)];
let hasList = /^list(:|$)/.test(wildType);
let longest = /(^|:)longest$/.test(wildType);
let full = !longest && /(^|:)full/.test(wildType);
// we need to build our completion list first
if (completionIndex == UNINITIALIZED)
@@ -837,20 +838,22 @@ function CommandLine() //{{{
// sort the completion list
// TODO: might not make sense anymore with our advanced completions, we should just sort when necessary
if (options.get("wildoptions").has("sort"))
completions.items.sort(function (a, b) String.localeCompare(a[0], b[0]));
// FIXME: CompletionContext
//if (options.get("wildoptions").has("sort"))
// completions.items.sort(function (a, b) String.localeCompare(a[0], b[0]));
completionList.setItems(completions.items);
completionList.setItems(completionContext.allItems);
}
if (completions.items.length == 0)
{
// try to fetch more items, if possible
// Wait for items to come available
// TODO: also use that code when we DO have completions but too few
if (completions.getMoreItems)
let end = Date.now() + 5000;
while (completionContext.incomplete && completions.items.length == 0 && Date.now() < end)
{
completions.items = completions.items.concat(completions.getMoreItems(1));
completionList.setItems(completions.items);
liberator.threadYield();
completions = completionContext.allItems;
}
if (completions.items.length == 0) // still not more matches
@@ -895,11 +898,11 @@ function CommandLine() //{{{
{
var compl = null;
if (longest && completions.items.length > 1)
compl = completion.getLongestSubstring();
compl = completion.longestSubstring;
else if (full)
compl = completions.items[completionIndex][0];
compl = completions.items[completionIndex].text;
else if (completions.items.length == 1)
compl = completions.items[0][0];
compl = completions.items[0].text;
if (compl)
{
@@ -1236,8 +1239,9 @@ function CommandLine() //{{{
return;
// don't show an empty result, if we are just waiting for data to arrive
if (newCompletions.incompleteResult && newCompletions.items.length == 0)
return;
// FIXME: Maybe. CompletionContext
//if (newCompletions.incompleteResult && newCompletions.items.length == 0)
// return;
completionList.setItems(newCompletions.items);
@@ -1332,32 +1336,30 @@ function ItemList(id) //{{{
// TODO: move to completions?
function createDefaultRow(item, dom)
{
if (item instanceof Array)
item = { text: item[0], description: item[1], icon: item[2] };
let { text: text, description: description, icon: icon } = item;
/* Kludge until we have completion contexts. */
let map = completion.filterMap;
if (map)
{
item.text = map[0] ? map[0](item.text) : item.text;
item.description = map[1] ? map[1](item.description) : item.description;
text = map[0] ? map[0](text) : text;
description = map[1] ? map[1](description) : description;
}
/* Obviously, ItemList shouldn't know or care about this. */
let filter = completion.filterString;
if (filter)
{
item.text = template.highlightFilter(item.text, filter);
item.description = template.highlightFilter(item.description, filter);
text = template.highlightFilter(text, filter);
description = template.highlightFilter(description, filter);
}
if (typeof item.icon == "function")
item.icon = item.icon();
if (typeof icon == "function")
icon = icon();
let row =
<ul class="hl-CompItem">
<li class="hl-CompIcon">{item.icon ? <img src={item.icon}/> : <></>}</li>
<li class="hl-CompResult">{item.text}</li>
<li class="hl-CompDesc">{item.description}</li>
<li class="hl-CompIcon">{icon ? <img src={icon}/> : <></>}</li>
<li class="hl-CompResult">{text}</li>
<li class="hl-CompDesc">{description}</li>
</ul>;
if (dom)

View File

@@ -130,10 +130,7 @@ const util = { //{{{
};
},
compareIgnoreCase: function (a, b)
{
return String.localeCompare(a.toLowerCase(), b.toLowerCase());
},
compareIgnoreCase: function (a, b) String.localeCompare(a.toLowerCase(), b.toLowerCase()),
clip: function (str, length)
{

View File

@@ -293,7 +293,7 @@ const config = { //{{{
},
{
bang: true,
completer: function (filter) completion.url(filter)
completer: function (filter, args, bang, context) completion.url(context)
});
commands.add(["redr[aw]"],
@@ -364,7 +364,7 @@ const config = { //{{{
else
liberator.open("about:blank", liberator.NEW_WINDOW);
},
{ completer: function (filter) completion.url(filter) });
{ completer: function (filter, bang, args, context) completion.url(context) });
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS /////////////////////////////////////////////////