• webstrom 插件开发(二)


    自定义右键菜单,实现一个提取CSS的效果

    指定依赖项

    我们的新插件将与CSS配合使用,因此我们需要将CSS插件指定为依赖项。

    <depends>com.intellij.css</depends>
    
    • 1

    为此,我们需要在plugin.xml文件中包含以下代码:

    intellij {
        version.set("2021.2")
        type.set("IU") // Target IDE Platform
    
        plugins.set(listOf(/* Plugin Dependencies */"JavaScriptLanguage", "CSS"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    添加一个新的Kotlin类

    首先,我们需要为源文件创建相应的目录结构。源文件应存储在src/main目录下。由于我们可以在IntelliJ IDEA中使用任何JVM语言,因此源通常按语言划分,例如java或kotlin。

    在这里插入图片描述
    创建具有以下内容的ExtractCSSAction.kt文件:

    package com.example.docstest1
    
    import com.intellij.openapi.actionSystem.AnAction
    import com.intellij.openapi.actionSystem.AnActionEvent
    import com.intellij.openapi.ui.Messages
    
    class ExtractCSSAction : AnAction() {
        override fun actionPerformed(e: AnActionEvent) {
            Messages.showMessageDialog(e.project, "Hello, extract css", "Welcome", null)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注册操作

    创建操作类后,我们需要将其注册到plugin.xml文件中。在theresources/META-INF/plugin.xml文件中,在顶级 标签中添加操作注册:

    <idea-plugin>
        <actions>
            <action text="Extract CSS" class="com.example.docstest1.ExtractCSSAction" />
        </actions>
    </idea-plugin>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行操作

    让我们运行我们的插件,并确保操作有效。我们在上一篇文章中提供了运行和调试插件的说明

    启动测试IDE后,我们必须打开一个项目或创建一个项目,并运行我们刚刚创建的操作。该操作没有分配给它的快捷方式,也没有添加到任何菜单中。这意味着运行它的唯一方法是将操作名称Extract CSS输入Find Action(⇧⌘A / Ctrl+Shift+A)或Search Everywhere(⇧⇧ / Shift+Shift)。

    在这里插入图片描述

    在这里插入图片描述

    处理HTML文件

    我们需要:

    1. 取上下文文件,并确保它是HTML。
    2. 从文件中收集带有名称类的所有属性。
    3. 创建一个包含属性中定义的类名的新CSS文件

    AST和PSI

    IDE通常不使用常规文件内容,而是对文件进行抽象操作。因此,我们将使用PSI(程序结构接口),这是AST(抽象语法树)上的抽象。PSI与AST相似,并以某种方式表示它,因为它具有相同的getParent()、getChildren()方法,但它也提供了一种更简单的树处理方式。

    因此,首先,我们希望从操作的上下文中获取文件的相应PSI。我们可以使用以下代码来做到这一点:

    val xmlFile = e.getData(CommonDataKeys.PSI_FILE) as? XmlFile ?: return
    
    • 1

    所有PSI HTML文件都实现了XmlFile接口

    我们希望在开放项目中处理该操作,因此我们需要确保相应的对象存在:

    val project = e.project ?: return
    
    • 1

    我们现在已经提取了文件,所以我们接下来需要做的就是找到带有名称类的所有属性。

    当然,我们可以手动处理文件结构:取文件,获取所有子标签,处理其属性,然后处理子标签子标签的任何属性,等等。但是,由于我们必须在IDE中一直进行这种遍历,因此实际上我们可以使用其中的类来简化流程。对于XML文件,我们可以使用已经实现递归访问的XmlRecursiveElementWalkingVisitor类。IDE中每种受支持的语言都有visitor类,例如JavaRecursiveElementWalkingVisitor、JSRecursiveWalkingElementVisitor等。

    我们需要覆盖visitXmlAttribute()方法,并将访问者传递给xmlFile的acceptChildren方法:

    xmlFile.acceptChildren(object : XmlRecursiveElementWalkingVisitor() {
      override fun visitXmlAttribute(attribute: XmlAttribute) {
      }
    })
    
    • 1
    • 2
    • 3
    • 4

    此代码已经访问了文件中的所有属性。接下来,我们需要确保当前属性具有名称class,并且我们需要将属性的值保存在某个地方。

    
    val classNames = mutableSetOf<String>()
    xmlFile.acceptChildren(object : XmlRecursiveElementVisitor() {
      override fun visitXmlAttribute(attribute: XmlAttribute?) {
        if (attribute.name == "class") {
          classNames.addAll(attribute.value?.split(" ")?.filter(String::isNotBlank) ?: emptyList())
        }
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    就这样!我们已经收集了所有需要处理的CSS类名,因此我们的下一步是创建一个新的CSS文件。

    生成CSS文件

    下一步是启动写入操作,然后调用创建所需CSS文件的命令。

    我们可以使用WriteCommandAction构建器类,该类提供了一种同时定义命令和调用write操作的方法。

    我们需要编写代码,在run {}正文中创建一个CSS文件。为了简单起见,让我们在同一目录中创建一个与当前HTML文件名称相同的新CSS文件。

    
    WriteCommandAction
      .writeCommandAction(project)
      .withName("Create CSS File")
      .run<Exception> {
        val newName = "${FileUtil.getNameWithoutExtension(xmlFile.name)}.css"
        val newFile = PsiFileFactory.getInstance(project)
          .createFileFromText(newName, CssFileType.INSTANCE, newContent)
        val directory = xmlFile.parent ?: return@run
        directory.add(newFile)
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    逻辑就完成了。现在,让我们运行这个插件,看看它是如何工作的。

    1. 运行插件。
    2. 创建一个包含以下内容的新HTML文件:
      test。
    3. 使用查找操作或搜索无处不在运行我们的提取CSS操作。
    4. 在同一目录中检查创建的CSS文件和HTML文件。

    如果您已正确完成所有操作,CSS文件将包含以下内容:

    .hello1 {}
    .hello2 {}
    
    • 1
    • 2

    打开新文件

    如果能够在操作后打开创建的文件,那就太好了。我们可以使用添加文件的navigate()方法来做到这一点。请注意,add(newFile)方法将创建一个新的CSS文件实例,而不是当前文件实例,因此我们需要调用navigate()作为theaddadd()操作的结果,而不是newFile对象:

    (directory.add(newFile) as? XmlFile)?.navigate(true)
    
    • 1

    重新格式化新文件

    创建新文件后,能够根据项目的CSS代码样式重新格式化它们也很好:

    val directory = xmlFile.parent ?: return@run
    val actualFile = directory.add(newFile) as? PsiFile ?: return@run
    CodeStyleManager.getInstance(project).reformat(actualFile)
    actualFile.navigate(true)
    
    • 1
    • 2
    • 3
    • 4

    分配快捷方式

    通过我们最初的实现,新操作只能从搜索中获得,这并不特别方便。让我们通过为操作分配一个快捷方式来解决这个问题。我们可以使用原始eCSStractor插件中使用的快捷方式:⌘⇧X / Ctrl+Shift+X。要为操作分配快捷方式,我们需要编辑plugin.xml文件。在操作注册中,我们必须添加带有相应选项的子标签

    <action text="Extract CSS" class="extract.css.actions.ExtractCSSAction">
      <keyboard-shortcut first-keystroke="control shift X" keymap="$default"/>
    </action>
    
    • 1
    • 2
    • 3

    将操作添加到上下文菜单中

    在某些情况下,从上下文菜单运行操作比使用快捷方式要容易得多。为此,只需在action标签内添加一个额外的嵌套add-to-group标签。

    
    <action text="Extract CSS" class="extract.css.actions.ExtractCSSAction">
      <keyboard-shortcut first-keystroke="control shift X" keymap="$default" />
      <add-to-group group-id="EditorPopupMenu" />
    </action>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    EditorPopupMenu的名称标识了我们要显示操作的位置:编辑器上下文菜单。许多可能的变体都可以在group-id中指定,几乎所有变体都可以通过代码完成获得,并具有自我描述的名称。

    在这里插入图片描述

    菜单适配

    该操作适用于所有文件,但仅适用于HTML(XML)。这意味着我们可以限制命令的可用性。

    AnAction类的update()方法用于控制行为。我们需要覆盖ExtractCSSAction类中的方法,并根据当前上下文设置可见性信息:

    
    override fun update(e: AnActionEvent) {
      e.presentation.isEnabledAndVisible =
        e.getData(CommonDataKeys.PSI_FILE) is XmlFile && e.project != null
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    时序预测 | Matlab实现ARIMA-LSTM差分自回归移动差分自回归移动平均模型模型结合长短期记忆神经网络时间序列预测
    《代码大全2》第14章 组织直线型代码
    BM23 二叉树的前序遍历
    基于Spring事件驱动模式实现业务解耦
    【vue3】08. 跟着官网学习vue3-列表渲染
    前端总结——计算机网络
    配置MySQL单个用户多个IP段白名单
    三面面试官:运行 npm run xxx 的时候发生了什么?
    推荐国产神器Eolink!API优先,Eolink领先!
    JavaScript大作业 基于HTML+CSS+JavaScript站酷静态页面官网7页
  • 原文地址:https://blog.csdn.net/wu_xianqiang/article/details/126213600