fix: 语法问题

This commit is contained in:
me
2024-06-06 15:20:08 +08:00
parent 39a81fcb5d
commit 1b9faa6f09
45 changed files with 578 additions and 444 deletions

View File

@@ -1,7 +1,7 @@
plugins { plugins {
id("java") id("java")
id("org.jetbrains.kotlin.jvm") version "1.9.23" id("org.jetbrains.kotlin.jvm") version "1.9.23"
id("org.jetbrains.intellij") version "1.17.2" id("org.jetbrains.intellij") version "1.17.3"
id("org.jetbrains.grammarkit") version "2022.3.2.2" id("org.jetbrains.grammarkit") version "2022.3.2.2"
id("de.undercouch.download") version "5.6.0" id("de.undercouch.download") version "5.6.0"
} }
@@ -9,16 +9,16 @@ val slintVersion: String by project
//github url //github url
val slintGithubUrl = "https://github.com/slint-ui/slint/releases/download/v$slintVersion" val slintGithubUrl = "https://github.com/slint-ui/slint/releases/download/v$slintVersion"
//download dir //download dir
val slintViewerDownloadDir = "${layout.buildDirectory.get()}/slint-viewer" val slintViewerDownloadDir = "${layout.buildDirectory.get()}/slint-lsp"
//decompression path //decompression path
val slintViewerBinaryDir = "${layout.buildDirectory.get()}/slint-viewer/$slintVersion" val slintViewerBinaryDir = "${layout.buildDirectory.get()}/slint-lsp/$slintVersion"
val grammarGeneratedRoot = "${layout.buildDirectory.get()}/generated/sources/grammar/" val grammarGeneratedRoot = "${layout.buildDirectory.get()}/generated/sources/grammar/"
//github filename-> decompression name //github filename-> decompression name
val slintViewerFilenames = arrayOf( val slintViewerFilenames = arrayOf(
"slint-viewer-linux.tar.gz" to "slint-viewer-linux", "slint-lsp-linux.tar.gz" to "slint-lsp-linux",
"slint-viewer-macos.tar.gz" to "slint-viewer-macos", "slint-lsp-macos.tar.gz" to "slint-lsp-macos",
"slint-viewer-windows.zip" to "slint-viewer-windows.exe", "slint-lsp-windows.zip" to "slint-lsp-windows.exe",
) )
sourceSets { sourceSets {
@@ -36,22 +36,17 @@ repositories {
} }
intellij { intellij {
version.set("IC-2023.2.5") version.set("IU-2024.1")
sandboxDir.set("idea-sandbox") sandboxDir.set("idea-sandbox")
plugins.set(listOf("java","Kotlin")) plugins.set(listOf("java"))
} }
dependencies { dependencies {
compileOnly("org.projectlombok:lombok:1.18.32")
annotationProcessor("org.projectlombok:lombok:1.18.32")
testCompileOnly("org.projectlombok:lombok:1.18.32")
testAnnotationProcessor("org.projectlombok:lombok:1.18.32")
// implementation("org.jetbrains:grammar-kit:2022.3.2")
} }
tasks { tasks {
buildPlugin { buildPlugin {
//copy slint-viewer to plugin dir //copy slint-lsp to plugin dir
from(slintViewerBinaryDir) { from(slintViewerBinaryDir) {
into("/slint-viewer") into("/slint-lsp")
} }
} }
withType<JavaExec>().configureEach { withType<JavaExec>().configureEach {
@@ -61,11 +56,11 @@ tasks {
} }
runIde { runIde {
//copy slint-viewer //copy slint-lsp
doFirst { doFirst {
copy { copy {
from(slintViewerBinaryDir) from(slintViewerBinaryDir)
into("idea-sandbox/plugins/intellij-slint/slint-viewer") into("idea-sandbox/plugins/intellij-slint/slint-lsp")
} }
} }
} }
@@ -100,7 +95,7 @@ tasks {
jvmArgs("-Djava.awt.headless=true") jvmArgs("-Djava.awt.headless=true")
} }
register("downloadSlintViewer") { register("downloadSlintResource") {
doFirst { doFirst {
slintViewerFilenames.forEach { fileDesc -> slintViewerFilenames.forEach { fileDesc ->
val (filename, renamed) = fileDesc val (filename, renamed) = fileDesc
@@ -118,7 +113,7 @@ tasks {
} }
from(fileTree) from(fileTree)
//include executable file path //include executable file path
include("slint-viewer/slint-viewer*") include("slint-lsp/slint-lsp*")
eachFile { eachFile {
//rename to platform name //rename to platform name
this.relativePath = RelativePath(true, renamed) this.relativePath = RelativePath(true, renamed)

View File

@@ -56,7 +56,7 @@
Percent = "%" Percent = "%"
Whitespace = "regexp:(\s+)" Whitespace = "regexp:(\s+)"
NumberLiteral = "regexp:\d+(\.(\d+)?)?([a-z]+|%)?" NumberLiteral = "regexp:\d+(\.(\d+)?)?([a-z]+|%)?"
Identifier = "regexp:^[a-zA-Z_][A-Za-z0-9\-_]*" IDENTIFIER = "regexp:^[a-zA-Z_][A-Za-z0-9\-_]*"
ColorLiteral = "regexp:#([a-zA-Z0-9]+)" ColorLiteral = "regexp:#([a-zA-Z0-9]+)"
StringLiteral = 'regexp:(^"[^"\r\n]*")' StringLiteral = 'regexp:(^"[^"\r\n]*")'
LineComment = 'regexp:^//[^\r\n]*' LineComment = 'regexp:^//[^\r\n]*'
@@ -70,26 +70,25 @@ private recoverTopElement ::= !('component' | 'struct' | 'enum' | 'global'| 'exp
private DocumentElement ::= Import | Struct | Enum | GlobalSingleton | Component | Export { private DocumentElement ::= Import | Struct | Enum | GlobalSingleton | Component | Export {
recoverWhile=recoverTopElement recoverWhile=recoverTopElement
} }
GlobalSingleton ::= ExportKeyword? GlobalKeyword ComponentName '{' ComponentElement* '}' { GlobalSingleton ::= ExportKeyword? GlobalKeyword Identifier '{' ComponentElement* '}' {
pin=2 pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
} }
//import 定义 //import 定义
Import ::= ImportKeyword (ImportElement|ImportResource)';'{ Import ::= ImportKeyword (ImportElement|ModuleRef)';'{
pin=1 pin=1
elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.slintImport" elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.slintImport"
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportStub" stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportStub"
extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiElementImpl<?>" extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiElementImpl<?>"
} }
ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* '}' FromKeyword ModuleLocation{ ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* '}' FromKeyword ModuleRef{
pin=1 pin=1
} }
ImportResource ::= ModuleLocation
// ABc as Def // ABc as Def
ImportSpecifier ::= ReferenceIdentifier ImportAlias?{ ImportSpecifier ::= ComponentRef ImportAlias?{
pin=1 pin=1
elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.importSpecifier" elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.importSpecifier"
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportSpecifierStub" stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportSpecifierStub"
@@ -97,21 +96,21 @@ ImportSpecifier ::= ReferenceIdentifier ImportAlias?{
} }
private AliasNameRecover::=!(','|'}'|';') private AliasNameRecover::=!(','|'}'|';')
ImportAlias ::= AsKeyword InternalName { ImportAlias ::= AsKeyword Identifier {
pin=1 pin=1
recoverWhile=AliasNameRecover recoverWhile=AliasNameRecover
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
} }
//Struct 定义 //Struct 定义
Struct ::= ExportKeyword? StructKeyword TypeName (':=')? StructBody { Struct ::= ExportKeyword? StructKeyword Identifier (':=')? StructBody {
pin=2 pin=2
} }
private StructBody ::= '{' FieldDeclarations? '}'{ private StructBody ::= '{' FieldDeclarations? '}'{
pin=1 pin=1
} }
//EnumDeclaration //EnumDeclaration
Enum ::= ExportKeyword? EnumKeyword EnumName '{' (EnumValue (','EnumValue)*','? )? '}'{ Enum ::= ExportKeyword? EnumKeyword Identifier '{' (Identifier (','Identifier)*','? )? '}'{
pin=2 pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
@@ -125,19 +124,19 @@ Export ::= ExportKeyword (ExportType | ExportModule) {
ExportType ::= '{' ExportSpecifier (','ExportSpecifier)* ','? '}'{ ExportType ::= '{' ExportSpecifier (','ExportSpecifier)* ','? '}'{
pin=1 pin=1
} }
ExportSpecifier::= ReferenceIdentifier ExportAlias?{ ExportSpecifier::= ComponentRef ExportAlias?{
recoverWhile=AliasNameRecover recoverWhile=AliasNameRecover
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
} }
ExportAlias ::= AsKeyword ExternalName{ ExportAlias ::= AsKeyword Identifier{
pin=1 pin=1
} }
ExportModule ::= '*' FromKeyword ModuleLocation ';'{ ExportModule ::= '*' FromKeyword ModuleRef ';'{
pin=1 pin=1
} }
Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration? '{' ComponentElement* '}' { Component ::= ExportKeyword? ComponentKeyword Identifier InheritDeclaration? '{' ComponentElement* '}' {
pin=2 pin=2
implements=[ implements=[
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement" "me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
@@ -148,7 +147,7 @@ Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration?
} }
//组件定义 //组件定义
//private LegacyComponent ::= (ComponentName|NamedIdentifier ':=' ) ComponentBody //private LegacyComponent ::= (ComponentName|NamedIdentifier ':=' ) ComponentBody
InheritDeclaration ::= InheritsKeyword ReferenceIdentifier { InheritDeclaration ::= InheritsKeyword ComponentRef {
pin=1 pin=1
recoverWhile=recoverInherit recoverWhile=recoverInherit
} }
@@ -157,18 +156,18 @@ private recoverInherit::=!('{')
private ComponentElement ::=ChildrenPlaceholder| Property | Callback private ComponentElement ::=ChildrenPlaceholder| Property | Callback
| Function | PropertyAnimation | CallbackConnection | Transitions | Function | PropertyAnimation | CallbackConnection | Transitions
| PropertyChanged | PropertyChanged
| States | TwoWayBinding|PropertyBinding | ConditionalElement | States | TwoWayBinding | ConditionalElement
| RepetitionElement | SubComponent { | RepetitionElement | SubComponent | PropertyBinding {
recoverWhile(".*")=recoverWhileForComponentBody recoverWhile=recoverWhileForComponentBody
} }
private recoverWhileForComponentBody::= !('{'|';'|'}'|'@'|GenericIdentifier) private recoverWhileForComponentBody::= !('{'|';'|'}'|'@'|Identifier)
ChildrenPlaceholder ::= '@' 'children'{ ChildrenPlaceholder ::= '@' 'children'{
pin=1 pin=1
} }
//--------------------------------PropertyDeclaration Start---------------------------------------------------- //--------------------------------PropertyDeclaration Start----------------------------------------------------
// 属性定义 in property <type> name: value / in property <type> name <=> value // 属性定义 in property <type> name: value / in property <type> name <=> value
Property ::= PropertyModifier? PropertyKeyword ('<' Type '>')? PropertyName(PropertyValue|PropertyTwoWayBindingValue|';'){ Property ::= PropertyModifier? PropertyKeyword ('<' Type '>')? Identifier (PropertyValue|PropertyTwoWayBindingValue|';'){
pin=2 pin=2
implements=[ implements=[
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement" "me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
@@ -181,7 +180,7 @@ PropertyModifier ::= InKeyword|OutKeyword|InOutKeyword|PrivateKeyword
private PropertyValue::= ':' BindingStatement { private PropertyValue::= ':' BindingStatement {
pin=1 pin=1
} }
private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyNameReference ';' { private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyRef ';' {
pin=1 pin=1
} }
//--------------------------------PropertyChanged Start---------------------------------------------------- //--------------------------------PropertyChanged Start----------------------------------------------------
@@ -190,29 +189,29 @@ PropertyChanged ::= ChangedKeyword LocalVariable '=>' CodeBlock{
} }
//--------------------------------CallbackDeclaration Start---------------------------------------------------- //--------------------------------CallbackDeclaration Start----------------------------------------------------
// 回调定义 pure callback abc()->int; callback abc; callback(..);callback()->type; // 回调定义 pure callback abc()->int; callback abc; callback(..);callback()->type;
Callback ::= PureKeyword? CallbackKeyword FunctionName CallbackBinding? ';'{ Callback ::= PureKeyword? CallbackKeyword Identifier CallbackBinding? ';'{
pin=2 pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
} }
//回调参数定义 //回调参数定义
CallbackBinding::= CallbackArgument | ('<=>'QualifiedPropertyNameReference) CallbackBinding::= CallbackArgument | ('<=>'QualifiedPropertyRef)
//入参定义 //入参定义
CallbackArgument ::= '(' (Type (','Type)* ','? )? ')' ReturnType?{ CallbackArgument ::= '(' (Type (','Type)* ','? )? ')' ReturnType?{
pin=1 pin=1
} }
private ReturnType ::= '->' (NamedType|ArrayType){ private ReturnType ::= '->' (QualifiedTypeRef|ArrayType){
pin=1 pin=1
} }
//--------------------------------TwoWayBindingDeclaration Start---------------------------------------------------- //--------------------------------TwoWayBindingDeclaration Start----------------------------------------------------
//组件双向绑定 //组件双向绑定
TwoWayBinding ::= ReferenceIdentifier '<=>' QualifiedPropertyNameReference ';' { TwoWayBinding ::= PropertyRef '<=>' QualifiedPropertyRef ';' {
pin=2 pin=2
} }
//--------------------------------FunctionDeclaration Start---------------------------------------------------- //--------------------------------FunctionDeclaration Start----------------------------------------------------
//函数定义 protected? pure? function f() //函数定义 protected? pure? function f()
Function ::= FunctionModifiers? FunctionKeyword FunctionName FunctionArguments ReturnType? CodeBlock{ Function ::= FunctionModifiers? FunctionKeyword Identifier FunctionArguments ReturnType? CodeBlock{
pin=2 pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
@@ -225,7 +224,7 @@ private FunctionArguments ::= '(' FieldDeclarations* ')'{
//--------------------------------CallbackConnectionDeclaration Start--------------------------------------------------- //--------------------------------CallbackConnectionDeclaration Start---------------------------------------------------
//回调绑定定义 abc()=> {} //回调绑定定义 abc()=> {}
CallbackConnection ::= FunctionReference CallbackConnectionArguments? '=>' CodeBlock ';'?{ CallbackConnection ::= FunctionRef CallbackConnectionArguments? '=>' CodeBlock ';'?{
pin=3 pin=3
} }
private CallbackConnectionArguments ::= '(' (LocalVariable (',' LocalVariable)* ','?)? ')' private CallbackConnectionArguments ::= '(' (LocalVariable (',' LocalVariable)* ','?)? ')'
@@ -245,10 +244,11 @@ RepetitionIndex ::= '[' LocalVariable ']'{
} }
//--------------------------------SubElementDeclaration Start--------------------------------------------------- //--------------------------------SubElementDeclaration Start---------------------------------------------------
//子组件结构元素定义 //子组件结构元素定义
SubComponent ::= (PropertyName ':=')? ReferenceIdentifier '{' ComponentElement* '}'{ SubComponent ::= (Identifier ':=')? ComponentRef '{' ComponentElement* '}'{
pin=3 pin=3
recoverWhile=recoverWhileForComponentBody // recoverWhile=recoverForBrace
} }
//private recoverForBrace::=!'}'
//--------------------------------TransitionsDeclaration Start--------------------------------------------------- //--------------------------------TransitionsDeclaration Start---------------------------------------------------
//过渡绑定 //过渡绑定
@@ -272,7 +272,7 @@ State ::= LocalVariable StateCondition? ':' '{' StateItem* '}' {
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
recoverWhile=recoverForRBracket recoverWhile=recoverForRBracket
} }
private recoverForRBracket::=!(']'|'}'|';'|GenericIdentifier) private recoverForRBracket::=!(']'|'}'|';'|Identifier)
StateCondition ::= WhenKeyword Expression { StateCondition ::= WhenKeyword Expression {
pin=1 pin=1
} }
@@ -283,12 +283,11 @@ StateTransition ::= (InKeyword|OutKeyword) ':' '{' PropertyAnimation* '}'{
} }
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
//类型定义 //类型定义
Type ::= NamedType | UnnamedType | ArrayType Type ::= QualifiedTypeRef | UnnamedType | ArrayType
private NamedType ::= QualifiedTypeNameReference
ArrayType ::= '[' Type ']' ArrayType ::= '[' Type ']'
UnnamedType ::= '{' FieldDeclarations* '}' UnnamedType ::= '{' FieldDeclarations* '}'
private FieldDeclarations ::= FieldDeclaration (',' FieldDeclaration)* ','? private FieldDeclarations ::= FieldDeclaration (',' FieldDeclaration)* ','?
private FieldDeclaration ::= PropertyName ':' Type private FieldDeclaration ::= Identifier ':' Type
//代码块 //代码块
@@ -320,18 +319,17 @@ ElseStatement ::= ElseKeyword CodeBlock {
pin=1 pin=1
} }
//动画定义 //动画定义
PropertyAnimation ::= AnimateKeyword ('*'| (QualifiedPropertyNameReference (',' QualifiedPropertyNameReference)*)) '{' QualifiedNamePropertyBinding*'}'{ PropertyAnimation ::= AnimateKeyword ('*'| (QualifiedPropertyRef (',' QualifiedPropertyRef)*)) '{' QualifiedNamePropertyBinding*'}'{
pin=1 pin=1
} }
//组件属性绑定 name: xxx ; name : {}; name : {} //组件属性绑定 name: xxx ; name : {}; name : {}
//只有对象定义和表达式需要 ; //只有对象定义和表达式需要 ;
private QualifiedNamePropertyBinding::= QualifiedPropertyNameReference ':' BindingStatement{ private QualifiedNamePropertyBinding::= QualifiedPropertyRef ':' BindingStatement{
pin=2 pin=2
} }
PropertyBinding ::= ReferenceIdentifier ':' BindingStatement{ PropertyBinding ::= PropertyRef ':' BindingStatement{
pin=2
} }
private WhileIdentifier::=!('}'|';'|GenericIdentifier) private WhileIdentifier::=!('}'|';'|Identifier)
//优先尝试表达式解析 {}属于代码块 {name:xx}属于表达式那么需要预测后面两个token,第二个token不是':'的情况下才是代码块 //优先尝试表达式解析 {}属于代码块 {name:xx}属于表达式那么需要预测后面两个token,第二个token不是':'的情况下才是代码块
//所以优先判断对象创建判断,然后进行代码块判断,最后进行表达式 //所以优先判断对象创建判断,然后进行代码块判断,最后进行表达式
//代码块分号可选 //代码块分号可选
@@ -365,7 +363,7 @@ Expression ::= AtExpression
{ {
recoverWhile=recoverForExpression recoverWhile=recoverForExpression
} }
private recoverForExpression ::= !(Expression|GenericIdentifier|';') private recoverForExpression ::= !(Expression|Identifier|';')
private BinaryExpression ::= SelfAssignExpression | ComparisonExpression | BooleanExpression private BinaryExpression ::= SelfAssignExpression | ComparisonExpression | BooleanExpression
//加法表达式 //加法表达式
@@ -446,7 +444,7 @@ ParenthesizedExpression ::= '(' Expression ')'{
pin=2 pin=2
} }
//fake PropertySuffixExpression::= Expression? '.' GenericIdentifier //fake PropertySuffixExpression::= Expression? '.' GenericIdentifier
PropertyExpression ::= Expression '.' GenericIdentifier { PropertyExpression ::= Expression '.' Identifier {
// pin=2 extends=PropertySuffixExpression elementType=PropertySuffixExpression // pin=2 extends=PropertySuffixExpression elementType=PropertySuffixExpression
} }
FunctionInvocationExpression ::= Expression '(' InvocationArguments? ')'{ FunctionInvocationExpression ::= Expression '(' InvocationArguments? ')'{
@@ -465,7 +463,7 @@ ArrayCreationExpression ::= '[' ArrayElements* ']'{
private ArrayElements ::= Expression (','Expression)* ','? private ArrayElements ::= Expression (','Expression)* ','?
LiteralExpression ::= Numbers | RawStringLiteral | GenericIdentifier | RawColorLiteral LiteralExpression ::= Numbers | RawStringLiteral | Identifier | RawColorLiteral
//------------------------------------------------- //-------------------------------------------------
@@ -477,7 +475,7 @@ ObjectCreationExpression ::= '{' ObjectPropertyBindings '}'{
private ObjectPropertyBindings ::= ObjectPropertyBinding (','ObjectPropertyBinding)* ','?{ private ObjectPropertyBindings ::= ObjectPropertyBinding (','ObjectPropertyBinding)* ','?{
pin=1 pin=1
} }
private ObjectPropertyBinding ::= PropertyName ':' Expression{ private ObjectPropertyBinding ::= Identifier ':' Expression{
pin=2 pin=2
recoverWhile=recover recoverWhile=recover
} }
@@ -513,55 +511,35 @@ PrivateKeyword::='private'
ReturnKeyword::='return' ReturnKeyword::='return'
//---------NamedIdentifier ,简化PsiTree----------------------------------- //---------NamedIdentifier ,简化PsiTree-----------------------------------
//noinspection BnfUnusedRule 用于标记命名节点对应的identifier //noinspection BnfUnusedRule 用于标记命名节点对应的identifier
Named ::= PropertyName | TypeName |ExternalName | InternalName|ComponentName|FunctionName{ LocalVariable ::= Identifier{
pin=1
}
{
extends("PropertyName|TypeName|ComponentName|FunctionName|InternalName|ExternalName")=Named
}
LocalVariable ::= GenericIdentifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl" extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"] implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
} }
FunctionName ::= GenericIdentifier fake Referred::=Identifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintRefIdentifierImpl"
ComponentName ::=GenericIdentifier
InternalName ::= GenericIdentifier
PropertyName ::= GenericIdentifier
TypeName ::= GenericIdentifier
EnumName ::= GenericIdentifier
EnumValue ::=GenericIdentifier
ExternalName ::=GenericIdentifier
ReferenceIdentifier::=GenericIdentifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
} }
//----------UnnamedIdentifier------------------ ComponentRef::=Identifier{
extends=Referred
PropertyNameReference ::= GenericIdentifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
} }
FunctionReference ::=GenericIdentifier{ QualifiedPropertyRef ::= PropertyRef ('.' PropertyRef)*{
extends=PropertyRef
} }
QualifiedPropertyNameReference ::= PropertyNameReference ('.' PropertyNameReference)*{ PropertyRef ::= Identifier{
extends=PropertyNameReference extends=Referred
} }
TypeNameReference::=GenericIdentifier QualifiedTypeRef::= TypeRef ('.' TypeRef)*{
extends=TypeRef
QualifiedTypeNameReference ::= TypeNameReference ('.' TypeNameReference)*{
extends=TypeNameReference
} }
ModuleLocation ::= RawStringLiteral{ TypeRef::=Identifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl" extends=Referred
}
FunctionRef::=Identifier{
extends=Referred
}
ModuleRef::=RawStringLiteral{
extends=Referred
} }
// //
//noinspection BnfSuspiciousToken //noinspection BnfSuspiciousToken
@@ -571,7 +549,7 @@ private RawStringLiteral ::= StringLiteral
private Numbers ::= NumberLiteral private Numbers ::= NumberLiteral
//noinspection BnfSuspiciousToken //noinspection BnfSuspiciousToken
private GenericIdentifier::= Identifier private Identifier::= IDENTIFIER
//noinspection BnfSuspiciousToken //noinspection BnfSuspiciousToken
private RawColorLiteral ::= ColorLiteral private RawColorLiteral ::= ColorLiteral

View File

@@ -73,7 +73,7 @@ LINECOMMENT=\/\/[^\r\n]*
"|" { return Pipe; } "|" { return Pipe; }
"%" { return Percent; } "%" { return Percent; }
{NUMBERLITERAL} { return NumberLiteral; } {NUMBERLITERAL} { return NumberLiteral; }
{IDENTIFIER} { return Identifier; } {IDENTIFIER} { return IDENTIFIER; }
{COLORLITERAL} { return ColorLiteral; } {COLORLITERAL} { return ColorLiteral; }
{LINECOMMENT} { return LineComment; } {LINECOMMENT} { return LineComment; }
} }

View File

@@ -1,68 +0,0 @@
{
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.impl.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]*?\*/'
]
}
Document ::= Binding*
Binding ::=Identifier ':' Identifier ';'{
pin=2
recoverWhile=while
}
private while::=!(';'|Identifier)

View File

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

View File

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

View File

@@ -3,12 +3,12 @@ package me.zhouxi.slint.lang.psi.impl;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import me.zhouxi.slint.lang.psi.SlintPsiReferencedIdentifier; import me.zhouxi.slint.lang.psi.SlintPsiRefIdentifier;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public abstract class SlintReferencedIdentifierImpl extends SlintPsiElementImpl implements SlintPsiReferencedIdentifier { public abstract class SlintRefIdentifierImpl extends SlintPsiElementImpl implements SlintPsiRefIdentifier {
public SlintReferencedIdentifierImpl(@NotNull ASTNode node) { public SlintRefIdentifierImpl(@NotNull ASTNode node) {
super(node); super(node);
} }

View File

@@ -9,9 +9,9 @@ import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubElement
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import me.zhouxi.slint.lang.createIdentifier import me.zhouxi.slint.lang.createIdentifier
import me.zhouxi.slint.lang.psi.SlintNamed
import me.zhouxi.slint.lang.psi.SlintPsiElement import me.zhouxi.slint.lang.psi.SlintPsiElement
import me.zhouxi.slint.lang.psi.SlintPsiNamedElement import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
import me.zhouxi.slint.lang.psi.SlintTypes
/** /**
* @author zhouxi 2024/5/23 * @author zhouxi 2024/5/23
@@ -24,7 +24,7 @@ abstract class SlintStubBasedPsiNamedElementImpl<T : StubElement<out PsiElement>
constructor(stub: T, nodeType: IStubElementType<*, *>) : super(stub, nodeType) constructor(stub: T, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
override fun getNameIdentifier(): PsiElement? { override fun getNameIdentifier(): PsiElement? {
return findChildByClass(SlintNamed::class.java) return findChildByType(SlintTypes.IDENTIFIER)
} }
override fun getName(): String? { override fun getName(): String? {

View File

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

View File

@@ -19,12 +19,11 @@ class SlintPairedBraceMatcher : PairedBraceMatcher {
return 0 return 0
} }
companion object { }
val Pair: Array<BracePair> = arrayOf( val Pair: Array<BracePair> = arrayOf(
BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true), BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true),
BracePair(SlintTypes.LParent, SlintTypes.RParent, true), BracePair(SlintTypes.LParent, SlintTypes.RParent, true),
BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true), BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true),
BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true) BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true)
) )
}
}

View File

@@ -19,12 +19,12 @@ class SlintCompletionAutoPopupHandler : TypedHandlerDelegate() {
override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result { override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {
val lookup = LookupManager.getActiveLookup(editor) as LookupImpl? val lookup = LookupManager.getActiveLookup(editor) as LookupImpl?
val phase = CompletionServiceImpl.getCompletionPhase() val phase = CompletionServiceImpl.completionPhase
if (LOG.isDebugEnabled) { if (LOG.isDebugEnabled) {
LOG.debug("checkAutoPopup: character=$charTyped;") LOG.debug("checkAutoPopup: character=$charTyped;")
LOG.debug("phase=$phase") LOG.debug("phase=$phase")
LOG.debug("lookup=$lookup") LOG.debug("lookup=$lookup")
LOG.debug("currentCompletion=" + CompletionServiceImpl.getCompletionService().currentCompletion) LOG.debug("currentCompletion=" + CompletionServiceImpl.completionService.currentCompletion)
} }
if (lookup != null) { if (lookup != null) {

View File

@@ -4,16 +4,20 @@ import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.completion.CompletionType
import me.zhouxi.slint.completion.provider.* import me.zhouxi.slint.completion.provider.*
import me.zhouxi.slint.completion.provider.ComponentNameProvider
import me.zhouxi.slint.completion.provider.SubComponentNameProvider
class SlintCompletionContributor : CompletionContributor() { class SlintCompletionContributor : CompletionContributor() {
init { init {
extend(TopKeywordProvider) extend(TopElementProvider)
extend(BasicTypeProvider) extend(BasicTypeProvider)
extend(AtChildrenCompletionProvider) extend(AtChildrenCompletionProvider)
extend(ComponentElementKeywordProvider) extend(ComponentElementKeywordProvider)
extend(ComponentNameProvider) extend(ComponentNameProvider)
extend(PropertyBindingProvider) extend(PropertyBindingProvider)
extend(InheritsCompletionProvider) extend(InheritsCompletionProvider)
extend(ExportElementProvider)
extend(SubComponentNameProvider)
} }
private fun extend( private fun extend(

View File

@@ -8,7 +8,7 @@ import com.intellij.psi.PsiElement
/** /**
* @author zhouxi 2024/5/29 * @author zhouxi 2024/5/29
*/ */
abstract class AbstractSlintCompletionProvider<V : CompletionParameters?> : CompletionProvider<V>() { abstract class AbstractSlintCompletionProvider<V : CompletionParameters> : CompletionProvider<V>() {
abstract fun pattern(): ElementPattern<out PsiElement> abstract fun pattern(): ElementPattern<out PsiElement>
} }

View File

@@ -22,7 +22,7 @@ object ComponentElementKeywordProvider : AbstractSlintCompletionProvider<Complet
"in", "out", "in-out", "callback", "in", "out", "in-out", "callback",
"property", "private", "changed", "property", "private", "changed",
"states", "transitions", "function" "states", "transitions", "function"
).map { LookupElementBuilder.create(it) } ).map { LookupElementBuilder.create(it).withBoldness(true) }
override fun pattern(): ElementPattern<out PsiElement> { override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(psiElement(Component)) return psiElement().withParent(psiElement(Component))

View File

@@ -1,34 +1,18 @@
package me.zhouxi.slint.completion.provider package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementDecorator.withInsertHandler
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.patterns.ElementPattern import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.childrenOfType
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.createComma import me.zhouxi.slint.lang.psi.SlintTypes.ExportSpecifier
import me.zhouxi.slint.lang.createImport
import me.zhouxi.slint.lang.createImportSpecifier
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintInheritDeclaration
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration
import me.zhouxi.slint.lang.psi.extension.importNames import me.zhouxi.slint.lang.psi.extension.relativePathOf
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
object ComponentNameProvider : AbstractSlintCompletionProvider<CompletionParameters>() { object ComponentNameProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
override fun addCompletions( override fun addCompletions(
@@ -37,88 +21,21 @@ object ComponentNameProvider : AbstractSlintCompletionProvider<CompletionParamet
result: CompletionResultSet result: CompletionResultSet
) { ) {
val project = parameters.position.project val project = parameters.position.project
val components = arrayListOf<SlintComponent>() val components = searchComponent(project) { result.prefixMatcher.prefixMatches(it) }
StubIndex.getInstance().processAllKeys(StubIndexKeys.Component, project) {
if (result.prefixMatcher.prefixMatches(it)) {
val elements = StubIndex.getElements(
StubIndexKeys.Component,
it,
project,
projectScope(project),
SlintComponent::class.java
)
components.addAll(elements)
}
true
}
val lookups = components.map { val lookups = components.map {
val targetFile = it.containingFile.originalFile
val currentFile = parameters.position.containingFile.originalFile.virtualFile val currentFile = parameters.position.containingFile.originalFile.virtualFile
val path = VfsUtil.findRelativePath(currentFile, targetFile.virtualFile, '/') LookupElementBuilder.create(it).withTypeText(it.relativePathOf(currentFile)).withIcon(AllIcons.Nodes.Class)
val builder = LookupElementBuilder.create(it).withTypeText(path).withIcon(AllIcons.Nodes.Class)
withInsertHandler(builder, ComponentInsertHandler(targetFile, path))
} }
result.addAllElements(lookups) result.addAllElements(lookups)
} }
class ComponentInsertHandler(
private val targetFile: PsiFile,
private val path: String?
) : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
//insert '{}'
val caretOffset: Int = context.editor.caretModel.offset
val elementAt = context.file.findElementAt(caretOffset)
if (elementAt?.prevSibling !is SlintInheritDeclaration) {
context.document.insertString(caretOffset, " { }")
context.editor.caretModel.moveToOffset(caretOffset + 3)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
}
val component = item.psiElement as SlintComponent
if (component.containingFile.isEquivalentTo(context.file)) {
return
}
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
val target = import.importElement?.moduleLocation?.reference?.resolve()
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
}
if (targetImport == null) {
//不存在对指定文件的Import插入
val import = createImport(context.project, component.componentName!!.text, path!!)
context.file.addBefore(import, context.file.firstChild)
return
}
//如果导入的Name里面包含了这个ComponentName
if (targetImport.importNames().any { it.textMatches(component.componentName!!) }) {
return
}
//引入的文件存在,但是没有导入对应组件
val importElement = targetImport.importElement!!
val specifier = createImportSpecifier(context.project, component.componentName!!.text)
val last = importElement.importSpecifierList.lastOrNull()
//不存在前置节点
if (last == null) {
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
val element = PsiTreeUtil.nextVisibleLeaf(last)
if (!element!!.textMatches(",")) {
val comma = importElement.addAfter(createComma(context.project), last)
importElement.addAfter(specifier, comma)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
importElement.addAfter(specifier, element)
}
}
override fun pattern(): ElementPattern<out PsiElement> { override fun pattern(): ElementPattern<out PsiElement> {
return or( return psiElement()
psiElement().withParent(psiElement(Component)).andNot(AtChildrenCompletionProvider.pattern()), .withSuperParent(
psiElement().withSuperParent(2, psiElement(InheritDeclaration)) 2, or(
psiElement(InheritDeclaration),
psiElement(ExportSpecifier)
)
) )
} }
} }

View File

@@ -0,0 +1,60 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder.create
import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.impl.ConstantNode
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
/**
* @author zhouxi 2024/6/6
*/
object ExportElementProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withSuperParent(2, psiElement(SlintFileElementType))
.andNot(psiElement().withParent(psiElement(Component)))
}
val lookup = arrayOf(
create("export "),
create("export ").withTailText(" {... }").withInsertHandler(MyInsertHandler())
).map { it.withBoldness(true) }
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(lookup)
}
class MyInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val manager = TemplateManager.getInstance(context.project)
val template = manager.createTemplate("", "", "{ \$Name$ }").apply {
isToReformat = true
addVariable("Name", ConstantNode(""), true)
}
manager.startTemplate(context.editor, template, object : TemplateEditingAdapter() {
override fun templateFinished(template: Template, brokenOff: Boolean) {
context.editor.caretModel.moveToOffset(context.tailOffset - 2);
}
})
}
}
}

View File

@@ -0,0 +1,5 @@
package me.zhouxi.slint.completion.provider
/**
* @author zhouxi 2024/6/6
*/

View File

@@ -15,7 +15,7 @@ import me.zhouxi.slint.lang.psi.SlintTypes
object InheritsCompletionProvider : AbstractSlintCompletionProvider<CompletionParameters>() { object InheritsCompletionProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
override fun pattern(): ElementPattern<out PsiElement> { override fun pattern(): ElementPattern<out PsiElement> {
return PlatformPatterns.psiElement() return PlatformPatterns.psiElement()
.afterLeaf(PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(SlintTypes.ComponentName))) .afterLeaf(PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(SlintTypes.IDENTIFIER)))
} }
override fun addCompletions( override fun addCompletions(

View File

@@ -0,0 +1,109 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.icons.AllIcons
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.childrenOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.createComma
import me.zhouxi.slint.lang.createImport
import me.zhouxi.slint.lang.createImportSpecifier
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.SubComponent
import me.zhouxi.slint.lang.psi.extension.importNames
import me.zhouxi.slint.lang.psi.extension.relativePathOf
import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
object SubComponentNameProvider
: AbstractSlintCompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
val project = parameters.position.project
val components = searchComponent(project) { result.prefixMatcher.prefixMatches(it) }
val lookups = components.map {
val targetFile = it.containingFile.originalFile
val path = it.relativePathOf(parameters.position) ?: targetFile.name
LookupElementBuilder.create(it)
.withInsertHandler(ImportComponentInsertHandler(targetFile, path))
.withTypeText(path)
.withIcon(AllIcons.Nodes.Class)
}
result.addAllElements(lookups)
}
class ImportComponentInsertHandler(
private val targetFile: PsiFile,
private val path: String
) : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
//insert '{}'
val caretOffset: Int = context.editor.caretModel.offset
context.document.insertString(caretOffset, " { }")
context.editor.caretModel.moveToOffset(caretOffset + 3)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
val component = item.psiElement as SlintComponent
if (component.containingFile.isEquivalentTo(context.file)) {
return
}
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
val target = import.importElement?.moduleRef?.reference?.resolve()
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
}
if (targetImport == null) {
//不存在对指定文件的Import插入
val import = createImport(context.project, component.identifier!!.text, path)
context.file.addBefore(import, context.file.firstChild)
return
}
//如果导入的Name里面包含了这个ComponentName
if (targetImport.importNames().any { it.textMatches(component.identifier!!) }) {
return
}
//引入的文件存在,但是没有导入对应组件
val importElement = targetImport.importElement!!
val specifier = createImportSpecifier(context.project, component.identifier!!.text)
val last = importElement.importSpecifierList.lastOrNull()
//不存在前置节点
if (last == null) {
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
val element = PsiTreeUtil.nextVisibleLeaf(last)
if (!element!!.textMatches(",")) {
val comma = importElement.addAfter(createComma(context.project), last)
importElement.addAfter(specifier, comma)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
importElement.addAfter(specifier, element)
}
}
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement()
.withParent(or(psiElement(Component), psiElement(SubComponent)))
.andNot(AtChildrenCompletionProvider.pattern())
}
}

View File

@@ -10,7 +10,6 @@ import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateEditingAdapter import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.codeInsight.template.TemplateManager import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.impl.ConstantNode import com.intellij.codeInsight.template.impl.ConstantNode
import com.intellij.icons.AllIcons
import com.intellij.patterns.ElementPattern import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -18,29 +17,26 @@ import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
object TopKeywordProvider : AbstractSlintCompletionProvider<CompletionParameters>() { object TopElementProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
override fun addCompletions( override fun addCompletions(
parameters: CompletionParameters, parameters: CompletionParameters,
context: ProcessingContext, context: ProcessingContext,
result: CompletionResultSet result: CompletionResultSet
) { ) {
result.addAllElements(completion) result.addAllElements(completion)
result.addElement(create("component"))
} }
val completion = arrayListOf(create("component "), create("struct "), create("enum ")) val completion = arrayListOf(
.flatMap { create("component"),
arrayListOf( create("export component"),
it, create("export component").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
it.withPresentableText(it.lookupString) create("struct"),
.withTailText("\$Name$ {...}") create("export struct"),
.withInsertHandler(MyInsertHandler()), create("export struct").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
create("export ${it.lookupString}"), create("enum"),
create("export ${it.lookupString}") create("export enum"),
.withTailText("\$Name$ {...}") create("export enum").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
.withInsertHandler(MyInsertHandler()), ).map { it.withBoldness(true) }
)
}
override fun pattern(): ElementPattern<out PsiElement> { override fun pattern(): ElementPattern<out PsiElement> {
@@ -51,9 +47,10 @@ object TopKeywordProvider : AbstractSlintCompletionProvider<CompletionParameters
class MyInsertHandler : InsertHandler<LookupElement> { class MyInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val manager = TemplateManager.getInstance(context.project) val manager = TemplateManager.getInstance(context.project)
val template = manager.createTemplate("", "", " \$Name$ { \n }") val template = manager.createTemplate("", "", " \$Name$ { \n }").apply {
template.isToReformat = true isToReformat = true
template.addVariable("Name", ConstantNode(""), true) addVariable("Name", ConstantNode(""), true)
}
manager.startTemplate(context.editor, template, object : TemplateEditingAdapter() { manager.startTemplate(context.editor, template, object : TemplateEditingAdapter() {
override fun templateFinished(template: Template, brokenOff: Boolean) { override fun templateFinished(template: Template, brokenOff: Boolean) {
context.editor.caretModel.moveToOffset(context.tailOffset - 2); context.editor.caretModel.moveToOffset(context.tailOffset - 2);

View File

@@ -8,6 +8,7 @@ import com.intellij.psi.tree.TokenSet
import me.zhouxi.slint.lang.psi.SlintPsiKeyword import me.zhouxi.slint.lang.psi.SlintPsiKeyword
import me.zhouxi.slint.lang.psi.SlintTypes import me.zhouxi.slint.lang.psi.SlintTypes
import me.zhouxi.slint.lang.psi.SlintTypes.* import me.zhouxi.slint.lang.psi.SlintTypes.*
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
import me.zhouxi.slint.lang.psi.utils.braces import me.zhouxi.slint.lang.psi.utils.braces
import me.zhouxi.slint.lang.psi.utils.expressions import me.zhouxi.slint.lang.psi.utils.expressions
import me.zhouxi.slint.lang.psi.utils.keywords import me.zhouxi.slint.lang.psi.utils.keywords
@@ -35,6 +36,10 @@ class FormattingBlock(
if (child.elementType == TokenType.WHITE_SPACE || child.textLength == 0) { if (child.elementType == TokenType.WHITE_SPACE || child.textLength == 0) {
continue continue
} }
if (Comment.contains(child.elementType)) {
current.add(FormattingBlock(child, wrap, alignment, myIndent, spacingBuilder))
continue
}
if (child.firstChildNode == null || leafTokens.contains(child.elementType)) { if (child.firstChildNode == null || leafTokens.contains(child.elementType)) {
current.add(LeafBlock(child, wrap, alignment, Indent.getNoneIndent(), spacingBuilder)) current.add(LeafBlock(child, wrap, alignment, Indent.getNoneIndent(), spacingBuilder))
continue continue
@@ -53,6 +58,13 @@ class FormattingBlock(
override fun getIndent() = indent override fun getIndent() = indent
override fun getChildAttributes(newChildIndex: Int): ChildAttributes {
if (node.elementType == SlintFileElementType) {
return ChildAttributes(Indent.getNoneIndent(), null)
}
return super.getChildAttributes(newChildIndex)
}
override fun getSpacing(child1: Block?, child2: Block): Spacing? { override fun getSpacing(child1: Block?, child2: Block): Spacing? {
if (child2 is SyntheticBlock) { if (child2 is SyntheticBlock) {
return Spacing.createSpacing(1, 1, 0, true, 2) return Spacing.createSpacing(1, 1, 0, true, 2)
@@ -65,17 +77,17 @@ class FormattingBlock(
} }
companion object { companion object {
private val Comment = TokenSet.create(BlockComment, LineComment)
val leafTokens = val leafTokens =
TokenSet.create( TokenSet.create(
ReferenceIdentifier, ComponentRef,
ComponentName, PropertyRef,
InternalName, FunctionRef,
TypeRef,
LocalVariable, LocalVariable,
PropertyName,
PropertyModifier, PropertyModifier,
SlintPsiKeyword.ElementType.TYPE SlintPsiKeyword.ElementType.TYPE
) )
}
private val syntheticParentTokens = TokenSet.create( private val syntheticParentTokens = TokenSet.create(
SubComponent, SubComponent,
@@ -83,3 +95,5 @@ class FormattingBlock(
GlobalSingleton GlobalSingleton
) )
} }
}

View File

@@ -6,6 +6,9 @@ import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.formatter.common.ExtraRangesProvider import com.intellij.psi.formatter.common.ExtraRangesProvider
import com.intellij.psi.formatter.common.NodeIndentRangesCalculator import com.intellij.psi.formatter.common.NodeIndentRangesCalculator
import com.intellij.psi.tree.TokenSet
import me.zhouxi.slint.lang.psi.SlintTypes.BlockComment
import me.zhouxi.slint.lang.psi.SlintTypes.LineComment
class LeafBlock( class LeafBlock(
private val treeNode: ASTNode, private val treeNode: ASTNode,

View File

@@ -9,6 +9,7 @@ import com.intellij.psi.tree.TokenSet
import me.zhouxi.slint.lang.SlintLanguage import me.zhouxi.slint.lang.SlintLanguage
import me.zhouxi.slint.lang.psi.SlintTypes import me.zhouxi.slint.lang.psi.SlintTypes
import me.zhouxi.slint.lang.psi.SlintTypes.* import me.zhouxi.slint.lang.psi.SlintTypes.*
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
import me.zhouxi.slint.lang.psi.utils.keywords import me.zhouxi.slint.lang.psi.utils.keywords
/** /**
@@ -45,7 +46,7 @@ class SlintFormatterModelBuilder : FormattingModelBuilder {
.spaces(0) .spaces(0)
.before(Semicolon) .before(Semicolon)
.spacing(0, 0, 0, false, 0) .spacing(0, 0, 0, false, 0)
.after(Semicolon) .after(TokenSet.create(Semicolon))
.lineBreakInCode() .lineBreakInCode()
.after(Colon) .after(Colon)
.spaces(1) .spaces(1)
@@ -57,6 +58,8 @@ class SlintFormatterModelBuilder : FormattingModelBuilder {
.spaces(1) .spaces(1)
.after(RAngle) .after(RAngle)
.spaces(1) .spaces(1)
.afterInside(TokenSet.ANY, SlintFileElementType)
.lineBreakInCode()
} }
} }
} }

View File

@@ -39,10 +39,11 @@ class SyntheticBlock(
) { ) {
return Spacing.createSpacing(1, 1, 0, false, 0) return Spacing.createSpacing(1, 1, 0, false, 0)
} }
return null return Spacing.createSpacing(0, 1, 1, true, 1)
} }
override fun getChildAttributes(newChildIndex: Int) = ChildAttributes(null, subBlocks[0].alignment) override fun getChildAttributes(newChildIndex: Int) =
ChildAttributes(Indent.getNormalIndent(), subBlocks[0].alignment)
override fun isIncomplete() = false override fun isIncomplete() = false

View File

@@ -24,26 +24,42 @@ class KeywordHighlightAnnotator : Annotator {
.create() .create()
return return
} }
if (element is SlintTypeNameReference && SlintPsiUtils.isInternalType(element)) { if (element is SlintTypeRef && SlintPsiUtils.isInternalType(element)) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION) holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element) .range(element)
.textAttributes(Definitions._KeyWord) .textAttributes(Definitions._KeyWord)
.create() .create()
return return
} }
if (element is SlintPropertyName) { if (element is SlintPropertyRef) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION) holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element) .range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD) .textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create() .create()
return
}
if (element is SlintProperty) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.identifier!!)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create()
return
} }
if (element is SlintPropertyBinding) { if (element is SlintPropertyBinding) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION) holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.getReferenceIdentifier()) .range(element.propertyRef)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD) .textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create() .create()
return
} }
if (element is SlintFunctionName || element is SlintFunctionReference) { if (element is SlintFunction) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.nameIdentifier!!)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD)
.create()
return
}
if (element is SlintFunctionRef) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION) holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element) .range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD) .textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD)

View File

@@ -16,7 +16,7 @@ import me.zhouxi.slint.lang.psi.SlintImportSpecifier
fun createIdentifier(project: Project?, text: String): PsiElement { fun createIdentifier(project: Project?, text: String): PsiElement {
val factory = PsiFileFactory.getInstance(project) val factory = PsiFileFactory.getInstance(project)
val file = factory.createFileFromText("dummy.slint", SlintLanguage.INSTANCE, "component $text{}") as SlintFile val file = factory.createFileFromText("dummy.slint", SlintLanguage.INSTANCE, "component $text{}") as SlintFile
return file.findChildByClass(SlintComponent::class.java)!!.componentName!!.identifier return file.findChildByClass(SlintComponent::class.java)!!.identifier!!
} }
fun createImport(project: Project?, text: String, location: String): PsiElement { fun createImport(project: Project?, text: String, location: String): PsiElement {

View File

@@ -2,7 +2,10 @@ package me.zhouxi.slint.lang.psi.extension
import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.parentOfType import com.intellij.psi.util.parentOfType
import me.zhouxi.slint.lang.psi.* import me.zhouxi.slint.lang.psi.*
@@ -10,16 +13,29 @@ import me.zhouxi.slint.lang.psi.*
fun SlintComponent.inheritsProperties(): List<SlintProperty> { fun SlintComponent.inheritsProperties(): List<SlintProperty> {
val properties = this.propertyList val properties = this.propertyList
val inherit = val inherit =
this.inheritDeclaration?.referenceIdentifier?.reference?.resolve() as SlintComponent? ?: return properties this.inheritDeclaration?.componentRef?.resolve() ?: return properties
if (inherit == this) { if (inherit == this) {
return properties return properties
} }
return properties + inherit.inheritsProperties() return properties + inherit.inheritsProperties()
} }
fun SlintComponent.relativePathOf(element: PsiElement): String? {
return relativePathOf(element.containingFile)
}
fun SlintImport.importNames(): List<SlintReferenceIdentifier> { fun SlintComponent.relativePathOf(psiFile: PsiFile): String? {
return this.importElement?.importSpecifierList?.map { it.referenceIdentifier } ?: emptyList() return psiFile.originalFile.virtualFile?.let { relativePathOf(it) }
}
fun SlintComponent.relativePathOf(file: VirtualFile): String? {
val targetFile = this.containingFile.originalFile
return VfsUtil.findRelativePath(file, targetFile.virtualFile, '/')
}
fun SlintImport.importNames(): List<SlintComponentRef> {
return this.importElement?.importSpecifierList?.map { it.componentRef } ?: emptyList()
} }
/** /**
@@ -28,18 +44,26 @@ fun SlintImport.importNames(): List<SlintReferenceIdentifier> {
*/ */
fun SlintImport.availableNames(): List<PsiElement> { fun SlintImport.availableNames(): List<PsiElement> {
return this.importElement?.importSpecifierList?.map { return this.importElement?.importSpecifierList?.map {
it.importAlias?.internalName ?: it.referenceIdentifier it.importAlias?.identifier ?: it.componentRef
} ?: emptyList() } ?: emptyList()
} }
fun SlintSubComponent.resolve(): SlintComponent? { fun SlintSubComponent.resolve(): SlintComponent? {
return this.referenceIdentifier.reference?.resolve() as SlintComponent? return this.componentRef.reference?.resolve() as SlintComponent?
} }
fun SlintProperty.toLookupElement(): LookupElementBuilder { fun SlintProperty.toLookupElement(): LookupElementBuilder {
return LookupElementBuilder.create(this) return LookupElementBuilder.create(this)
.withTypeText(this.parentOfType<SlintComponent>()?.componentName?.text) .withTypeText(this.parentOfType<SlintComponent>()?.identifier?.text)
.withIcon(AllIcons.Nodes.Property) .withIcon(AllIcons.Nodes.Property)
} }
fun SlintComponentRef?.resolve(): SlintComponent? {
return this?.reference?.resolve() as SlintComponent?
}
fun SlintPropertyRef?.resolve(): SlintProperty? {
return this?.reference?.resolve() as SlintProperty?
}

View File

@@ -7,6 +7,7 @@ import com.intellij.util.IncorrectOperationException
import me.zhouxi.slint.lang.createIdentifier import me.zhouxi.slint.lang.createIdentifier
import me.zhouxi.slint.lang.psi.SlintNamed import me.zhouxi.slint.lang.psi.SlintNamed
import me.zhouxi.slint.lang.psi.SlintPsiNamedElement import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
import me.zhouxi.slint.lang.psi.SlintTypes
/** /**
* @author zhouxi 2024/5/15 * @author zhouxi 2024/5/15
@@ -14,7 +15,7 @@ import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
abstract class SlintPsiNamedElementImpl(node: ASTNode) : SlintPsiElementImpl(node), abstract class SlintPsiNamedElementImpl(node: ASTNode) : SlintPsiElementImpl(node),
SlintPsiNamedElement { SlintPsiNamedElement {
override fun getNameIdentifier(): PsiElement? { override fun getNameIdentifier(): PsiElement? {
return findChildByClass(SlintNamed::class.java) ?: this return findChildByType(SlintTypes.IDENTIFIER) ?: this
} }
override fun getName(): String? { override fun getName(): String? {

View File

@@ -1,4 +0,0 @@
package me.zhouxi.slint.lang.psi.stubs.index;
public class ExportedNameIndex {
}

View File

@@ -3,12 +3,16 @@ package me.zhouxi.slint.lang.psi.stubs.index
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndex
import me.zhouxi.slint.lang.psi.SlintComponent import me.zhouxi.slint.lang.psi.SlintComponent
import java.util.function.Predicate
/**
* Search Component With Key
*/
fun searchComponent(key: String, project: Project, psiFile: PsiFile? = null): Collection<SlintComponent> { fun searchComponent(key: String, project: Project, psiFile: PsiFile? = null): Collection<SlintComponent> {
val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: GlobalSearchScope.projectScope(project) val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: projectScope(project)
return StubIndex.getElements( return StubIndex.getElements(
StubIndexKeys.Component, StubIndexKeys.Component,
key, key,
@@ -16,3 +20,29 @@ fun searchComponent(key: String, project: Project, psiFile: PsiFile? = null): Co
SlintComponent::class.java SlintComponent::class.java
) )
} }
/**
* Search Component With predicate
*/
fun searchComponent(
project: Project,
psiFile: PsiFile? = null,
predicate: Predicate<String>
): Collection<SlintComponent> {
val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: projectScope(project)
val components = arrayListOf<SlintComponent>()
StubIndex.getInstance().processAllKeys(StubIndexKeys.Component, project) {
if (predicate.test(it)) {
val elements = StubIndex.getElements(
StubIndexKeys.Component,
it,
project,
scope,
SlintComponent::class.java
)
components.addAll(elements)
}
true
}
return components
}

View File

@@ -27,7 +27,7 @@ object SlintComponentElementType :
return SlintComponentStubImpl( return SlintComponentStubImpl(
parentStub, parentStub,
psi.exportKeyword != null, psi.exportKeyword != null,
psi.componentName!!.text psi.identifier!!.text
) )
} }
@@ -50,9 +50,9 @@ object SlintComponentElementType :
override fun createStub(tree: LighterAST, node: LighterASTNode, parentStub: StubElement<*>): SlintComponentStub { override fun createStub(tree: LighterAST, node: LighterASTNode, parentStub: StubElement<*>): SlintComponentStub {
val exported = tree.getChildren(node).any { it.tokenType == ExportKeyword } val exported = tree.getChildren(node).any { it.tokenType == ExportKeyword }
val token = tree.getChildren(node).find { it.tokenType == ComponentName } val token = tree.getChildren(node).find { it.tokenType == IDENTIFIER }
?.let { ?.let {
val text = tree.getChildren(it)[0] as LighterASTTokenNode val text = it as LighterASTTokenNode
tree.charTable.intern(text.text) tree.charTable.intern(text.text)
} }

View File

@@ -25,14 +25,14 @@ object SlintImportSpecifierElementType :
): SlintImportSpecifierStub { ): SlintImportSpecifierStub {
val children = tree.getChildren(node); val children = tree.getChildren(node);
val name = run { val name = run {
val nameNode = children.first { it.tokenType == ReferenceIdentifier } val nameNode = children.first { it.tokenType == ComponentRef }
val token = tree.getChildren(nameNode)[0] as LighterASTTokenNode val token = tree.getChildren(nameNode)[0] as LighterASTTokenNode
tree.charTable.intern(token.text).toString() tree.charTable.intern(token.text).toString()
} }
val alias = run { val alias = run {
val aliasNode = children.firstOrNull { it.tokenType == ImportAlias } val aliasNode = children.firstOrNull { it.tokenType == ImportAlias }
aliasNode?.let { aliasNode?.let {
val nameNode = tree.getChildren(aliasNode).firstOrNull { it.tokenType == InternalName } val nameNode = tree.getChildren(aliasNode).firstOrNull { it.tokenType == IDENTIFIER }
val token = nameNode?.let { tree.getChildren(nameNode) }?.firstOrNull() as LighterASTTokenNode? val token = nameNode?.let { tree.getChildren(nameNode) }?.firstOrNull() as LighterASTTokenNode?
token?.let { token?.let {
tree.charTable.intern(it.text).toString() tree.charTable.intern(it.text).toString()
@@ -48,8 +48,8 @@ object SlintImportSpecifierElementType :
): SlintImportSpecifierStub { ): SlintImportSpecifierStub {
return SlintImportSpecifierStubImpl( return SlintImportSpecifierStubImpl(
parentStub, parentStub,
psi.referenceIdentifier.text, psi.componentRef.text,
psi.importAlias?.internalName?.text psi.importAlias?.identifier?.text
) )
} }

View File

@@ -27,7 +27,7 @@ object SlintPropertyElementType :
else -> 0 else -> 0
} }
} ?: 0 } ?: 0
val name = tree.getChildren(children.first { it.tokenType == PropertyName })[0] as LighterASTTokenNode val name = children.first { it.tokenType == IDENTIFIER } as LighterASTTokenNode
return SlintPropertyStubImpl( return SlintPropertyStubImpl(
parentStub, parentStub,
modifierNode.toShort(), modifierNode.toShort(),
@@ -50,7 +50,7 @@ object SlintPropertyElementType :
val modifier = psi.propertyModifier val modifier = psi.propertyModifier
val isIn = modifier?.inKeyword != null || modifier?.inOutKeyword != null val isIn = modifier?.inKeyword != null || modifier?.inOutKeyword != null
val isOut = modifier?.outKeyword != null || modifier?.inOutKeyword != null val isOut = modifier?.outKeyword != null || modifier?.inOutKeyword != null
val name = psi.propertyName!!.text val name = psi.identifier!!.text
val flag = SlintPropertyStubImpl.pack(isIn, isOut) val flag = SlintPropertyStubImpl.pack(isIn, isOut)
return SlintPropertyStubImpl(parentStub, flag, name) return SlintPropertyStubImpl(parentStub, flag, name)
} }

View File

@@ -0,0 +1,16 @@
package me.zhouxi.slint.lsp
import com.intellij.platform.lsp.api.Lsp4jClient
import com.intellij.platform.lsp.api.LspServerNotificationsHandler
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
class SlintLspClient(handler: LspServerNotificationsHandler) : Lsp4jClient(handler) {
@JsonNotification("@slint/showPreview")
fun formatDocument() {
println("Formatting document")
}
}

View File

@@ -0,0 +1,25 @@
package me.zhouxi.slint.lsp
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.Lsp4jClient
import com.intellij.platform.lsp.api.LspServerDescriptor
import com.intellij.platform.lsp.api.LspServerNotificationsHandler
import me.zhouxi.slint.preview.SlintViewer
/**
* @author zhouxi 2024/6/3
*/
class SlintLspServerDescriptor(project: Project) : LspServerDescriptor(project, "slint") {
override fun isSupportedFile(file: VirtualFile) = file.extension == "slint"
override fun createCommandLine() = GeneralCommandLine(SlintViewer.executable())
override fun createLsp4jClient(handler: LspServerNotificationsHandler): Lsp4jClient {
return SlintLspClient(handler)
}
override val lspCompletionSupport = null
}

View File

@@ -0,0 +1,18 @@
package me.zhouxi.slint.lsp
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.LspServerSupportProvider
import org.jetbrains.annotations.ApiStatus
class SlintLspServerSupportProvider : LspServerSupportProvider {
override fun fileOpened(
project: Project,
file: VirtualFile,
serverStarter: LspServerSupportProvider.LspServerStarter
) {
if (file.extension == "slint") {
serverStarter.ensureServerStarted(SlintLspServerDescriptor(project))
}
}
}

View File

@@ -3,15 +3,17 @@ package me.zhouxi.slint.preview
import com.intellij.execution.lineMarker.ExecutorAction import com.intellij.execution.lineMarker.ExecutorAction
import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.execution.lineMarker.RunLineMarkerContributor
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.elementType
import me.zhouxi.slint.lang.SlintLanguage import me.zhouxi.slint.lang.SlintLanguage
import me.zhouxi.slint.lang.psi.SlintComponentName import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintTypes
/** /**
* @author zhouxi 2024/5/16 * @author zhouxi 2024/5/16
*/ */
class PreviewRunLineMarkerContributor : RunLineMarkerContributor() { class PreviewRunLineMarkerContributor : RunLineMarkerContributor() {
override fun getInfo(element: PsiElement): Info? { override fun getInfo(element: PsiElement): Info? {
if (element.parent is SlintComponentName) { if (element.parent is SlintComponent && element.elementType == SlintTypes.IDENTIFIER) {
return Info( return Info(
SlintLanguage.ICON, null, SlintLanguage.ICON, null,
*ExecutorAction.getActions(1) *ExecutorAction.getActions(1)

View File

@@ -10,17 +10,17 @@ import java.nio.file.Paths
object SlintViewer { object SlintViewer {
private val executable = if (SystemInfo.isWindows) { private val executable = if (SystemInfo.isWindows) {
"slint-viewer-windows.exe" "slint-lsp-windows.exe"
} else if (SystemInfo.isLinux) { } else if (SystemInfo.isLinux) {
"slint-viewer-linux" "slint-lsp-linux"
} else if (SystemInfo.isMac) { } else if (SystemInfo.isMac) {
"slint-viewer-macos" "slint-lsp-macos"
} else { } else {
throw RuntimeException("Unsupported OS") throw RuntimeException("Unsupported OS")
} }
fun executable(): String { fun executable(): String {
return Paths.get(Plugin.Path.toAbsolutePath().toString(), "slint-viewer", executable).toString() return Paths.get(Plugin.Path.toAbsolutePath().toString(), "slint-lsp", executable).toString()
} }

View File

@@ -16,31 +16,17 @@ class SlintReferenceContributor : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
//component Reference //component Reference
registrar.registerReferenceProvider( registrar.registerReferenceProvider(
psiElement(ReferenceIdentifier) psiElement(ComponentRef),
.withParent(
or(
psiElement(SubComponent),
psiElement(Component),
psiElement(InheritDeclaration),
psiElement(ImportSpecifier)
)
),
ComponentReferenceProvider ComponentReferenceProvider
) )
//moduleLocation Reference //moduleLocation Reference
registrar.registerReferenceProvider( registrar.registerReferenceProvider(
psiElement(ModuleLocation), psiElement(ModuleRef),
ModuleLocationReferenceProvider() ModuleLocationReferenceProvider()
) )
//property binding //property binding
registrar.registerReferenceProvider( registrar.registerReferenceProvider(
psiElement().withParent( psiElement(PropertyRef),
or(
psiElement(PropertyBinding),
psiElement(Component),
psiElement(SubComponent)
)
),
PropertyReferenceProvider PropertyReferenceProvider
) )
} }

View File

@@ -15,27 +15,27 @@ import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
*/ */
object ComponentReferenceProvider : PsiReferenceProvider() { object ComponentReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> { override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
return arrayOf(SlintComponentNameReference(element as SlintReferenceIdentifier)) return arrayOf(SlintComponentNameReference(element as SlintComponentRef))
} }
class SlintComponentNameReference(element: SlintReferenceIdentifier) : PsiReferenceBase<PsiElement?>(element) { class SlintComponentNameReference(element: SlintComponentRef) : PsiReferenceBase<PsiElement?>(element) {
override fun resolve(): PsiElement? { override fun resolve(): PsiElement? {
val file = element.containingFile as SlintFile val file = element.containingFile as SlintFile
file.childrenOfType<SlintComponent>() file.childrenOfType<SlintComponent>()
.find { it.componentName?.textMatches(element) == true } .find { it.identifier?.textMatches(element) == true }
?.let { return it } ?.let { return it }
val specifier = if (element.parent is SlintImportSpecifier) { val specifier = if (element.parent is SlintImportSpecifier) {
element.parent as SlintImportSpecifier element.parent as SlintImportSpecifier
} else { } else {
file.childrenOfType<SlintImport>() file.childrenOfType<SlintImport>()
.flatMap { it.importElement?.importSpecifierList ?: arrayListOf() } .flatMap { it.importElement?.importSpecifierList ?: arrayListOf() }
.find { (it.importAlias?.internalName ?: it.referenceIdentifier).textMatches(element) } .find { (it.importAlias?.identifier ?: it.componentRef).textMatches(element) }
?: return null ?: return null
} }
val componentName = specifier.referenceIdentifier.text val componentName = specifier.componentRef.text
val location = specifier.parentOfType<SlintImportElement>()?.moduleLocation ?: return null val location = specifier.parentOfType<SlintImportElement>()?.moduleRef ?: return null
val targetFile = location.reference?.resolve()?.containingFile ?: return null val targetFile = location.reference?.resolve()?.containingFile ?: return null
val components = searchComponent(componentName, element.project, targetFile) val components = searchComponent(componentName, element.project, targetFile)
return components.firstOrNull() return components.firstOrNull()

View File

@@ -4,7 +4,7 @@ import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintModuleLocation import me.zhouxi.slint.lang.psi.SlintModuleRef
/** /**
* @author zhouxi 2024/5/17 * @author zhouxi 2024/5/17
@@ -16,7 +16,7 @@ class ModuleLocationReferenceProvider : PsiReferenceProvider() {
class ModuleLocationReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) { class ModuleLocationReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) {
override fun resolve(): PsiElement? { override fun resolve(): PsiElement? {
val location = element as SlintModuleLocation val location = element as SlintModuleRef
val filenameText = location.stringLiteral.text val filenameText = location.stringLiteral.text
if (filenameText.length < 3) return null if (filenameText.length < 3) return null
val directory = element.containingFile.originalFile.virtualFile?.parent ?: return null val directory = element.containingFile.originalFile.virtualFile?.parent ?: return null
@@ -29,6 +29,7 @@ class ModuleLocationReferenceProvider : PsiReferenceProvider() {
override fun calculateDefaultRangeInElement(): TextRange { override fun calculateDefaultRangeInElement(): TextRange {
return TextRange(1, element.textLength - 1) return TextRange(1, element.textLength - 1)
} }
override fun bindToElement(element: PsiElement): PsiElement { override fun bindToElement(element: PsiElement): PsiElement {
return element return element
} }

View File

@@ -9,6 +9,7 @@ import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintComponent import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintSubComponent import me.zhouxi.slint.lang.psi.SlintSubComponent
import me.zhouxi.slint.lang.psi.extension.inheritsProperties import me.zhouxi.slint.lang.psi.extension.inheritsProperties
import me.zhouxi.slint.lang.psi.extension.resolve
object PropertyReferenceProvider : PsiReferenceProvider() { object PropertyReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> { override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
@@ -19,11 +20,11 @@ object PropertyReferenceProvider : PsiReferenceProvider() {
override fun resolve(): PsiElement? { override fun resolve(): PsiElement? {
val subComponent = element.parentOfType<SlintSubComponent>() val subComponent = element.parentOfType<SlintSubComponent>()
if (subComponent != null) { if (subComponent != null) {
val component = subComponent.referenceIdentifier.reference?.resolve() as SlintComponent? ?: return null val component = subComponent.resolve() ?: return null
return component.inheritsProperties().find { it.propertyName?.textMatches(element) == true } return component.inheritsProperties().find { it.identifier?.textMatches(element) == true }
} }
val component = element.parentOfType<SlintComponent>() ?: return null val component = element.parentOfType<SlintComponent>() ?: return null
return component.inheritsProperties().find { it.propertyName?.textMatches(element) == true } return component.inheritsProperties().find { it.identifier?.textMatches(element) == true }
} }
} }
} }

View File

@@ -11,6 +11,9 @@
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
<platform.lsp.serverSupportProvider implementation="me.zhouxi.slint.lsp.SlintLspServerSupportProvider"/>
<typedHandler implementation="me.zhouxi.slint.completion.SlintCompletionAutoPopupHandler" <typedHandler implementation="me.zhouxi.slint.completion.SlintCompletionAutoPopupHandler"
id="completionAutoPopup" id="completionAutoPopup"
order="first"/> order="first"/>
@@ -29,7 +32,7 @@
<lang.braceMatcher language="Slint" <lang.braceMatcher language="Slint"
implementationClass="me.zhouxi.slint.brace.SlintPairedBraceMatcher"/> implementationClass="me.zhouxi.slint.brace.SlintPairedBraceMatcher"/>
<lang.elementManipulator forClass="me.zhouxi.slint.lang.psi.SlintPsiReferencedIdentifier" <lang.elementManipulator forClass="me.zhouxi.slint.lang.psi.SlintPsiRefIdentifier"
implementationClass="me.zhouxi.slint.reference.SlintElementNameManipulator"/> implementationClass="me.zhouxi.slint.reference.SlintElementNameManipulator"/>
<completion.contributor language="Slint" <completion.contributor language="Slint"

View File

@@ -1,7 +1,6 @@
import com.intellij.lexer.FlexAdapter; import com.intellij.lexer.FlexAdapter;
import com.intellij.lexer.Lexer; import com.intellij.lexer.Lexer;
import com.intellij.testFramework.LexerTestCase; import com.intellij.testFramework.LexerTestCase;
import lombok.extern.slf4j.Slf4j;
import me.zhouxi.slint.lang.lexer.SlintLexer; import me.zhouxi.slint.lang.lexer.SlintLexer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.Rule; import org.junit.Rule;
@@ -16,7 +15,6 @@ import java.util.concurrent.TimeUnit;
/** /**
* @author zhouxi 2024/4/30 * @author zhouxi 2024/4/30
*/ */
@Slf4j
public class LexerTest extends LexerTestCase { public class LexerTest extends LexerTestCase {
@Override @Override
@@ -44,12 +42,12 @@ public class LexerTest extends LexerTestCase {
public Stopwatch stopwatch = new Stopwatch() { public Stopwatch stopwatch = new Stopwatch() {
@Override @Override
protected void succeeded(long nanos, Description description) { protected void succeeded(long nanos, Description description) {
log.info("{} succeeded, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); // log.info("{} succeeded, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos));
} }
@Override @Override
protected void failed(long nanos, Throwable e, Description description) { protected void failed(long nanos, Throwable e, Description description) {
log.info("{} failed, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos)); // log.info("{} failed, time taken: {} ms", description.getMethodName(), TimeUnit.NANOSECONDS.toMillis(nanos));
} }
}; };
} }

View File

@@ -22,12 +22,12 @@ public class ParserTest extends ParsingTestCase {
public void test() throws IOException { public void test() throws IOException {
final var path = Paths.get("src/test/resources/slint/main.slint"); final var path = Paths.get("src/test/resources/slint/main.slint");
var source = Files.readString(path); var source = """
StopWatch started = StopWatch.createStarted(); component App{
for (int i = 0; i < 50000; i++) { aaa;
property name;
}
""";
doCodeTest(source); doCodeTest(source);
} }
started.stop();
System.out.println(started.getNanoTime()/50000);
}
} }