initial commit

This commit is contained in:
me
2024-05-23 15:45:59 +08:00
commit 70275b14e9
55 changed files with 2661 additions and 0 deletions

568
src/main/grammar/main.bnf Normal file
View File

@@ -0,0 +1,568 @@
{
generate=[token-case="as-is" element-case="as-is"]
parserClass="me.zhouxi.slint.lang.parser.SlintParser"
implements="me.zhouxi.slint.lang.psi.SlintPsiElement"
extends="me.zhouxi.slint.lang.psi.SlintPsiElementImpl"
elementTypeHolderClass="me.zhouxi.slint.lang.psi.SlintTypes"
elementTypeClass="me.zhouxi.slint.lang.SlintElementType"
tokenTypeClass="me.zhouxi.slint.lang.SlintTokenType"
psiClassPrefix="Slint"
psiImplClassSuffix="Impl"
psiPackage="me.zhouxi.slint.lang.psi"
psiImplPackage="me.zhouxi.slint.lang.psi.impl"
tokens=[
Comma = ","
FatArrow = "=>"
DoubleArrow = "<=>"
PlusEqual = "+="
MinusEqual = "-="
StarEqual = "*="
DivEqual = "/="
LessEqual = "<="
GreaterEqual = ">="
EqualEqual = "=="
NotEqual = "!="
ColonEqual = ":="
Arrow = "->"
OrOr = "||"
AndAnd = "&&"
LBrace = "{"
RBrace = "}"
LParent = "("
RParent = ")"
LAngle = "<"
RAngle = ">"
LBracket = "["
RBracket = "]"
Plus = "+"
Minus = "-"
Star = "*"
Div = "/"
Equal = "="
Colon = ":"
Comma = ","
Semicolon = ";"
Bang = "!"
Dot = "."
Question = "?"
Dollar = "$"
At = "@"
Pipe = "|"
Percent = "%"
Whitespace = "regexp:(\s+)"
NumberLiteral = "regexp:\d+(\.(\d+)?)?([a-z]+|%)?"
Identifier = "regexp:^[a-zA-Z_][A-Za-z0-9\-_]*"
ColorLiteral = "regexp:#([a-zA-Z0-9]+)"
StringLiteral = 'regexp:(^"[^"\r\n]*")'
LineComment = 'regexp:^//[^\r\n]*'
BlockComment = 'regexp:/\*[\s\S]*?\*/'
]
extends(".*Expression")=Expression
implements(".*Keyword")=[
"me.zhouxi.slint.lang.psi.SlintPsiKeywordIdentifier"
]
}
//
Document ::= DocumentElement*
private recoverTopElement ::= !('component' | 'struct' | 'enum' | 'global'| 'export'|'import' )
private DocumentElement ::= Import | Struct |Export | Enum | GlobalSingleton | Component {
recoverWhile=recoverTopElement
}
GlobalSingleton ::= GlobalKeyword ComponentName ComponentBody {
pin=1
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
//import 定义
Import ::= ImportKeyword ImportElement? ModuleLocation';'{
pin=1
}
private ImportElement ::= '{' ImportedIdentifier (',' ImportedIdentifier)* ','? '}' FromKeyword {
pin=1
}
// ABc as Def
ImportedIdentifier ::= ReferenceIdentifier ImportAlias?{
pin=1
recoverWhile=AliasNameRecover
}
private AliasNameRecover::=!(','|'}'|';')
private ImportAlias ::= AsKeyword InternalName {
pin=1
}
//Struct 定义
Struct ::= StructKeyword TypeName (':=')? StructBody {
pin=1
}
private StructBody ::= '{' FieldDeclarations? '}'{
pin=1
}
//EnumDeclaration
Enum ::= EnumKeyword EnumName '{' (EnumValue (','EnumValue)*','? )? '}'{
pin=1
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
//--------------ExportsList -------------------------------------
//ExportsList
Export ::= ExportKeyword ExportElement {
pin=1
}
private ExportElement ::= ExportType | Struct| Component | ExportModule | GlobalSingleton|Enum
ExportType ::= '{' ExportIdentifier (','ExportIdentifier)* ','? '}'{
pin=1
}
ExportIdentifier ::= ReferenceIdentifier ExportAlias?{
}
private ExportAlias ::= AsKeyword ExternalName{
pin=1
}
ExportModule ::= '*' FromKeyword ModuleLocation ';'{
pin=1
}
//---------- component ------------------------------------------------------------
//Component ::= NewComponent
//| LegacyComponent
//Old syntax
//private LegacyComponent ::=(GlobalKeyword ComponentName ':=' ComponentName? ComponentBody)| ('global'? SubComponent)
Component ::= ComponentKeyword ComponentName InheritDeclaration? ComponentBody {
pin=1
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
//组件定义
//private LegacyComponent ::= (ComponentName|NamedIdentifier ':=' ) ComponentBody
InheritDeclaration ::= InheritsKeyword ReferenceIdentifier {
pin=1
}
ComponentBody ::= '{' ComponentElement* '}'{
pin=1
}
//组件元素定义
private ComponentElement ::=ChildrenPlaceholder| PropertyDeclaration | CallbackDeclaration
| Function | PropertyAnimation | CallbackConnection | Transitions
| PropertyChanged
| States | TwoWayBinding | PropertyBinding | ConditionalElement
| RepetitionElement | SubComponent {
recoverWhile=recoverWhileForComponentBody
}
private recoverWhileForComponentBody::= !(
'property'|'callback'|'changed'
|'states'|'transitions'|'pure'
|'function'|'public'|'protected'|'for'|'if'|'}'|';'|'@'
|GenericIdentifier|PropertyModifier)
ChildrenPlaceholder ::= '@' 'children'{
pin=1
}
//--------------------------------PropertyDeclaration Start----------------------------------------------------
// 属性定义 in property <type> name: value / in property <type> name <=> value
PropertyDeclaration ::= PropertyModifier? PropertyKeyword ('<' Type '>')? PropertyName(PropertyValue|PropertyTwoWayBindingValue|';'){
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
private PropertyModifier ::= InKeyword|OutKeyword|InOutKeyword|PrivateKeyword
private PropertyValue::= ':' BindingStatement {
pin=1
}
private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyNameReference ';' {
pin=1
}
//--------------------------------PropertyChanged Start----------------------------------------------------
PropertyChanged ::= ChangedKeyword LocalVariable '=>' CodeBlock{
pin=1
}
//--------------------------------CallbackDeclaration Start----------------------------------------------------
// 回调定义 pure callback abc()->int; callback abc; callback(..);callback()->type;
CallbackDeclaration ::= PureKeyword? CallbackKeyword FunctionName CallbackBinding? ';'{
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
//回调参数定义
CallbackBinding::= CallbackArgument | ('<=>'QualifiedPropertyNameReference)
//入参定义
CallbackArgument ::= '(' (Type (','Type)* ','? )? ')' ReturnType?{
pin=1
}
private ReturnType ::= '->' (NamedType|ArrayType){
pin=1
}
//--------------------------------TwoWayBindingDeclaration Start----------------------------------------------------
//组件双向绑定
TwoWayBinding ::= ReferenceIdentifier '<=>' QualifiedPropertyNameReference ';' {
pin=2
}
//--------------------------------FunctionDeclaration Start----------------------------------------------------
//函数定义 protected? pure? function f()
Function ::= FunctionModifiers? FunctionKeyword FunctionName FunctionArguments ReturnType? CodeBlock{
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
}
private FunctionModifiers ::= ((PublicKeyword | ProtectedKeyword) PureKeyword?) | (PureKeyword (PublicKeyword | ProtectedKeyword)?)
private FunctionArguments ::= '(' FieldDeclarations* ')'{
pin=1
}
//--------------------------------CallbackConnectionDeclaration Start---------------------------------------------------
//回调绑定定义 abc()=> {}
CallbackConnection ::= FunctionReference CallbackConnectionArguments? '=>' CodeBlock ';'?{
pin=3
}
private CallbackConnectionArguments ::= '(' (LocalVariable (',' LocalVariable)* ','?)? ')'
//--------------------------------ConditionalElementDeclaration Start---------------------------------------------------
ConditionalElement ::= IfKeyword Expression ':' SubComponent{
pin=1
}
RepetitionElement ::= ForKeyword RepetitionVariable InKeyword Expression ':' SubComponent {
pin=1
}
private RepetitionVariable ::= LocalVariable RepetitionIndex?{
pin=1
}
RepetitionIndex ::= '[' LocalVariable ']'{
pin=1
}
//--------------------------------SubElementDeclaration Start---------------------------------------------------
//子组件结构元素定义
SubComponent ::= (PropertyName ':=')? ReferenceIdentifier ComponentBody{
}
//--------------------------------TransitionsDeclaration Start---------------------------------------------------
//过渡绑定
Transitions ::= TransitionsKeyword '[' Transition* ']'{
pin=1
}
//
Transition ::= (InKeyword|OutKeyword) LocalVariable ':' '{' PropertyAnimation* '}'{
pin=1
recoverWhile=recoverForRBracket
}
//--------------------------------TransitionsDeclaration End---------------------------------------------------
// in | out name : { }
States ::= StatesKeyword '[' State* ']'{
pin=1
}
// identifier [when] : { ... }
State ::= LocalVariable StateCondition? ':' '{' StateItem* '}' {
pin=3
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
recoverWhile=recoverForRBracket
}
private recoverForRBracket::=!(']'|'}'|';'|GenericIdentifier)
StateCondition ::= WhenKeyword Expression {
pin=1
}
//状态可以由transition propertyBinding 和 animationBinding组成
private StateItem ::= QualifiedNamePropertyBinding | StateTransition
StateTransition ::= (InKeyword|OutKeyword) ':' '{' PropertyAnimation* '}'{
pin=1
}
//------------------------------------------------------------------------------------------
//类型定义
Type ::= NamedType | UnnamedType | ArrayType
private NamedType ::= QualifiedTypeNameReference
ArrayType ::= '[' Type ']'
UnnamedType ::= '{' FieldDeclarations* '}'
private FieldDeclarations ::= FieldDeclaration (',' FieldDeclaration)* ','?
private FieldDeclaration ::= PropertyName ':' Type
//代码块
CodeBlock ::= '{' Statement* '}'{
pin=1
}
private Statement ::= (ReturnStatement|IfElseStatement|ExpressionStatement) ';'?{
recoverWhile=recoverWhileStatement
}
ExpressionStatement ::= Expression (';' &Statement)?
private recoverWhileStatement::=!(GenericIdentifier|';'|'}')
ReturnStatement ::= 'return' (Expression)?{
pin=1
}
private IfElseStatement ::= IfStatement (ElseIfStatement)* ElseStatement?{
pin=1
}
IfStatement ::= IfKeyword Expression CodeBlock {
pin=1
}
ElseIfStatement ::= ElseKeyword IfKeyword Expression CodeBlock{
pin=2
}
ElseStatement ::= ElseKeyword CodeBlock {
pin=1
}
//动画定义
PropertyAnimation ::= AnimateKeyword ('*'| (QualifiedPropertyNameReference (',' QualifiedPropertyNameReference)*)) '{' QualifiedNamePropertyBinding*'}'{
pin=1
}
//组件属性绑定 name: xxx ; name : {}; name : {}
//只有对象定义和表达式需要 ;
private QualifiedNamePropertyBinding::= QualifiedPropertyNameReference ':' BindingStatement{
pin=2
}
PropertyBinding ::= ReferenceIdentifier ':' BindingStatement{
pin=2
}
//优先尝试表达式解析 {}属于代码块 {name:xx}属于表达式那么需要预测后面两个token,第二个token不是':'的情况下才是代码块
//所以优先判断对象创建判断,然后进行代码块判断,最后进行表达式
//代码块分号可选
//表达式需要分号结尾
BindingStatement ::=ObjectCreationExpressionWithSem|(CodeBlock ';'?)| ExpressionWithSem
//用于错误的直观化
private ObjectCreationExpressionWithSem::=ObjectCreationExpression';'{
pin=1
}
private ExpressionWithSem::= Expression ';'{
pin=1
}
//-----------
//////////////////////////////////////////////////////////////////////////////////////////////
//expression
Expression ::= AtExpression
| TernaryExpression
| UnaryExpression
| BinaryExpression
| AdditiveExpression
| MultiplicativeExpression
| ArrayAccessExpression
| ArrayCreationExpression
| ObjectCreationExpression
| AssignmentExpression
| PropertyExpression
| PrimaryExpression
{
recoverWhile=recoverForExpression
}
private recoverForExpression ::= !(Expression|GenericIdentifier|';')
private BinaryExpression ::= SelfAssignExpression | ComparisonExpression | BooleanExpression
//加法表达式
AdditiveExpression ::= Expression ('+'|'-') Expression{
pin=2
}
//乘法表达式
MultiplicativeExpression ::= Expression ('*'|'/')Expression{
pin=2
}
//------------------------------AtExpression---------------------------------------
AtExpression ::= '@' (LineGradient|RadialGradient|ImageUrl|Tr) {
pin=1
}
LineGradient ::= 'linear-gradient' '(' LineGradientArgument ')' {
pin=1
}
private LineGradientArgument ::= Expression (',' ColorStops)? {
pin=1
recoverWhile=recoverForColorStops
}
private recoverForColorStops::=!(Expression|','|')')
RadialGradient ::= 'radial-gradient' '(' RadialGradientArgument ')' {
pin=1
}
private RadialGradientArgument ::= 'circle' (',' ColorStops)?{
pin=1
recoverWhile=recoverForColorStops
}
ImageUrl ::= 'image-url' ImageUrlArgs {
pin=1
}
private ImageUrlArgs ::= '(' RawStringLiteral NineSlice? ')'{
pin=1
recoverWhile=RP
}
NineSlice ::= ',' 'nine-slice' '('Numbers*')'{
// pin=1
}
private RP::=!(';'|'}'|')')
ColorStops ::= ColorStop (','ColorStop)* ','?
ColorStop::= Expression Expression?
Tr::='tr' '(' TrArg (','Expression)* ')'{
pin=2
}
TrArg::= ContextTr | Plurals | SimpleTrText
ContextTr ::= RawStringLiteral '=>' (Plurals|SimpleTrText){
pin=2
}
SimpleTrText::=RawStringLiteral
Plurals ::= RawStringLiteral '|' RawStringLiteral '%' Expression{
pin=2
}
//----------------------------TernaryExpression-----------------------------------------
TernaryExpression ::= Expression '?' Expression ':' Expression
//----------------------------BooleanExpression-----------------------------------------
BooleanExpression ::= Expression ('&&' | '||') Expression{
pin=2
}
ComparisonExpression ::= Expression ('>='|'<='|'=='|'!='|'>'|'<') Expression{
pin=2
}
AssignmentExpression ::= Expression '=' Expression
SelfAssignExpression ::= Expression ('+='|'-='|'/='|'*='|'=') Expression
UnaryExpression ::= ('!' | '+' | '-') Expression{
pin=1
}
private PrimaryExpression::=ParenthesizedExpression|FunctionInvocationExpression|LiteralExpression
ParenthesizedExpression ::= '(' Expression ')'{
pin=2
}
//fake PropertySuffixExpression::= Expression? '.' GenericIdentifier
PropertyExpression ::= Expression '.' GenericIdentifier {
// pin=2 extends=PropertySuffixExpression elementType=PropertySuffixExpression
}
FunctionInvocationExpression ::= Expression '(' InvocationArguments? ')'{
extends=PropertyExpression
}
InvocationArguments ::= Expression (','Expression)* ','?{
recoverWhile=recoverOnRP
}
private recoverOnRP::=!(')')
ArrayAccessExpression ::=Expression '[' Expression ']'
ArrayCreationExpression ::= '[' ArrayElements* ']'{
pin=1
}
private ArrayElements ::= Expression (','Expression)* ','?
LiteralExpression ::= Numbers | RawStringLiteral | GenericIdentifier | RawColorLiteral
//-------------------------------------------------
//-------------------------------------------------
ObjectCreationExpression ::= '{' ObjectPropertyBindings '}'{
pin=2
}
private ObjectPropertyBindings ::= ObjectPropertyBinding (','ObjectPropertyBinding)* ','?{
pin=1
}
private ObjectPropertyBinding ::= PropertyName ':' Expression{
pin=2
recoverWhile=recover
}
private recover::=!(';'|'}'|',')
//-------------------------------For Keyword highlighting------------------------------------
ComponentKeyword ::= 'component'
StructKeyword ::='struct'
EnumKeyword::='enum'
GlobalKeyword::='global'
ExportKeyword::='export'
ImportKeyword::='import'
AsKeyword::='as'
FromKeyword::='from'
InheritsKeyword::='inherits'
PropertyKeyword::='property'
CallbackKeyword::='callback'
StatesKeyword::='states'
TransitionsKeyword::='transitions'
PureKeyword::='pure'
FunctionKeyword::='function'
PublicKeyword::='public'
ProtectedKeyword::='protected'
ForKeyword::='for'
IfKeyword::='if'
ChangedKeyword::='changed'
InKeyword::='in'
WhenKeyword::='when'
ElseKeyword::='else'
AnimateKeyword::='animate'
OutKeyword::='out'
InOutKeyword::='in-out'
PrivateKeyword::='private'
//---------NamedIdentifier ,简化PsiTree-----------------------------------
//noinspection BnfUnusedRule 用于标记命名节点对应的identifier
Named ::= PropertyName | TypeName |ExternalName | InternalName|ComponentName|FunctionName{
pin=1
}
{
extends("PropertyName|TypeName|ComponentName|FunctionName")=Named
}
LocalVariable ::= GenericIdentifier{
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
}
FunctionName ::= GenericIdentifier
ComponentName ::=GenericIdentifier
InternalName ::= GenericIdentifier{
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
}
PropertyName ::= GenericIdentifier
TypeName ::= GenericIdentifier
EnumName ::= GenericIdentifier
EnumValue ::=GenericIdentifier
ExternalName ::=GenericIdentifier{
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementMixinImpl"
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
}
ReferenceIdentifier::=GenericIdentifier{
mixin="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierMixinImpl"
}
//----------UnnamedIdentifier------------------
PropertyNameReference ::= GenericIdentifier{
mixin="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierMixinImpl"
}
FunctionReference ::=GenericIdentifier{
}
QualifiedPropertyNameReference ::= PropertyNameReference ('.' PropertyNameReference)*{
extends=PropertyNameReference
}
TypeNameReference::=GenericIdentifier
QualifiedTypeNameReference ::= TypeNameReference ('.' TypeNameReference)*{
extends=TypeNameReference
}
ModuleLocation ::= RawStringLiteral{
mixin="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierMixinImpl"
}
//
//noinspection BnfSuspiciousToken
private RawStringLiteral ::= StringLiteral
//noinspection BnfSuspiciousToken
private Numbers ::= NumberLiteral
//noinspection BnfSuspiciousToken
private GenericIdentifier::= Identifier
//noinspection BnfSuspiciousToken
private RawColorLiteral ::= ColorLiteral

View File

@@ -0,0 +1,93 @@
package me.zhouxi.slint.lang.lexer;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import static com.intellij.psi.TokenType.BAD_CHARACTER;
import static com.intellij.psi.TokenType.WHITE_SPACE;
import static me.zhouxi.slint.lang.psi.SlintTypes.*;
%%
%{
public SlintLexer() {
this((java.io.Reader)null);
}
%}
%public
%class SlintLexer
%implements FlexLexer
%function advance
%type IElementType
%unicode
WHITE_SPACE=\s+
NUMBERLITERAL=[0-9]+(\.([0-9]+)?)?([a-z]+|%)?
IDENTIFIER=[a-zA-Z][A-Za-z0-9\-_]*
COLORLITERAL=#([a-zA-Z0-9]+)
LINECOMMENT=\/\/[^\r\n]*
%xstate COMMENT STRINGLITERAL
%%
<YYINITIAL> {
{WHITE_SPACE} { return WHITE_SPACE; }
"/*" { yybegin(COMMENT);}
"\"" { yybegin(STRINGLITERAL);}
"," { return Comma; }
"=>" { return FatArrow; }
"<=>" { return DoubleArrow; }
"+=" { return PlusEqual; }
"-=" { return MinusEqual; }
"*=" { return StarEqual; }
"/=" { return DivEqual; }
"<=" { return LessEqual; }
">=" { return GreaterEqual; }
"==" { return EqualEqual; }
"!=" { return NotEqual; }
":=" { return ColonEqual; }
"->" { return Arrow; }
"||" { return OrOr; }
"&&" { return AndAnd; }
"{" { return LBrace; }
"}" { return RBrace; }
"(" { return LParent; }
")" { return RParent; }
"<" { return LAngle; }
">" { return RAngle; }
"[" { return LBracket; }
"]" { return RBracket; }
"+" { return Plus; }
"-" { return Minus; }
"*" { return Star; }
"/" { return Div; }
"=" { return Equal; }
":" { return Colon; }
";" { return Semicolon; }
"!" { return Bang; }
"." { return Dot; }
"?" { return Question; }
"$" { return Dollar; }
"@" { return At; }
"|" { return Pipe; }
"%" { return Percent; }
{NUMBERLITERAL} { return NumberLiteral; }
{IDENTIFIER} { return Identifier; }
{COLORLITERAL} { return ColorLiteral; }
{LINECOMMENT} { return LineComment; }
}
<COMMENT>{
"*/" { yybegin(YYINITIAL);return BlockComment; }
[^*]+ {}
"*" {}
}
<STRINGLITERAL>{
"\\" { }
"\\\"" { }
"\"" { yybegin(YYINITIAL); return StringLiteral; }
[^\\\"\n]+ { }
[\n] {yybegin(YYINITIAL);}
}
[^] { return BAD_CHARACTER; }

View File

@@ -0,0 +1,21 @@
package me.zhouxi.slint.lang;
import com.intellij.lang.Language;
import javax.swing.*;
import static com.intellij.openapi.util.IconLoader.getIcon;
/**
* @author zhouxi 2024/4/30
*/
public class Slint extends Language {
public static final Slint INSTANCE = new Slint();
protected Slint() {
super("Slint");
}
public static final Icon ICON = getIcon("META-INF/pluginIcon.svg", Slint.class);
}

View File

@@ -0,0 +1,15 @@
package me.zhouxi.slint.lang;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public final class SlintElementType extends IElementType {
public SlintElementType(@NonNls @NotNull String rawKind) {
super(rawKind, Slint.INSTANCE);
}
}

View File

@@ -0,0 +1,62 @@
package me.zhouxi.slint.lang;
import com.intellij.lang.ASTNode;
import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiParser;
import com.intellij.lexer.FlexAdapter;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import me.zhouxi.slint.lang.lexer.SlintLexer;
import me.zhouxi.slint.lang.parser.SlintParser;
import me.zhouxi.slint.lang.psi.SlintFile;
import me.zhouxi.slint.lang.psi.SlintTypes;
import org.jetbrains.annotations.NotNull;
import static me.zhouxi.slint.lang.psi.SlintTypes.*;
/**
* @author zhouxi 2024/4/30
*/
public class SlintParserDefinition implements ParserDefinition {
@Override
public @NotNull Lexer createLexer(Project project) {
return new FlexAdapter(new SlintLexer());
}
@Override
public @NotNull PsiParser createParser(Project project) {
return new SlintParser();
}
@Override
public @NotNull IFileElementType getFileNodeType() {
return FileType;
}
public static final IFileElementType FileType = new IFileElementType("SlintFile",Slint.INSTANCE);
@Override
public @NotNull TokenSet getCommentTokens() {
return TokenSet.create(LineComment, BlockComment);
}
@Override
public @NotNull TokenSet getStringLiteralElements() {
return TokenSet.create(StringLiteral);
}
@Override
public @NotNull PsiElement createElement(ASTNode node) {
return SlintTypes.Factory.createElement(node);
}
@Override
public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) {
return new SlintFile(viewProvider);
}
}

View File

@@ -0,0 +1,16 @@
package me.zhouxi.slint.lang;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* @author zhouxi 2024/4/30
*/
public class SlintTokenType extends IElementType {
public SlintTokenType(@NonNls @NotNull String kind) {
super(kind, Slint.INSTANCE);
}
}

View File

@@ -0,0 +1,23 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiFile;
import me.zhouxi.slint.lang.Slint;
import org.jetbrains.annotations.NotNull;
/**
* @author zhouxi 2024/4/30
*/
public class SlintFile extends PsiFileBase implements PsiFile {
public SlintFile(@NotNull FileViewProvider viewProvider) {
super(viewProvider, Slint.INSTANCE);
}
@Override
public @NotNull FileType getFileType() {
return SlintFileType.INSTANCE;
}
}

View File

@@ -0,0 +1,42 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import me.zhouxi.slint.lang.Slint;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author zhouxi 2024/4/30
*/
public class SlintFileType extends LanguageFileType {
public static final SlintFileType INSTANCE = new SlintFileType();
protected SlintFileType() {
super(Slint.INSTANCE);
}
@Override
public @NonNls @NotNull String getName() {
return "Slint File";
}
@Override
public @NlsContexts.Label @NotNull String getDescription() {
return "Slint file dsl";
}
@Override
public @NlsSafe @NotNull String getDefaultExtension() {
return "slint";
}
@Override
public Icon getIcon() {
return Slint.ICON;
}
}

View File

@@ -0,0 +1,9 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.psi.PsiElement;
public interface SlintPsiElement extends PsiElement {
}

View File

@@ -0,0 +1,12 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
public class SlintPsiElementImpl extends ASTWrapperPsiElement implements SlintPsiElement {
public SlintPsiElementImpl(@NotNull ASTNode node) {
super(node);
}
}

View File

@@ -0,0 +1,4 @@
package me.zhouxi.slint.lang.psi;
public interface SlintPsiKeywordIdentifier extends SlintPsiElement{
}

View File

@@ -0,0 +1,11 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.psi.NavigatablePsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
public interface SlintPsiNamedElement extends SlintPsiElement, PsiNameIdentifierOwner, NavigatablePsiElement {
@Override
default boolean canNavigate() {
return true;
}
}

View File

@@ -0,0 +1,5 @@
package me.zhouxi.slint.lang.psi;
public interface SlintPsiReferencedIdentifier extends SlintPsiElement {
}

View File

@@ -0,0 +1,28 @@
package me.zhouxi.slint.lang.psi;
import java.util.List;
public class SlintPsiUtils {
public static final List<String> InternalTypes = List.of(
"angle",
"bool",
"brush",
"color",
"duration",
"easing",
"float",
"image",
"int",
"length",
"percent",
"physical-length",
"relative-font-size",
"string"
);
public static boolean isInternalType(SlintPsiElement identifier) {
return InternalTypes.stream().anyMatch(identifier::textMatches);
}
}

View File

@@ -0,0 +1,27 @@
package me.zhouxi.slint.lang.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import me.zhouxi.slint.lang.psi.SlintPsiElementImpl;
import me.zhouxi.slint.lang.psi.SlintPsiReferencedIdentifier;
import org.jetbrains.annotations.NotNull;
public abstract class SlintReferencedIdentifierMixinImpl extends SlintPsiElementImpl implements SlintPsiReferencedIdentifier {
public SlintReferencedIdentifierMixinImpl(@NotNull ASTNode node) {
super(node);
}
@Override
public PsiReference getReference() {
PsiReference[] references = getReferences();
return references.length > 0 ? references[0] : null;
}
@Override
public PsiReference @NotNull [] getReferences() {
return ReferenceProvidersRegistry.getReferencesFromProviders(this);
}
}

View File

@@ -0,0 +1,32 @@
package me.zhouxi.slint.reference;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.AbstractElementManipulator;
import com.intellij.util.IncorrectOperationException;
import me.zhouxi.slint.lang.psi.SlintPsiReferencedIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static me.zhouxi.slint.lang.SlintElementFactoryKt.createIdentifier;
/**
* @author zhouxi 2024/5/8
*/
public class SlintElementNameManipulator extends AbstractElementManipulator<SlintPsiReferencedIdentifier> {
@Override
public @Nullable SlintPsiReferencedIdentifier handleContentChange(@NotNull SlintPsiReferencedIdentifier element, @NotNull TextRange range, String newContent) throws IncorrectOperationException {
final var identifier = element.getFirstChild();
if (identifier==null){
throw new IncorrectOperationException("identifier doesn't exist");
}
final var newIdentifier = createIdentifier(element.getProject(), newContent);
identifier.replace(newIdentifier);
return element;
}
@Override
@NotNull
public TextRange getRangeInElement(@NotNull final SlintPsiReferencedIdentifier element) {
return new TextRange(0,element.getTextLength());
}
}

View File

@@ -0,0 +1,12 @@
package me.zhouxi.slint
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.extensions.PluginId
import java.nio.file.Path
object Plugin {
val Id = PluginId.findId("me.zhouxi.intellij-slint")
val Path: Path = PluginManagerCore.getPlugin(Id)!!.pluginPath
}

View File

@@ -0,0 +1,30 @@
package me.zhouxi.slint.brace
import com.intellij.lang.BracePair
import com.intellij.lang.PairedBraceMatcher
import com.intellij.psi.PsiFile
import com.intellij.psi.tree.IElementType
import me.zhouxi.slint.lang.psi.SlintTypes
class SlintPairedBraceMatcher : PairedBraceMatcher {
override fun getPairs(): Array<BracePair> {
return Pair
}
override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?): Boolean {
return true
}
override fun getCodeConstructStart(file: PsiFile, openingBraceOffset: Int): Int {
return 0
}
companion object {
val Pair: Array<BracePair> = arrayOf(
BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true),
BracePair(SlintTypes.LParent, SlintTypes.RParent, true),
BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true),
BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true)
)
}
}

View File

@@ -0,0 +1,84 @@
package me.zhouxi.slint.completion
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.icons.AllIcons
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.util.childrenOfType
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.SlintParserDefinition
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintFile
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintPsiUtils.InternalTypes
import me.zhouxi.slint.lang.psi.SlintTypes
import me.zhouxi.slint.lang.psi.utils.exportedElements
import me.zhouxi.slint.lang.psi.utils.inheritDeclaredElements
class SlintCompletionContributor : CompletionContributor() {
init {
//文件级别
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(4, PlatformPatterns.psiElement(SlintParserDefinition.FileType)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(TopKeywords)
}
})
//类型定义
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintTypes.Type)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(BasicTypes)
}
})
//componentBody
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintTypes.ComponentBody)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(ElementKeywords)
val component = parameters.position.parentOfType<SlintComponent>() ?: return
val elements = component.inheritDeclaredElements()
for (element in elements) {
result.addElement(LookupElementBuilder.create(element).withIcon(AllIcons.Nodes.Property))
}
val file = component.containingFile as SlintFile
val array = file.childrenOfType<SlintImport>()
.mapNotNull { it.moduleLocation?.reference?.resolve() as SlintFile? }
.flatMap { it.exportedElements() }
for (element in array) {
result.addElement(LookupElementBuilder.create(element).withIcon(AllIcons.Nodes.Class))
}
}
})
}
}
val BasicTypes = InternalTypes.map { LookupElementBuilder.create(it) }
val TopKeywords = arrayOf("export", "component", "global", "enum", "struct").map { LookupElementBuilder.create(it) }
val ElementKeywords = arrayOf(
"in", "out", "in-out", "callback",
"property", "private", "changed",
"states", "transitions", "@children"
).map { LookupElementBuilder.create(it) }

