IntelliJ IDE 插件开发 | (五)VFS 与编辑器

这篇具有很好参考价值的文章主要介绍了IntelliJ IDE 插件开发 | (五)VFS 与编辑器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章

  • IntelliJ IDE 插件开发 |(一)快速入门
  • IntelliJ IDE 插件开发 |(二)UI 界面与数据持久化
  • IntelliJ IDE 插件开发 |(三)消息通知与事件监听
  • IntelliJ IDE 插件开发 |(四)来查收你的 IDEA 使用报告吧
  • IntelliJ IDE 插件开发 |(五)VFS 与编辑器

前言

在前几篇文章中主要介绍了关于 IntelliJ IDE 插件开发的基础知识,这部分内容对开发一些小功能的插件的开发已经足够。不过,如果想要开发一些与具体编程语言相关的、提升开发效率的插件(例如MybatisX),那么前几篇的内容就不足以支撑了。而从本篇开始,则会介绍实现相关功能所需要的知识: VFS、编辑器、PSI、自定义语言等,最后再以两三个插件的实战开发(例如老生常谈的代码生成)进行结尾,本文涉及到的完整代码已上传到GitHub。

VFS(Virtual File System)

VFS(虚拟文件系统)可以看作是 IntelliJ 平台对各种类型(本地磁盘的文件、DIFF 信息文件、远程文件)文件操作的封装,通过提供一致的 API 以及文件变更事件,让开发人员可以专注于对文件的处理。此外之所以使用虚拟二字,也是因为我们在对文件操作时并没有直接修改源文件,而是修改了源文件所对应的快照,然后 VFS 会通过同步或者异步的方式去修改源文件。关于 VFS 主要有以下注意点:

  1. 文件快照是应用级别的,因此一个文件即使被多个项目使用也只会对应一份快照。

  2. 文件快照内容和源文件内容并非实时对应,例如文件已从资源管理器中删除,但 IntelliJ 平台只有接收并处理了文件删除事件后才会从快照中删除指定文件。

  3. VFS 通过操作系统的 File Watcher 去感知文件的变化(基于时间戳),可以通过下述步骤去查看被监听的文件根节点:

    IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

    IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

  4. 如果在代码中访问了被忽略的文件(例如下图中配置的),VFS 就会直接加载并返回文件的内容而非是快照的内容:

    IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

  5. 如果需要保证代码是在文件刷新后完成,则可以使用以下方式:

    val virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE)
    virtualFile?.let {
        it.refresh(true, true) {
            // 文件刷新完成
        }
    }
    

    refresh 的方法签名如下,第三个参数 postRunnable 用于传递我们要执行的代码:

    /**
     * The same as {@link #refresh(boolean, boolean)} but also runs {@code postRunnable}
     * after the operation is completed. The runnable is executed on event dispatch thread inside write action.
     */
    public abstract void refresh(boolean asynchronous, boolean recursive, @Nullable Runnable postRunnable);
    

获取 VirtualFile 对象的方式

以下内容来自官网:

Context API
Action AnActionEvent.getData(PlatformDataKeys.VIRTUAL_FILE) AnActionEvent.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY) for multiple selection
Document FileDocumentManager.getFile()
PSI File PsiFile.getVirtualFile()(may return null if the PSI file exists only in memory)
File Name FilenameIndex.getVirtualFilesByName()
Local File System Path LocalFileSystem.findFileByIoFile() VirtualFileManager.findFileByNioPath()/refreshAndFindFileByNioPath() (2020.2+)

在上面的示例中就是使用的第一种方式:AnActionEvent.getData(PlatformDataKeys.VIRTUAL_FILE) 。

VirtualFile 在文件的基础内容上还增加了类型、文件系统等扩展信息:

class VFSAction : AnAction() {
    override fun actionPerformed(e: AnActionEvent) {
        val virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE)
        virtualFile?.let {
            it.refresh(true, true) {
                // 文件刷新完成
                Utils.info("""
                    文件路径: ${it.path}
                    文件类型: ${it.fileType}
                    文件系统: ${it.fileSystem}
                    文件后缀: ${it.extension}
                    文件时间戳: ${it.timeStamp}
                """.trimIndent())
            }
        }
    }
}

