IntelliJ Platform Plugin development example – #1 Generate class from template

This if first blog post about IntelliJ Plugin development, which will cover development of custom basic plugin for IntelliJ which will add new action to right click (in Project view) menu, define new class and generate it from template.

Idea of these blog posts is that provide example which cover common problems like custom dialogs, action system, menu contribution, internationalization, etc… Source code is on Github, where you can find a specific commit which is related to ceratin blog post (First blog post #1 -> Tag: No1).

Basic structure

IntelliJ Platform Plugin - Project structure
IntelliJ Platform Plugin – Project structure

Basic structure is pretty simple and most important part is plugin.xml file, which is Plugin Configuration File.

If you are going to create a new project, then use IntelliJ Platform Plugin project type. If you are going to import existing source, then there is need to do this workaround to import it as IntelliJ Platform Plugin type.

Building our plugin

At first, create a holder class which will keeps all information used for generating:

public class GeneratedClass {

  public static final GeneratedClass INSTANCE = new GeneratedClass();

  private String className;
  private boolean hasPsvm;

  public String getClassName() {
    return className;
  }

  public void setClassName(String className) {
    this.className = className;
  }

  public boolean isHasPsvm() {
    return hasPsvm;
  }

  public void setHasPsvm(boolean hasPsvm) {
    this.hasPsvm = hasPsvm;
  }
}

In future it will be better to do it in other way or add something like clean method to keep given parameters up to date. But for our purpose we can use this.

Templates

We are going to generate class from templates and we’d like to offer create simple empty class and also offer generated class with prepared public static void main method. These templates has to be created in resources/fileTemplates/ and has to have name like template_name.java.ft.

First simple template should look like:

public class ${NAME} {

}

and second with public static void main:

public class ${NAME} {

    public static void main(String[] args) {

    }
}

You can see ${NAME} which is default holder used from class name given to createClass method which will be described later.

UI Dialog

Next step is our custom form which will be displayed in Dialog. I used built-in Swing designer in IntelliJ and for our example it looks very simple:

Dialog UI
Dialog UI

In this generated form class we have to implement configuration of GeneratedClass instance in registered listeners of elements:

public class ClassGeneratorForm {

  private JPanel mainPanel;
  private JLabel classNameLabel;
  private JTextField classNameTextField;
  private JLabel hasPsvmLabel;
  private JPanel classNamePanel;
  private JRadioButton yesRadioButton;
  private JRadioButton noRadioButton;
  private JPanel psvmPanel;

public ClassGeneratorForm() {
  yesRadioButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
      GeneratedClass.INSTANCE.setHasPsvm(true);
    }
  });
  noRadioButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
      GeneratedClass.INSTANCE.setHasPsvm(false);
    }
  });
  classNameTextField.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
      GeneratedClass.INSTANCE.setClassName(classNameTextField.getText());
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
      GeneratedClass.INSTANCE.setClassName(classNameTextField.getText());
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
      GeneratedClass.INSTANCE.setClassName(classNameTextField.getText());
    }
  });
}

public JComponent getContent() {
  return mainPanel;
}

All others Swing stuffs are generated by IntelliJ (check on GitHub).

Finally, we can wrap this form to our custom Dialog where we can extends IntelliJ DialogWrapper class which is used for all dialogs in IntelliJ (so we don’t have to care about IDE themes).

public class GenDialogWrapper extends DialogWrapper {

  private ClassGeneratorForm classGenForm;

  public GenDialogWrapper(@Nullable Project project) {
    super(project);
    classGenForm = new ClassGeneratorForm();
    init();
    setTitle("Class generator");
  }

  @Nullable
  @Override
  protected JComponent createCenterPanel() {
    return classGenForm.getContent();
  }

}

 

How to create custom dialog with custom form?

Create getContent() method in form class which will return root panel and call this method in override method createCenterPanel().

So now we are done with UI work.

IntelliJ Platform Action system

Now we have to create an Action class which will be bound to registered menu item:

public class ClassGenerateAction extends AnAction {

  private PsiDirectory selectedDir;

  @Override
  public void update(AnActionEvent e) {
    PsiElement selectedElement = CommonDataKeys.PSI_ELEMENT.getData(e.getDataContext());
    if (selectedElement instanceof PsiDirectory) {
      selectedDir = (PsiDirectory) selectedElement;
    } else if (selectedElement instanceof PsiClass) {
      PsiFile psiFile = selectedElement.getContainingFile();
      selectedDir = psiFile.getContainingDirectory();
    } else {
      e.getPresentation().setEnabledAndVisible(false);
    }

  }

