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 emptyFileDocumentManager.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);
}
}
});
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 displayBALLOON
: disappear automaticallySTICKY_BALLOON
: The user clicks the close button and disappearsTOOL_WINDOW
: The instance effect is the same asSTICKY_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
- Configure the token of the yapi document project
- 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
- Enter the link address of the interface details and choose whether to generate the request body type or return the data type
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。