SyntaxHighlighter
用于指定应如何突出显示特定范围的文本,ColorSettingPage可以定义颜色。
一、Syntax Highter
1、文本属性键
TextAttributesKey用于指定应如何突出显示特定范围的文本。不同类型的数据比如关键字、数字、字符串等如果要突出显示都需要创建一个TextAttributesKey实例。一个类型如果拥有多个TextAttributesKey
突出显示时可以分层——例如,一个键可以定义类型的粗体和另一种颜色。
2、颜色设置
EditorColorsScheme用来定义一个编辑器要使用哪些TextAttributesKey。这些是可以通过Settings | Editor | Color Scheme 来设置的。可通过com.intellij.colorSettingsPage进行扩展。
另外
File | Export | Files or Selection to HTML 也采用了同样的配色方案。颜色设置可以参考示例:
final class PropertiesColorsPage implements ColorSettingsPage {
private static final AttributesDescriptor[] ATTRS;
static {
ATTRS = Arrays.stream(PropertiesComponent.values())
.map(component -> new AttributesDescriptor(component.getMessagePointer(), component.getTextAttributesKey()))
.toArray(AttributesDescriptor[]::new)
;
}
@Override
@NotNull
public String getDisplayName() {
return OptionsBundle.message("properties.options.display.name");
}
@Override
public Icon getIcon() {
return AllIcons.FileTypes.Properties;
}
@Override
public AttributesDescriptor @NotNull [] getAttributeDescriptors() {
return ATTRS;
}
@Override
public ColorDescriptor @NotNull [] getColorDescriptors() {
return ColorDescriptor.EMPTY_ARRAY;
}
@Override
@NotNull
public SyntaxHighlighter getHighlighter() {
return new PropertiesHighlighter();
}
@Override
@NotNull
public String getDemoText() {
return """
# This comment starts with '#'
greetings=Hello
! This comment starts with '!'
what\\=to\\=greet : \\'W\\o\\rld\\',\\tUniverse\\n\\uXXXX
"""
;
}
@Override
public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
return null;
}
}
3、词法分析器
SyntaxHighlighter提供了第一级的语法高亮级别功能,语法高亮器返回TextAttributesKey
每个标记类型的实例都可以特别高亮显示。可参考示例:
public class PropertiesHighlighter extends SyntaxHighlighterBase {
@Override
@NotNull
public Lexer getHighlightingLexer() {
return new PropertiesHighlightingLexer();
}
@Override
public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) {
final PropertiesComponent type = PropertiesComponent.getByTokenType(tokenType);
TextAttributesKey key = null;
if (type != null) {
key = type.getTextAttributesKey();
}
return SyntaxHighlighterBase.pack(key);
}
public enum PropertiesComponent {
PROPERTY_KEY(
TextAttributesKey.createTextAttributesKey("PROPERTIES.KEY", DefaultLanguageHighlighterColors.KEYWORD),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.property.key"),
PropertiesTokenTypes.KEY_CHARACTERS
),
PROPERTY_VALUE(
TextAttributesKey.createTextAttributesKey("PROPERTIES.VALUE", DefaultLanguageHighlighterColors.STRING),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.property.value"),
PropertiesTokenTypes.VALUE_CHARACTERS
),
PROPERTY_COMMENT(
TextAttributesKey.createTextAttributesKey("PROPERTIES.LINE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.comment"),
PropertiesTokenTypes.END_OF_LINE_COMMENT
),
PROPERTY_KEY_VALUE_SEPARATOR(
TextAttributesKey.createTextAttributesKey("PROPERTIES.KEY_VALUE_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.key.value.separator"),
PropertiesTokenTypes.KEY_VALUE_SEPARATOR
),
PROPERTIES_VALID_STRING_ESCAPE(
TextAttributesKey.createTextAttributesKey("PROPERTIES.VALID_STRING_ESCAPE", DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.valid.string.escape"),
StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN
),
PROPERTIES_INVALID_STRING_ESCAPE(
TextAttributesKey.createTextAttributesKey("PROPERTIES.INVALID_STRING_ESCAPE", DefaultLanguageHighlighterColors.INVALID_STRING_ESCAPE),
PropertiesBundle.messagePointer("options.properties.attribute.descriptor.invalid.string.escape"),
StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN
);
private static final Map<IElementType, PropertiesComponent> elementTypeToComponent;
private static final Map<TextAttributesKey, PropertiesComponent> textAttributeKeyToComponent;
static {
elementTypeToComponent = Arrays.stream(values())
.collect(Collectors.toMap(PropertiesComponent::getTokenType, Function.identity()));
textAttributeKeyToComponent = Arrays.stream(values())
.collect(Collectors.toMap(PropertiesComponent::getTextAttributesKey, Function.identity()));
}
private final TextAttributesKey myTextAttributesKey;
private final Supplier<@Nls String> myMessagePointer;
private final IElementType myTokenType;
PropertiesComponent(TextAttributesKey textAttributesKey, Supplier<@Nls String> messagePointer, IElementType tokenType) {
myTextAttributesKey = textAttributesKey;
myMessagePointer = messagePointer;
myTokenType = tokenType;
}
public TextAttributesKey getTextAttributesKey() {
return myTextAttributesKey;
}
Supplier<@Nls String> getMessagePointer() {
return myMessagePointer;
}
IElementType getTokenType() {
return myTokenType;
}
static PropertiesComponent getByTokenType(IElementType tokenType) {
return elementTypeToComponent.get(tokenType);
}
static PropertiesComponent getByTextAttribute(TextAttributesKey textAttributesKey) {
return textAttributeKeyToComponent.get(textAttributesKey);
}
static @Nls String getDisplayName(TextAttributesKey key) {
final PropertiesComponent component = getByTextAttribute(key);
if (component == null) return null;
return component.getMessagePointer().get();
}
static @Nls HighlightSeverity getSeverity(TextAttributesKey key) {
final PropertiesComponent component = getByTextAttribute(key);
return component == PROPERTIES_INVALID_STRING_ESCAPE
? HighlightSeverity.WARNING
: null;
}
}
}
语义高亮
语义突出显示其实是提供了一个额外的着色层,以改善几个相关项(例如,方法参数、局部变量)的视觉区分。可实现com.intellij.highlightVisitor扩展点,这里的颜色设置要实现
RainbowColorSettingsPage接口。
4、解析器
第二级错误突出显示发生在解析期间。如果根据语言的语法,特定的标记序列是无效的,则PsiBuilder.error()方法可以突出显示无效标记并显示一条错误消息,说明它们无效的原因。
5、注释器
第三层高亮是通过Annotator接口实现。一个插件可以在扩展点中注册一个或多个注释器com.intellij.annotator
,这些注释器在后台高亮过程中被调用,以处理自定义语言的 PSI 树中的元素。属性language
应设置为此注释器适用的语言 ID。
注释器不仅可以分析语法,还可以用于 PSI 分析语义,因此可以提供更复杂的语法和错误突出显示逻辑。注释器还可以为其检测到的问题提供快速修复。文件更改时,将增量调用注释器以仅处理 PSI 树中更改的元素。
错误/警告
大体的代码实现如下:
holder.newAnnotation(HighlightSeverity.WARNING,"Invalid code") // or HighlightSeverity.ERROR
.withFix(new MyFix(psiElement))
.create();
句法
大体的代码实现如下:
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(rangeToHighlight)
.textAttributes(MyHighlighter.EXTRA_HIGHLIGHT_ATTRIBUTE)
.create();
public class PropertiesAnnotator implements Annotator {
private static final ExtensionPointName<DuplicatePropertyKeyAnnotationSuppressor>
EP_NAME = ExtensionPointName.create("com.intellij.properties.duplicatePropertyKeyAnnotationSuppressor");
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
if (!(element instanceof Property property)) return;
PropertiesFile propertiesFile = property.getPropertiesFile();
final String key = property.getUnescapedKey();
if (key == null) return;
Collection<IProperty> others = propertiesFile.findPropertiesByKey(key);
ASTNode keyNode = ((PropertyImpl)property).getKeyNode();
if (keyNode == null) return;
if (others.size() != 1 &&
EP_NAME.findFirstSafe(suppressor -> suppressor.suppressAnnotationFor(property)) == null) {
holder.newAnnotation(HighlightSeverity.ERROR,PropertiesBundle.message("duplicate.property.key.error.message")).range(keyNode)
.withFix(PropertiesQuickFixFactory.getInstance().createRemovePropertyFix(property)).create();
}
highlightTokens(property, keyNode, holder, new PropertiesHighlighter());
ASTNode valueNode = ((PropertyImpl)property).getValueNode();
if (valueNode != null) {
highlightTokens(property, valueNode, holder, new PropertiesValueHighlighter());
}
}
private static void highlightTokens(final Property property, final ASTNode node, final AnnotationHolder holder, PropertiesHighlighter highlighter) {
Lexer lexer = highlighter.getHighlightingLexer();
final String s = node.getText();
lexer.start(s);
while (lexer.getTokenType() != null) {
IElementType elementType = lexer.getTokenType();
TextAttributesKey[] keys = highlighter.getTokenHighlights(elementType);
for (TextAttributesKey key : keys) {
final String displayName = PropertiesComponent.getDisplayName(key);
final HighlightSeverity severity = PropertiesComponent.getSeverity(key);
if (severity != null && displayName != null) {
int start = lexer.getTokenStart() + node.getTextRange().getStartOffset();
int end = lexer.getTokenEnd() + node.getTextRange().getStartOffset();
TextRange textRange = new TextRange(start, end);
TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(key);
AnnotationBuilder builder = holder.newAnnotation(severity, displayName).range(textRange).enforcedTextAttributes(attributes);
int startOffset = textRange.getStartOffset();
if (key == PropertiesComponent.PROPERTIES_INVALID_STRING_ESCAPE.getTextAttributesKey()) {
builder = builder.withFix(new IntentionAction() {
@Override
@NotNull
public String getText() {
return PropertiesBundle.message("unescape");
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!BaseIntentionAction.canModify(file)) return false;
String text = file.getText();
return text.length() > startOffset && text.charAt(startOffset) == '\\';
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
if (file.getText().charAt(startOffset) == '\\') {
editor.getDocument().deleteString(startOffset, startOffset + 1);
}
}
@Override
public boolean startInWriteAction() {
return true;
}
});
}
builder.create();
}
}
lexer.advance();
}
}
}
二、示例
1、定义SyntaxHighlighterFactory
public class SimpleSyntaxHighlighter extends SyntaxHighlighterBase {
public static final TextAttributesKey SEPARATOR =
createTextAttributesKey("SIMPLE_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN);
public static final TextAttributesKey KEY =
createTextAttributesKey("SIMPLE_KEY", DefaultLanguageHighlighterColors.KEYWORD);
public static final TextAttributesKey VALUE =
createTextAttributesKey("SIMPLE_VALUE", DefaultLanguageHighlighterColors.STRING);
public static final TextAttributesKey COMMENT =
createTextAttributesKey("SIMPLE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
public static final TextAttributesKey BAD_CHARACTER =
createTextAttributesKey("SIMPLE_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER);
private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER};
private static final TextAttributesKey[] SEPARATOR_KEYS = new TextAttributesKey[]{SEPARATOR};
private static final TextAttributesKey[] KEY_KEYS = new TextAttributesKey[]{KEY};
private static final TextAttributesKey[] VALUE_KEYS = new TextAttributesKey[]{VALUE};
private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT};
private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
@NotNull
@Override
public Lexer getHighlightingLexer() {
return new SimpleLexerAdapter();
}
@Override
public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) {
if (tokenType.equals(SimpleTypes.SEPARATOR)) {
return SEPARATOR_KEYS;
}
if (tokenType.equals(SimpleTypes.KEY)) {
return KEY_KEYS;
}
if (tokenType.equals(SimpleTypes.VALUE)) {
return VALUE_KEYS;
}
if (tokenType.equals(SimpleTypes.COMMENT)) {
return COMMENT_KEYS;
}
if (tokenType.equals(TokenType.BAD_CHARACTER)) {
return BAD_CHAR_KEYS;
}
return EMPTY_KEYS;
}
}
<extensions defaultExtensionNs="com.intellij">
<lang.syntaxHighlighterFactory
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleSyntaxHighlighterFactory"/>
</extensions>
2、定义颜色设置界面
public class SimpleColorSettingsPage implements ColorSettingsPage {
private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{
new AttributesDescriptor("Key", SimpleSyntaxHighlighter.KEY),
new AttributesDescriptor("Separator", SimpleSyntaxHighlighter.SEPARATOR),
new AttributesDescriptor("Value", SimpleSyntaxHighlighter.VALUE),
new AttributesDescriptor("Bad value", SimpleSyntaxHighlighter.BAD_CHARACTER)
};
@Nullable
@Override
public Icon getIcon() {
return SimpleIcons.FILE;
}
@NotNull
@Override
public SyntaxHighlighter getHighlighter() {
return new SimpleSyntaxHighlighter();
}
@NotNull
@Override
public String getDemoText() {
return "# You are reading the \".properties\" entry.\n" +
"! The exclamation mark can also mark text as comments.\n" +
"website = https://en.wikipedia.org/\n" +
"language = English\n" +
"# The backslash below tells the application to continue reading\n" +
"# the value onto the next line.\n" +
"message = Welcome to \\\n" +
" Wikipedia!\n" +
"# Add spaces to the key\n" +
"key\\ with\\ spaces = This is the value that could be looked up with the key \"key with spaces\".\n" +
"# Unicode\n" +
"tab : \\u0009";
}
@Nullable
@Override
public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
return null;
}
@Override
public AttributesDescriptor @NotNull [] getAttributeDescriptors() {
return DESCRIPTORS;
}
@Override
public ColorDescriptor @NotNull [] getColorDescriptors() {
return ColorDescriptor.EMPTY_ARRAY;
}
@NotNull
@Override
public String getDisplayName() {
return "Simple";
}
}
支持通过用 分隔节点来对运算符或大括号等相关属性进行分组//
,例如:
AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[] {
new AttributesDescriptor("Operators//Plus", MySyntaxHighlighter.PLUS),
new AttributesDescriptor("Operators//Minus", MySyntaxHighlighter.MINUS),
new AttributesDescriptor("Operators//Advanced//Sigma", MySyntaxHighlighter.SIGMA),
new AttributesDescriptor("Operators//Advanced//Pi", MySyntaxHighlighter.PI),
//...
};
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="org.intellij.sdk.language.SimpleColorSettingsPage"/>
</extensions>
3、测试运行
颜色配置页面可从Settings | Editor | Color Scheme | Simple查看。如下图:文章来源:https://www.toymoban.com/news/detail-616310.html
文章来源地址https://www.toymoban.com/news/detail-616310.html
到了这里,关于idea插件开发-自定义语言4-Syntax Highlighter的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!