1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-23 04:07:58 +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.parent = parent;
self.offset = parent.offset + (offset || 0); self.offset = parent.offset + (offset || 0);
self.keys = util.cloneObject(parent.keys); self.keys = util.cloneObject(parent.keys);
delete self._generate;
delete self._filter; // FIXME? delete self._filter; // FIXME?
delete self._generate;
delete self._ignoreCase; delete self._ignoreCase;
["anchored", "compare", "editor", "filterFunc", "keys", "_process", "quote", "title", "top"].forEach(function (key) ["anchored", "compare", "editor", "filterFunc", "keys", "_process", "quote", "title", "top"].forEach(function (key)
self[key] = parent[key]); self[key] = parent[key]);
if (self != this) if (self != this)
return self; 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.__defineGetter__(key, function () this.top[key]);
self.__defineSetter__(key, function (val) this.top[key] = val); 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")); let text = Array.concat(this.getKey(item, "text"));
for (let [i, str] in Iterator(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; 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.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
this.reset(); this.reset();
} }
this.name = name || "";
this.cache = {}; this.cache = {};
this.key = "";
this.itemCache = {}; this.itemCache = {};
this.key = "";
this.message = null;
this.name = name || "";
this._completions = []; // FIXME this._completions = []; // FIXME
this.getKey = function (item, key) (typeof self.keys[key] == "function") ? self.keys[key].call(this, item) : 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]];
} }
@@ -111,15 +112,23 @@ CompletionContext.prototype = {
// Temporary // Temporary
get allItems() get allItems()
{ {
let self = this; try
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) { let self = this;
if (!context.hasItems) let minStart = Math.min.apply(Math, [context.offset for ([k, context] in Iterator(this.contexts)) if (context.items.length && context.hasItems)]);
return []; let items = this.contextList.map(function (context) {
let prefix = self.value.substring(minStart, context.offset); if (!context.hasItems)
return context.items.map(function makeItem(item) ({ text: prefix + item.text, item: item.item })); return [];
}); let prefix = self.value.substring(minStart, context.offset);
return { start: minStart, items: util.Array.flatten(items), longestSubstring: this.longestAllSubstring } 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 // Temporary
get allSubstrings() get allSubstrings()
@@ -135,6 +144,8 @@ CompletionContext.prototype = {
function (res, list) res.filter( function (res, list) res.filter(
function (str) list.some(function (s) s.substr(0, str.length) == str)), function (str) list.some(function (s) s.substr(0, str.length) == str)),
lists.pop()); lists.pop());
if (!substrings) // FIXME: How is this undefined?
return [];
return util.Array.uniq(substrings); return util.Array.uniq(substrings);
}, },
// Temporary // Temporary
@@ -429,6 +440,7 @@ CompletionContext.prototype = {
this.selectionTypes = {}; this.selectionTypes = {};
this.tabPressed = false; this.tabPressed = false;
this.title = ["Completions"]; this.title = ["Completions"];
this.waitingForTab = false;
this.updateAsync = false; this.updateAsync = false;
if (this.editor) if (this.editor)
{ {
@@ -473,6 +485,7 @@ function Completion() //{{{
.createInstance(Components.interfaces.nsIJSON); .createInstance(Components.interfaces.nsIJSON);
const OFFSET = 0, CHAR = 1, STATEMENTS = 2, DOTS = 3, FULL_STATEMENTS = 4, FUNCTIONS = 5; const OFFSET = 0, CHAR = 1, STATEMENTS = 2, DOTS = 3, FULL_STATEMENTS = 4, FUNCTIONS = 5;
let stack = []; let stack = [];
let functions = [];
let top = []; /* The element on the top of the stack. */ let top = []; /* The element on the top of the stack. */
let last = ""; /* The last opening char pushed onto the stack. */ let last = ""; /* The last opening char pushed onto the stack. */
let lastNonwhite = ""; /* Last non-whitespace character we saw. */ let lastNonwhite = ""; /* Last non-whitespace character we saw. */
@@ -564,8 +577,6 @@ function Completion() //{{{
this.eval = function eval(arg, key, tmp) this.eval = function eval(arg, key, tmp)
{ {
if (!this.context.cache.eval)
this.context.cache.eval = {};
let cache = this.context.cache.eval; let cache = this.context.cache.eval;
if (!key) if (!key)
key = arg; key = arg;
@@ -629,6 +640,7 @@ function Completion() //{{{
let i = 0, c = ""; /* Current index and character, respectively. */ let i = 0, c = ""; /* Current index and character, respectively. */
stack = []; stack = [];
functions = [];
push("#root"); push("#root");
/* Build a parse stack, discarding entries as opening characters /* Build a parse stack, discarding entries as opening characters
@@ -669,6 +681,7 @@ function Completion() //{{{
/* Function call, or if/while/for/... */ /* Function call, or if/while/for/... */
if (/[\w\d$]/.test(lastNonwhite)) if (/[\w\d$]/.test(lastNonwhite))
{ {
functions.push(i);
top[FUNCTIONS].push(i); top[FUNCTIONS].push(i);
top[STATEMENTS].pop(); top[STATEMENTS].pop();
} }
@@ -723,6 +736,9 @@ function Completion() //{{{
return; return;
} }
if (!this.context.cache.eval)
this.context.cache.eval = {};
/* Okay, have parse stack. Figure out what we're completing. */ /* Okay, have parse stack. Figure out what we're completing. */
if (/[\])}"';]/.test(str[lastIdx - 1]) && last != '"' && last != '"') if (/[\])}"';]/.test(str[lastIdx - 1]) && last != '"' && last != '"')
@@ -733,8 +749,21 @@ function Completion() //{{{
let prev = 0; let prev = 0;
for (let [,v] in Iterator(get(0)[FULL_STATEMENTS])) for (let [,v] in Iterator(get(0)[FULL_STATEMENTS]))
{ {
this.eval(str.substring(prev, v + 1)); let key = str.substring(prev, v + 1);
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, // For each DOT in a statement, prefix it with TMP, eval it,
@@ -757,10 +786,15 @@ function Completion() //{{{
if (dot > stop) if (dot > stop)
break; break;
let s = str.substring(prev, dot); let s = str.substring(prev, dot);
if (prev != statement) if (prev != statement)
s = EVAL_TMP + "." + s; s = EVAL_TMP + "." + s;
prev = dot + 1;
cacheKey = str.substring(statement, dot); cacheKey = str.substring(statement, dot);
if (checkFunction(prev, dot, cacheKey))
return [];
prev = dot + 1;
obj = self.eval(s, cacheKey, obj); obj = self.eval(s, cacheKey, obj);
} }
return [[obj, cacheKey]] return [[obj, cacheKey]]
@@ -889,6 +923,8 @@ function Completion() //{{{
let [offset, obj, func] = getObjKey(-3); let [offset, obj, func] = getObjKey(-3);
let key = str.substring(get(-2, 0, STATEMENTS), top[OFFSET]) + "''"; let key = str.substring(get(-2, 0, STATEMENTS), top[OFFSET]) + "''";
if (!obj.length)
return;
try 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. the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/ }}} ***** 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? // TODO: why are we passing around strings rather than file objects?
function IO() //{{{ function IO() //{{{
{ {
@@ -815,7 +825,7 @@ lookup:
.getService(Components.interfaces.mozIJSSubScriptLoader); .getService(Components.interfaces.mozIJSSubScriptLoader);
try try
{ {
loader.loadSubScript(uri.spec, {__proto__: plugins}); loader.loadSubScript(uri.spec, new Script(file.path));
} }
catch (e) catch (e)
{ {

View File

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

View File

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