  @Override
  public void actionPerformed(AnActionEvent anActionEvent) {
    GenDialogWrapper dialogWrapper = new GenDialogWrapper(anActionEvent.getProject());
    dialogWrapper.show();

    if (dialogWrapper.isOK()) {

      String template;
      if (GeneratedClass.INSTANCE.isHasPsvm()) {
        template = "PsvmClassTemplate.java";
      } else {
        template = "BasicClassTemplate.java";
      }

      JavaDirectoryService.getInstance().createClass(selectedDir,
          GeneratedClass.INSTANCE.getClassName(), template, true);
    }
  }
}

As you can see, our action class extends AnAction and overrides two methods – update() and actionPerformed().

Update method handles showing our menu item only if action is invoked on file or folder in Project View.

How to get PsiElement from AnActionEvent?

CommonDataKeys.PSI_ELEMENT.getData(e.getDataContext());

 

How to get parent directory from PsiFile?

psiFile.getContainingDirectory();

 

How to set visibility for AnAction?

e.getPresentation().setVisible(Boolean);
e.getPresentation().setEnabledAndVisible(Boolean);

 

An actionPerformed method is our custom action where we want to show our GenDialogWrapper, handle submitting and generate Java class from template.

 

How to generate Java class from template?

JavaDirectoryService.getInstance().createClass(PsiDirectory dir, String className, String templateName);

Simple generating class from template.

 

JavaDirectoryService.getInstance().createClass(PsiDirectory dir, String className, String templateName, Boolean askForUndefined);

Generating class from template and ask for parameters, which are in templates.

 

JavaDirectoryService.getInstance().createClass(PsiDirectory dir, String className, String templateName, Boolean askForUndefined, Map<String, String> parametersMap);

Generating class from template and ask for parameters, which are in templates and which are not in given parameters map.

Last step is modify plugin.xml file and register our new action to menu in plugin.xml:

<idea-plugin version="2">
  <id>hrabosch_example</id>
  <name>IntelliJ Plugin Example</name>
  <version>1.0</version>
  <vendor email="your_email@example.com" url="http://www.hrabosch.com">Hrabosch</vendor>

  <description><![CDATA[
      Example IntelliJ IDEA plugin for blog posts on <a href="http://hrabosch.com/".<br>
    ]]></description>

  <change-notes><![CDATA[
      Add change notes here.<br>
      <em>most HTML tags may be used</em>
    ]]>
  </change-notes>

  <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
  <idea-version since-build="145.0"/>

  <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
       on how to target different products -->
  <!-- uncomment to enable plugin in all products
  <depends>com.com.ca.ipv.plugins.intellij.modules.lang</depends>
  -->

  <extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
  </extensions>

  <actions>
    <group id="genClassProject" text="Generate class" popup="true">
      <add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
      <action id="classGenerator"
        class="org.hrabosch.plugin.intellij.example.action.ClassGenerateAction"
        text="Class generator"
        description="UI for generating class.">
      </action>
    </group>
  </actions>

</idea-plugin>

Important part is in <actions> … </actions> which contains:

  • Create a new group (<group> .. </group>) which is presented by text, has id and it is popup;
  • add this group (<add-to-group .. />) to already existing group ProjectViewPopupMenu and place it on first position via anchor;
  • and register action (<action>…</action>) to this group, which has unique id, is presented by text and has linked class org.hrabosch.plugin.intellij.example.action.ClassGenerateAction.

Summary

Now you should be able to write your owns basic plugins for IntelliJ IDEA and use templates. In next post, we will modify this example to support internationalization and support more configurable templates.

Screenshots

IntelliJ Plugin example - Menu
IntelliJ Plugin example – Menu
IntelliJ Plugin example - Dialog
IntelliJ Plugin example – Dialog

IntelliJ Plugin example - Generated class
IntelliJ Plugin example – Generated class
Aleš Laňar
Senior Engineer Software ve společnosti CA Technologies

3 thoughts on “IntelliJ Platform Plugin development example – #1 Generate class from template

  1. Hi,
    I just have one question. is it possible to use template file to create class inside community edition?

    1. Hi,
      If you will use this example and if you will build i, you can try to publish it as plugin locally, you can manually install this plugin to both versions and it will works. Templates are just inner resources of plugin so it does not depend on type of IDEA.
      Ales

  2. thank you very much. Yes, actually I tried to use your example as an reference for my work.
    actually, I need this template class to generate some basic classes. so, as you mentioned it is not a matter, because when I generate template files inside my plugin folder it said .ft files supported by the ultimate edition, so I can ignore this error and continue.

Leave a Reply

Your email address will not be published. Required fields are marked *