前端人開發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
使用
- 配置yapi文件專案的token
- 在需要生成型別檔案的js/ts/jsx/tsx檔案中開啟tools -> yapi2ts 或者 右鍵檔案開啟選單,選中yapi2ts開啟工具彈窗
- 輸入介面詳情連結地址,選擇生成請求體型別還是返回資料型別