View File

@@ -0,0 +1,38 @@
package me.zhouxi.slint.formatter
import com.intellij.lang.Language
import com.intellij.psi.TokenType
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition.SyntaxElement
import com.intellij.psi.impl.source.codeStyle.lineIndent.JavaLikeLangLineIndentProvider
import com.intellij.psi.tree.IElementType
import me.zhouxi.slint.lang.Slint
import me.zhouxi.slint.lang.psi.SlintTypes
class SlintLineIndentProvider : JavaLikeLangLineIndentProvider() {
override fun mapType(tokenType: IElementType): SyntaxElement? {
return SyntaxMap[tokenType]
}
override fun isSuitableForLanguage(language: Language): Boolean {
return language.isKindOf(Slint.INSTANCE)
}
companion object {
val SyntaxMap: Map<IElementType, SyntaxElement> = java.util.Map.ofEntries<IElementType, SyntaxElement>(
java.util.Map.entry(SlintTypes.LBracket, JavaLikeElement.BlockOpeningBrace),
java.util.Map.entry(SlintTypes.RBracket, JavaLikeElement.BlockClosingBrace),
java.util.Map.entry(SlintTypes.LBrace, JavaLikeElement.BlockOpeningBrace),
java.util.Map.entry(SlintTypes.RBrace, JavaLikeElement.BlockClosingBrace),
java.util.Map.entry(TokenType.WHITE_SPACE, JavaLikeElement.Whitespace),
java.util.Map.entry(SlintTypes.Semicolon, JavaLikeElement.Semicolon),
java.util.Map.entry(SlintTypes.LineComment, JavaLikeElement.LineComment),
java.util.Map.entry(SlintTypes.BlockComment, JavaLikeElement.BlockComment),
java.util.Map.entry(SlintTypes.Colon, JavaLikeElement.Colon),
java.util.Map.entry(SlintTypes.Comma, JavaLikeElement.Comma),
java.util.Map.entry(SlintTypes.LParent, JavaLikeElement.LeftParenthesis),
java.util.Map.entry(SlintTypes.RParent, JavaLikeElement.RightParenthesis)
)
}
}

