4
头图

Official plug-in development document

Front-end people develop intellij plug-in, crying while learning java...

API

PSI

PSI (Program Structure Interface) file is the root of the structure that represents the content of a file as a hierarchical structure of elements in a specific programming language.

PsiFile The class is the common base class of all PSI files. Files in a specific language are usually represented by its subclasses. For example, PsiJavaFile class represents a java file, XmlFile class represents an xml file.

Document VirtualFile and 06191d44717091 (even if multiple projects are opened, there is still only one corresponding instance), the scope of PSI is project level (multiple projects open the same file at the same time, there will be multiple instances).

How to obtain PSI file?
  • e.getData(PlatformDataKeys.VIRTUAL_FILE) , if it is multiple selection, use e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY)
  • LocalFileSystem.getInstance().findFileByIoFile()
  • psiFile.getVirtualFile() , if the PSI FILE only exists in memory, it returns empty
  • FileDocumentManager.getInstance().getFile()
Monitoring file changes
PsiTreeChangeListener
BulkFileListener batch file monitoring
1. Add
beforeChildrenChange

beforeChildAddition

beforeChildAddition

beforeChildAddition

childAdded

childAdded

childAdded

childrenChanged
2. Delete
beforeChildrenChange

beforeChildRemoval

beforeChildRemoval

beforeChildRemoval

childRemoved

childRemoved

childRemoved

childrenChanged
3. Modify the name
propertyChanged
childrenChanged
System.out.println("------"+e.getPropertyName());
// getPropertyName == fileName
System.out.println("e.getOldValue()"+e.getOldValue());
System.out.println("e.getNewValue()"+e.getNewValue());
<psi.treeChangeListener implementation="com.theblind.test5"/>
Monitor file open
FileEditorManagerListener
<projectListeners>
  <listener class="com.theblind.MyFileEditorManagerListener"
            topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/>
</projectListeners>

Configuration

Right click menu
<!-- plugin.xml -->
<actions>
        <action id="com.hunliji.MainAction" class="com.hunliji.MainAction" text="yapi2ts" icon="/icon/logo.png">
            <!--右键菜单-->
            <add-to-group group-id="EditorPopupMenu" anchor="last" />
        </action>
    </actions>
tools toolbar
<!-- plugin.xml -->
<actions>
        <action id="com.hunliji.MainAction" class="com.hunliji.MainAction" text="yapi2ts" icon="/icon/logo.png">
            <!--tool工具栏-->
            <add-to-group group-id="ToolsMenu" anchor="last"/>
        </action>
    </actions>
hot key
<!-- plugin.xml -->
<actions>
        <action id="com.hunliji.MainAction" class="com.hunliji.MainAction" text="yapi2ts" icon="/icon/logo.png">
            <!--快捷键-->
            <keyboard-shortcut first-keystroke="control alt A" second-keystroke="C" keymap="$default"/>

            <!--ctrl + N (generate快捷生成)-->
            <!--<add-to-group group-id="GenerateGroup" anchor="last" />-->
        </action>
    </actions>
action grouping
<actions>
        <group id="" text="yapi2ts"  
               popup="true" icon=" ">
            <add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="ReplaceInPath"/>
        </group>
     
        <action class="" 
                id="" description=""
                icon=""
                text="Basic Class">
            <add-to-group group-id="Halo Tools"/>
        </action>
    </actions>

Get selected text

public class PopupAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        //获取当前编辑器对象
        Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
        //获取选择的数据模型
        SelectionModel selectionModel = editor.getSelectionModel();
        //获取当前选择的文本
        String selectedText = selectionModel.getSelectedText();
        System.out.println(selectedText);
    }
}

The user selects the document generation directory

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

Line marker

<extensions defaultExtensionNs="com.intellij">
    <!--如下所示添加行标记扩展 -->
    <codeInsight.lineMarkerProvider language="JAVA"
                                    implementationClass="org.xujin.idea.right.linemarker.HaloLineMarker"/>
