Develop a piece of your own Idea plug-in

preface

I usually use IDEA development, thinking about the development of IDEA plug-in. The general development process of IDEA is introduced here. And their own nothing to develop two simple idea plug-ins. One is the chicken soup plugin and the other is the code reading notes plugin. There is not much information about idea plug-in development on the Internet. The main way to learn is to read the official documents and find some other open source plug-in projects to read the source code.

Develop the process using the DevKit plug-in

  1. Setting up the development environment
  2. Create a plug-in project
  3. Create action (plug-in specific content development)
  4. Run and debug plug-ins
  5. Deploy the plug-in
  6. Release the plugin

Environment to prepare

  1. Install the IDEA
  2. Plugin DevKit in IDEA provides support for developing IDEA plug-ins
  3. Configure the IntelliJ Platform Plugin SDK

Create a plug-in project

  1. file->new Project

  2. Fill in project name

  3. The project was created successfully. The project directory is as follows. Plugin.xml is the core configuration file

  4. Description of the core configuration file

    <idea-plugin>
        
      <! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      <id>com.your.company.unique.plugin.id</id>
       
      <! -- Plugin name, the name people use when searching for your plugin in the official plugin library -->
      <name>Plugin display name here</name>
      
      <! -- plugin version -->
      <version>1.0</version>
        
      <! -- Vendor home page and email (cannot use default value, must change to own) -->
      <vendor email="[email protected]" url="http://www.yourcompany.com">YourCompany</vendor>
      <! Description of the plug-in (cannot use the default value, must be changed to their own. And must be greater than 40 characters) -->
      <description><! [CDATA[ Enter short description for your plugin here.<br> <em>most HTML tags may be used</em> ]]></description>
      <! -- Plug-in version change information, support HTML tags; Will show in the Settings | Plugins dialog and plug-in warehouse Web page - >
      <change-notes><! [CDATA[ Add change notes here.<br> <em>most HTML tags may be used</em> ]]></change-notes>
    
     <! -- Plug-in compatible with IDEAbuild -->
      <idea-version since-build="173.0"/>
    
      <! -- id of other plug-ins on which the plug-in depends -->
      <depends>com.intellij.modules.platform</depends>
    
      <extensions defaultExtensionNs="com.intellij">
      <! -- Declare extensions to IDEA Core or other plug-ins -->
      </extensions>
    
      <! -- Write plug-in actions -->
      <actions>
      </actions>
    
    </idea-plugin>
    Copy the code

Create an action

  1. Create an action
  2. Fill in related parameters
  • (1) The basic information of the action, where the value of the Name attribute is the text content of the future menu
  • ② As a submenu under the Tools menu
  • ③ The submenu is placed first
  • ④ Add shortcut keys to submenus

  1. Write notifications for click menus

    /** * Actions created through the Pulgins Devkit inherit Ananction ** /
    public class TestAction extends AnAction {
    
    
        /** * Need to implement the abstract method after the click event */
        @Override
        public void actionPerformed(AnActionEvent e) {
            NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false);
            /** * Content: notification content * type: notification type, such as warning,info,error */
            Notification notification = notificationGroup.createNotification("Test Notification", MessageType.INFO); Notifications.Bus.notify(notification); }}Copy the code

Run and debug plug-ins

  1. Just as I normally debug Java code, I can put breakpoints where I want them.

Deploy the plug-in

  1. packaging

Release the plugin

Registered idea plugins.jetbrains.com/author/me/ account access

  1. Logging in to the plug-in Library

Try the chicken soup plugin

demand

Requirements describe
  • When idea is launched, a dialog box pops up showing a bowl of toxic chicken soup. When we click on another bowl, we switch.

Demand analysis
  1. How to seize the idea to start this time point?
  2. How do I display a dialog box?
  3. How to add button click event?
  4. The source of poison chicken soup content?

The code

The Components Components
Component type describe interface Plugins.xml loads the configuration elements
ApplicationComponent IDEA is initialized when the IDEA is started. There is only one instance in the entire IDEA. ApplicationComponent
ProjectComponent IDEA creates the corresponding level of Component for each Project instance ProjectComponent
ModuleComponent IDEA creates Module-level components for each Module in each Project that has been loaded ModuleComponent

Application-level Components are loaded and initialized at IDEA startup: the initComponent() method is called. So we override the initComponent() method to find the point at which idea was started.

Pop-up dialog box

Through the official documentation www.jetbrains.org/intellij/sd…

package icu.jogeen.dialog;

import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;

/** * DialogWrapper from IDEA */
public class TuantDialog extends DialogWrapper {