View File

@@ -0,0 +1,58 @@
package me.zhouxi.slint.highlight
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.openapi.editor.HighlighterColors
import com.intellij.openapi.editor.colors.TextAttributesKey
object Definitions {
@JvmField
val _KeyWord: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_KeyWord", DefaultLanguageHighlighterColors.KEYWORD)
val _StringLiteral: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_StringLiteral", DefaultLanguageHighlighterColors.STRING)
val _Comment: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_Comment", DefaultLanguageHighlighterColors.LINE_COMMENT)
val _DeclaredIdentifier: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
"_DeclaredIdentifier", DefaultLanguageHighlighterColors.INSTANCE_FIELD
)
val _BadCharacter: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("BadCharacter", HighlighterColors.BAD_CHARACTER)
val _NumberLiteral: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_NumberLiteral", DefaultLanguageHighlighterColors.NUMBER)
val _SemiColon: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_SemiColon", DefaultLanguageHighlighterColors.SEMICOLON)
val _Error: TextAttributesKey = TextAttributesKey.createTextAttributesKey("_Error", HighlighterColors.BAD_CHARACTER)
private val BadCharacter = arrayOf(_BadCharacter)
@JvmField
val KeyWord: Array<TextAttributesKey> = arrayOf(_KeyWord)
@JvmField
val StringLiteral: Array<TextAttributesKey> = arrayOf(_StringLiteral)
val DeclaredIdentifier: Array<TextAttributesKey> = arrayOf(_DeclaredIdentifier)
@JvmField
val Comment: Array<TextAttributesKey> = arrayOf(_Comment)
val Empty: Array<TextAttributesKey?> = arrayOfNulls(0)
@JvmField
val NumberLiteral: Array<TextAttributesKey> = arrayOf(_NumberLiteral)
@JvmField
val Brace: Array<TextAttributesKey> = arrayOf(DefaultLanguageHighlighterColors.BRACES)
@JvmField
val SemiColon: Array<TextAttributesKey> = arrayOf(_SemiColon)
val Error: Array<TextAttributesKey> = arrayOf(_Error)
}

