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

Wait for <Tab> before evaluating funcitons in JS completion.

This commit is contained in:
Kris Maglione
2008-11-28 11:19:29 +00:00
parent bca9a9e26f
commit 18cf334054
4 changed files with 84 additions and 24 deletions

View File

@@ -54,14 +54,14 @@ function CompletionContext(editor, name, offset)
self.parent = parent;
self.offset = parent.offset + (offset || 0);
self.keys = util.cloneObject(parent.keys);
delete self._generate;
delete self._filter; // FIXME?
delete self._generate;
delete self._ignoreCase;
["anchored", "compare", "editor", "filterFunc", "keys", "_process", "quote", "title", "top"].forEach(function (key)
self[key] = parent[key]);
if (self != this)
return self;
["_caret", "contextList", "onUpdate", "selectionTypes", "tabPressed", "updateAsync", "value"].forEach(function (key) {
["_caret", "contextList", "onUpdate", "selectionTypes", "tabPressed", "updateAsync", "value", "waitingForTab"].forEach(function (key) {
self.__defineGetter__(key, function () this.top[key]);
self.__defineSetter__(key, function (val) this.top[key] = val);
});
@@ -84,9 +84,9 @@ function CompletionContext(editor, name, offset)
let text = Array.concat(this.getKey(item, "text"));
for (let [i, str] in Iterator(text))
{
if (this.match(str))
if (this.match(String(str)))
{
item.text = text[i];
item.text = String(text[i]);
return true;
}
}
@@ -100,10 +100,11 @@ function CompletionContext(editor, name, offset)
this.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
this.reset();
}
this.name = name || "";
this.cache = {};
this.key = "";
this.itemCache = {};
this.key = "";
this.message = null;
this.name = name || "";
this._completions = []; // FIXME
this.getKey = function (item, key) (typeof self.keys[key] == "function") ? self.keys[key].call(this, item) : item.item[self.keys[key]];
}
@@ -111,15 +112,23 @@ CompletionContext.prototype = {
// Temporary
get allItems()
{
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.items.map(function makeItem(item) ({ text: prefix + item.text, item: item.item }));
});
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring }
try
{
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.items.map(function makeItem(item) ({ text: prefix + item.text, item: item.item }));
});
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring }
}
catch (e)
{
liberator.reportError(e);
return { start: 0, items: [], longestAllSubstring: "" }
}
},
// Temporary
get allSubstrings()
@@ -135,6 +144,8 @@ CompletionContext.prototype = {
function (res, list) res.filter(
function (str) list.some(function (s) s.substr(0, str.length) == str)),
lists.pop());
if (!substrings) // FIXME: How is this undefined?
return [];
return util.Array.uniq(substrings);
},
// Temporary
@@ -429,6 +440,7 @@ CompletionContext.prototype = {
this.selectionTypes = {};
this.tabPressed = false;
this.title = ["Completions"];
this.waitingForTab = false;
this.updateAsync = false;
if (this.editor)
{
@@ -473,6 +485,7 @@ function Completion() //{{{
.createInstance(Components.interfaces.nsIJSON);
const OFFSET = 0, CHAR = 1, STATEMENTS = 2, DOTS = 3, FULL_STATEMENTS = 4, FUNCTIONS = 5;
let stack = [];
let functions = [];
let top = []; /* The element on the top of the stack. */
let last = ""; /* The last opening char pushed onto the stack. */
let lastNonwhite = ""; /* Last non-whitespace character we saw. */
@@ -564,8 +577,6 @@ function Completion() //{{{
this.eval = function eval(arg, key, tmp)
{
if (!this.context.cache.eval)
this.context.cache.eval = {};
let cache = this.context.cache.eval;
if (!key)
key = arg;
@@ -629,6 +640,7 @@ function Completion() //{{{
let i = 0, c = ""; /* Current index and character, respectively. */
stack = [];
functions = [];
push("#root");
/* Build a parse stack, discarding entries as opening characters
@@ -669,6 +681,7 @@ function Completion() //{{{
/* Function call, or if/while/for/... */
if (/[\w\d$]/.test(lastNonwhite))
{
functions.push(i);
top[FUNCTIONS].push(i);
top[STATEMENTS].pop();
}
@@ -723,6 +736,9 @@ function Completion() //{{{
return;
}
if (!this.context.cache.eval)
this.context.cache.eval = {};
/* Okay, have parse stack. Figure out what we're completing. */
if (/[\])}"';]/.test(str[lastIdx - 1]) && last != '"' && last != '"')
@@ -733,8 +749,21 @@ function Completion() //{{{
let prev = 0;
for (let [,v] in Iterator(get(0)[FULL_STATEMENTS]))
{
this.eval(str.substring(prev, v + 1));
prev = v + 1;
let key = str.substring(prev, v + 1);
if (checkFunction(prev, v, key))
return;
this.eval(key);
prev = v + 1;
}
function checkFunction(start, end, key)
{
let res = functions.some(function (idx) idx >= start && idx < end);
if (!res || self.context.tabPressed || key in self.context.cache.eval)
return false;
self.context.waitingForTab = true;
self.context.message = "Waiting for <Tab>";
return true;
}
// For each DOT in a statement, prefix it with TMP, eval it,
@@ -757,10 +786,15 @@ function Completion() //{{{
if (dot > stop)
break;
let s = str.substring(prev, dot);
if (prev != statement)
s = EVAL_TMP + "." + s;
prev = dot + 1;
cacheKey = str.substring(statement, dot);
if (checkFunction(prev, dot, cacheKey))
return [];
prev = dot + 1;
obj = self.eval(s, cacheKey, obj);
}
return [[obj, cacheKey]]
@@ -889,6 +923,8 @@ function Completion() //{{{
let [offset, obj, func] = getObjKey(-3);
let key = str.substring(get(-2, 0, STATEMENTS), top[OFFSET]) + "''";
if (!obj.length)
return;
try
{

View File

@@ -27,6 +27,16 @@ the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/
plugins.contexts = {};
function Script(name)
{
if (plugins.contexts[name])
return plugins.contexts[name];
plugins.contexts[name] = this;
this.NAME = name;
}
Script.prototype = plugins;
// TODO: why are we passing around strings rather than file objects?
function IO() //{{{
{
@@ -815,7 +825,7 @@ lookup:
.getService(Components.interfaces.mozIJSSubScriptLoader);
try
{
loader.loadSubScript(uri.spec, {__proto__: plugins});
loader.loadSubScript(uri.spec, new Script(file.path));
}
catch (e)
{

View File

@@ -28,6 +28,7 @@ function Highlights(name, store, serial)
CompTitle color: magenta; background: white; font-weight: bold;
CompTitle>* border-bottom: 1px dashed magenta;
CompMsg font-style: italic; margin-left: 16px;
CompItem
CompItem[selected] background: yellow;
CompItem>* padding: 0 .5ex;

View File

@@ -137,7 +137,7 @@ function CommandLine() //{{{
let full = !longest && wildmode.checkHas(wildType, "full");
// we need to build our completion list first
if (completionIndex == UNINITIALIZED)
if (completionIndex == UNINITIALIZED || completionContext.waitingForTab)
{
completionIndex = -1;
completionPrefix = command.substring(0, commandWidget.selectionStart);
@@ -1365,13 +1365,14 @@ function ItemList(id) //{{{
items.contextList.forEach(function init_eachContext(context) {
delete context.cache.nodes;
if (!context.items.length)
if (!context.items.length && !context.message)
return;
context.cache.nodes = [];
dom(<div key="root">
<div highlight="Completions">
{context.createRow(context.title || [], "CompTitle")}
</div>
<div key="message" highlight="CompMsg" style="display: none"/>
<div key="up" highlight="CompLess"/>
<div key="items" highlight="Completions"/>
<div key="down" highlight="CompMore"/>
@@ -1396,6 +1397,7 @@ function ItemList(id) //{{{
startIndex = offset;
endIndex = Math.min(startIndex + maxItems, items.allItems.items.length);
let haveCompletions = false;
let off = 0;
function getRows(context)
{
@@ -1410,9 +1412,20 @@ function ItemList(id) //{{{
let nodes = context.cache.nodes;
if (!nodes)
return;
haveCompletions = true;
nodes.message.style.display = "none";
if (context.message)
{
nodes.message.textContent = context.message;
nodes.message.style.display = "block";
}
let root = nodes.root
let items = nodes.items;
let [start, end] = getRows(context);
if (start == end)
return;
for (let [i, row] in Iterator(context.getRows(start, end, doc)))
nodes[i] = row;
for (let [i, row] in util.Array.iterator2(nodes))
@@ -1434,7 +1447,7 @@ function ItemList(id) //{{{
nodes.down.style.display = (end == context.items.length) ? "none" : "block";
});
divNodes.noCompletions.style.display = (off > 0) ? "none" : "block";
divNodes.noCompletions.style.display = haveCompletions ? "none" : "block";
completionElements = buffer.evaluateXPath("//xhtml:div[@liberator:highlight='CompItem']", doc);