Webstorm 外掛開發

MangoGoing發表於2021-11-15

官方外掛開發文件

前端人開發intellij外掛,一邊哭一邊學java...

API

PSI

PSI(Program Structure Interface) 檔案是表示一個檔案的內容作為在一個特定的程式語言元素的層次結構的結構的根。

PsiFile 類是所有PSI檔案共同的基類, 在特定語言中的檔案通常使用它的子類表示。比如,PsiJavaFile 類表示一個java檔案, XmlFile 類表示xml檔案。

VirtualFile 以及 Document的應用作用域不同(即使開啟多個projects,仍然只有一個對應的例項),PSI的作用域是專案級(多個projects中同時開啟同一個檔案,會有多個例項)。

如何獲取PSI檔案?
  • e.getData(PlatformDataKeys.VIRTUAL_FILE),如果是多選,使用e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY)
  • LocalFileSystem.getInstance().findFileByIoFile()
  • psiFile.getVirtualFile(),如果PSI FILE僅存在記憶體中,則返回空
  • FileDocumentManager.getInstance().getFile()
監聽檔案改變
PsiTreeChangeListener
BulkFileListener 批量檔案監聽
1.新增
beforeChildrenChange

beforeChildAddition

beforeChildAddition

beforeChildAddition

childAdded

childAdded

childAdded

childrenChanged
2.刪除
beforeChildrenChange

beforeChildRemoval

beforeChildRemoval

beforeChildRemoval

childRemoved

childRemoved

childRemoved

childrenChanged
3.修改名稱
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"/>
監聽檔案開啟
FileEditorManagerListener
<projectListeners>
  <listener class="com.theblind.MyFileEditorManagerListener"
            topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/>
</projectListeners>

配置

右鍵選單
<!-- 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工具欄
<!-- 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>
快捷鍵
<!-- 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分組
<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>

獲取選中文字

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

使用者選擇文件生成目錄

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

行標記

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

生成文件

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

貼上

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

介面

下拉框

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)

    }
});

通知

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

/**
 * content :  通知內容
 * type  :通知的型別,warning,info,error
 */
Notification notification = notificationGroup.createNotification("測試通知", MessageType.INFO);
Notifications.Bus.notify(notification);

非模態框式通知

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

高版本不相容NotificationGroup 直接採用 new Notification

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

其中,NotificationDisplayType可以是以下幾種:

  • NONE:無彈框,不展示
  • BALLOON:自動消失
  • STICKY_BALLOON:使用者點選關閉按鈕消失
  • TOOL_WINDOW:例項效果同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);
    }
}

持久化

ApplicationSettingsConfigurable是settins配置頁面

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

實戰

開發了一個idea 小外掛,可以在編輯器裡面輸入yapi文件介面詳情的連結自動生成所需要的請求體ts型別或者返回資料的ts型別,提高前端開發者的工具效率。

安裝

Using IDE built-in plugin system:

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

使用前注意

!!!務必先配置專案token: settings -> tools -> yapi2ts

使用

  1. 配置yapi文件專案的token
  2. 在需要生成型別檔案的js/ts/jsx/tsx檔案中開啟tools -> yapi2ts 或者 右鍵檔案開啟選單,選中yapi2ts開啟工具彈窗
  3. 輸入介面詳情連結地址,選擇生成請求體型別還是返回資料型別

相關文章