View File

@@ -0,0 +1,45 @@
package me.zhouxi.slint.highlight
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.psi.PsiElement
import me.zhouxi.slint.lang.psi.*
class KeywordHighlightAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if (element is SlintPsiKeywordIdentifier) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(Definitions._KeyWord)
.create()
return
}
if (element is SlintTypeNameReference && SlintPsiUtils.isInternalType(element)) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(Definitions._KeyWord)
.create()
return
}
if (element is SlintPropertyName) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create()
}
if (element is SlintPropertyBinding) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.getReferenceIdentifier())
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create()
}
if (element is SlintFunctionName || element is SlintFunctionReference) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD)
.create()
}
}
}

View File

@@ -0,0 +1,37 @@
package me.zhouxi.slint.highlight
import com.intellij.lexer.FlexAdapter
import com.intellij.lexer.Lexer
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
import com.intellij.psi.tree.IElementType
import me.zhouxi.slint.lang.lexer.SlintLexer
import me.zhouxi.slint.lang.psi.SlintTypes
class SlintSyntaxHighlighter : SyntaxHighlighterBase() {
override fun getHighlightingLexer(): Lexer {
return FlexAdapter(SlintLexer())
}
override fun getTokenHighlights(tokenType: IElementType): Array<TextAttributesKey> {
if (tokenType === SlintTypes.StringLiteral) {
return Definitions.StringLiteral
}
if (tokenType === SlintTypes.LineComment || tokenType === SlintTypes.BlockComment) {
return Definitions.Comment
}
if (tokenType === SlintTypes.NumberLiteral) {
return Definitions.NumberLiteral
}
if (tokenType === SlintTypes.LBrace || tokenType === SlintTypes.RBrace) {
return Definitions.Brace
}
if (tokenType === SlintTypes.DoubleArrow) {
return Definitions.KeyWord
}
if (tokenType === SlintTypes.Semicolon) {
return Definitions.SemiColon
}
return TextAttributesKey.EMPTY_ARRAY
}
}

