This example is based on
Custom Language Support Tutorial (2-5).
package com.yuchesc.intellij.simple
import com.intellij.lang.Language
import com.intellij.openapi.diagnostic.Logger
class SimpleLanguage extends Language("Simple") {
val logger = Logger.getInstance(classOf[SimpleLanguage])
logger.warn("simple language")
}
object SimpleLanguage {
val Instance = new SimpleLanguage
}
Download jar-gray.png into src/com/yuchesc/intellij/simple/
package com.yuchesc.intellij.simple
import javax.swing.Icon
import com.intellij.openapi.util.IconLoader
object SimpleIcons {
val File: Icon = IconLoader.getIcon("/com/yuchesc/intellij/simple/jar-gray.png")
}
This example defines extension ‘.smpl’ instead of ‘.simple’
package com.yuchesc.intellij.simple
import javax.swing.Icon
import com.intellij.openapi.fileTypes.LanguageFileType
class SimpleFileType extends LanguageFileType(SimpleLanguage.Instance) {
override def getName: String = "Simple file"
override def getDescription: String = "Simple language file"
override def getIcon: Icon = SimpleIcons.File
override def getDefaultExtension: String = "smpl"
}
object SimpleFileType {
val Instance: SimpleFileType = new SimpleFileType
}
package com.yuchesc.intellij.simple
import com.intellij.openapi.fileTypes.{FileTypeConsumer, FileTypeFactory}
class SimpleFileTypeFactory extends FileTypeFactory {
override def createFileTypes(fileTypeConsumer: FileTypeConsumer): Unit =
fileTypeConsumer.consume(SimpleFileType.Instance, "smpl")
}
<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.yuchesc.intellij.simple.SimpleFileTypeFactory" />
</extensions>
Create psi package.
package com.yuchesc.intellij.simple.psi
import com.intellij.psi.tree.IElementType
import com.yuchesc.intellij.simple.SimpleLanguage
class SimpleTokenType(debugName: String)
extends IElementType(debugName, SimpleLanguage.Instance) {
override def toString: String = "SimpleTokenType." + super.toString
}
package com.yuchesc.intellij.simple.psi
import com.intellij.psi.tree.IElementType
import com.yuchesc.intellij.simple.SimpleLanguage
class SimpleElementType(debugName: String)
extends IElementType(debugName, SimpleLanguage.Instance) {
}
Create src/com/yuchesc/intellij/simple/Simple.bnf
{
parserClass="com.yuchesc.intellij.simple.SimpleParser"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="Simple"
psiImplClassSuffix="Impl"
psiPackage="com.yuchesc.intellij.simple.psi"
psiImplPackage="com.yuchesc.intellij.simple.psi.impl"
elementTypeHolderClass="com.yuchesc.intellij.simple.psi.SimpleTypes"
elementTypeClass="com.yuchesc.intellij.simple.psi.SimpleElementType"
tokenTypeClass="com.yuchesc.intellij.simple.psi.SimpleTokenType"
}
simpleFile ::= item_*
private item_ ::= (property|COMMENT|CRLF)
property ::= (KEY? SEPARATOR VALUE?) | KEY
After making the bnf, select the file on the project panel, right-click, and choose Generate Parser Code
.
You may need JFlex.
Create src/com/yuchesc/intellij/simple/Simple.flex
package com.yuchesc.intellij.simple;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import com.yuchesc.intellij.simple.psi.SimpleTypes;
import com.intellij.psi.TokenType;
%%
%class SimpleLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
%eof{ return;
%eof}
CRLF=\R
WHITE_SPACE=[\ \n\t\f]
FIRST_VALUE_CHARACTER=[^ \n\f\\] | "\\"{CRLF} | "\\".
VALUE_CHARACTER=[^\n\f\\] | "\\"{CRLF} | "\\".
END_OF_LINE_COMMENT=("#"|"!")[^\r\n]*
SEPARATOR=[:=]
KEY_CHARACTER=[^:=\ \n\t\f\\] | "\\ "
%state WAITING_VALUE
%%
<YYINITIAL> {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return SimpleTypes.COMMENT; }
<YYINITIAL> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return SimpleTypes.KEY; }
<YYINITIAL> {SEPARATOR} { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; }
<WAITING_VALUE> {CRLF}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return SimpleTypes.VALUE; }
({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
. { return TokenType.BAD_CHARACTER; }
After making the flex, select the file on the project panel, right-click, and choose Run JFlex Generator
.
Modify gen/com/yuchesc/intellij/simple/SimpleLexerAdapter.java
- public SimpleLexerAdapter() { super(new SimpleLexer()); }
+ public SimpleLexerAdapter() { super(new SimpleLexer(null)); }
package com.yuchesc.intellij.simple.psi
import com.intellij.extapi.psi.PsiFileBase
import com.intellij.openapi.fileTypes.FileType
import com.intellij.psi.FileViewProvider
import com.yuchesc.intellij.simple.{SimpleFileType, SimpleLanguage}
class SimpleFile(fileViewProvider: FileViewProvider)
extends PsiFileBase(fileViewProvider, SimpleLanguage.Instance) {
override def getFileType: FileType = SimpleFileType.Instance
override def toString: String = "Simple File"
}
package com.yuchesc.intellij.simple
import com.intellij.lang.ParserDefinition.SpaceRequirements
import com.intellij.lang.{ASTNode, ParserDefinition, PsiParser}
import com.intellij.lexer.Lexer
import com.intellij.openapi.project.Project
import com.intellij.psi.{FileViewProvider, PsiElement, PsiFile}
import com.intellij.psi.tree.{IFileElementType, TokenSet}
import com.yuchesc.intellij.simple.psi.{SimpleFile, SimpleTypes}
class SimpleParserDefinition extends ParserDefinition {
val File: IFileElementType = new IFileElementType(SimpleLanguage.Instance)
override def getFileNodeType: IFileElementType = File
override def createElement(astNode: ASTNode): PsiElement = SimpleTypes.Factory.createElement(astNode)
override def createFile(fileViewProvider: FileViewProvider): PsiFile = new SimpleFile(fileViewProvider)
override def getStringLiteralElements: TokenSet = TokenSet.EMPTY
override def spaceExistanceTypeBetweenTokens(astNode: ASTNode, astNode1: ASTNode): ParserDefinition.SpaceRequirements = SpaceRequirements.MAY
override def createLexer(project: Project): Lexer = new SimpleLexerAdapter()
val Comments: TokenSet = TokenSet.create(SimpleTypes.COMMENT)
override def getCommentTokens: TokenSet = Comments
override def createParser(project: Project): PsiParser = new SimpleParser
}
Write this tag in an extensions tag.
<lang.parserDefinition language="Simple" implementationClass="com.yuchesc.intellij.simple.SimpleParserDefinition"/>
package com.yuchesc.intellij.simple
import com.intellij.lexer.Lexer
import com.intellij.openapi.editor.{HighlighterColors, DefaultLanguageHighlighterColors => DefaultColors}
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
import com.intellij.psi.tree.IElementType
import com.yuchesc.intellij.simple.psi.SimpleTypes
import com.intellij.openapi.editor.colors.TextAttributesKey.{createTextAttributesKey => createKey}
import com.intellij.psi.TokenType
class SimpleSyntaxHighlighter extends SyntaxHighlighterBase {
override def getTokenHighlights(iElementType: IElementType): Array[TextAttributesKey] =
SimpleSyntaxHighlighter.KeysMap.getOrElse(iElementType, SimpleSyntaxHighlighter.EmptyKeys)
override def getHighlightingLexer: Lexer = new SimpleLexerAdapter
}
object SimpleSyntaxHighlighter {
val Key: TextAttributesKey = createKey("SIMPLE_KEY", DefaultColors.KEYWORD)
val Separator: TextAttributesKey = createKey("SIMPLE_SEPARATOR", DefaultColors.OPERATION_SIGN)
val Value: TextAttributesKey = createKey("SIMPLE_VALUE", DefaultColors.STRING)
val KeysMap: Map[IElementType, Array[TextAttributesKey]] = Map(
SimpleTypes.SEPARATOR -> Array(Separator),
SimpleTypes.KEY -> Array[TextAttributesKey](Key),
SimpleTypes.VALUE -> Array[TextAttributesKey](Value),
TokenType.BAD_CHARACTER -> Array[TextAttributesKey](createKey("SIMPLE_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER)),
SimpleTypes.COMMENT -> Array(createKey("SIMPLE_COMMENT", DefaultColors.LINE_COMMENT))
)
val EmptyKeys: Array[TextAttributesKey] = Array.empty[TextAttributesKey]
}
package com.yuchesc.intellij.simple
import com.intellij.openapi.fileTypes.{SyntaxHighlighter, SyntaxHighlighterFactory}
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
class SimpleSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
override def getSyntaxHighlighter(project: Project, virtualFile: VirtualFile): SyntaxHighlighter = new SimpleSyntaxHighlighter()
}
Write this tag in an extensions tag.
<lang.syntaxHighlighterFactory language="Simple" implementationClass="com.yuchesc.intellij.simple.SimpleSyntaxHighlighterFactory"/>
package com.yuchesc.intellij.simple
import java.util
import javax.swing.Icon
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighter
import com.intellij.openapi.options.colors.{AttributesDescriptor, ColorDescriptor, ColorSettingsPage}
import com.yuchesc.intellij.simple.psi.SimpleTypes
class SimpleColorSettingsPage extends ColorSettingsPage {
override def getHighlighter: SyntaxHighlighter = new SimpleSyntaxHighlighter
override def getAdditionalHighlightingTagToDescriptorMap: util.Map[String, TextAttributesKey] = null
override def getIcon: Icon = SimpleIcons.File
override def getDemoText: String = "# You are reading the \".properties\" entry.\n" +
"! The exclamation mark can also mark text as comments.\n" +
"website = http://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"
override def getAttributeDescriptors: Array[AttributesDescriptor] = SimpleColorSettingsPage.Descriptors
override def getDisplayName: String = "Simple Lang"
override def getColorDescriptors: Array[ColorDescriptor] = ColorDescriptor.EMPTY_ARRAY
}
object SimpleColorSettingsPage {
val Descriptors = Array(
new AttributesDescriptor("Key", SimpleSyntaxHighlighter.Key),
new AttributesDescriptor("Separator", SimpleSyntaxHighlighter.Separator),
new AttributesDescriptor("Value", SimpleSyntaxHighlighter.Value)
)
}
Write this tag in an extensions tag.
<colorSettingsPage implementation="com.yuchesc.intellij.simple.SimpleColorSettingsPage"/>
That’s all.