Closed ScottyB closed 11 years ago
Are you talking about this situation?
function foo() {
var constants = {
PI : 3.14,
E : 2.72
};
constants. // get completions here
}
Let's review. You have two sources of completion candidates:
skewer-eval
. Skewer can get you completions for things only available at run time, like globally-accessible objects being created or updated as the program runs, DOM stuff, etc. However, there's a risk of querying completions on anything but identifiers: you will necessarily invoke any side-effects of the expression you're completing. The jQuery completion example in your README is an example of this.Imagine computing the completions on this,
fire('nuclear missiles').getStatus(). // want to eval this while coding?
Notice that the Chrome REPL won't do completions on jQuery selections. It doesn't want to cause side-effects in order to get that list.
In your constants
case above you'll definitely need to consult js2-mode, which is well suited for the task. From the current node in the js2-mode AST, look in the non-global, static environment for anything named constants
. (This is the same thing js2-mode does to warn about undeclared variables.) If it exists, you shouldn't try to get any completion candidates for the name "constants
" from Skewer because the identifier refers to a local, static variable, inaccessible to Skewer (keyword: static). If it doesn't exist, it's a free variable and you can query the global variables with Skewer.
The tricky part is completing on that right-hand-side object literal. For dealing with assignment right-hand-side expressions you have a few options:
json-read-from-string
, hoping it's JSON-y enough, and pick apart the s-expression result. This may be easier to navigate than the AST.If you're going the body transplant route, I suggest perhaps using "use strict" to limit side-effect damage (i.e. global variable declarations/trashing). This may be what you're looking for if you need another client. Purely speculation here: another idea would be to load up an iframe or another window/tab containing a skewered page, creating another fresh client for Emacs. Evaluations would still be dispatched to both clients but you could choose to only respond with a full list of completions from the other iframe/tab (and an empty completion list from the other so that it requeues with skewer).
I am talking about the code snippet you posted. After reading your response I am starting to think that dynamic evaluation wasn't such a good idea. Personally I like the idea of having my code constantly being evaluated but it presents other issues like how do you recover from a broken state and the example you showed. Something I should have thought about earlier.
I think the best thing to do, is do as you say and use js2-mode's AST and resort to skewer for safe completions i.e. identifiers only. I'll leave the idea of constant evaluation for another project. I didn't think of using json-read-from-string
to get completion candidates but I think I will use the AST and only use this approach as a last resort.
I had a closer look at jQuery and to get completions I will pretty much have to evaluate the code anyway. Are there any other techniques that you know of that can minimise the side effects of evaluation? At the moment I am thinking that I will only evaluate libraries but have that feature turned off by default.
I think you're just up against a fundamental information limit and there's nothing you could do. I mentioned before about Chrome not doing certain kinds of completions. I think they're completing everything that can safely be completed (at a top level REPL), so, as a rule of thumb, you can look at what Chrome's not doing as a limit of what you can do safely.
I do like the idea of optionally being able to evaluate dangerously anyway. Sometimes I bet a few side-effects are worth the completions.
Thanks for your help with the issues I have raised so far. ac-js2 is coming along nicely. The issue I am faced with at the moment is completions inside of a function. If this code:
is located outside of a function or closure then when I type
constants.
I should get a list of candidates two of which are PI and E both showing their respective values of 3.14 and 2.72. Of course if I wrap this in a function then I can no longer evaluateconstants
so I cannot get PI and E in the candidate list.If I evaluate the code inside of the surrounding function then
constants
will get added to the global object and be available for completion everywhere. I could use js2-mode's parser to solve this problem and I may still have to.To get around this I created a
ScopeObject
and added all of the functions and variables to it from the current scope. I do this for all of the surrounding parent functions. It still needs a fair bit of work but the idea is there https://github.com/ScottyB/ac-js2/commit/c2bf1fe1e3d36f638af8b193f49f0b7a886b3b69. Doing this enables me to filter and deleteScopeObject
properties as required. However, evaluating any code block using the methods provided in skewer will add false completion candidates. Now at last to my question, is it possible to setup a separate skewer-client for completion? Or do you have any suggestions?Btw, a really cool side project occurred to me while writing this and that would be to combine impatient mode with skewer mode. Hopefully I can tidy up ac-js2 quickly :).