View File

@@ -0,0 +1,18 @@
package me.zhouxi.slint.lang
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileFactory
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintFile
/**
* @author zhouxi 2024/5/8
*/
fun createIdentifier(project: Project?, text: String): PsiElement {
val factory = PsiFileFactory.getInstance(project)
val file = factory.createFileFromText("dummy.slint", Slint.INSTANCE, "component $text{}") as SlintFile
return file.findChildByClass(SlintComponent::class.java)!!.componentName!!.identifier
}

View File

@@ -0,0 +1,39 @@
package me.zhouxi.slint.lang.psi.impl
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiElement
import com.intellij.util.IncorrectOperationException
import me.zhouxi.slint.lang.createIdentifier
import me.zhouxi.slint.lang.psi.SlintNamed
import me.zhouxi.slint.lang.psi.SlintPsiElementImpl
import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
/**
* @author zhouxi 2024/5/15
*/
abstract class SlintPsiNamedElementMixinImpl(node: ASTNode) : SlintPsiElementImpl(node),
SlintPsiNamedElement {
override fun getNameIdentifier(): PsiElement? {
return findChildByClass(SlintNamed::class.java)
}
override fun getName(): String? {
return nameIdentifier?.text ?: this.text
}
override fun canNavigate(): Boolean {
return true
}
override fun getTextOffset(): Int {
return nameIdentifier?.textOffset ?: super.getTextOffset()
}
@Throws(IncorrectOperationException::class)
override fun setName(name: @NlsSafe String): PsiElement {
val element = nameIdentifier ?: throw IncorrectOperationException()
element.replace(createIdentifier(project, name))
return this
}
}

View File

