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:
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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]"],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user