IntelliJ插件开发 - Editor组件

简介

Swing提供了JTextArea, JTextPane等组件提供编辑器功能,IntelliJ也提供了一个编辑器组件,提供了许多高级的特性: 语法高亮、代码提示、折叠等功能。Editors除了可以展示在editor tab页中外,还可以被内嵌到dialogs, tool windows中。

使用方法

基本使用

IntelliJ提供了EditorTextField类来实现Editor功能,可以指定以下特性:

  • 文件类型
  • 文件是否只读
  • 文件是单行或者多行模式
EditorTextField textField = new EditorTextField(content, project, JavaFileType.INSTANCE);
textField.setOneLineMode(false);
textField.setViewer(true);

需要注意的是,上面代码使用的JavaFileType.INSTANCE类,并不在底层API中,它依赖于特定的平台:java。你可以手动新建这个类在自己的插件项目中,或者给你的插件增加java的依赖(增加java依赖后,你的插件将无法安装到intelliJ平台的其他软件中)

# build.gradle.kts
intellij {
  plugins.set(listOf("com.intellij.java"))
}
# plugin.xml
<depends>com.intellij.java</depends>
水平滚动条

除了基本使用可以指定的特性外,其他特性你都无法直接设置,需要继承EditorTextField类,重写createEditor方法。这里推荐将自定义的部分封装成类,实现EditorCustomization接口。IntelliJ内置了一些EditorCustomization的实现,如:

  • SpellCheckingEditorCustomization: 禁用拼写检查
  • HorizontalScrollBarEditorCustomization: 开启或关闭水平滚动条
  • ErrorStripeEditorCustomization: 打开或关闭编辑器最右侧的错误条纹提示

以下是增加水平滚动条的示例:

public class HorizontalScrollBarEditor extends EditorTextField  {

	public HorizontalScrollBarEditor(Project project, FileType fileType) {
        super(project, fileType);
    }

	@Override
    protected @NotNull EditorEx createEditor() {
        EditorEx editor = super.createEditor();
        HorizontalScrollBarEditorCustomization.ENABLED.customize(editor);
        return editor;
    }

如果你直接在EditorTextField 外部套一个JScrollBar,水平滚动条会挡住编辑器底部的内容。除了继承EditorTextField类进行定制化外,IntelliJ还内置了许多子类,如果你想在一个dialog中放置一个editor来接受用户的额输入,使用LanguageTextField会更合适。

写入文本

Editor使用了读写锁,写入文本时需要获取写锁。

WriteCommandAction.runWriteCommandAction(project, () -> {
    Document document = textField.getDocument();
    document.insertString(document.getTextLength(), stringToAppend);
});
带补全功能的编辑器

当你在自己Dialog或者ToolWindow等内嵌EditorTextField的场景,需要实现补全功能时,可以使用TextFieldWithCompletion,并实现TextFieldCompletionProvider接口。

EditorTextField input = new TextFieldWithCompletion(project, new MyTextFieldCompletionProvider(), ""
		, false, true, false);
input.addSettingsProvider(editorEx -> {
	// 如果你不需要"/"作为提示符,则不需要添加DocumentListener
	Disposable inputDisposable = Disposer.newDisposable("inputListener");
	EditorUtil.disposeWithEditor(editorEx, inputDisposable);
	editorEx.getDocument().addDocumentListener(new InputDocumentListener(editorEx), inputDisposable);
	editorEx.setBorder(null);
});

// Idea Completion对于"/"字符直接忽略,如果你需要输入"/"的时候就唤醒提示,需要手动调用scheduleAutoPopup方法
@RequiredArgsConstructor
static class InputDocumentListener implements DocumentListener {
	@NotNull
	private final Editor editor;

	@Override
	public void documentChanged(@NotNull DocumentEvent event) {
		String text = event.getDocument().getText();
		if (text.equals("/")) {
			AutoPopupController.getInstance(Objects.requireNonNull(editor.getProject())).scheduleAutoPopup(editor);
		}
	}
}

static class MyTextFieldCompletionProvider extends TextFieldCompletionProvider {
	@Override
	protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix
			, @NotNull CompletionResultSet result) {
		// 添加补全项
		LookupElementBuilder elementBuilder = LookupElementBuilder.create("/clear");
		result.addElement(elementBuilder);
	}
}

工具方法

// 获取Editor对应的PsiFile
PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());

// 往Editor存入用户自定义数据
Key<String> myKey = Key.create("myKey")
//Key<Boolean> myKeyWithDefaultValue = Key.create("myKey", Boolean.FALSE);
myKey.set(editor, "myValue");
String myValue = myKey.get(editor);

// 判断当前补全popup是否处于打开状态
CompletionServiceImpl.getCurrentCompletionProgressIndicator() != null
// 关闭popup的提示栏
editor.putUserData(AutoPopupController.NO_ADS, true);

注意事项

  1. 设置Empty Border必须用IntelliJ的JBEmptyBorder,否则在部分版本下容易出现空文本下光标看不到的问题。editor.setBorder(JBUI.Borders.empty(JBUI.insets(5)));
  2. 部分ide版本在行高发生变化后,页面渲染不太及时,需要自己调用repaint刷新
Disposable inputDisposable = Disposer.newDisposable("inputListener");
EditorUtil.disposeWithEditor(editor, inputDisposable);
editor.getDocument().addDocumentListener(new MyDocumentListener(editor), inputDisposable);

@RequiredArgsConstructor
static class InputDocumentListener implements DocumentListener {
	@NotNull
	private final EditorEx editor;
	private int lastLineCount = 0;

	@Override
	public void documentChanged(@NotNull DocumentEvent event) {
		Document document = event.getDocument();
		int newLineCount = document.getLineCount();
		if (lastLineCount != newLineCount) {
			lastLineCount = newLineCount;
			editor.getComponent().revalidate();
			editor.getComponent().repaint();
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值