    public TuantDialog(a) {
        super(true);
        init();// Initialize dialog
        setTitle("A bowl of chicken soup a day.");// Set the dialog title title
    }

    /** * Create the content panel in the middle of the dialog@return* /
    @Nullable
    @Override
    protected JComponent createCenterPanel(a) {
        // Create a panel and set its layout to a border layout
        JPanel centerPanel = new JPanel(new BorderLayout());
        // Create a text tag to hold the content
        JLabel label = new JLabel("What's in chicken Soup?");
        // Set the size first
        label.setPreferredSize(new Dimension(100.100));
        // Add the text label to the center of the panel
        centerPanel.add(label,BorderLayout.CENTER);
        returncenterPanel; }}Copy the code

Customize the button and add click events

Override the createSouthPanel() method.

@Override
protected JComponent createSouthPanel(a) {
    JPanel southPanel = new JPanel(new FlowLayout());
    JButton button=new JButton("One more bowl.");
    button.addActionListener(e -> {
        label.setText("One more bowl.");
    });
    southPanel.add(button);
    return southPanel;
}
Copy the code
Poison chicken soup content source
  • Get the chicken Soup API address on the web, api.nextrt.com/V1/Dutang.
  • To send Http requests using the RestTemplate, add three dependency packages.

public class ContentUtil {
    public static String getContent(a) {
        RestTemplate restTemplate = new RestTemplate();
        try {
            ResponseEntity<Map> forEntity = new RestTemplate().getForEntity("https://api.nextrt.com/V1/Dutang", Map.class);
            HttpStatus statusCode = forEntity.getStatusCode();
            String content = "";
            if (statusCode.is2xxSuccessful()) {
                List data = (List) forEntity.getBody().get("data");
                Map<String, String> contontMap = (Map<String, String>) data.get(0);
                return contontMap.get("content"); }}catch (Exception e) {
            return  "The soup bowl is broken.";
        }
        return "No chicken soup today."; }}Copy the code
  • layout

Enter the notes plugin

demand

Requirements describe
  • Select any text in IDEA and add the title and content of the note. Finally, you can use your notes to generate markDown articles by specifying a specific template.
  • Right-click any text to bring up a submenu containing customJogeenNoteAction

  • Click on the submenuJogeenNoteActionA dialog box pops up, in the dialog box, edit the title and content of this note, click Add to note list

  • Fill in the title of the document and click Generate Document. Select the directory where the generated document will be saved

  • Open the generated document to display the generated document

Demand analysis
  1. How to add a right click after submenu
  2. How do I get selected text in the editor
  3. How do I pop up a dialog to obtain the contents of the notes edited by the user
  4. How do I use ToolWindow to display a list of notes
  5. How do I add tables to the ToolWindow
  6. How do I let the user choose the directory in which the document is generated
  7. How to statically generate a list of notes into a document

The code

Create a project

Create a new project called MarkBook as the name of our project and as the name of the plug-in product

<idea-plugin>
  <id>com.itheima.cd.markbook.id</id>
  <name>MarkBook</name>
  <version>1.0</version>
  <vendor email="[email protected]" url="http://www.itheima.com">itheima</vendor>

  <description><! <br> <em>MarkDown documentation </em>]]></description>

  <change-notes><! [CDATA[first version, contains the main function of note adding and document generation <br> <em> Only supports the generation of Markdown form notes. </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="173.0"/>

  <! -- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html on how to target different products -->
  <depends>com.intellij.modules.platform</depends>

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

  <actions>
    <! -- Add your actions here -->
  </actions>

</idea-plugin>
Copy the code
Add a submenu after right-clicking
  • To create an Action, select EditorPopupMenu and set the shortcut key CTRL + P

  • Create word after automatically generated configuration file and PopupAction class
    <action id="MB_PopupAction" class="com.itheima.markbook.action.PopupAction" text="Add MB Notes" description="Add MB Notes submenu">
      <add-to-group group-id="EditorPopupMenu" anchor="first"/>
      <keyboard-shortcut keymap="$default" first-keystroke="ctrl P"/>
    </action>
Copy the code
public class PopupAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        // TODO: insert action logic here
        System.out.println("Operations to add notes"); }}Copy the code
  • The test results

Gets the selected text in the editor
  • Modify thePopupActionobject
public class PopupAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        // Get the current editor object
        Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
        // Get the selected data model
        SelectionModel selectionModel = editor.getSelectionModel();
        // Gets the currently selected textString selectedText = selectionModel.getSelectedText(); System.out.println(selectedText); }}Copy the code
A dialog box is displayed to obtain the edited notes
  • Create AddNoteDialog

    package com.itheima.markbook.dialog;
    
    import com.intellij.openapi.ui.DialogWrapper;
    import com.intellij.ui.EditorTextField;
    import org.jetbrains.annotations.Nullable;
    
    import javax.swing.*;
    import java.awt.*;
    
    public class AddNoteDialog extends DialogWrapper {
        /**
         * 标题输入框
         */
        private EditorTextField etfTitle;
        /** * Content input box */
        private EditorTextField etfMark;
    
    
        public AddNoteDialog(a) {
            super(true);
            init();
            setTitle("Add notes");
        }
    
