API

PSI

PSI(Program Structure Interface) 文件是表示一个文件的内容作为在一个特定的编程语言元素的层次结构的结构的根。

PsiFile 类是所有PSI文件共同的基类, 在特定语言中的文件通常使用它的子类表示。比如,PsiJavaFile 类表示一个java文件, XmlFile 类表示xml文件。

VirtualFile 以及 Document的应用作用域不同(即使打开多个projects,仍然只有一个对应的实例),PSI的作用域是项目级(多个projects中同时打开同一个文件,会有多个实例)。

如何获取PSI文件?

  • 从action获取: e.getData(LangDataKeys.PSI_FILE).
  • 从虚拟文件获取: PsiManager.getInstance(project).findFile()
  • 从文档获取: PsiDocumentManager.getInstance(project).getPsiFile()
  • 从文件内的一个元素获取: psiElement.getContainingFile()
  • 在project中的任意位置通过指定的名字寻找文件,使用 FilenameIndex.getFilesByName(project, name, scope)

获取方法

  • 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>

右键

<action id="MB_PopupAction" class=" " text="私人注释" description="描述">
      <add-to-group group-id="EditorPopupMenu" anchor="first"/>
      <keyboard-shortcut keymap="$default" first-keystroke="ctrl P"/>
    </action>

ation分组

<actions>
        <group id="" text="私人注释"  
               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);
          }
      }
  });

通知管理

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

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

行标记

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

非模态框式通知

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

粘贴

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)

    }
});

实战

开发了一个idea 小插件,可以在任意地方加注释。
场景:

  • 项目中添加注释,不被git提交,在平常工作中往往需要添加一些自己看的懂的注释,不过这些注释有的时候可能不能通过审查
  • 阅读源码的时,源码文件为只读文件

psi java PsiJavaFile_psi java

已开源: https://gitee.com/Lovxy/private-notes

点一下star ✨,是对作者最大的支持