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

18 comments

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.

0

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
0

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?

0

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

0

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?

0

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");
    }

}

0

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.

0

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");
    }

}

0

You should read my comment more carefully :)

presentableName = name + "()"


I.e. you need to have

    public String getName() {
        return "btbTestMacro";
    }

    public String getPresentableName() {
        return "btbTestMacro()"; // or return getName() + "()"
    }

0

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




0

Looks like you didn't add
<depends>com.intellij.flex</depends>
to your plugin.xml (see my 2nd comment).

And a few notes:

  • PsiTreeUtil.getParentOfType(here, JSFunction.class) returns JSFunction, there's no need to give Object type to test variable.
  • If you want your macro to work inside fx:Script block of MXML file then you need to find PsiElement at caret in this way:


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);
}

0

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

0

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.

0

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

0

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.

0

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

0

Please sign in to leave a comment.