        @Nullable
        @Override
        protected JComponent createCenterPanel(a) {
            JPanel panel = new JPanel(new BorderLayout());
            etfTitle = new EditorTextField("Title of note");
            etfMark = new EditorTextField("Contents of notes");
            etfMark.setPreferredSize(new Dimension(200.100));
            panel.add(etfTitle, BorderLayout.NORTH);
            panel.add(etfMark, BorderLayout.CENTER);
            return panel;
        }
    
        @Override
        protected JComponent createSouthPanel(a) {
            JPanel panel = new JPanel(new FlowLayout());
            JButton btnAdd = new JButton("Add to Note List");
            // Button click event handling
            btnAdd.addActionListener(e -> {
                // Get the title
                String title = etfTitle.getText();
                // Get the content
                String content = etfMark.getText();
                System.out.println(title + ":" + content);
            });
            panel.add(btnAdd);
            returnpanel; }}Copy the code
Improve your notes
  • Create the NoteData class by identifying the fields needed for a note
  package com.itheima.markbook.data;
  
  public class NoteData {
      /** * Note title */
      private String title;
      /** * Contents of notes */
      private String mark;
      /** * tag source */
      private String content;
      /** * source file name */
      private String fileName;
      /** * source file type */
      private String fileType;
      
      // Omit the get set method
  }
Copy the code
  • Find a storage location
  package com.itheima.markbook.data;
  
  import java.util.LinkedList;
  import java.util.List;
  
  public class DataCenter {
      /** * Select text */
      public static String SELECTED_TEXT = null;
      /** * Current file name */
      public static String CURRENT_FILE_NAME = null;
      /** * Current file type */
      public static String CURRENT_FILE_TYPE = null;
      /**
       * 笔记列表集合
       */
      public static List<NoteData> NOTE_LIST = new LinkedList<>();
  
  
  }
Copy the code
  • Gets the name and type of the file stored in a global variable
  // File name
  DataCenter.CURRENT_FILE_NAME = e.getRequiredData(CommonDataKeys.PSI_FILE).getViewProvider().getVirtualFile().getName();
  DataCenter.CURRENT_FILE_TYPE =DataCenter.CURRENT_FILE_NAME.substring(DataCenter.CURRENT_FILE_NAME.lastIndexOf(".") +1);
Copy the code
  • Adds notes to the notes list collection
  // Select the content
  DataCenter.SELECTED_TEXT = selectedText;
  // File name
  DataCenter.CURRENT_FILE_NAME = e.getRequiredData(CommonDataKeys.PSI_FILE).getViewProvider().getVirtualFile().getName();
  DataCenter.CURRENT_FILE_TYPE =DataCenter.CURRENT_FILE_NAME.substring(DataCenter.CURRENT_FILE_NAME.lastIndexOf(".") +1);
Copy the code
How do I create a ToolWindow
  • Create a GUI Form

  • The NoteListWindow is automatically generated after creation
  package com.itheima.markbook.window;
  
  import com.intellij.openapi.project.Project;
  import com.intellij.openapi.wm.ToolWindow;
  
  import javax.swing.*;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  
  public class NoteListWindow {
      private JPanel jcontent;
      private JTextField topicEtf;
      private JTable contentTable;
      private JButton createBtn;
      private JButton clearBtn;
      private JButton closeBtn;
  
      public NoteListWindow(Project project, ToolWindow toolWindow) {
          createBtn.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {}}); clearBtn.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {}}); closeBtn.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {}}); }public JPanel getJcontent(a) {
          returnjcontent; }}Copy the code
  • Create NoteListWindowFactory
package com.itheima.markbook.window;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import org.jetbrains.annotations.NotNull;