@@ -0,0 +1,43 @@
package me.zhouxi.slint.lang.psi.utils
import com.intellij.psi.PsiElement
import com.intellij.psi.util.childrenOfType
import me.zhouxi.slint.lang.psi.*
fun SlintExport.exportedElements(): List<SlintPsiNamedElement> {
val list = mutableListOf<SlintPsiNamedElement>()
this.exportType?.exportIdentifierList?.forEach {
if (it.externalName == null) {
val component = it.referenceIdentifier.reference?.resolve() as SlintComponent?
component?.let { list.add(component) }
} else {
list.add(it.externalName!!)
}
}
this.component?.let { list.add(it) }
this.globalSingleton?.let { list.add(it) }
val file = this.exportModule?.moduleLocation?.reference?.resolve() as SlintFile? ?: return list
val exports = file.childrenOfType<SlintExport>()
//TODO recursion error
exports.forEach { list.addAll(it.exportedElements()) }
return list
}
fun SlintImport.importedElements(): List<PsiElement> {
val list = mutableListOf<PsiElement>()
this.importedIdentifierList.forEach { identifier ->
list.add(identifier.referenceIdentifier)
identifier.internalName?.let { list.add(it) }
}
return list
}
fun SlintFile.importedElements(): List<PsiElement> {
return this.childrenOfType<SlintImport>().flatMap { it.importedElements() }
}
fun SlintFile.exportedElements(): List<SlintPsiNamedElement> {
return this.childrenOfType<SlintExport>().flatMap { it.exportedElements() }
}

View File

@@ -0,0 +1,132 @@
package me.zhouxi.slint.lang.psi.utils
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import me.zhouxi.slint.lang.psi.*
import java.util.function.Function
import java.util.function.Predicate
fun resolveComponent(element: SlintReferenceIdentifier?): SlintComponent? {
if (element == null) {
return null
}
val maybeComponent = element.reference?.resolve()
if (maybeComponent is SlintComponent) {
return maybeComponent
}
return resolveReferencedComponent(maybeComponent ?: return null)
}
fun resolveComponent(element: SlintInternalName): SlintComponent? {
//内部名字解析引用
val resolve = element.reference?.resolve()
if (resolve is SlintComponent) {
return resolve
}
//InternalName解析不到东西,换成External试一下
if (resolve == null) {
val externalName = PsiTreeUtil.getPrevSiblingOfType(element, SlintExternalName::class.java)
if (externalName == null) {
val externalName1 = PsiTreeUtil.getNextSiblingOfType(element, SlintExternalName::class.java) ?: return null
return resolveReferencedComponent(externalName1)
}
return resolveReferencedComponent(externalName)
}
return resolveReferencedComponent(resolve)
}
fun resolveComponent(element: SlintExternalName): SlintComponent? {
val resolve = element.reference?.resolve()
if (resolve is SlintComponent) {
return resolve
}
//InternalName解析不到东西,换成External试一下
if (resolve == null) {
val internalName = PsiTreeUtil.getPrevSiblingOfType(element, SlintInternalName::class.java)
if (internalName == null) {
val internalName1 = PsiTreeUtil.getNextSiblingOfType(element, SlintInternalName::class.java) ?: return null
return resolveReferencedComponent(internalName1)
}
return resolveReferencedComponent(internalName)
}
return resolveReferencedComponent(resolve)
}
fun resolveReferencedComponent(element: PsiElement?): SlintComponent? {
if (element == null) {
return null
}
when (element) {
is SlintComponentName -> return element.parent as SlintComponent?
is SlintComponent -> return element
is SlintReferenceIdentifier -> return resolveComponent(element)
is SlintInternalName -> return resolveComponent(element)
is SlintExternalName -> return resolveComponent(element)
}
return null
}
fun searchProperty(
component: PsiElement?,
predicate: Predicate<SlintPropertyDeclaration>
): SlintPropertyDeclaration? {
if (component is SlintSubComponent) {
return searchProperty(component, predicate)
}
if (component is SlintComponent) {
return searchElementInParents(component, predicate) { it.componentBody?.propertyDeclarationList }
}
return null
}
fun searchProperty(
subComponent: SlintSubComponent?,
predicate: Predicate<SlintPropertyDeclaration>
): SlintPropertyDeclaration? {
val component = resolveComponent(subComponent?.referenceIdentifier) ?: return null
return searchElementInParents(component, predicate) { it.componentBody?.propertyDeclarationList }
}
fun searchCallback(
subComponent: SlintSubComponent?,
predicate: Predicate<SlintCallbackDeclaration>
): SlintCallbackDeclaration? {
val component = subComponent?.referenceIdentifier?.reference?.resolve() ?: return null
return searchElementInParents(
component as SlintComponent,
predicate
) { it.componentBody?.callbackDeclarationList }
}
fun <T> searchElementInParents(
component: SlintComponent?,
predicate: Predicate<T>,
function: Function<SlintComponent, List<T>?>
): T? {
if (component == null) {
return null
}
val properties = function.apply(component) ?: arrayListOf<T>()
for (property in properties) {
if (predicate.test(property)) {
return property
}
}
val inheritComponent = resolveReferencedComponent(component.inheritDeclaration?.referenceIdentifier)
return searchElementInParents(inheritComponent, predicate, function)
}
fun SlintComponent.currentDeclaredElements(): List<SlintPsiNamedElement> {
val properties = this.componentBody?.propertyDeclarationList ?: emptyList()
val callbacks = this.componentBody?.callbackDeclarationList ?: emptyList()
return properties + callbacks
}
fun SlintComponent.inheritDeclaredElements(): List<SlintPsiNamedElement> {
val properties = this.componentBody?.propertyDeclarationList ?: emptyList()
val callbacks = this.componentBody?.callbackDeclarationList ?: emptyList()
val inheritedComponent = resolveReferencedComponent(this.inheritDeclaration?.referenceIdentifier)
?: return properties + callbacks
return properties + callbacks + inheritedComponent.inheritDeclaredElements()
}

View File

@@ -0,0 +1,11 @@
package me.zhouxi.slint.preview
import com.intellij.execution.configurations.LocatableRunConfigurationOptions
/**
* @author zhouxi 2024/5/16
*/
class CommandOptions : LocatableRunConfigurationOptions() {
var arguments by string()
}

View File

@@ -0,0 +1,15 @@
package me.zhouxi.slint.preview
import com.intellij.execution.RunManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
class PreviewAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val manager = RunManager.getInstance(e.project!!)
// e.si
// manager.createConfiguration()
}
}

View File

@@ -0,0 +1,26 @@
package me.zhouxi.slint.preview
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.ConfigurationType
import com.intellij.execution.configurations.RunConfiguration
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.project.Project
/**
* @author zhouxi 2024/5/16
*/
class PreviewConfigurationFactory(type: ConfigurationType) : ConfigurationFactory(type) {
override fun getOptionsClass(): Class<out BaseState?> {
return CommandOptions::class.java
}
override fun getId(): String {
return PreviewConfigurationType.id
}
override fun createTemplateConfiguration(
project: Project
): RunConfiguration {
return PreviewRunConfiguration(project, this, "Slint Viewer")
}
}

View File

@@ -0,0 +1,22 @@
package me.zhouxi.slint.preview
import com.intellij.execution.configurations.ConfigurationTypeBase
import com.intellij.openapi.util.NotNullLazyValue
import me.zhouxi.slint.lang.Slint
/**
* @author zhouxi 2024/5/16
*/
object PreviewConfigurationType : ConfigurationTypeBase(
"SlintViewerConfiguration",
"Slint viewer",
"Slint component preview",
NotNullLazyValue.createValue { Slint.ICON }
) {
init {
addFactory(PreviewConfigurationFactory(this))
}
val factory: PreviewConfigurationFactory
get() = this.configurationFactories[0] as PreviewConfigurationFactory
}

View File