</extensions>
 public class HaloLineMarker implements LineMarkerProvider {
    @Nullable
    @Override
    public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) {
        LineMarkerInfo lineMarkerInfo= null;
        try {
            lineMarkerInfo = null;
            String anno="org.springframework.boot.autoconfigure.SpringBootApplication";
            if(!judgeHaveAnnotation(psiElement,anno)){
               return lineMarkerInfo;
            }
            PsiClassImpl field = ((PsiClassImpl) psiElement);
            PsiAnnotation psiAnnotation = field.getAnnotation(anno);
            lineMarkerInfo = new LineMarkerInfo<>(psiAnnotation, psiAnnotation.getTextRange(), IconLoader.findIcon("/icons/right/HaloBasic.png"),
                    new FunctionTooltip("快速导航"),
                    new AppMgmtNavigationHandler(),
                    GutterIconRenderer.Alignment.LEFT);
        } catch (Exception e) {
            e.printStackTrace(); 
        }
        return lineMarkerInfo;
    }
    @Override
    public void collectSlowLineMarkers(@NotNull List<PsiElement> list, @NotNull Collection<LineMarkerInfo> collection) {
    }
    private boolean judgeHaveAnnotation(@NotNull PsiElement psiElement, String anno) {
        if (psiElement instanceof PsiClass) {
            PsiClassImpl field = ((PsiClassImpl) psiElement);
            PsiAnnotation psiAnnotation = field.getAnnotation(anno);
            if (null != psiAnnotation) {
                return true;
            }
            return false;
        }
        return false;
    }
}

Generate documentation

public interface Processor{
    public void process(SourceNoteData sourceNoteData) throws Exception;
}



编写Freemarker的抽象类
public abstract class AbstractFreeMarkerProcessor implements Processor{
  
      protected abstract Template getTemplate() 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);
      }
  }



编写MDFreeMarkProcessor继承AbstractFreeMarkerProcessor。实现抽象方法
public class MDFreeMarkProcessor extends AbstractFreeMarkerProcessor{
      @Override
      protected Template getTemplate() throws Exception{
          //加载模板字符串
          String templateString = UrlUtil.loadText(MDFreeMarkProcessor.class.getResource("/template/md.ftl"));
          //创建模板配置
          Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
          //创建字符串模板的导入器
          StringTemplateLoader stringTemplateLoader=new StringTemplateLoader();
          //导入字符串模板
          stringTemplateLoader.putTemplate("MDTemplate",templateString);
          configuration.setTemplateLoader(stringTemplateLoader);
          //获取模板
          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"));
      }
  
  }



添加处理操作
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();
              }
          }
      }
  });

Paste

CopyPasteManager.getInstance().setContents(new StringSelection(xml));
CopyPasteManager.getInstance().setContents(new SimpleTransferable(table.toString(), DataFlavor.allHtmlFlavor));

interface

Drop down box

private JComboBox<SelectedTypeModel> kind;

kind.addItem(seleType);

kind.addItemListener(new ItemListener() {
    @Override
    public void itemStateChanged(ItemEvent e) {
        SelectedTypeModel selectedTypeModel = (SelectedTypeModel) e.getItem();
        switch (selectedTypeModel.getValue()) {
            case HaloConstant.COMBOX_CONTROLLER:
                NewRightContext.setClassType(HaloConstant.COMBOX_CONTROLLER);
                break;
            default:
                NewRightContext.setClassType(null);
        }![在这里插入图片描述](https://gitee.com/Lovxy/private-notes/raw/master/doc/show.gif#pic_center)

    }
});

notify

NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false);

/**
 * content :  通知内容
 * type  :通知的类型,warning,info,error
 */
Notification notification = notificationGroup.createNotification("测试通知", MessageType.INFO);
Notifications.Bus.notify(notification);

Modeless notification

NotificationGroup notificationGroup = new NotificationGroup("notificationGroup", NotificationDisplayType.BALLOON, true);
Notification notification = notificationGroup.createNotification("notification",NotificationType.ERROR);
Notifications.Bus.notify(notification);