public class NoteListWindowFactory implements ToolWindowFactory {
    @Override
    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
        // Create NoteListWindow object
        NoteListWindow noteListWindow = new NoteListWindow(project, toolWindow);
        // Get an instance of the content factory
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        // Get the content for the toolWindow display
        Content content = contentFactory.createContent(noteListWindow.getJcontent(), "".false);
        // Set the content for the toolWindowtoolWindow.getContentManager().addContent(content); }}Copy the code
  • Configure to load the toolWindow extension
  <extensions defaultExtensionNs="com.intellij">
    <! -- Add your extensions here -->
    <toolWindow id="MarkBookWindown" 
                secondary="true" 
                anchor="right" factoryClass="com.itheima.markbook.window.NoteListWindowFactory" icon="/markbook/pluginIcon.svg">

    </toolWindow>
  </extensions>
Copy the code
Add a table to the ToolWindow
  • Add content in the data center
  private static String[] COLUMN_NAME={"Title"."Note"."File name"."Code snippets"};
  
  public static DefaultTableModel TABLE_MODEL = new DefaultTableModel(null,COLUMN_NAME);
Copy the code
  • Define table initialization Settings, and inNoteListWindowConstructorinit
  public void init(a){
      contentTable.setModel(DataCenter.TABLE_MODEL);
      contentTable.setEnabled(true);
  }
Copy the code
  • inbtnAddButton click event added
  / / add
  DataCenter.TABLE_MODEL.addRow(DataConvert.toStringArray(noteData));
Copy the code
  • Set off
  toolWindow.hide(null);
Copy the code
  • Set clear list
DataCenter.reset();
Copy the code
Let the user select the directory where the document will be generated
  • Add file selection to obtain the directory selected by the user
  createBtn.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
          VirtualFile virtualFile = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, project.getBaseDir());
          if(virtualFile! =null){ String path = virtualFile.getPath(); System.out.println(path); }}});Copy the code

Statically generates a list of notes into a document
  • Define the interface for processing
public interface Processor {
    public void process(SourceNoteData sourceNoteData) throws Exception;
}
Copy the code
  • Write an abstract class from Freemarker
  public abstract class AbstractFreeMarkerProcessor implements Processor {
  
      protected abstract Template getTemplate(a) throws IOException, Exception;
  
      protected abstract Object getModel(SourceNoteData sourceNoteData);
  
      protected abstract Writer getWriter(SourceNoteData sourceNoteData) throws FileNotFoundException, Exception;
  
  
      @Override
      public final void process(SourceNoteData sourceNoteData) throws Exception { Template template = getTemplate(); Object model = getModel(sourceNoteData); Writer writer = getWriter(sourceNoteData); template.process(model, writer); }}Copy the code
  • writeMDFreeMarkProcessorinheritanceAbstractFreeMarkerProcessor. Implementing abstract methods
  public class MDFreeMarkProcessor extends AbstractFreeMarkerProcessor {
      @Override
      protected Template getTemplate(a) throws Exception {
          // Load the template string
          String templateString = UrlUtil.loadText(MDFreeMarkProcessor.class.getResource("/template/md.ftl"));
          // Create a template configuration
          Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
          // Create a string template importer
          StringTemplateLoader stringTemplateLoader=new StringTemplateLoader();
          // Import the string template
          stringTemplateLoader.putTemplate("MDTemplate",templateString);
          configuration.setTemplateLoader(stringTemplateLoader);
          // Get the template
          return configuration.getTemplate("MDTemplate");
      }
  
      @Override
      protected Object getModel(SourceNoteData sourceNoteData) {
          HashMap model = new HashMap();
          model.put("topic",sourceNoteData.getNoteTopic());
          model.put("noteList",sourceNoteData.getNoteDataList());
          return model;
      }
  
      @Override
      protected Writer getWriter(SourceNoteData sourceNoteData) throws Exception {
          String filePath = sourceNoteData.getFilePath();
          File file = new File(filePath);
          return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8")); }}Copy the code
  • Add processing Action
  createBtn.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
          VirtualFile virtualFile = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, project.getBaseDir());
          if(virtualFile ! =null) {
              String path = virtualFile.getPath();
              String topic = topicEtf.getText();
              String filePath = path + "/" + topic + ".md";
              Processor processor = new MDFreeMarkProcessor();
              try {
                  processor.process(new DefaultSourceNoteData(topic, filePath, DataCenter.NOTE_LIST));
              } catch(Exception ex) { ex.printStackTrace(); }}}});Copy the code
  • Perfect the prompt
  • Dialog box Prompt
MessageDialogBuilder.yesNo("Operation result"."Added successfully!").show();
Copy the code
  • notification
NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false);
/** * Content: notification content * type: notification type, such as warning,info,error */
Notification notification = notificationGroup.createNotification("Test Notification", MessageType.INFO); Notifications.Bus.notify(notification); ` ` `Copy the code