@@ -0,0 +1,71 @@
package me.zhouxi.slint.preview
import com.intellij.execution.ExecutionException
import com.intellij.execution.Executor
import com.intellij.execution.configurations.*
import com.intellij.execution.process.ColoredProcessHandler
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.process.ProcessTerminatedListener
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.InvalidDataException
import com.intellij.util.io.BaseOutputReader
import org.apache.commons.lang3.StringUtils
import org.jdom.Element
/**
* @author zhouxi 2024/5/16
*/
class PreviewRunConfiguration(project: Project, factory: ConfigurationFactory?, name: String?) :
LocatableConfigurationBase<CommandOptions?>(project, factory!!, name) {
public override fun getOptions(): CommandOptions {
return super.getOptions() as CommandOptions
}
override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration?> {
return SlintSettingEditor()
}
override fun suggestedName(): String? {
println(options.arguments)
return suggestedName()
}
@Throws(InvalidDataException::class)
override fun readExternal(element: Element) {
super.readExternal(element)
val attribute = element.getAttributeValue("RunArguments")
if (StringUtils.isNoneBlank(attribute)) {
options.arguments = attribute
}
}
override fun writeExternal(element: Element) {
super.writeExternal(element)
if (StringUtils.isNoneBlank(options.arguments)) {
element.setAttribute("RunArguments", options.arguments)
}
}
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState {
return object : CommandLineState(environment) {
@Throws(ExecutionException::class)
override fun startProcess(): ProcessHandler {
val args = options.arguments?.split(" ")?.filter { it.isNotBlank() }?.toTypedArray() ?: arrayOf()
val commandLine = GeneralCommandLine(
SlintViewer.executable(), *args
)
val processHandler = object : ColoredProcessHandler(commandLine) {
override fun readerOptions(): BaseOutputReader.Options {
return BaseOutputReader.Options.forMostlySilentProcess()
}
}
ProcessTerminatedListener.attach(processHandler)
return processHandler
}
}
}
}

View File

@@ -0,0 +1,40 @@
package me.zhouxi.slint.preview
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.LazyRunConfigurationProducer
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import me.zhouxi.slint.lang.psi.SlintFile
/**
* @author zhouxi 2024/5/16
*/
class PreviewRunConfigurationProducer : LazyRunConfigurationProducer<PreviewRunConfiguration>() {
override fun getConfigurationFactory(): ConfigurationFactory {
return PreviewConfigurationType.factory
}
override fun setupConfigurationFromContext(
configuration: PreviewRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
val file = sourceElement.get().containingFile
if (file !is SlintFile) {
return false
}
configuration.options.arguments = "${file.virtualFile.path} --auto-reload"
configuration.name = file.name
return true
}
override fun isConfigurationFromContext(
configuration: PreviewRunConfiguration,
context: ConfigurationContext
): Boolean {
val path = context.psiLocation?.context?.containingFile?.virtualFile?.path ?: return false
return configuration.options.arguments?.contains(path) == true
}
}

View File

@@ -0,0 +1,22 @@
package me.zhouxi.slint.preview
import com.intellij.execution.lineMarker.ExecutorAction
import com.intellij.execution.lineMarker.RunLineMarkerContributor
import com.intellij.psi.PsiElement
import me.zhouxi.slint.lang.Slint
import me.zhouxi.slint.lang.psi.SlintComponentName
/**
* @author zhouxi 2024/5/16
*/
class PreviewRunLineMarkerContributor : RunLineMarkerContributor() {
override fun getInfo(element: PsiElement): Info? {
if (element.parent is SlintComponentName) {
return Info(
Slint.ICON, null,
*ExecutorAction.getActions(1)
)
}
return null
}
}

View File

@@ -0,0 +1,47 @@
package me.zhouxi.slint.preview
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.options.SettingsEditor
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.*
import java.awt.Desktop
import java.awt.Font
import java.net.URI
import javax.swing.JComponent
/**
* @author zhouxi 2024/5/16
*/
class SlintSettingEditor : SettingsEditor<PreviewRunConfiguration>() {
private val commandInput = JBTextField()
override fun resetEditorFrom(config: PreviewRunConfiguration) {
commandInput.text = config.options.arguments
}
@Throws(ConfigurationException::class)
override fun applyEditorTo(config: PreviewRunConfiguration) {
config.options.arguments = commandInput.text
}
override fun createEditor(): JComponent {
return panel {
separator()
row {
label("Slint viewer arguments").also {
it.component.font = it.component.font.deriveFont(Font.BOLD)
}
}
row {
cell(commandInput).columns(COLUMNS_LARGE).align(Align.FILL)
}
row {
link("Github document") {
Desktop.getDesktop().browse(URI("https://github.com/slint-ui/slint/tree/master/tools/viewer"))
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
package me.zhouxi.slint.preview
import com.intellij.openapi.util.SystemInfo
import me.zhouxi.slint.Plugin
import java.nio.file.Paths
/**
* @author zhouxi 2024/5/17
*/
object SlintViewer {
private val executable = if (SystemInfo.isWindows) {
"slint-viewer-windows.exe"
} else if (SystemInfo.isLinux) {
"slint-viewer-linux"
} else if (SystemInfo.isMac) {
"slint-viewer-macos"
} else {
throw RuntimeException("Unsupported OS")
}
fun executable(): String {
return Paths.get(Plugin.Path.toAbsolutePath().toString(), "slint-viewer", executable).toString()
}
}

View File

@@ -0,0 +1,47 @@
package me.zhouxi.slint.reference
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiReferenceContributor
import com.intellij.psi.PsiReferenceRegistrar
import me.zhouxi.slint.lang.psi.SlintTypes.*
import me.zhouxi.slint.reference.provider.ComponentReferenceProvider
import me.zhouxi.slint.reference.provider.ModuleLocationReferenceProvider
import me.zhouxi.slint.reference.provider.PropertyReferenceProvider
/**
* @author zhouxi 2024/5/17
*/
class SlintReferenceContributor : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
//component Reference
registrar.registerReferenceProvider(
psiElement(ReferenceIdentifier)
.withParent(
or(
psiElement(SubComponent),
psiElement(Component),
psiElement(InheritDeclaration),
psiElement(ImportedIdentifier),
psiElement(ExportIdentifier)
)
),
ComponentReferenceProvider()
)
//moduleLocation Reference
registrar.registerReferenceProvider(
psiElement(ModuleLocation),
ModuleLocationReferenceProvider()
)
//property binding
registrar.registerReferenceProvider(
psiElement().withParent(
or(
psiElement(PropertyBinding),
psiElement(ComponentBody)
)
),
PropertyReferenceProvider()
)
}
}

View File

@@ -0,0 +1,56 @@
package me.zhouxi.slint.reference.provider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.util.childrenOfType
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.*
import me.zhouxi.slint.lang.psi.utils.exportedElements
import me.zhouxi.slint.lang.psi.utils.importedElements
/**
* @author zhouxi 2024/5/17
*/
class ComponentReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return arrayOf(SlintComponentNameReference(element as SlintReferenceIdentifier))
}
class SlintComponentNameReference(element: SlintReferenceIdentifier) : PsiReferenceBase<PsiElement?>(element) {
override fun resolve(): PsiElement? {
val file = element.containingFile as SlintFile
//优先查找当前文件内的组件定义
val component = file.childrenOfType<SlintComponent>()
.firstOrNull { it.componentName?.textMatches(element) == true }
if (component != null) {
return component
}
// TODO psiTreeUtils
//然后是导出的组件 maybe component or externalName
val externalElement = file.exportedElements().firstOrNull { it.textMatches(element) }
if (externalElement != null) {
return externalElement
}
//maybe internalName or referencedIdentifier;
val element = file.importedElements().firstOrNull { it.textMatches(element) } ?: return null
if (element is SlintPsiReferencedIdentifier) {
val location = element.parentOfType<SlintImport>()?.moduleLocation ?: return null
val targetFile = location.reference?.resolve() as SlintFile? ?: return null
return targetFile.exportedElements()
.firstOrNull { it.nameIdentifier?.textMatches(element) == true }
}
return element
}
override fun getVariants(): Array<Any> {
val file = element.containingFile as SlintFile
val array: Array<Any> = file.childrenOfType<SlintImport>()
.mapNotNull { it.moduleLocation?.reference?.resolve() as SlintFile? }
.flatMap { it.exportedElements() }.toTypedArray()
return array
}
}
}

View File

@@ -0,0 +1,29 @@
package me.zhouxi.slint.reference.provider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintFile
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintInternalName
import me.zhouxi.slint.lang.psi.utils.exportedElements
/**
* @author zhouxi 2024/5/17
*/
class InternalNameReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return arrayOf(InternalNameReference(element as SlintInternalName))
}
class InternalNameReference(element: SlintInternalName) : PsiReferenceBase<SlintInternalName?>(element) {
override fun resolve(): PsiElement? {
val slintImport = element.parentOfType<SlintImport>() ?: return null
val slintFile = slintImport.moduleLocation?.reference?.resolve() as? SlintFile ?: return null
return slintFile.exportedElements().firstOrNull { it.textMatches(element) }
}
}
}