High version is not compatible with NotificationGroup directly adopts new Notification

Notification notification = new Notification("PNGroup", "Private Notes Message", content, type);

Among them, NotificationDisplayType can be the following:

  • NONE : No bullet frame, no display
  • BALLOON : disappear automatically
  • STICKY_BALLOON : The user clicks the close button and disappears
  • TOOL_WINDOW : The instance effect is the same as STICKY_BALLOON

Toast

import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.awt.RelativePoint;

import javax.swing.*;

public class Toast {

    /**
     * Display simple notification of given type
     *
     * @param jComponent
     * @param type
     * @param text
     */
    public static void make(JComponent jComponent, MessageType type, String text) {
        JBPopupFactory.getInstance()
                .createHtmlTextBalloonBuilder(text, type, null)
                .setFadeoutTime(7500)
                .createBalloon()
                .show(RelativePoint.getCenterOf(jComponent), Balloon.Position.above);
    }

    /**
     * Display simple notification of given type
     *
     * @param project
     * @param type
     * @param text
     */
    public static void make(Project project, MessageType type, String text) {

        StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);

        JBPopupFactory.getInstance()
                .createHtmlTextBalloonBuilder(text, type, null)
                .setFadeoutTime(7500)
                .createBalloon()
                .show(RelativePoint.getCenterOf(statusBar.getComponent()), Balloon.Position.atRight);
    }
}

Persistence

ApplicationSettingsConfigurable is the settins configuration page

<!-- plugin.xml -->
    <extensions defaultExtensionNs="com.intellij">
        <applicationConfigurable parentId="tools" instance="com.hunliji.components.ApplicationSettingsConfigurable"
                                 id="com.hunliji.components.ApplicationSettingsConfigurable" displayName="Yapi Settings"/>
        <!-- applicationService 这个是插件配置文件的持久化 -->
        <applicationService serviceInterface="com.hunliji.config.ConfigPersistence"
                            serviceImplementation="com.hunliji.config.ConfigPersistence"/>
    </extensions>
// ConfigPersistence
/**
 * 配置持久化
 */
@State(name = "yapi2ts", storages = {@Storage(value = "yapi2ts.xml")})
public class ConfigPersistence implements PersistentStateComponent<MyPersistenceConfiguration> {
    private MyPersistenceConfiguration config;

    @Nullable
    @Override
    public MyPersistenceConfiguration getState() {
        if (config == null) {
            config = new MyPersistenceConfiguration();
            List<ConfigDTO> configDTOS = new ArrayList<>();
            config.setConfigs(configDTOS);
        }
        return config;
    }

    public static ConfigPersistence getInstance() {
        return ApplicationManager.getApplication().getService(ConfigPersistence.class);
    }

    @Override
    public void loadState(@NotNull MyPersistenceConfiguration state) {
        XmlSerializerUtil.copyBean(state, Objects.requireNonNull(getState()));
    }
}
public class MyPersistenceConfiguration {
    private List<ConfigDTO> configs;

    public List<ConfigDTO> getConfigs() {
        return configs;
    }

    public void setConfigs(List<ConfigDTO> configs) {
        this.configs = configs;
    }
}

Actual combat

Developed a small idea plug-in, you can enter the link of the yapi document interface details in the editor to automatically generate the required request body ts type or return data ts type, which improves the tool efficiency of front-end developers.

Install

Using IDE built-in plugin system:

Settings/Preferences > Plugins > Marketplace > Search for "yapi2ts" >
Install Plugin

Note before use

!!!Be sure to configure the project token first: settings -> tools -> yapi2ts

use

  1. Configure the token of the yapi document project
  2. Open tools -> yapi2ts in the js/ts/jsx/tsx file that needs to generate type files or right-click the file to open the menu, select yapi2ts to open the tool pop-up window
  3. Enter the link address of the interface details and choose whether to generate the request body type or return the data type

MangoGoing
785 声望1.2k 粉丝

开源项目:详见个人详情