create a new expression for a live template for actionscript
I'm working in actionscript, love macros that let me create trace statements and so far I have one that gives me:
Logger.log(className:methodName);
which is great. this uses the epressions found in the edit variables window, via the expressions dropdown, eg jsMethodName()
but now I want to add an expression that lets me list the params that any given method takes and their values
this exists for java, I believe, or groovy, but not for actionscript, so I guess I have to write it myself
first question: has anybody already done this already?
second question: if not, where does the source for, eg, jsMethodName() live? if I could look at that I could probably figure out my new expression from that
other tips? thanks
请先登录再写评论。
Yes, you can write a custom IntelliJ IDEA plugin that would implement corresponding extension:
<extensions defaultExtensionNs="com.intellij">
<liveTemplateMacro implementation="foo.bar.MyMacro"/>
</extensions>
with corresponding class implementation:
public class MyMacro extends com.intellij.codeInsight.template.Macro {...}
Code of jsMethodName() is not open, but you'll find dozens of other Macro implementations in IntelliJ IDEA Community Edition source code.
Other way is to file a YouTrack request and hope that we'll implement it ourselves.
thanks...
ok, so I grabbed the source for the community edition, I can see the package/s where all the macros live and I can see how I'd go about creating my own... more or less...
but then what? because I use the ultimate edition, would make this or that module, then copy and paste the resultant jar files from my output folder, to the install folder for ultimate?
still think someone else *must have done this already, but no one has stepped up... anybody?
thanks
You need to follow the standard procedure of a custom plugin development. You should use Ultimate Edition for plugin development because your plugin must depend on 'com.intellij.flex'. You'll need to add few more jars to the Plugin SDK:
JavascriptLanguage.jar and resources_en.jar from [IDE installation]/plugins/JavaScriptLanguage/lib
flex-shared.jar, FlexSupport.jar and resources_en.jar from [IDE installation]/plugins/flex/lib.
ActionScript PSI is not open-sourced so you'll need to explore syntax tree yourself and guess few things while implementing your macro.
As a result you'll export your plugin jar and will be able to install it to your IntelliJ IDEA Ultimate.
These steps may appear to be rather tricky for the first time. By the way how is Java analog called that you want to implement for ActionScript?
well for example I'd like to do in actionscript what you see in the live templates panel: output/soutp: Prints method parameter names and values to System.out
and that one depends on some groovy script and a method call to methodParameters()
then there's output/soutv:Prints a value to System.out which I'd like to port as well, it depends on a call to variableOfType("") -- here I would want to wind up w/ a thing where you copy a variable name and run your macro and you get MethodName:VariableName:VariableValue which would need some kind of a clipboard paste but I havent seen that around yet
ok I have a plugin project going, and a test class going that extends macro, etc
when I invoke it from the editor, the only things that get called are the contructor, and getName(), although I dont see where my harcoded name string ever shows up
shouldnt calculateResult(...) or calculateQuickResult(...) get called?
Can I take a look at your plugin?
here you go, just a minimal implementation as you can see. all I can figure is that calculateResult is where it all happens? assuming I can get whatever I need from the context objects passed in but any help you can give me would be great -- or, point me to docs on this? I dont think Macro is open sourced
package btb.macros;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.template.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class TestMacro extends Macro {
@Nullable
@Override
public LookupElement[] calculateLookupItems(@NotNull Expression[] params, ExpressionContext context) {
System.out.println("calculateLookupItems");
return super.calculateLookupItems(params, context);
}
@Override
public String getName() {
System.out.println("getName");
return "my name is test macro";
}
@Override
public String getPresentableName() {
System.out.println("getPresentableName");
return ("testMacro()");
}
@NotNull
@Override
public String getDefaultValue() {
System.out.println("getDefaultValue");
return "this is the default value";
}
public TestMacro() {
super();
System.out.println("contructor");
}
@Override
public Result calculateResult(@NotNull Expression[] params, final ExpressionContext context) {
System.out.println("calculateResult");
return new TextResult("this is the full result");
}
@Override
public boolean isAcceptableInContext(TemplateContextType context) {
System.out.println("isAcceptableInContext");
return super.isAcceptableInContext(context);
// return context instanceof JavaScriptCodeContextType; // this fails
}
@Override
public Result calculateQuickResult(@NotNull Expression[] params, ExpressionContext context) {
System.out.println("calculateQuickResult");
return new TextResult("this is the quick result");
}
}
I was very surprised to find the cause, but it appears that macro works only if its name doesn't contain spaces and presentableName = name + "()". I'm sure this is not done by design and will try to fix it. For now you know how to proceed. Next step is to work with the syntax tree. Your friend is Tools | View PSI Structure action. Class Macro and all Live Templates infrastructure is open sourced, but I don't think you need to explore it. You may take a look at other macro implementations. For example, source code of methodParameters() macro is in com.intellij.codeInsight.template.macro.MethodParametersMacro class. Probably you'll find methods of class PsiTreeUtil useful for your task.
ok, changed the hardcoded return values to eliminate spaces, and confirmed format of return value for getPresentableName, as you can see
but situation unchanged: I can get my macro to load, etc, but no call to calculateResult
am I wrong in assuming that's how a macros work?
psi viewer tool very interesting, looks like I'll have no problem traversing the tree once I get a reference
thanks for your help
//
package macros;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.template.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class TestMacro extends Macro {
@Nullable
@Override
public LookupElement[] calculateLookupItems(@NotNull Expression[] params, ExpressionContext context) {
System.out.println("calculateLookupItems");
return super.calculateLookupItems(params, context);
}
@Override
public String getName() {
System.out.println("getName");
return "name_no_spaces";
}
@Override
public String getPresentableName() {
System.out.println("getPresentableName");
return ("btbTestMacro()");
}
@NotNull
@Override
public String getDefaultValue() {
System.out.println("getDefaultValue");
return "default_value_no_spaces";
}
public TestMacro() {
super();
System.out.println("constructor");
}
@Override
public Result calculateResult(@NotNull Expression[] params, final ExpressionContext context) {
System.out.println("calculateResult");
return new TextResult("full_result_no_spaces");
}
@Override
public boolean isAcceptableInContext(TemplateContextType context) {
System.out.println("isAcceptableInContext");
// return super.isAcceptableInContext(context);
// return context instanceof JavaScriptCodeContextType; // this fails
return true;
}
@Override
public Result calculateQuickResult(@NotNull Expression[] params, ExpressionContext context) {
System.out.println("calculateQuickResult");
return new TextResult("quick_result_no_spaces");
}
}
You should read my comment more carefully :)
I.e. you need to have
public String getName() {
return "btbTestMacro";
}
public String getPresentableName() {
return "btbTestMacro()"; // or return getName() + "()"
}
ah, right, ok, thanks
ok, so now calculateResult() is called as expected
but there seems to be a classloader issue:
I need to use the com.intellij.lang.javascript.psi.* package, eg:
public Result calculateResult(@NotNull Expression[] params, final ExpressionContext context) {
PsiElement here = context.getPsiElementAtStartOffset();
Object test = PsiTreeUtil.getParentOfType(here, JSFunction.class);
etc...
since I added the javascript and flex support libs to my SDK definition, as you advised, the above resolves and compiles
but when I debug the above, I get:
java.lang.NoClassDefFoundError: com/intellij/lang/javascript/psi/JSFunction
I am using ulitimate edition, so the flex/js packages are available at runtime -- true?
btw the SDK definition, which I amended, was created by pointing to my idea app installation dir -- was that correct?
suggestions?
thanks
Looks like you didn't add
<depends>com.intellij.flex</depends>
to your plugin.xml (see my 2nd comment).
And a few notes:
public static PsiElement findElementAtCaret(final ExpressionContext context) {
final int templateStartOffset = context.getTemplateStartOffset();
final int offset = templateStartOffset > 0 ? templateStartOffset - 1 : templateStartOffset;
final PsiFile file = PsiDocumentManager.getInstance(context.getProject()).getPsiFile(context.getEditor().getDocument());
return InjectedLanguageUtil.findElementAtNoCommit(file, offset);
}
ok, thanks for the tip, I'm almost in the clear...
one thing: how come calculateResult gets called 4 times? here's my trace:
calculateResult
calculateResult
calculateResult
calculateResult
calculateLookupItems
and the call to calculateLookupItems? what does that do?
and, how could I get the clipboard contents from inside my macro?
btw, are there docs on all this? hate to waste your time!
thanks again
calculateLookupItems() is used to form completion list if you want to give user a choice of the macro variable values.
You shouldn't worry about multiple calculateResult() invocations. It is called each time when InteliJ IDEA thinks that something is changed in the code, and may be in some other cases. You can look through the invocation trace to satisfy your curiosity, though generally you shouldn't care.
I'm afraid there's no complete docs on all these aspects. Source code of IntelliJ IDEA Community is the best reference to understand the best practices.
ok so here's my macro so far:
public class jsParams extends Macro {
public String getName() {
return "jsParams";
}
public String getPresentableName() {
return "jsParams()";
}
public Result calculateResult( Expression[] inParams, final ExpressionContext context) {
PsiElement here = context.getPsiElementAtStartOffset();
JSParameter[] params = PsiTreeUtil.getParentOfType(here, JSFunction.class).getParameterList().getParameters();
String output = "";
for (int i = 0; i < params.length; i++) {
JSParameter param = params[i];
output += param.getName();
}
return new TextResult(output);
}
}
which when I run it in the dbugger gives me:
param1param2param3
which is correct
but when I deploy it and edit some real as3 code, I get:
type
just that, the string "type"
thoughts?
thanks
That must be some misconfiguration. E.g. differently configured custom live template in different environments. Your plugin works for me. Don't forget to fix potential NPEs and would be good make it work in MXML.
Sorry to drag an old thread up, but I'm wondering if methodProperties is going to be added for JS/AS3 live templates?
On the off chance that the original author of this thread is still tuned in, is there any chance the macro/plugin that you created could be shared?
Thanks in advance,
Jason