View File

@@ -0,0 +1,31 @@
package me.zhouxi.slint.reference.provider
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.*
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintModuleLocation
/**
* @author zhouxi 2024/5/17
*/
class ModuleLocationReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return arrayOf(ModuleLocationReference(element))
}
class ModuleLocationReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) {
override fun resolve(): PsiElement? {
val location = element as SlintModuleLocation
val filename = location.stringLiteral.text
if (filename.length < 3) return null
val directory = element.containingFile.originalFile.virtualFile?.parent ?: return null
val file = VfsUtil.findRelativeFile(directory, filename.substring(1, filename.length - 1)) ?: return null
return PsiManager.getInstance(element.project).findFile(file)
}
override fun calculateDefaultRangeInElement(): TextRange {
return TextRange(1, element.textLength - 1)
}
}
}

View File

@@ -0,0 +1,27 @@
package me.zhouxi.slint.reference.provider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.util.parentOfType
import com.intellij.psi.util.parentOfTypes
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintPropertyBinding
import me.zhouxi.slint.lang.psi.SlintSubComponent
import me.zhouxi.slint.lang.psi.utils.searchProperty
class PropertyReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return arrayOf(PropertyReference(element))
}
class PropertyReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) {
override fun resolve(): PsiElement? {
val binding = element.parentOfType<SlintPropertyBinding>() ?: return null
val parent = binding.parentOfTypes(SlintSubComponent::class, SlintComponent::class) ?: return null
return searchProperty(parent) { it.propertyName?.textMatches(element) == true }
}
}
}

View File

@@ -0,0 +1,56 @@
<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
<idea-plugin>
<id>me.zhouxi.intellij-slint</id>
<name>intellij-slint</name>
<vendor email="zhouxi0106@gmail.com" url="https://www.yourcompany.com"/>
<description><![CDATA[slint language support.]]></description>
<depends>com.intellij.modules.platform</depends>
<extensions defaultExtensionNs="com.intellij">
<fileType name="Slint File"
implementationClass="me.zhouxi.slint.lang.psi.SlintFileType"
fieldName="INSTANCE"
language="Slint"
extensions="slint"/>
<lang.parserDefinition language="Slint"
implementationClass="me.zhouxi.slint.lang.SlintParserDefinition"/>
<lang.syntaxHighlighter language="Slint"
implementationClass="me.zhouxi.slint.highlight.SlintSyntaxHighlighter"/>
<annotator implementationClass="me.zhouxi.slint.highlight.KeywordHighlightAnnotator"
language="Slint"/>
<lang.braceMatcher language="Slint"
implementationClass="me.zhouxi.slint.brace.SlintPairedBraceMatcher"/>
<lang.elementManipulator forClass="me.zhouxi.slint.lang.psi.SlintPsiReferencedIdentifier"
implementationClass="me.zhouxi.slint.reference.SlintElementNameManipulator"/>
<completion.contributor language="Slint"
implementationClass="me.zhouxi.slint.completion.SlintCompletionContributor"/>
<psi.referenceContributor language="Slint"
implementation="me.zhouxi.slint.reference.SlintReferenceContributor"/>
<lineIndentProvider implementation="me.zhouxi.slint.formatter.SlintLineIndentProvider"/>
<!-- <codeInsight.lineMarkerProvider language="Slint"-->
<!-- implementationClass="me.zhouxi.slint.preview.PreviewLineMarkerProvider"/>-->
<configurationType
implementation="me.zhouxi.slint.preview.PreviewConfigurationType"/>
<runConfigurationProducer implementation="me.zhouxi.slint.preview.PreviewRunConfigurationProducer"/>
<runLineMarkerContributor implementationClass="me.zhouxi.slint.preview.PreviewRunLineMarkerContributor"
language="Slint"/>
</extensions>
<actions>
<action id="slint.preview" class="me.zhouxi.slint.preview.PreviewAction"
text="Slint Viewer" description="Preview current slint file">
</action>
</actions>
</idea-plugin>

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.6632 55.828L48.7333 37.0309C48.7333 37.0309 50 36.3278 50 35.2182C50 33.7406 48.3981 33.2599 48.3981 33.2599L32.9557 27.355C32.4047 27.1462 31.6464 27.7312 32.3564 28.4728L37.4689 33.4165C37.4689 33.4165 38.889 34.765 38.889 35.6494C38.889 36.5338 38.017 37.322 38.017 37.322L19.4135 54.6909C18.7517 55.3089 19.6464 56.4294 20.6632 55.828Z" fill="#2379F4"/>
<path d="M43.3368 8.17339L15.2667 26.9677C15.2667 26.9677 14 27.6708 14 28.7804C14 30.258 15.6019 30.7387 15.6019 30.7387L31.0443 36.6464C31.5953 36.8524 32.3565 36.2674 31.6436 35.5286L26.5311 30.5684C26.5311 30.5684 25.111 29.2226 25.111 28.3355C25.111 27.4483 25.983 26.6628 25.983 26.6628L44.5752 9.30769C45.2483 8.68973 44.3565 7.56916 43.3368 8.17339Z" fill="#2379F4"/>
</svg>

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,55 @@
import com.intellij.lexer.FlexAdapter;
import com.intellij.lexer.Lexer;
import com.intellij.testFramework.LexerTestCase;
import lombok.extern.slf4j.Slf4j;
import me.zhouxi.slint.lang.lexer.SlintLexer;
import org.jetbrains.annotations.NotNull;
import org.junit.Rule;
import org.junit.rules.Stopwatch;
import org.junit.runner.Description;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
/**
* @author zhouxi 2024/4/30
*/
@Slf4j
public class LexerTest extends LexerTestCase {
@Override
protected Lexer createLexer() {
return new FlexAdapter(new SlintLexer());
}
@Override
protected String getDirPath() {
return "src/test/resources/slint";
}
public void test() throws IOException {
var source = Files.readString(Paths.get("src/test/resources/slint/main.slint"));
doTest(source);
}
protected @NotNull String getPathToTestDataFile(String extension) {
return "src/test/resources/slint/lexer/output" + extension;
}
@Rule
public Stopwatch stopwatch = new Stopwatch() {
@Override
protected void succeeded(long nanos, Description description) {
log.info("{} succeeded, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos));
}
@Override
protected void failed(long nanos, Throwable e, Description description) {
log.info("{} failed, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos));
}
};
}

View File

@@ -0,0 +1,33 @@
import com.intellij.testFramework.ParsingTestCase;
import me.zhouxi.slint.lang.SlintParserDefinition;
import org.apache.commons.lang3.time.StopWatch;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* @author zhouxi 2024/4/30
*/
public class ParserTest extends ParsingTestCase {
public ParserTest() {
super("", ".slint", new SlintParserDefinition());
}
@Override
protected @NotNull String getTestDataPath() {
return "src/test/resources/slint/parser";
}
public void test() throws IOException {
final var path = Paths.get("src/test/resources/slint/main.slint");
var source = Files.readString(path);
StopWatch started = StopWatch.createStarted();
for (int i = 0; i < 50000; i++) {
doCodeTest(source);
}
started.stop();
System.out.println(started.getNanoTime()/50000);
}
}

View File

@@ -0,0 +1,5 @@
/* dsadas */
/* dsadas */
"dasdasd\""
"dada"
"