Syntax and Error Highlighting
Edit pageLast modified: 13 June 2024Product Help: Colors and fonts
UI Guidelines: Inspections
The syntax and error highlighting are performed on multiple levels: Lexer, Parser, and Annotator/External Annotator.
Text Attributes Key
How a particular range of text should be highlighted is defined via TextAttributesKey
. An instance of this class is created for every distinct type of item that should be highlighted (keyword, number, string literal, etc.).
The TextAttributesKey
defines the default attributes applied to items of the corresponding type (for example, keywords are bold, numbers are blue, string literals are bold and green). Highlighting from multiple TextAttributesKey
items can be layered — for example, one key may define an item's boldness and another one its color.
tip
Looking up existing TextAttributeKeyTo inspect applied
TextAttributesKey
(s) in the editor for the element at the caret, use Jump to Colors and Fonts action.The underlying
TextAttributeKey
's external name for items in Settings | Editor | Color Scheme can be inspected using UI Inspector.
Color Settings
The mapping of the TextAttributesKey
to specific attributes used in an editor is defined by the EditorColorsScheme
class. It can be configured by the user via Settings | Editor | Color Scheme by providing an implementation of ColorSettingPage
registered in com.intellij.colorSettingsPage
extension point. To look up the external name for a setting in the IDE, use UI Inspector.
The File | Export | Files or Selection to HTML feature uses the same syntax highlighting mechanism as the editor. Thus, it will work automatically for custom languages that provide a syntax highlighter.
Examples:
note
See note about Language Defaults and support for additional color schemes in Color Scheme Management.
Lexer
The first syntax highlighting level is based on the lexer output and is provided through the SyntaxHighlighter
interface. The syntax highlighter returns the TextAttributesKey
instances for each token type, which needs special highlighting. For highlighting lexer errors HighlighterColors.BAD_CHARACTER
should be used.
Examples:
SyntaxHighlighter
implementation for Properties language plugin
tip
Creating highlighted code sampleUse
HtmlSyntaxInfoUtil
to create Lexer-based highlighted code samples, e.g., for usage in documentation.
Semantic Highlighting
Semantic highlighting provides an additional coloring layer to improve the visual distinction of several related items (e.g., method parameters, local variables).
Register RainbowVisitor
in com.intellij.highlightVisitor
extension point. Color Settings must implement RainbowColorSettingsPage
in addition.
Parser
The second level of error highlighting happens during parsing. If according to the grammar of the language a particular sequence of tokens is invalid, the PsiBuilder.error()
method can highlight the invalid tokens and display an error message showing why they are not valid.
See Syntax Errors on how to programmatically suppress these errors in certain contexts.
Annotator
The third level of highlighting is performed through the Annotator
interface. A plugin can register one or more annotators in the com.intellij.annotator
extension point, and these annotators are called during the background highlighting pass to process the elements in the custom language's PSI tree. Attribute language
should be set to the Language ID where this annotator applies to. If highlighting data requires invoking external tools, use External Annotator instead.
Annotators can analyze not only the syntax, but also the semantics using PSI, and thus can provide much more complex syntax and error highlighting logic. The annotator can also provide quick fixes to problems it detects. When the file is changed, the annotator is called incrementally to process only changed elements in the PSI tree.
Annotators not requiring information from indexes can be marked dumb aware to work during indexing (e.g., for additional syntax highlighting). (2023.1+)
note
See also Code Inspections which offer more fine-grained control and some additional features.
Errors/Warning
See Inspections topic in UI Guidelines on how to write message texts for highlighting/quick fixes.
To highlight a region of text as a warning or error:
holder.newAnnotation(HighlightSeverity.WARNING, "Invalid code") // or HighlightSeverity.ERROR
.withFix(new MyFix(psiElement))
.create();
Call createWarningAnnotation()
/createErrorAnnotation()
on the AnnotationHolder
, and optionally call registerFix()
on the returned Annotation
object to add a quick fix for the error or warning.
Syntax
To apply additional syntax highlighting:
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(rangeToHighlight)
.textAttributes(MyHighlighter.EXTRA_HIGHLIGHT_ATTRIBUTE)
.create();
Call AnnotationHolder.createInfoAnnotation()
with an empty message and then Annotation.setTextAttributes()
.
Examples:
External Annotator
If the custom language employs external tools for validating files in the language (for example, using the Xerces library for XML schema validation), it can provide an implementation of the ExternalAnnotator
interface and register it in com.intellij.externalAnnotator
extension point (language
attribute must be specified).
The ExternalAnnotator
highlighting has the lowest priority and is invoked only after all other background processing has completed. It uses the same AnnotationHolder
interface for converting the output of the external tool into editor highlighting.
To skip running specific ExternalAnnotator
for given file, register ExternalAnnotatorsFilter
extension in com.intellij.daemon.externalAnnotatorsFilter
extension point.
To enable running ExternalAnnotator
during indexing in dumb mode, it can be marked dumb aware (2023.3).
Controlling Highlighting
Existing highlighting can be suppressed programmatically in certain contexts, see Controlling Highlighting.
To force re-highlighting all open or specific file(s) (e.g., after changing plugin specific settings), use DaemonCodeAnalyzer.restart()
.
2024.1+Order of Running Highlighting
Inspections and annotators do not run sequentially on each PsiElement
anymore. Instead, they're run in parallel on all relevant PSI independently with the following consequences.
Independent Annotators
Annotators are run independent of each other: if an annotator found an error, it no longer stops annotating the PsiElement
's parents. Effectively, there is "more" highlighting now.
Highlight Range
Producing highlights must be done as close as possible for the relevant PsiElement
. For example, instead of
annotate(PsiFile) {
<<highlight all relevant identifiers>>
}
this approach should be used:
annotate(PsiIdentifier) {
<<highlight this identifier if it's relevant>>
}
The latter version:
performs faster highlighting – it doesn't have to wait until all other identifiers are visited
removes outdated highlights faster – right after the identifier was visited and the annotator didn't produce a highlighting anymore
Thanks for your feedback!