监听文件变更事件

只需要在项目启动监听中注册我们的文件变更监听事件即可:

class ProjectStartListener: ProjectActivity {
    
    override suspend fun execute(project: Project) {
        project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
            override fun after(events: MutableList<out VFileEvent>) {
                for (event in events) {
                    Utils.info("变更的文件: ${event.file?.path}")
                }
            }
        })
    }
    
}

VFS 的监听是应用级别,因此当打开多个项目的时候,每个项目中的文件变更事件都会被所有的项目共享,因此需要注意对文件进行过滤处理。

通过以上简单的代码我们就可以获取所有的文件变更事件,做我们想做的事了(例如统一在文件前增加版权信息)。

编辑器

在第四篇文章中为了实现对用户的编码活动的统计,我们简单使用了 Editor 对象,主要是对其文档和光标活动的监听。而本文文章则会详细介绍关于 Editor 中的一些基础概念,主要是几个模型对象和文档对象,在正式介绍前,先说一下获取 Editor 对象的两种方式:

// 通过 project 获取
var editor = FileEditorManager.getInstance(project).selectedEditor
// 通过 Action 的事件对象获取
val editor = e.getData(PlatformDataKeys.EDITOR)

下面正式开始介绍 Editor 中的一些对象

Document

document 对象主要提供了获取和替换文件文本的一些方法(还可以添加对文档内容的监听):

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

下面会结合模型对象介绍其使用。

CaretModel(插入符模型)

CaretModel(插入符模型) 用于获取编辑器中光标所处的位置,如果细分还可以分为 Logical Position(逻辑位置)和 Visual Position(视觉位置)。下面以一个简单的代码样例展示区别:

val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
val caretModel = editor.caretModel
Utils.info("""
    逻辑位置:<br/>
    ${"-".repeat(20)}<br/>
    行号: ${caretModel.logicalPosition.line + 1}<br/>
    列号: ${caretModel.logicalPosition.column + 1}<br/><br/>
    视觉位置:<br/>
    ${"-".repeat(20)}<br/>
    行号: ${caretModel.visualPosition.line + 1}<br/>
    列号: ${caretModel.visualPosition.column + 1}<br/>
""".trimIndent())

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

可以看出来逻辑位置对应光标在文件中所处的真实位置(5 行 1 列),而视觉位置则如表面意思,由于存在折叠代码块(中间3行),所以行号为 3。除此之外,可以看到上述代码中,不管是caretModel.logicalPosition还是caretModel.visualPosition在获取行号和列号的时候都在结果加 1,这是因为行号和列号在代码中是从 0 开始计算。这里要注意的是,上面的折叠代码只是影响视觉位置结果的一种情况,如果文件开启自动断行(Soft-Wrap line),也同样会影响视觉位置的结果:

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

使用类似阿拉伯语这种右向文字也会影响逻辑位置和视觉位置的结果。

除了以上两种定位方式,我们还可以通过caretModel.offset直接获取光标所在位置的全局偏移量:

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

这里可以看到第 3 行开始位置对应的偏移量是 22(20个显示的字符加上两个换行符,空白字符都会被统计)。

除了单光标,多光标的时候(例如按住 alt 结合鼠标左键进行多行选择),每一个光标的位置也是按照上述规则进行计算:

caretModel.allCarets.forEach { 
    Utils.info("""
        偏移量:<br/>
        ${"-".repeat(20)}<br/>
        偏移量: ${it.offset}<br/>
    """.trimIndent())
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

最后以 Doucument 和 CaretModel 结合使用的例子结尾:

// 写操作需要放入 WriteCommandAction.runWriteCommandAction 中
WriteCommandAction.runWriteCommandAction(e.project) {
    // 插入字符串并移动光标位置到结尾
    val msg = "庄周de蝴蝶"
    document.insertString(caretModel.offset, msg)
    caretModel.moveToOffset(caretModel.offset + msg.length)
}

上述代码实现了在光标处插入指定文本信息并移动光标位置的效果。

InlayModel(嵌入模型)

InlayModel(嵌入模型)用于在代码行中嵌入各种信息,例如下图中msg:就属于嵌入信息,通过使用嵌入信息可以在不改变文本内容的情况下给用户一个直观的提示内容:

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

除了上面这种使用方式,我们还可以使用 InlayModel 模拟各种 gpt 插件生成代码的效果,代码及效果如下(使用到了 caretModel 中的偏移量和视觉位置等内容):

// 清除所有嵌入信息
val inlayModel = editor.inlayModel
inlayModel.getInlineElementsInRange(0, editor.document.textLength).forEach { Disposer.dispose(it) }
inlayModel.getBlockElementsInRange(0, editor.document.textLength).forEach { Disposer.dispose(it) }
// 分别增加单行和多行嵌入信息
val offset = caretModel.offset
val column = caretModel.visualPosition.column
inlayModel.addInlineElement(offset, DemoRender(editor, "庄周de蝴蝶"))
inlayModel.addBlockElement(offset, InlayProperties(),
    DemoRender(editor, mutableListOf("first line", "second line", "third line"), column.toFloat()))
// 移动光标位置到初始位置
caretModel.moveToVisualPosition(VisualPosition(caretModel.visualPosition.line, column))


class DemoRender<T>(
    private val editor: Editor,
    private val renderText: T,
    private var wordCount: Float = 0f
): EditorCustomElementRenderer {
    
    // 设置字体
    private val font = Font("Microsoft YaHei", Font.ITALIC, editor.colorsScheme.editorFontSize)
    
    override fun calcWidthInPixels(p0: Inlay<*>): Int {
        // 获取渲染内容的宽度, 如果是多行文本则取最长文本行的宽度
        return when (renderText) {
            is String -> calcTextWidth(renderText)
            is MutableList<*> -> renderText.maxOfOrNull { calcTextWidth(it.toString()) } ?: 0
            else -> 0
        }
    }
    override fun calcHeightInPixels(inlay: Inlay<*>): Int {
        // 获取渲染内容的高度, 如果是多行文本则需要将行高乘以行数
        return when (renderText) {
            is MutableList<*> -> super.calcHeightInPixels(inlay) * renderText.size
            else -> super.calcHeightInPixels(inlay)
        }
    }
    override fun paint(inlay: Inlay<*>, g: Graphics, targetRegion: Rectangle, textAttributes: TextAttributes) {
        val g2 = g.create() as Graphics2D
        GraphicsUtil.setupAAPainting(g2)
        textAttributes.foregroundColor = JBColor.GRAY
        val lineHeight = editor.lineHeight.toDouble()
        val fontBaseline = ceil(font.createGlyphVector(getFontMetrics().fontRenderContext, "中文").visualBounds.height)
        val linePadding = (lineHeight - fontBaseline) / 2.0
        val offsetX = targetRegion.x
        val offsetY = targetRegion.y + fontBaseline + linePadding
        val lineOffset = 0
        g2.font = font
        g2.color = JBColor.GRAY
        when (renderText) {
            is String -> {
                g2.drawString(renderText, offsetX.toFloat(), (offsetY + lineOffset).toFloat())
            }
            is MutableList<*> -> {
                // 多行文本渲染的时候设置缩进
                val tabSize = editor.settings.getTabSize(editor.project)
                val startOffset = calcTextWidth("Z") * (wordCount + tabSize)
                renderText.forEach {
                    g2.drawString(it.toString(), startOffset, (offsetY + lineOffset).toFloat())
                    g2.translate(0.0, lineHeight)
                }
            }
            else -> return
        }
        g2.dispose()
    }
    
     private fun calcTextWidth(text: String): Int {
        return getFontMetrics().stringWidth(text) 
     }
    
    private fun getFontMetrics(): FontMetrics {
        val editorContext = FontInfo.getFontRenderContext(editor.contentComponent)
        val context = FontRenderContext(editorContext.transform, AntialiasingType.getKeyForCurrentScope(false), 
            editorContext.fractionalMetricsHint)
        return FontInfo.getFontMetrics(font, context)
    }
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

这里需要知道 addInlineElement 用于添加单行的嵌入信息,addBlockElement 用于添加多行嵌入信息,渲染内容需要实现 EditorCustomElementRenderer 类中的 paint 方法即可。通过使用 Graphics2D 对象,我们可以添加文本,图形甚至是图片信息,这里就不再逐一介绍。

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

SoftWrapModel(自动断行模型)

SoftWrapModel(自动断行模型)主要用于获取自动断行相关信息,其包含的方法较少,也很少会用到,简单了解即可,如下是一个简单的使用及效果:

val softWrapModel = editor.softWrapModel
Utils.info("""
    是否开启自动断行: ${softWrapModel.isSoftWrappingEnabled}<br/>
    当前行是否存在自动断行: ${softWrapModel.getSoftWrapsForLine(caretModel.offset).isNotEmpty()}<br/>
""".trimIndent())

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

MarkupModel(标记模型)

MarkupModel(标记模型)主要用于用于将指定行(或者某个区间)设置为高亮,下面直接展示其使用方式和效果,很容易理解和使用:

val markupModel = editor.markupModel
markupModel.removeAllHighlighters()
// 设置当前行为红色高亮背景
val lineAttr = TextAttributes()
lineAttr.backgroundColor = JBColor.RED
val line = caretModel.logicalPosition.line
markupModel.addLineHighlighter(line, HighlighterLayer.ERROR, lineAttr)

// 设置指定范围为绿色高亮背景
// 最后一个参数指定为 HighlighterTargetArea.EXACT_RANGE 则为精确范围
// 指定为 HighlighterTargetArea.LINES_IN_RANGE 则会将偏移量所在的整行都进行设置
val rangeAttr = TextAttributes()
rangeAttr.backgroundColor = JBColor.BLUE
markupModel.addRangeHighlighter(0, 6, HighlighterLayer.SELECTION, rangeAttr, HighlighterTargetArea.EXACT_RANGE)

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

SelectionModel(选中模型)

SelectionModel(选中模型)用于处理编辑器中文本选中相关的操作,下面是一个简单的使用样例:

// 先移除所有文本选中, 然后将偏移量为 0 ~ 6 的内容进行选中
val selectionModel = editor.selectionModel
selectionModel.removeSelection(true)
selectionModel.setSelection(0, 6)
Utils.info("""
    选中的文本内容: ${selectionModel.selectedText}<br/>
    选中文本的开始和结束位置: (${selectionModel.selectionStart}, ${selectionModel.selectionEnd})<br/>
""".trimIndent())

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

FoldingModel(折叠模型)

FoldingModel(折叠模型)用于对代码块进行折叠操作,通过结合 SelectionModel,可以很方便地对选中的文本进行折叠,下面是使用方式及效果:

val selectionModel = editor.selectionModel
val foldingModel = editor.foldingModel
// 批量折叠操作需要放在 runBatchFoldingOperation 中
foldingModel.runBatchFoldingOperation {
    // 移除所有的折叠
    foldingModel.allFoldRegions.forEach { foldingModel.removeFoldRegion(it) }
    // 对选中文本进行折叠并设置显示的提示文本
    foldingModel.addFoldRegion(selectionModel.selectionStart, selectionModel.selectionEnd, "庄周de蝴蝶")
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

IndentsModel(缩进模型)

IndentsModel(缩进模型)用于获取文本中的缩进信息,使用方式也很简单,如下所示:

val indentsModel = editor.indentsModel
val guide = indentsModel.caretIndentGuide ?: return
Utils.info("""
    缩进级别: ${guide.indentLevel}<br/>
    开始行: ${guide.startLine + 1}<br/>
    结束行: ${guide.endLine + 1}<br/>
""".trimIndent())

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

ScrollingModel(滚动模型)

ScrollingModel(滚动模型)可以用于获取编辑器的可视区域范围以及执行滚动操作,使用方法如下:

val scrollingModel = editor.scrollingModel
scrollingModel.scrollToCaret(ScrollType.CENTER)
scrollingModel.runActionOnScrollingFinished {
    scrollingModel.visibleArea.let {
        Utils.info("""
            可视区域左上角坐标: (${it.x}, ${it.y})<br/>
            可视区域宽度: ${it.width}<br/>
            可视区域高度: ${it.height}<br/>
        """.trimIndent())
    }
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

编辑器事件

在第三篇文章中我们简单了解了 IntelliJ 中的事件监听,当时只介绍了项目的启用与关闭事件,这里再简单介绍一下关于编辑器的事件。

EditorActionHandler

IntelliJ 平台默认为我们提供了一些编辑器事件,例如复制、粘贴等,可以在IdeActions类中看到:

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

相应地,使用方式也很简单,只需要实现 EditorActionHandler 类重写其中的 doExecute 方法即可,下面代码展示了对编辑器的复制事件进行监听并提示:

class EditorCopyListener: EditorActionHandler(), ProjectActivity {
    
    override suspend fun execute(project: Project) {
        // 注册复制监听
        EditorActionManager.getInstance().setActionHandler(IdeActions.ACTION_EDITOR_COPY, this)
    }
    
    override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
        // 将选中的文本加入到粘贴板中, 这里使用前文中提到的 selectionModel
        editor.selectionModel.copySelectionToClipboard()
        // 显示复制文本
        Messages.showInfoMessage("Copy text: ${CopyPasteManager.getInstance()
            .getContents<String>(DataFlavor.stringFlavor)}", "Copy")
    }
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

这里需要注意的是,在重写了编辑器的事件后(这里是复制事件),则该事件就不再生效了,因此这里通过editor.selectionModel.copySelectionToClipboard()这行代码手动了复制的操作,在重写其它编辑器事件的时候也需要注意这个问题。

TypedHandlerDelegate

除了使用 IntelliJ 中已经定义好的事件,还可以通过实现 TypedHandlerDelegate 来对键盘中输入的字符进行监听(无法监听空白字符,例如回车、TAB等,也无法在出现编码提示时进行监听):

class KeyboardListener: TypedHandlerDelegate() {
    
    override fun charTyped(c: Char, project: Project, editor: Editor, file: PsiFile): Result {
        Messages.showInfoMessage("Input character: $c", "Input")
        return Result.CONTINUE
    }
    
}

IntelliJ IDE 插件开发 | (五)VFS 与编辑器,Kotlin,intellij-idea,kotlin,ide

同时需要在plugin.xml中进行配置:

<extensions defaultExtensionNs="com.intellij">
    <typedHandler implementation="cn.butterfly.vfs.listener.KeyboardListener"/>
</extensions>

总结

本文主要介绍了关于 VFS 和编辑器相关的知识,其中编辑器相关的内容则需要重点关注,这是开发各类插件都离开的东西,毕竟我们开发的插件大多都要和编辑器里的代码打交道,同时如果有错误之处,也欢迎一起交流讨论。

题外话

本来准备下篇文章就开始介绍关于 PSI 的知识,不过想一下还是决定先写一篇在第三篇文章中提到的关于内部工具(能极大提升初学者开发插件的效率,我个人是这么认为的)使用的文章,然后再继续介绍关于 PSI 的内容。文章来源地址https://www.toymoban.com/news/detail-823123.html

到了这里,关于IntelliJ IDE 插件开发 | (五)VFS 与编辑器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 2023最佳python编辑器和IDE

    IDE没有统一的标准,自己习惯就是最好的。本文列出一些较常用的IDE,供大家参考。 一般而言,WingIDE、PyCharm、Spyder、Vim是比较常用的IDE。 Spyder是Python(x,y)的作者为它开发的一个简单的集成开发环境。和其他的Python开发环境相比,它最大的优点就是模仿MATLAB的\\\"工作空间\\\"的功

    2024年02月05日
    浏览(57)
  • 11 个最佳 C++ IDE(和代码编辑器)

    C++ 是一种功能强大、用途广泛的编程语言。它也可以是一个艰难的大师。 这意味着在您的工具带中拥有正确的工具以帮助您更高效、更有效、更自信地编写代码至关重要。 在为 C++ 编程寻找最佳 IDE 或代码编辑器时,您应该从哪里开始?IDE 选项列表几乎是无限的,很难判断

    2024年02月04日
    浏览(59)
  • 轻量级 IDE & 文本编辑器 Geany 发布 2.0

    导读 Geany 是功能强大、稳定、轻量的开发者专用文本编辑器,支持 Linux、Windows 和 macOS,内置支持 50 多种编程语言。 2005 年Geany 发布首个版本 0.1。上周四刚好是 Geany 诞生 18 周年纪念日,官方发布了 2.0 正式版以表庆祝。 下载地址:https://www.geany.org/download/releases/ Geany 2.0 主

    2024年02月07日
    浏览(41)
  • IDE /Qt Creator 文本编辑器之文件编码设置

    在 Qt Creator 工具 - 选项 - 文本编辑器 - 行为选项卡中,存在一个叫作 “文件编码” 的配置项。其默认配置如下, 我是栽了不少坑之后,才理解到它的用途和作用方式的。这是一个有用的配置项,尤其是在使用 Qt Creator + MSVC 组成的集成开发环境时。我是在整理 《IDE/warning:

    2024年02月13日
    浏览(57)
  • 如何使用Mac终端给树莓派pico构建C/C++程序进行开发,以及遇到各种问题该怎么处理,不使用任何IDE或编辑器(例如VS Code)

    写本文的原因是官方的教程已经过时了,如果你现在按照官方教程来在 Mac 上进行配置,那么会遇到一堆问题,比如我几乎把能踩的“雷”都踩了。所以这里记录了完整过程,以及各种错误的原因和处理方法,不然以后换 Mac 了或者在其他平台遇到同样的问题,忘记怎么处理的

    2024年02月13日
    浏览(64)
  • IntelliJ IDEA 编辑器的全局搜索中使用正则表达式

    打开 IntelliJ IDEA 编辑器。 使用快捷键 Ctrl + Shift + F(Windows / Linux)或 Cmd + Shift + F(Mac)打开全局搜索窗口。 在搜索框中输入要搜索的内容,例如:“设备【】已经绑定网关”。 在搜索框旁边的 “.*” 图标上点击,将搜索模式切换为正则表达式模式。 填入所需的正则表达式

    2024年02月03日
    浏览(44)
  • IDE + ChatGPT,这款编辑器真的做到可以自动写代码了

    介绍 Cursor 是集成了 GPT-4 的 IDE 工具,目前免费并且无需 API Key,支持 Win、Mac、Linux 平台,可以按要求生成代码,或者让 AI 帮助优化代码,分析代码。Cursor目前已经集成了openai的GPT-4,它或将彻底改变我们写代码的方式。 以前程序员被调侃是“CV”工程师,以后我们恐怕要成

    2023年04月25日
    浏览(53)
  • 【Linux应用部署篇】在CSDN云IDE平台部署Etherpad文档编辑器

    官网网址:CSDN开发云IDE平台 CSDN云IDE平台是一个在线的集成开发环境,可用于编写、测试和部署代码。CSDN云IDE平台支持多种语言和框架,包括Java、Python、Node.js等,提供了丰富的编辑器、调试器和版本管理工具,帮助开发者快速创建和部署应用程序。CSDN云IDE平台在云端运行,

    2024年02月11日
    浏览(49)
  • 工业组态 物联网组态 组态编辑器 web组态 组态插件 编辑器

     体验地址:by组态[web组态插件] BY组态是一款非常优秀的纯前端的【web组态插件工具】,可无缝嵌入到vue项目,react项目等,由于是原生js开发,对于前端的集成没有框架的限制。同时由于BY组态只是一个插件,不能独立运行,必须嵌入到你方软件平台才能使用,所以你方软件

    2024年04月15日
    浏览(48)
  • 2401编辑器,好插件

    原文 CScintillaCtrl,CScintillaView 和 CScintillaDocv1.82 Scintilla 编辑控件的免费 MFC 和 ATL/WTL 包装类 这些类为 Scintilla 编辑控件 (http://www.scintilla.org) 提供 MFC 和 ATL/WTL 包装. 此控件 提供 开源 跨平台 编辑控件 . 部分功能包括 语法高亮 , 调用提示 , 括号高亮 , 风格,边距和标记 等等.提供的

    2024年02月03日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包