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

Completion improvements, including basic completion contexts (very incomplete)

This commit is contained in:
Kris Maglione
2008-11-21 22:06:33 +00:00
parent fd1bc6adb1
commit 423d0e9f70
5 changed files with 160 additions and 59 deletions

View File

@@ -32,6 +32,82 @@ modules._cleanEval = function (__liberator_eval_arg, __liberator_eval_tmp)
return window.eval(__liberator_eval_arg); return window.eval(__liberator_eval_arg);
} }
function CompletionContext(editor, offset)
{
if (editor instanceof arguments.callee)
{
let parent = editor;
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;
}
else
{
this.editor = editor;
this.offset = offset || 0;
this.tabPressed = false;
this.contexts = {};
}
this.selectionTypes = {};
}
CompletionContext.prototype = {
get caret() this.editor.selection.getRangeAt(0).startOffset - this.offset,
get filter() this.value.substr(this.offset, this.caret),
get value() this.editor.rootElement.textContent,
advance: function (count)
{
this.offset += count;
},
fork: function (name, offset)
{
if (!(name in this.contexts))
this.contexts[name] = new CompletionContext(this);
this.contexts[name].offset = this.offset + offset;
return this.contexts[name];
},
highlight: function (start, length, type)
{
try // Firefox <3.1 doesn't have repaintSelection
{
this.selectionTypes[type] = null;
const selType = Components.interfaces.nsISelectionController["SELECTION_" + type];
const editor = this.editor;
let sel = editor.selectionController.getSelection(selType);
if (length == 0)
{
sel.removeAllRanges();
editor.selectionController.repaintSelection(selType);
return;
}
let range = editor.selection.getRangeAt(0).cloneRange();
range.setStart(range.startContainer, this.offset + start);
range.setEnd(range.startContainer, this.offset + start + length);
sel.addRange(range);
editor.selectionController.repaintSelection(selType);
}
catch (e) {}
},
reset: function ()
{
// Not ideal.
for (let type in this.selectionTypes)
this.highlight(0, 0, type);
this.selectionTypes = {};
this.tabPressed = false;
this.offset = 0;
},
}
function Completion() //{{{ function Completion() //{{{
{ {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -235,6 +311,7 @@ function Completion() //{{{
function buildStack(start) function buildStack(start)
{ {
let self = this;
/* Push and pop the stack, maintaining references to 'top' and 'last'. */ /* Push and pop the stack, maintaining references to 'top' and 'last'. */
let push = function push(arg) let push = function push(arg)
{ {
@@ -246,12 +323,12 @@ function Completion() //{{{
{ {
if (top[CHAR] != arg) if (top[CHAR] != arg)
{ {
commandline.highlight(top[OFFSET] + 1, i + 1, "SPELLCHECK"); self.context.highlight(top[OFFSET] + 1, i - top[OFFSET], "SPELLCHECK");
commandline.highlight(top[OFFSET], top[OFFSET] + 1, "FIND"); self.context.highlight(top[OFFSET], 1, "FIND");
throw new Error("Invalid JS"); throw new Error("Invalid JS");
} }
if (i == str.length - 1) if (i == str.length - 1)
commandline.highlight(top[OFFSET], top[OFFSET] + 1, "FIND"); self.context.highlight(top[OFFSET], 1, "FIND");
// The closing character of this stack frame will have pushed a new // The closing character of this stack frame will have pushed a new
// statement, leaving us with an empty statement. This doesn't matter, // statement, leaving us with an empty statement. This doesn't matter,
// now, as we simply throw away the frame when we pop it, but it may later. // now, as we simply throw away the frame when we pop it, but it may later.
@@ -354,20 +431,22 @@ function Completion() //{{{
lastIdx = i; lastIdx = i;
} }
this.complete = function complete(string) this.complete = function complete(context)
{ {
commandline.highlight(0, 0, "SPELLCHECK"); this.context = context;
commandline.highlight(0, 0, "FIND"); let string = context.filter;
let self = this; let self = this;
try try
{ {
continuing = lastIdx && string.indexOf(str) == 0; continuing = lastIdx && string.indexOf(str) == 0;
str = string; str = string;
buildStack(continuing ? lastIdx : 0); buildStack.call(this, continuing ? lastIdx : 0);
} }
catch (e) catch (e)
{ {
if (e.message != "Invalid JS")
Components.utils.reportError(e);
// liberator.dump(util.escapeString(string) + ": " + e + "\n" + e.stack); // liberator.dump(util.escapeString(string) + ": " + e + "\n" + e.stack);
lastIdx = 0; lastIdx = 0;
return [0, []]; return [0, []];
@@ -891,12 +970,12 @@ function Completion() //{{{
return [0, completion.filter(schemes, filter)]; return [0, completion.filter(schemes, filter)];
}, },
command: function command(filter) command: function command(context)
{ {
if (!filter) if (!context.filter)
return [0, [[c.name, c.description] for (c in commands)]]; return { start: 0, items: [[c.name, c.description] for (c in commands)] };
else else
return [0, this.filter([[c.longNames, c.description] for (c in commands)], filter, true)]; return { start: 0, items: this.filter([[c.longNames, c.description] for (c in commands)], context.filter, true) };
}, },
dialog: function dialog(filter) [0, this.filter(config.dialogs, filter)], dialog: function dialog(filter) [0, this.filter(config.dialogs, filter)],
@@ -919,39 +998,57 @@ function Completion() //{{{
}, },
// provides completions for ex commands, including their arguments // provides completions for ex commands, including their arguments
ex: function ex(str) ex: function ex(context)
{ {
this.filterMap = null; this.filterMap = null;
this.filterString = ""; this.filterString = "";
this.parenMatch = null;
substrings = []; substrings = [];
if (str.indexOf(cacheFilter["ex"]) != 0) if (context.filter.indexOf(cacheFilter["ex"]) != 0)
{ {
cacheFilter = {}; cacheFilter = {};
cacheResults = {}; cacheResults = {};
} }
cacheFilter["ex"] = str; cacheFilter["ex"] = context.filter;
// if there is no space between the command name and the cursor // if there is no space between the command name and the cursor
// then get completions of the command name // then get completions of the command name
var [count, cmd, special, args] = commands.parseCommand(str); let [count, cmd, special, args] = commands.parseCommand(context.filter);
var matches = str.match(/^(:*\d*)\w*$/); let [, prefix, junk] = context.filter.match(/^(:*\d*)\w*(.?)/) || [];
if (matches) context.advance(junk.length)
return { start: matches[1].length, items: this.command(cmd)[1] }; if (!junk)
return this.command(context);
// dynamically get completions as specified with the command's completer function // dynamically get completions as specified with the command's completer function
var compObject = { start: 0, completions: [] }; let command = commands.get(cmd);
var exLength = 0; let compObject = { start: 0, items: [] };
var command = commands.get(cmd);
if (command && command.completer) if (command && command.completer)
{ {
matches = str.match(/^:*\d*(?:\w+[\s!]|!)\s*/); [prefix] = context.filter.match(/^(?:\w+[\s!]|!)\s*/);
exLength = matches ? matches[0].length : 0; context.advance((prefix || "").length);
compObject = command.completer.call(command, args, special); 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 if (compObject instanceof Array) // for now at least, let completion functions return arrays instead of objects
compObject = { start: compObject[0], items: compObject[1] }; 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);
}
liberator.dump(compObject);
liberator.dump("\n");
}
} }
compObject.start += exLength;
return compObject; return compObject;
}, },
@@ -1025,9 +1122,9 @@ function Completion() //{{{
get javascriptCompleter() javascript, get javascriptCompleter() javascript,
javascript: function _javascript(str) javascript: function (context)
{ {
return javascript.complete(str); return javascript.complete(context);
}, },
macro: function macro(filter) macro: function macro(filter)

View File

@@ -336,7 +336,7 @@ const liberator = (function () //{{{
}, },
{ {
bang: true, bang: true,
completer: function (filter) completion.javascript(filter), completer: function (filter, bang, args, context) completion.javascript(context),
hereDoc: true hereDoc: true
}); });
@@ -1161,10 +1161,6 @@ const liberator = (function () //{{{
liberator.log("All modules loaded", 3); liberator.log("All modules loaded", 3);
// TODO: move elsewhere
liberator.registerCallback("submit", modes.EX, function (command) { liberator.execute(command); });
liberator.registerCallback("complete", modes.EX, function (str) { return completion.ex(str); });
// first time intro message // first time intro message
const firstTime = "extensions." + config.name.toLowerCase() + ".firsttime"; const firstTime = "extensions." + config.name.toLowerCase() + ".firsttime";
if (options.getPref(firstTime, true)) if (options.getPref(firstTime, true))

View File

@@ -429,12 +429,10 @@ liberator.registerObserver("load_commands", function ()
{ {
argCount: "2", argCount: "2",
bang: true, bang: true,
completer: function (filter) { completer: function (filter, bang, args) {
let args = this.parseArgs(filter, true); let compl = [];
let compl = args ? args.completions : []; if (args.completeArg == 0)
if (args && args.completeOpt) {
return [args.completeStart, compl];
try try
{ {
compl.push([content.location.host, "Current Host"]); compl.push([content.location.host, "Current Host"]);
@@ -442,7 +440,9 @@ liberator.registerObserver("load_commands", function ()
} }
catch (e) {} catch (e) {}
comp = compl.concat([[s, ""] for each (s in styles.sites)]) comp = compl.concat([[s, ""] for each (s in styles.sites)])
return [0, completion.filter(compl, filter)]; return [0, completion.filter(compl, args.arguments[0])];
}
return [0, []];
}, },
hereDoc: true, hereDoc: true,
literal: true, literal: true,
@@ -473,8 +473,8 @@ liberator.registerObserver("load_commands", function ()
.concat([[s, ""] for each (s in styles.sites)]) .concat([[s, ""] for each (s in styles.sites)])
, filter)], , filter)],
literal: true, literal: true,
options: [[["-index", "-i"], commands.OPTION_INT], options: [[["-index", "-i"], commands.OPTION_INT, null, function () [[k, v.name || v.sites.join(",") + " " + v.css] for ([k, v] in Iterator(styles.userNames))]],
[["-name", "-n"], commands.OPTION_STRING]] [["-name", "-n"], commands.OPTION_STRING, null, function () [[k, v.css] for ([k, v] in Iterator(styles.userNames))]]]
}); });
commands.add(["hi[ghlight]"], commands.add(["hi[ghlight]"],

View File

@@ -98,6 +98,7 @@ function CommandLine() //{{{
var completionList = new ItemList("liberator-completions"); var completionList = new ItemList("liberator-completions");
var completions = { start: 0, items: [] }; var completions = { start: 0, items: [] };
var completionContext = null;
// for the example command "open sometext| othertext" (| is the cursor pos): // for the example command "open sometext| othertext" (| is the cursor pos):
var completionPrefix = ""; // will be: "open sometext" var completionPrefix = ""; // will be: "open sometext"
var completionPostfix = ""; // will be: " othertext" var completionPostfix = ""; // will be: " othertext"
@@ -112,11 +113,11 @@ function CommandLine() //{{{
else else
statusline.updateProgress("match " + (completionIndex + 1) + " of " + completions.items.length); statusline.updateProgress("match " + (completionIndex + 1) + " of " + completions.items.length);
}); });
var autocompleteTimer = new util.Timer(201, 300, function (command) { var autocompleteTimer = new util.Timer(201, 300, function (tabPressed) {
if (events.feedingKeys) if (events.feedingKeys)
return; return;
completionContext.reset();
commandline.setCompletions(completion.ex(command)); commandline.setCompletions(completion.ex(completionContext));
}); });
// the containing box for the promptWidget and commandWidget // the containing box for the promptWidget and commandWidget
@@ -151,10 +152,16 @@ function CommandLine() //{{{
var promptChangeCallback = null; var promptChangeCallback = null;
var promptCompleter = null; var promptCompleter = null;
liberator.registerCallback("submit", modes.EX, function (command) { liberator.execute(command); });
liberator.registerCallback("complete", modes.EX, function (str) {
completionContext.reset();
completionContext.tabPressed = true;
return completion.ex(completionContext);
});
liberator.registerCallback("change", modes.EX, function (command) { liberator.registerCallback("change", modes.EX, function (command) {
completion.cancel(); // cancel any previous completion function completion.cancel(); // cancel any previous completion function
if (options.get("wildoptions").has("auto")) if (options.get("wildoptions").has("auto"))
autocompleteTimer.tell(command); autocompleteTimer.tell(false);
else else
completionIndex = UNINITIALIZED; completionIndex = UNINITIALIZED;
}); });
@@ -209,9 +216,7 @@ function CommandLine() //{{{
setPrompt(""); setPrompt("");
setCommand(str); setCommand(str);
if (!forceSingle && if (!forceSingle &&
commandWidget.inputField.editor commandWidget.inputField.editor.rootElement
.selection.getRangeAt(0)
.startContainer.parentNode
.scrollWidth > commandWidget.inputField.scrollWidth) .scrollWidth > commandWidget.inputField.scrollWidth)
{ {
setCommand(""); setCommand("");
@@ -493,7 +498,7 @@ function CommandLine() //{{{
if (str != null) if (str != null)
command.action(str); command.action(str);
}, },
{ completer: function (filter) completion.javascript(filter) }); { completer: function (filter, bang, args, context) completion.javascript(context) });
}); });
commands.add(["mes[sages]"], commands.add(["mes[sages]"],
@@ -542,6 +547,8 @@ function CommandLine() //{{{
// FORCE_MULTILINE is given, FORCE_MULTILINE takes precedence // FORCE_MULTILINE is given, FORCE_MULTILINE takes precedence
APPEND_TO_MESSAGES : 1 << 3, // add the string to the message history APPEND_TO_MESSAGES : 1 << 3, // add the string to the message history
get completionContext() completionContext,
get mode() (modes.extended == modes.EX) ? "cmd" : "search", get mode() (modes.extended == modes.EX) ? "cmd" : "search",
get silent() silent, get silent() silent,
@@ -576,11 +583,12 @@ function CommandLine() //{{{
commandWidget.focus(); commandWidget.focus();
completionContext = new CompletionContext(commandWidget.inputField.editor);
// open the completion list automatically if wanted // open the completion list automatically if wanted
if (/\s/.test(cmd) && if (/\s/.test(cmd) &&
options.get("wildoptions").has("auto") && options.get("wildoptions").has("auto") &&
extendedMode == modes.EX) extendedMode == modes.EX)
autocompleteTimer.tell(cmd); autocompleteTimer.tell(false);
}, },
// normally used when pressing esc, does not execute a command // normally used when pressing esc, does not execute a command

View File

@@ -336,7 +336,7 @@ const util = { //{{{
i = parseInt(i); i = parseInt(i);
else if (/^[A-Z_]+$/.test(i)) else if (/^[A-Z_]+$/.test(i))
i = ""; i = "";
keys.push([i, <>{key}{noVal ? "" : <>:{value}</>}<br/>&#xa;</>]); keys.push([i, <>{key}{noVal ? "" : <>: {value}</>}<br/>&#xa;</>]);
} }
} }
catch (e) {} catch (e) {}