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

Better JS completion cacheing

This commit is contained in:
Kris Maglione
2008-10-15 18:50:42 +00:00
parent 6ae49d3dbc
commit eafa04da64
2 changed files with 55 additions and 30 deletions

View File

@@ -72,7 +72,6 @@ function Completion() //{{{
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. */
let lastChar = ""; /* Last character we saw, used for \ escaping quotes. */ let lastChar = ""; /* Last character we saw, used for \ escaping quotes. */
let lastObjs = [];
let lastKey = null; let lastKey = null;
let compl = []; let compl = [];
let str = ""; let str = "";
@@ -129,23 +128,10 @@ function Completion() //{{{
if (!(objects instanceof Array)) if (!(objects instanceof Array))
objects = [objects]; objects = [objects];
// See if we can use the cached member list from a previous call.
if (continuing && objects != lastObjs)
{
let k = 0;
continuing = objects.every(function (obj) obj == lastObjs[k++]);
}
lastObjs = objects;
if (continuing)
return compl;
// Can't use the cache. Build a member list. // Can't use the cache. Build a member list.
compl = []; compl = [];
for (let [,obj] in Iterator(objects)) for (let [,obj] in Iterator(objects))
{ {
if (typeof obj == "string")
obj = eval("with (liberator) {" + obj + "}");
// Things we can dereference // Things we can dereference
if (["object", "string", "function"].indexOf(typeof obj) == -1) if (["object", "string", "function"].indexOf(typeof obj) == -1)
continue; continue;
@@ -186,12 +172,19 @@ function Completion() //{{{
} }
function eval(arg) this.eval = function eval(arg, key)
{ {
if (!("eval" in cacheResults))
cacheResults.eval = {};
let cache = cacheResults.eval;
if (!key)
key = arg;
if (key in cache)
return cache[key];
try try
{ {
// liberator.dump("eval(" + util.escapeString(arg) + ")\n"); return cache[key] = window.eval(arg);
return liberator.eval(arg);
} }
catch (e) catch (e)
{ {
@@ -331,6 +324,7 @@ function Completion() //{{{
this.complete = function complete(string) this.complete = function complete(string)
{ {
let self = this;
try try
{ {
continuing = lastIdx && string.indexOf(str) == 0; continuing = lastIdx && string.indexOf(str) == 0;
@@ -348,22 +342,53 @@ function Completion() //{{{
// Find any complete statements that we can eval before we eval our object. // Find any complete statements that we can eval before we eval our object.
// This allows for things like: let doc = window.content.document; let elem = doc.createElement...; elem.<Tab> // This allows for things like: let doc = window.content.document; let elem = doc.createElement...; elem.<Tab>
let end = get(0, 0, FULL_STATEMENTS) || 0; let prev = 0;
let preEval = str.substring(0, end) + ";"; for (let [,v] in Iterator(get(0)[FULL_STATEMENTS]))
{
this.eval(str.substring(prev, v + 1));
prev = v + 1;
}
// For each DOT in a statement, prefix it with TMP, eval it,
// and save the result back to TMP. The point of this is to
// cache the entire path through an object chain, mainly in
// the presence of function calls. There are drawbacks. For
// instance, if the value of a variable changes in the course
// of inputting a command (let foo=bar; frob(foo); foo=foo.bar; ...),
// we'll still use the old value. But, it's worth it.
let getObj = function (frame, stop)
{
const TMP = "__liberator_eval_tmp";
let statement = get(frame, 0, STATEMENTS) || 0; // Current statement.
let prev = statement;
window[TMP] = null;
for (let [i, dot] in Iterator(get(frame)[DOTS]))
{
if (dot < statement)
continue;
if (dot > stop)
break;
let s = str.substring(prev, dot);
if (prev != statement)
s = TMP + "." + s;
prev = dot + 1;
window[TMP] = self.eval(s, str.substring(statement, dot));
}
return window[TMP];
}
let getObjKey = function (frame) let getObjKey = function (frame)
{ {
let obj = [liberator, window]; // Default objects;
let dot = get(frame, 0, DOTS) || -1; // Last dot in frame. let dot = get(frame, 0, DOTS) || -1; // Last dot in frame.
let statement = get(frame, 0, STATEMENTS) || 0; // Current statement. let statement = get(frame, 0, STATEMENTS) || 0; // Current statement.
let end = (frame == -1 ? lastIdx : get(frame + 1)[OFFSET]); let end = (frame == -1 ? lastIdx : get(frame + 1)[OFFSET]);
let obj = [liberator, window]; // Default objects;
/* Is this an object dereference? */ /* Is this an object dereference? */
if (dot < statement) // No. if (dot < statement) // No.
dot = statement - 1; dot = statement - 1;
else // Yes. Set the object to the string before the dot. else // Yes. Set the object to the string before the dot.
obj = preEval + str.substring(statement, dot); obj = getObj(frame, dot);
let [, space, key] = str.substring(dot + 1, end).match(/^(\s*)(.*)/); let [, space, key] = str.substring(dot + 1, end).match(/^(\s*)(.*)/);
return [dot + 1 + space.length, obj, key]; return [dot + 1 + space.length, obj, key];
@@ -400,11 +425,11 @@ function Completion() //{{{
return [0, []]; return [0, []];
// Begining of the statement upto the opening [ // Begining of the statement upto the opening [
let obj = preEval + str.substring(get(-3, 0, STATEMENTS), get(-2)[OFFSET]); let obj = getObj(-3, get(-2)[OFFSET]);
// After the opening [ upto the opening ", plus '' to take care of any operators before it // After the opening [ upto the opening ", plus '' to take care of any operators before it
let key = preEval + str.substring(get(-2)[OFFSET] + 1, top[OFFSET]) + "''"; let key = str.substring(get(-2)[OFFSET] + 1, top[OFFSET]) + "''";
// Now eval the key, to process any referenced variables. // Now eval the key, to process any referenced variables.
key = eval(key); key = this.eval(key);
let compl = this.objectKeys(obj); let compl = this.objectKeys(obj);
return [top[OFFSET], this.filter(compl, key + string, last, key.length)]; return [top[OFFSET], this.filter(compl, key + string, last, key.length)];
@@ -430,9 +455,10 @@ function Completion() //{{{
try try
{ {
if (!completer) if (!completer)
completer = eval(obj)[func].liberatorCompleter; completer = obj[func].liberatorCompleter;
} }
catch (e) {} catch (e) {}
liberator.dump({call: completer, func: func, obj: obj, string: string, args: "args"});
if (!completer) if (!completer)
return [0, []]; return [0, []];
@@ -446,8 +472,8 @@ function Completion() //{{{
}); });
args.push(key); args.push(key);
let compl = completer.call(this, func, preEval, obj, string, args); let compl = completer.call(this, func, obj, string, args);
key = eval(preEval + key); key = this.eval(key);
return [top[OFFSET], this.filter(compl, key + string, last, key.length)]; return [top[OFFSET], this.filter(compl, key + string, last, key.length)];
} }

View File

@@ -640,14 +640,13 @@ const liberator = (function () //{{{
liberator.dump((msg || "") + (new Error()).stack.replace(/.*\n/, "")); liberator.dump((msg || "") + (new Error()).stack.replace(/.*\n/, ""));
}, },
// with (liberator) means, liberator is the default namespace "inside" eval
eval: function (str) eval: function (str)
{ {
const fileName = "chrome://liberator/content/liberator.js"; const fileName = "chrome://liberator/content/liberator.js";
const line = new Error().lineNumber + 3; const line = new Error().lineNumber + 3;
try try
{ {
return eval("with (liberator) {" + str + "}"); return window.eval(str);
} }
catch (e) catch (e)
{ {