Compare commits

...

10 Commits

Author SHA1 Message Date
me
578f8d4661 fix: 语法问题 2025-08-08 15:49:35 +08:00
me0106
e0c006cb94 Update main.slint 2024-06-07 16:28:56 +08:00
me
1b9faa6f09 fix: 语法问题 2024-06-06 15:20:08 +08:00
me
39a81fcb5d fix: 语法问题 2024-05-31 17:58:39 +08:00
me
ada52875ad fix: 语法问题 2024-05-31 16:31:26 +08:00
me
03a336bac3 fix: 语法问题 2024-05-31 16:31:13 +08:00
me
ad35338f49 fix: 语法问题 2024-05-29 19:30:01 +08:00
me
c6f45b3368 fix: 语法问题 2024-05-28 18:17:06 +08:00
me
06cf13484d 添加自动插入 2024-05-27 19:17:44 +08:00
me
088dbd5661 删除componentBody 2024-05-26 23:56:15 +08:00
63 changed files with 1326 additions and 720 deletions

View File

@@ -1,7 +1,7 @@
plugins {
id("java")
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("de.undercouch.download") version "5.6.0"
}
@@ -9,16 +9,16 @@ val slintVersion: String by project
//github url
val slintGithubUrl = "https://github.com/slint-ui/slint/releases/download/v$slintVersion"
//download dir
val slintViewerDownloadDir = "${layout.buildDirectory.get()}/slint-viewer"
val slintViewerDownloadDir = "${layout.buildDirectory.get()}/slint-lsp"
//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/"
//github filename-> decompression name
val slintViewerFilenames = arrayOf(
"slint-viewer-linux.tar.gz" to "slint-viewer-linux",
"slint-viewer-macos.tar.gz" to "slint-viewer-macos",
"slint-viewer-windows.zip" to "slint-viewer-windows.exe",
"slint-lsp-linux.tar.gz" to "slint-lsp-linux",
"slint-lsp-macos.tar.gz" to "slint-lsp-macos",
"slint-lsp-windows.zip" to "slint-lsp-windows.exe",
)
sourceSets {
@@ -36,22 +36,17 @@ repositories {
}
intellij {
version.set("IC-2023.2.5")
version.set("IU-241-EAP-SNAPSHOT")
sandboxDir.set("idea-sandbox")
plugins.set(listOf("java"))
plugins.set(listOf("java","Git4Idea"))
}
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 {
buildPlugin {
//copy slint-viewer to plugin dir
//copy slint-lsp to plugin dir
from(slintViewerBinaryDir) {
into("/slint-viewer")
into("/slint-lsp")
}
}
withType<JavaExec>().configureEach {
@@ -61,11 +56,11 @@ tasks {
}
runIde {
//copy slint-viewer
//copy slint-lsp
doFirst {
copy {
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")
}
register("downloadSlintViewer") {
register("downloadSlintResource") {
doFirst {
slintViewerFilenames.forEach { fileDesc ->
val (filename, renamed) = fileDesc
@@ -118,7 +113,7 @@ tasks {
}
from(fileTree)
//include executable file path
include("slint-viewer/slint-viewer*")
include("slint-lsp/slint-lsp*")
eachFile {
//rename to platform name
this.relativePath = RelativePath(true, renamed)

View File

@@ -1,6 +1,7 @@
{
generate=[token-case="as-is" element-case="as-is"]
parserClass="me.zhouxi.slint.lang.parser.SlintParser"
parserUtilClass="me.zhouxi.slint.lang.psi.SlintParserUtil"
implements="me.zhouxi.slint.lang.psi.SlintPsiElement"
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiElementImpl"
elementTypeHolderClass="me.zhouxi.slint.lang.psi.SlintTypes"
@@ -10,6 +11,7 @@
psiImplClassSuffix="Impl"
psiPackage="me.zhouxi.slint.lang.psi"
psiImplPackage="me.zhouxi.slint.lang.psi.impl"
extends(".*Expression")=Expression
tokens=[
Comma = ","
FatArrow = "=>"
@@ -51,16 +53,13 @@
Percent = "%"
Whitespace = "regexp:(\s+)"
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]+)"
StringLiteral = 'regexp:(^"[^"\r\n]*")'
LineComment = 'regexp:^//[^\r\n]*'
BlockComment = 'regexp:/\*[\s\S]*?\*/'
]
extends(".*Expression")=Expression
implements(".*Keyword")=["me.zhouxi.slint.lang.psi.keyword.SlintPsiKeywordIdentifier"]
mixin(".*Keyword")="me.zhouxi.slint.lang.psi.keyword.SlintPsiKeywordIdentifierImpl"
}
//
Document ::= DocumentElement*
@@ -68,26 +67,25 @@ private recoverTopElement ::= !('component' | 'struct' | 'enum' | 'global'| 'exp
private DocumentElement ::= Import | Struct | Enum | GlobalSingleton | Component | Export {
recoverWhile=recoverTopElement
}
GlobalSingleton ::= ExportKeyword? GlobalKeyword ComponentName '{' ComponentElement* '}' {
GlobalSingleton ::= 'export'? 'global' Identifier '{' ComponentElement* '}' {
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
mixin="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
//import 定义
Import ::= ImportKeyword (ImportElement|ImportResource)';'{
Import ::= 'import' (ImportElement|ModuleRef)';'{
pin=1
elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.slintImport"
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportStub"
extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiElementImpl<?>"
}
ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* ','? '}' FromKeyword ModuleLocation{
ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* '}' 'from' ModuleRef{
pin=1
}
ImportResource ::= ModuleLocation
// ABc as Def
ImportSpecifier ::= ReferenceIdentifier ImportAlias?{
ImportSpecifier ::= ComponentRef ImportAlias?{
pin=1
elementTypeFactory="me.zhouxi.slint.lang.psi.stubs.types.SlintStubTypes.importSpecifier"
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintImportSpecifierStub"
@@ -95,48 +93,48 @@ ImportSpecifier ::= ReferenceIdentifier ImportAlias?{
}
private AliasNameRecover::=!(','|'}'|';')
ImportAlias ::= AsKeyword InternalName {
ImportAlias ::= 'as' Identifier {
pin=1
recoverWhile=AliasNameRecover
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
//Struct 定义
Struct ::= ExportKeyword? StructKeyword TypeName (':=')? StructBody {
Struct ::= 'export'? 'struct' Identifier (':=')? StructBody {
pin=2
}
private StructBody ::= '{' FieldDeclarations? '}'{
pin=1
}
//EnumDeclaration
Enum ::= ExportKeyword? EnumKeyword EnumName '{' (EnumValue (','EnumValue)*','? )? '}'{
Enum ::= 'export'? 'enum' Identifier '{' [Identifier (','Identifier)*','? ] '}'{
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
//--------------ExportsList -------------------------------------
//ExportsList
Export ::= ExportKeyword (ExportType | ExportModule) {
Export ::= 'export' (ExportType | ExportModule) {
pin=1
}
ExportType ::= '{' ExportSpecifier (','ExportSpecifier)* ','? '}'{
pin=1
}
ExportSpecifier::= ReferenceIdentifier ExportAlias?{
ExportSpecifier::= ComponentRef ExportAlias?{
recoverWhile=AliasNameRecover
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
ExportAlias ::= AsKeyword ExternalName{
ExportAlias ::= 'as' Identifier{
pin=1
}
ExportModule ::= '*' FromKeyword ModuleLocation ';'{
ExportModule ::= '*' 'from' ModuleRef ';'{
pin=1
}
Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration? '{' ComponentElement* '}' {
pin=2
Component ::= ComponentModifier '{' ComponentElement* '}' {
pin=1
implements=[
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
]
@@ -144,28 +142,33 @@ Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration?
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintComponentStub"
extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiNamedElementImpl<?>"
}
//组件定义
//private LegacyComponent ::= (ComponentName|NamedIdentifier ':=' ) ComponentBody
InheritDeclaration ::= InheritsKeyword ReferenceIdentifier {
pin=1
private ComponentModifier::= 'export'? 'component' Identifier InheritDeclaration?{
pin=2
recoverWhile=recoverInherit
}
//组件定义
InheritDeclaration ::= 'inherits' ComponentRef {
pin=1
recoverWhile=recoverInherit
}
private recoverInherit::=!('{')
//组件元素定义
private ComponentElement ::=ChildrenPlaceholder| Property | Callback
private ComponentElement ::= ChildrenPlaceholder| PropertyDeclaration | CallbackDeclaration
| Function | PropertyAnimation | CallbackConnection | Transitions
| PropertyChanged
| States | TwoWayBinding|PropertyBinding | ConditionalElement
| RepetitionElement | SubComponent {
recoverWhile(".*")=recoverWhileForComponentBody
| PropertyChanged | States | TwoWayBinding | ConditionalElement
| RepetitionElement | SubComponent | PropertyBinding {
recoverWhile=recoverWhileForComponentBody
}
private recoverWhileForComponentBody::= !('{'|';'|'}'|'@'|GenericIdentifier)
private recoverWhileForComponentBody::= !('{'|';'|'}'|'@'|Identifier)
ChildrenPlaceholder ::= '@' 'children'{
pin=1
}
//--------------------------------PropertyDeclaration Start----------------------------------------------------
// 属性定义 in property <type> name: value / in property <type> name <=> value
Property ::= PropertyModifier? PropertyKeyword ('<' Type '>')? PropertyName(PropertyValue|PropertyTwoWayBindingValue|';'){
pin=2
PropertyDeclaration ::= PropertyModifier? 'property' ('<' Type '>')? Identifier PropertyDeclarationValue{
pin=4
implements=[
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
]
@@ -173,47 +176,48 @@ Property ::= PropertyModifier? PropertyKeyword ('<' Type '>')? PropertyName(Prop
stubClass="me.zhouxi.slint.lang.psi.stubs.stub.SlintPropertyStub"
extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiNamedElementImpl<?>"
}
PropertyModifier ::= InKeyword|OutKeyword|InOutKeyword|PrivateKeyword
private PropertyValue::= ':' BindingStatement {
pin=1
}
private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyNameReference ';' {
pin=1
}
//修饰符
PropertyModifier ::= 'in'|'out'|'in-out'|'private'
//属性定义的ValueBinding
PropertyDeclarationValue::=(':' BindingExpressionStatement)| ('<=>' Expression ';') | ';'
//绑定类型表达式
BindingExpressionStatement::= '{' '}' | Expression ';' | '{' Expression '}' | ObjectCreationExpression ';'
//--------------------------------PropertyChanged Start----------------------------------------------------
PropertyChanged ::= ChangedKeyword LocalVariable '=>' CodeBlock{
PropertyChanged ::= 'changed' LocalVariable '=>' CodeBlock{
pin=1
}
//--------------------------------CallbackDeclaration Start----------------------------------------------------
// 回调定义 pure callback abc()->int; callback abc; callback(..);callback()->type;
Callback ::= PureKeyword? CallbackKeyword FunctionName CallbackBinding? ';'{
CallbackDeclaration ::= 'pure'? 'callback' Identifier CallbackArgument? CallbackBinding? ';'{
pin=2
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
//回调参数定义
CallbackBinding::= CallbackArgument | ('<=>'QualifiedPropertyNameReference)
CallbackBinding::= CallbackArgument | ('<=>'QualifiedPropertyRef)
//入参定义
CallbackArgument ::= '(' (Type (','Type)* ','? )? ')' ReturnType?{
pin=1
}
private ReturnType ::= '->' (NamedType|ArrayType){
CallbackArgument ::= '(' (Type (','Type)* ','? )? ')' ('->' Type)?{
pin=1
}
//--------------------------------TwoWayBindingDeclaration Start----------------------------------------------------
//组件双向绑定
TwoWayBinding ::= ReferenceIdentifier '<=>' QualifiedPropertyNameReference ';' {
TwoWayBinding ::= PropertyRef '<=>' QualifiedPropertyRef ';' {
pin=2
}
//--------------------------------FunctionDeclaration Start----------------------------------------------------
//函数定义 protected? pure? function f()
Function ::= FunctionModifiers? FunctionKeyword FunctionName FunctionArguments ReturnType? CodeBlock{
pin=2
Function ::= FunctionModifiers? 'function' Identifier FunctionArguments ReturnType? CodeBlock{
pin=3
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
}
private FunctionModifiers ::= ((PublicKeyword | ProtectedKeyword) PureKeyword?) | (PureKeyword (PublicKeyword | ProtectedKeyword)?)
private FunctionModifiers ::= (('public' | 'protected') 'pure'?) | ('pure' ('public' | 'protected')?)
private FunctionArguments ::= '(' FieldDeclarations* ')'{
pin=1
@@ -221,16 +225,16 @@ private FunctionArguments ::= '(' FieldDeclarations* ')'{
//--------------------------------CallbackConnectionDeclaration Start---------------------------------------------------
//回调绑定定义 abc()=> {}
CallbackConnection ::= FunctionReference CallbackConnectionArguments? '=>' CodeBlock ';'?{
CallbackConnection ::= FunctionRef CallbackConnectionArguments? '=>' CodeBlock ';'?{
pin=3
}
private CallbackConnectionArguments ::= '(' (LocalVariable (',' LocalVariable)* ','?)? ')'
//--------------------------------ConditionalElementDeclaration Start---------------------------------------------------
ConditionalElement ::= IfKeyword Expression ':' SubComponent{
ConditionalElement ::= 'if' Expression ':' SubComponent{
pin=1
}
RepetitionElement ::= ForKeyword RepetitionVariable InKeyword Expression ':' SubComponent {
RepetitionElement ::= 'for' RepetitionVariable 'in' Expression ':' SubComponent {
pin=1
}
private RepetitionVariable ::= LocalVariable RepetitionIndex?{
@@ -241,24 +245,25 @@ RepetitionIndex ::= '[' LocalVariable ']'{
}
//--------------------------------SubElementDeclaration Start---------------------------------------------------
//子组件结构元素定义
SubComponent ::= (PropertyName ':=')? ReferenceIdentifier '{' ComponentElement* '}'{
SubComponent ::= (Identifier ':=')? ComponentRef '{' ComponentElement* '}'{
pin=3
recoverWhile=recoverWhileForComponentBody
// recoverWhile=recoverForBrace
}
//private recoverForBrace::=!'}'
//--------------------------------TransitionsDeclaration Start---------------------------------------------------
//过渡绑定
Transitions ::= TransitionsKeyword '[' Transition* ']'{
Transitions ::= 'transitions' '[' Transition* ']'{
pin=1
}
//
Transition ::= (InKeyword|OutKeyword) LocalVariable ':' '{' PropertyAnimation* '}'{
Transition ::= ('in'|'out') LocalVariable ':' '{' PropertyAnimation* '}'{
pin=1
recoverWhile=recoverForRBracket
}
//--------------------------------TransitionsDeclaration End---------------------------------------------------
// in | out name : { }
States ::= StatesKeyword '[' State* ']'{
States ::= 'states' '[' State* ']'{
pin=1
}
// identifier [when] : { ... }
@@ -268,33 +273,37 @@ State ::= LocalVariable StateCondition? ':' '{' StateItem* '}' {
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
recoverWhile=recoverForRBracket
}
private recoverForRBracket::=!(']'|'}'|';'|GenericIdentifier)
StateCondition ::= WhenKeyword Expression {
private recoverForRBracket::=!(']'|'}'|';'|Identifier)
StateCondition ::= 'when' Expression {
pin=1
}
//状态可以由transition propertyBinding 和 animationBinding组成
private StateItem ::= QualifiedNamePropertyBinding | StateTransition
StateTransition ::= (InKeyword|OutKeyword) ':' '{' PropertyAnimation* '}'{
StateTransition ::= ('in'|'out') ':' '{' PropertyAnimation* '}'{
pin=1
}
//------------------------------------------------------------------------------------------
//类型定义
Type ::= NamedType | UnnamedType | ArrayType
private NamedType ::= QualifiedTypeNameReference
Type ::= TypeRef | UnnamedType | ArrayType
TypeRef::=QualifiedName{
extends=Referred
}
ArrayType ::= '[' Type ']'
UnnamedType ::= '{' FieldDeclarations* '}'
private FieldDeclarations ::= FieldDeclaration (',' FieldDeclaration)* ','?
private FieldDeclaration ::= PropertyName ':' Type
private FieldDeclaration ::= Identifier ':' Type
//代码块
CodeBlock ::= '{' Statement* '}'{
pin=1
}
private Statement ::= (ReturnStatement|IfElseStatement|ExpressionStatement) ';'?{
recoverWhile=recoverWhileForComponentBody
private Statement ::= (ReturnStatement|IfElseStatement|ExpressionStatement) ';'*{
}
ExpressionStatement ::= Expression (';'+ | &'}') {
pin=1
// recoverWhile=recoverWhileForComponentBody
}
ExpressionStatement ::= Expression (';' &Statement)?
//private recoverWhileStatement::=!(GenericIdentifier|';'|'}')
@@ -304,28 +313,27 @@ ReturnStatement ::= 'return' (Expression)?{
private IfElseStatement ::= IfStatement (ElseIfStatement)* ElseStatement?{
pin=1
}
IfStatement ::= IfKeyword Expression CodeBlock {
IfStatement ::= 'if' Expression CodeBlock {
pin=1
}
ElseIfStatement ::= ElseKeyword IfKeyword Expression CodeBlock{
ElseIfStatement ::= 'else' 'if' Expression CodeBlock{
pin=2
}
ElseStatement ::= ElseKeyword CodeBlock {
ElseStatement ::= 'else' CodeBlock {
pin=1
}
//动画定义
PropertyAnimation ::= AnimateKeyword ('*'| (QualifiedPropertyNameReference (',' QualifiedPropertyNameReference)*)) '{' QualifiedNamePropertyBinding*'}'{
PropertyAnimation ::= 'animate' ('*'| (QualifiedPropertyRef (',' QualifiedPropertyRef)*)) '{' QualifiedNamePropertyBinding*'}'{
pin=1
}
//组件属性绑定 name: xxx ; name : {}; name : {}
//只有对象定义和表达式需要 ;
private QualifiedNamePropertyBinding::= QualifiedPropertyNameReference ':' BindingStatement{
private QualifiedNamePropertyBinding::= QualifiedPropertyRef ':' BindingStatement{
pin=2
}
PropertyBinding ::= ReferenceIdentifier ':' BindingStatement{
pin=2
PropertyBinding ::= PropertyRef ':' BindingStatement{
}
private WhileIdentifier::=!('}'|';'|GenericIdentifier)
private WhileIdentifier::=!('}'|';'|Identifier)
//优先尝试表达式解析 {}属于代码块 {name:xx}属于表达式那么需要预测后面两个token,第二个token不是':'的情况下才是代码块
//所以优先判断对象创建判断,然后进行代码块判断,最后进行表达式
//代码块分号可选
@@ -359,7 +367,7 @@ Expression ::= AtExpression
{
recoverWhile=recoverForExpression
}
private recoverForExpression ::= !(Expression|GenericIdentifier|';')
private recoverForExpression ::= !(Expression|Identifier|';')
private BinaryExpression ::= SelfAssignExpression | ComparisonExpression | BooleanExpression
//加法表达式
@@ -440,7 +448,7 @@ ParenthesizedExpression ::= '(' Expression ')'{
pin=2
}
//fake PropertySuffixExpression::= Expression? '.' GenericIdentifier
PropertyExpression ::= Expression '.' GenericIdentifier {
PropertyExpression ::= Expression '.' Identifier {
// pin=2 extends=PropertySuffixExpression elementType=PropertySuffixExpression
}
FunctionInvocationExpression ::= Expression '(' InvocationArguments? ')'{
@@ -459,7 +467,7 @@ ArrayCreationExpression ::= '[' ArrayElements* ']'{
private ArrayElements ::= Expression (','Expression)* ','?
LiteralExpression ::= Numbers | RawStringLiteral | GenericIdentifier | RawColorLiteral
LiteralExpression ::= Numbers | RawStringLiteral | Identifier | RawColorLiteral
//-------------------------------------------------
@@ -471,91 +479,37 @@ ObjectCreationExpression ::= '{' ObjectPropertyBindings '}'{
private ObjectPropertyBindings ::= ObjectPropertyBinding (','ObjectPropertyBinding)* ','?{
pin=1
}
private ObjectPropertyBinding ::= PropertyName ':' Expression{
private ObjectPropertyBinding ::= Identifier ':' 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|InternalName|ExternalName")=Named
}
LocalVariable ::= GenericIdentifier{
LocalVariable ::= Identifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
}
FunctionName ::= GenericIdentifier
ComponentName ::=GenericIdentifier
InternalName ::= GenericIdentifier
PropertyName ::= GenericIdentifier
TypeName ::= GenericIdentifier
EnumName ::= GenericIdentifier
EnumValue ::=GenericIdentifier
ExternalName ::=GenericIdentifier
ReferenceIdentifier::=GenericIdentifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
fake Referred::=Identifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintRefIdentifierImpl"
}
//----------UnnamedIdentifier------------------
PropertyNameReference ::= GenericIdentifier{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
ComponentRef::=Identifier{
extends=Referred
}
FunctionReference ::=GenericIdentifier{
QualifiedPropertyRef ::= PropertyRef ('.' PropertyRef)*{
extends=PropertyRef
}
QualifiedPropertyNameReference ::= PropertyNameReference ('.' PropertyNameReference)*{
extends=PropertyNameReference
}
TypeNameReference::=GenericIdentifier
QualifiedTypeNameReference ::= TypeNameReference ('.' TypeNameReference)*{
extends=TypeNameReference
PropertyRef ::= Identifier{
extends=Referred
}
ModuleLocation ::= RawStringLiteral{
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
QualifiedName::=Identifier('.' Identifier)*
FunctionRef::=Identifier{
extends=Referred
}
ModuleRef::=RawStringLiteral{
extends=Referred
}
//
//noinspection BnfSuspiciousToken
@@ -565,7 +519,7 @@ private RawStringLiteral ::= StringLiteral
private Numbers ::= NumberLiteral
//noinspection BnfSuspiciousToken
private GenericIdentifier::= Identifier
private Identifier::= IDENTIFIER
//noinspection BnfSuspiciousToken
private RawColorLiteral ::= ColorLiteral

View File

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

@@ -4,7 +4,7 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public final class SlintElementType extends IElementType {
public class SlintElementType extends IElementType {
public SlintElementType(@NonNls @NotNull String rawKind) {

View File

@@ -0,0 +1,12 @@
package me.zhouxi.slint.lang.psi;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.parser.GeneratedParserUtilBase;
import com.intellij.psi.tree.IElementType;
/**
* @author zhouxi 2024/5/30
*/
public class SlintParserUtil extends GeneratedParserUtilBase {
}

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.psi.PsiReference;
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;
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);
}

View File

@@ -2,6 +2,7 @@ package me.zhouxi.slint.lang.psi.impl
import com.intellij.extapi.psi.StubBasedPsiElementBase
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubElement
@@ -14,5 +15,8 @@ abstract class SlintStubBasedPsiElementImpl<T : StubElement<out PsiElement>> :
constructor(stub: T, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
override fun toString(): String {
return StringUtil.trimEnd(this::class.java.simpleName, "Impl") + "(" + elementType + ")"
}
}

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
package me.zhouxi.slint.lang.psi.keyword;
import com.intellij.lang.ASTNode;
import me.zhouxi.slint.lang.psi.impl.SlintPsiElementImpl;
import org.jetbrains.annotations.NotNull;
public abstract class SlintPsiKeywordIdentifierImpl extends SlintPsiElementImpl implements SlintPsiKeywordIdentifier {
public SlintPsiKeywordIdentifierImpl(@NotNull ASTNode node) {
super(node);
}
@Override
public String toString() {
return "SlintKeyword:" + getText();
}
}

View File

@@ -3,7 +3,7 @@ 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 me.zhouxi.slint.lang.psi.SlintPsiRefIdentifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -12,9 +12,9 @@ import static me.zhouxi.slint.lang.SlintElementFactoryKt.createIdentifier;
/**
* @author zhouxi 2024/5/8
*/
public class SlintElementNameManipulator extends AbstractElementManipulator<SlintPsiReferencedIdentifier> {
public class SlintElementNameManipulator extends AbstractElementManipulator<SlintPsiRefIdentifier> {
@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();
if (identifier==null){
throw new IncorrectOperationException("identifier doesn't exist");
@@ -26,7 +26,7 @@ public class SlintElementNameManipulator extends AbstractElementManipulator<Slin
@Override
@NotNull
public TextRange getRangeInElement(@NotNull final SlintPsiReferencedIdentifier element) {
public TextRange getRangeInElement(@NotNull final SlintPsiRefIdentifier element) {
return new TextRange(0,element.getTextLength());
}
}

View File

@@ -0,0 +1,55 @@
package me.zhouxi.slint.completion
import com.intellij.codeInsight.AutoPopupController
import com.intellij.codeInsight.completion.CompletionPhase.EmptyAutoPopup
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
import com.intellij.codeInsight.lookup.LookupManager
import com.intellij.codeInsight.lookup.impl.LookupImpl
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
/**
* @author zhouxi 2024/5/29
*/
class SlintCompletionAutoPopupHandler : TypedHandlerDelegate() {
override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {
val lookup = LookupManager.getActiveLookup(editor) as LookupImpl?
val phase = CompletionServiceImpl.completionPhase
if (LOG.isDebugEnabled) {
LOG.debug("checkAutoPopup: character=$charTyped;")
LOG.debug("phase=$phase")
LOG.debug("lookup=$lookup")
LOG.debug("currentCompletion=" + CompletionServiceImpl.completionService.currentCompletion)
}
if (lookup != null) {
if (editor.selectionModel.hasSelection()) {
lookup.performGuardedChange(Runnable { EditorModificationUtil.deleteSelectedText(editor) })
}
return Result.STOP
}
if (Character.isLetterOrDigit(charTyped) || charTyped == '_' || charTyped == '@') {
if (phase is EmptyAutoPopup && phase.allowsSkippingNewAutoPopup(editor, charTyped)) {
return Result.CONTINUE
}
AutoPopupController.getInstance(project).scheduleAutoPopup(editor)
return Result.STOP
}
return Result.CONTINUE
}
companion object {
private val LOG = Logger.getInstance(
SlintCompletionAutoPopupHandler::class.java
)
}
}

View File

@@ -1,47 +1,31 @@
package me.zhouxi.slint.completion
import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.patterns.PlatformPatterns
import me.zhouxi.slint.completion.provider.*
import me.zhouxi.slint.lang.psi.SlintTypes
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
import me.zhouxi.slint.completion.provider.ComponentNameProvider
import me.zhouxi.slint.completion.provider.SubComponentNameProvider
class SlintCompletionContributor : CompletionContributor() {
init {
//文件级别
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintFileElementType)),
TopKeywordProvider
)
extend(TopElementProvider)
extend(BasicTypeProvider)
extend(AtChildrenCompletionProvider)
extend(ComponentElementKeywordProvider)
extend(ComponentNameProvider)
extend(PropertyBindingProvider)
extend(InheritsCompletionProvider)
extend(ExportElementProvider)
extend(SubComponentNameProvider)
extend(PropertyDeclarationProvider)
}
//类型定义
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintTypes.Type)),
BasicTypeProvider
)
//componentBody
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(2, PlatformPatterns.psiElement(Component)),
ElementKeywordProvider
)
//componentBody
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(2,PlatformPatterns.psiElement(Component)),
ComponentProvider
)
//propertyBinding
extend(
CompletionType.BASIC,
PlatformPatterns.psiElement().withAncestor(2, PlatformPatterns.psiElement(Component)),
PropertyBindingProvider
)
private fun extend(
provider: AbstractSlintCompletionProvider,
type: CompletionType = CompletionType.BASIC,
) {
extend(type, provider.pattern(), provider)
}
}

View File

@@ -0,0 +1,14 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.patterns.ElementPattern
import com.intellij.psi.PsiElement
/**
* @author zhouxi 2024/5/29
*/
abstract class AbstractSlintCompletionProvider : CompletionProvider<CompletionParameters>() {
abstract fun pattern(): ElementPattern<out PsiElement>
}

View File

@@ -0,0 +1,33 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
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.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component
/**
* @author zhouxi 2024/5/29
*/
object AtChildrenCompletionProvider : AbstractSlintCompletionProvider() {
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement()
.afterLeaf(psiElement().withText("@"))
.withParent(psiElement(Component))
}
val element = LookupElementBuilder.create("children").withIcon(AllIcons.Nodes.Annotationtype)
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addElement(element)
}
}

View File

@@ -1,13 +1,16 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
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.SlintPsiUtils.InternalTypes
import me.zhouxi.slint.lang.psi.SlintTypes.Type
object BasicTypeProvider : CompletionProvider<CompletionParameters>() {
object BasicTypeProvider : AbstractSlintCompletionProvider() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
@@ -17,4 +20,9 @@ object BasicTypeProvider : CompletionProvider<CompletionParameters>() {
}
private val basicTypes = InternalTypes.map { LookupElementBuilder.create(it) }
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withAncestor(3, psiElement(Type))
}
}

View File

@@ -1,12 +1,15 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
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
object ElementKeywordProvider : CompletionProvider<CompletionParameters>() {
object ComponentElementKeywordProvider : AbstractSlintCompletionProvider() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
@@ -18,6 +21,11 @@ object ElementKeywordProvider : CompletionProvider<CompletionParameters>() {
private val elementKeywords = arrayOf(
"in", "out", "in-out", "callback",
"property", "private", "changed",
"states", "transitions", "@children"
).map { LookupElementBuilder.create(it) }
"states", "transitions", "function"
).map { LookupElementBuilder.create(it).withBoldness(true) }
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(psiElement(Component))
.andNot(AtChildrenCompletionProvider.pattern())
}
}

View File

@@ -0,0 +1,41 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
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.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.ExportSpecifier
import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration
import me.zhouxi.slint.lang.psi.extension.relativePathOf
import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
object ComponentNameProvider : AbstractSlintCompletionProvider() {
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 currentFile = parameters.position.containingFile.originalFile.virtualFile
LookupElementBuilder.create(it).withTypeText(it.relativePathOf(currentFile)).withIcon(AllIcons.Nodes.Class)
}
result.addAllElements(lookups)
}
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement()
.withSuperParent(
2, or(
psiElement(InheritDeclaration),
psiElement(ExportSpecifier)
)
)
}
}

View File

@@ -1,49 +0,0 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys
object ComponentProvider : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
val project = parameters.position.project
val components = arrayListOf<SlintComponent>()
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 targetFile = it.containingFile.originalFile.virtualFile
val currentFile = parameters.position.containingFile.originalFile.virtualFile
val path = VfsUtil.findRelativePath(currentFile, targetFile, '/')
val builder =
LookupElementBuilder.create(it).withTypeText(path).withIcon(AllIcons.Nodes.Class)
LookupElementDecorator.withInsertHandler(builder) { _, item ->
println(item.psiElement)
}
}
result.addAllElements(lookups)
}
}

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() {
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

@@ -0,0 +1,28 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes
/**
* @author zhouxi 2024/5/29
*/
object InheritsCompletionProvider : AbstractSlintCompletionProvider() {
override fun pattern(): ElementPattern<out PsiElement> {
return PlatformPatterns.psiElement()
.afterLeaf(PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(SlintTypes.IDENTIFIER)))
}
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addElement(LookupElementBuilder.create("inherits"))
}
}

View File

@@ -5,30 +5,36 @@ import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
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.PsiElement
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintSubComponent
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.SubComponent
import me.zhouxi.slint.lang.psi.extension.inheritsProperties
import me.zhouxi.slint.lang.psi.extension.resolve
import me.zhouxi.slint.lang.psi.extension.toLookupElement
object PropertyBindingProvider : CompletionProvider<CompletionParameters>() {
object PropertyBindingProvider : AbstractSlintCompletionProvider() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
val element = parameters.position
val subComponent = element.parentOfType<SlintSubComponent>()
if (subComponent != null) {
val component = subComponent.referenceIdentifier.reference?.resolve() as SlintComponent? ?: return
val builders = component.inheritsProperties()
.map { LookupElementBuilder.create(it).withTypeText(component.name).withIcon(AllIcons.Nodes.Property) }
result.addAllElements(builders)
} else {
val component = element.parentOfType<SlintComponent>() ?: return
val builders = component.inheritsProperties()
.map { LookupElementBuilder.create(it).withTypeText(component.name).withIcon(AllIcons.Nodes.Property) }
result.addAllElements(builders)
val component = element.parentOfType<SlintSubComponent>()?.resolve()
?: element.parentOfType<SlintComponent>()
component?.let {
result.addAllElements(it.inheritsProperties().map { it.toLookupElement() })
}
}
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(or(psiElement(Component), psiElement(SubComponent)))
.andNot(AtChildrenCompletionProvider.pattern())
}
}

View File

@@ -0,0 +1,65 @@
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.codeInsight.lookup.LookupElementBuilder.*
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.patterns.StandardPatterns.or
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.SubComponent
/**
* @author zhouxi 2024/6/6
*/
object PropertyDeclarationProvider : AbstractSlintCompletionProvider() {
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(or(psiElement(Component), psiElement(SubComponent)))
}
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(lookup)
}
val lookup = arrayOf(
create("property"),
create("in property"),
create("out property"),
create("in-out property")
).flatMap { arrayListOf(it.withInsertHandler(InternalInsertHandler())) }
.map { it.withBoldness(true) }
class InternalInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val manager = TemplateManager.getInstance(context.project)
val template = manager.createTemplate("", "", "<\$Type$> \$Name$;").apply {
isToReformat = true
addVariable("Type", ConstantNode(""), 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,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() {
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

@@ -0,0 +1,62 @@
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.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
object TopElementProvider : AbstractSlintCompletionProvider() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(completion)
}
val completion = arrayListOf(
create("component"),
create("export component"),
create("export component").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
create("struct"),
create("export struct"),
create("export struct").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
create("enum"),
create("export enum"),
create("export enum").withTailText(" Name {...}").withInsertHandler(MyInsertHandler()),
).map { it.withBoldness(true) }
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withSuperParent(2, psiElement(SlintFileElementType))
.andNot(psiElement().withParent(psiElement(Component)))
}
class MyInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val manager = TemplateManager.getInstance(context.project)
val template = manager.createTemplate("", "", " \$Name$ { \n }").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

@@ -1,20 +0,0 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.util.ProcessingContext
object TopKeywordProvider : CompletionProvider<CompletionParameters>() {
override fun addCompletions(
parameters: CompletionParameters,
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(topKeywords)
}
private val topKeywords =
arrayOf("export", "component", "global", "enum", "struct").map { LookupElementBuilder.create(it) }
}

View File

@@ -0,0 +1,95 @@
package me.zhouxi.slint.formatter
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.psi.TokenType
import com.intellij.psi.formatter.common.AbstractBlock
import com.intellij.psi.tree.TokenSet
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.expressions
/**
* @author zhouxi 2024/5/28
*/
class FormattingBlock(
node: ASTNode,
wrap: Wrap?,
alignment: Alignment?,
private val indent: Indent?,
private val spacingBuilder: SpacingBuilder
) : AbstractBlock(node, wrap, alignment) {
override fun buildChildren(): List<Block> {
val childBlocks = arrayListOf<Block>()
val syntheticBlocks = arrayListOf<Block>()
var current = childBlocks
var myIndent: Indent? = indent
for (child in node.getChildren(null)) {
if (braces.contains(child.elementType) && syntheticParentTokens.contains(node.elementType)) {
current = syntheticBlocks
myIndent = Indent.getNormalIndent()
}
if (child.elementType == TokenType.WHITE_SPACE || child.textLength == 0) {
continue
}
if (Comment.contains(child.elementType)) {
current.add(FormattingBlock(child, wrap, alignment, myIndent, spacingBuilder))
continue
}
if (child.firstChildNode == null || leafTokens.contains(child.elementType)) {
current.add(LeafBlock(child, wrap, alignment, Indent.getNoneIndent(), spacingBuilder))
continue
}
if (expressions.contains(child.elementType)) {
current.add(FormattingBlock(child, wrap, alignment, null, spacingBuilder))
continue
}
current.add(FormattingBlock(child, wrap, alignment, myIndent, spacingBuilder))
}
if (syntheticBlocks.isNotEmpty()) {
childBlocks.add(SyntheticBlock(syntheticBlocks, wrap, Indent.getNormalIndent(), alignment, spacingBuilder))
}
return childBlocks
}
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? {
if (child2 is SyntheticBlock) {
return Spacing.createSpacing(1, 1, 0, true, 2)
}
return spacingBuilder.getSpacing(this, child1, child2)
}
override fun isLeaf(): Boolean {
return false
}
companion object {
private val Comment = TokenSet.create(BlockComment, LineComment)
val leafTokens =
TokenSet.create(
ComponentRef,
PropertyRef,
FunctionRef,
TypeRef,
LocalVariable,
PropertyModifier
)
private val syntheticParentTokens = TokenSet.create(
SubComponent,
Component,
GlobalSingleton
)
}
}

View File

@@ -0,0 +1,58 @@
package me.zhouxi.slint.formatter
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.formatter.common.ExtraRangesProvider
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(
private val treeNode: ASTNode,
private val wrap: Wrap?,
private val alignment: Alignment?,
private val indent: Indent?,
private val spacingBuilder: SpacingBuilder
) : ASTBlock, ExtraRangesProvider {
override fun getNode() = treeNode
override fun getTextRange(): TextRange = treeNode.textRange
override fun getSubBlocks() = EMPTY_SUB_BLOCKS
override fun getWrap() = wrap
override fun getIndent() = indent
override fun getAlignment() = alignment
override fun getSpacing(child1: Block?, child2: Block) = null
override fun getChildAttributes(newChildIndex: Int) = ChildAttributes(indent, null)
override fun isIncomplete() = false
override fun isLeaf() = true
override fun getExtraRangesToFormat(info: FormattingRangesInfo): List<TextRange>? {
val startOffset = textRange.startOffset
if (info.isOnInsertedLine(startOffset) && treeNode.textLength == 1 && treeNode.textContains('}')) {
val parent = treeNode.treeParent
return NodeIndentRangesCalculator(parent).calculateExtraRanges()
}
return null
}
override fun toString(): String {
return javaClass.simpleName + " '" + StringUtil.escapeLineBreak(node.text) + "' " + textRange
}
companion object {
private val EMPTY_SUB_BLOCKS = ArrayList<Block>()
}
}

View File

@@ -0,0 +1,61 @@
package me.zhouxi.slint.formatter
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.tree.TokenSet
import me.zhouxi.slint.lang.SlintLanguage
import me.zhouxi.slint.lang.psi.SlintTypes.*
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
/**
* @author zhouxi 2024/5/7
*/
class SlintFormatterModelBuilder : FormattingModelBuilder {
override fun createModel(formattingContext: FormattingContext): FormattingModel {
val element = formattingContext.psiElement
val spacingBuilder = createSpaceBuilder(formattingContext.codeStyleSettings)
val slintBlock = FormattingBlock(
formattingContext.node,
null,
null,
Indent.getNoneIndent(),
spacingBuilder
)
return FormattingModelProvider.createFormattingModelForPsiFile(
element.containingFile,
slintBlock,
formattingContext.codeStyleSettings
)
}
override fun getRangeAffectingIndent(file: PsiFile, offset: Int, elementAtOffset: ASTNode): TextRange? {
return file.textRange
}
companion object {
private fun createSpaceBuilder(settings: CodeStyleSettings): SpacingBuilder {
return SpacingBuilder(settings, SlintLanguage.INSTANCE)
.after(Comma)
.spaces(1)
.before(Comma)
.spaces(0)
.before(Semicolon)
.spacing(0, 0, 0, false, 0)
.after(TokenSet.create(Semicolon))
.lineBreakInCode()
.after(Colon)
.spaces(1)
.around(TokenSet.create(DoubleArrow, Plus, Minus, Star, Div))
.spaces(1)
.before(CodeBlock)
.spacing(1, 1, 0, true, 2)
.after(RAngle)
.spaces(1)
.afterInside(TokenSet.ANY, SlintFileElementType)
.lineBreakInCode()
}
}
}

View File

@@ -20,19 +20,19 @@ class SlintLineIndentProvider : JavaLikeLangLineIndentProvider() {
}
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)
val SyntaxMap: Map<IElementType, SyntaxElement> = mapOf(
SlintTypes.LBracket to JavaLikeElement.BlockOpeningBrace,
SlintTypes.RBracket to JavaLikeElement.BlockClosingBrace,
SlintTypes.LBrace to JavaLikeElement.BlockOpeningBrace,
SlintTypes.RBrace to JavaLikeElement.BlockClosingBrace,
TokenType.WHITE_SPACE to JavaLikeElement.Whitespace,
SlintTypes.Semicolon to JavaLikeElement.Semicolon,
SlintTypes.LineComment to JavaLikeElement.LineComment,
SlintTypes.BlockComment to JavaLikeElement.BlockComment,
SlintTypes.Colon to JavaLikeElement.Colon,
SlintTypes.Comma to JavaLikeElement.Comma,
SlintTypes.LParent to JavaLikeElement.LeftParenthesis,
SlintTypes.RParent to JavaLikeElement.RightParenthesis
)
}
}

View File

@@ -0,0 +1,51 @@
package me.zhouxi.slint.formatter
import com.intellij.formatting.*
import com.intellij.openapi.util.TextRange
import com.intellij.psi.formatter.common.ExtraRangesProvider
import com.intellij.psi.formatter.common.NodeIndentRangesCalculator
import me.zhouxi.slint.lang.psi.SlintTypes
import me.zhouxi.slint.lang.psi.SlintTypes.LBrace
import me.zhouxi.slint.lang.psi.SlintTypes.RBrace
/**
* @author zhouxi 2024/5/28
*/
class SyntheticBlock(
private val subBlocks: List<Block>,
private val wrap: Wrap?,
private val indent: Indent,
private val alignment: Alignment?,
private val spacingBuilder: SpacingBuilder
) : Block {
override fun getTextRange(): TextRange {
return TextRange(subBlocks.first().textRange.startOffset, subBlocks.last().textRange.endOffset)
}
override fun getSubBlocks(): List<Block> {
return subBlocks
}
override fun getWrap() = wrap
override fun getIndent() = indent
override fun getAlignment() = alignment
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
//如果是连续的{}
if (child1 is ASTBlock && child2 is ASTBlock
&& child1.node!!.elementType == LBrace && child2.node!!.elementType == RBrace
) {
return Spacing.createSpacing(1, 1, 0, false, 0)
}
return Spacing.createSpacing(0, 1, 1, true, 1)
}
override fun getChildAttributes(newChildIndex: Int) =
ChildAttributes(Indent.getNormalIndent(), subBlocks[0].alignment)
override fun isIncomplete() = false
override fun isLeaf() = false
}

View File

@@ -5,7 +5,9 @@ import com.intellij.openapi.editor.HighlighterColors
import com.intellij.openapi.editor.colors.TextAttributesKey
object Definitions {
@JvmField
val _Annotation =
TextAttributesKey.createTextAttributesKey("_Annotation", DefaultLanguageHighlighterColors.METADATA)
val _KeyWord: TextAttributesKey =
TextAttributesKey.createTextAttributesKey("_KeyWord", DefaultLanguageHighlighterColors.KEYWORD)
@@ -55,4 +57,6 @@ object Definitions {
val SemiColon: Array<TextAttributesKey> = arrayOf(_SemiColon)
val Error: Array<TextAttributesKey> = arrayOf(_Error)
val Annotation: Array<TextAttributesKey> = arrayOf(_Annotation)
}

View File

@@ -6,37 +6,52 @@ import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.psi.PsiElement
import me.zhouxi.slint.lang.psi.*
import me.zhouxi.slint.lang.psi.keyword.SlintPsiKeywordIdentifier
class KeywordHighlightAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if (element is SlintPsiKeywordIdentifier) {
if (element is SlintChildrenPlaceholder) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(Definitions._Annotation)
.create()
return
}
if (element is SlintTypeRef && SlintPsiUtils.isInternalType(element)) {
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) {
if (element is SlintPropertyRef) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create()
return
}
if (element is SlintProperty) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.identifier)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.create()
return
}
if (element is SlintPropertyBinding) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.getReferenceIdentifier())
.range(element.propertyRef)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
.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)
.range(element)
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD)

View File

@@ -3,8 +3,10 @@ package me.zhouxi.slint.lang
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.util.PsiTreeUtil
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintFile
import me.zhouxi.slint.lang.psi.SlintImportSpecifier
/**
* @author zhouxi 2024/5/8
@@ -13,6 +15,26 @@ import me.zhouxi.slint.lang.psi.SlintFile
fun createIdentifier(project: Project?, text: String): PsiElement {
val factory = PsiFileFactory.getInstance(project)
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 {
val factory = PsiFileFactory.getInstance(project)
val file = factory.createFileFromText(
"dummy.slint",
SlintLanguage.INSTANCE,
"""import {$text} from "$location";"""
) as SlintFile
return file.firstChild
}
fun createImportSpecifier(project: Project?, text: String): PsiElement {
val element = createImport(project, text, "-")
return PsiTreeUtil.findChildOfType(element, SlintImportSpecifier::class.java)!!
}
fun createComma(project: Project?): PsiElement {
val factory = PsiFileFactory.getInstance(project)
val file = factory.createFileFromText("dummy.slint", SlintLanguage.INSTANCE, ",")
return file.children[0].children[0]
}

View File

@@ -1,12 +0,0 @@
package me.zhouxi.slint.lang.psi.extension
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintProperty
fun SlintComponent.inheritsProperties(): List<SlintProperty> {
val properties = this.propertyList
val inherit =
this.inheritDeclaration?.referenceIdentifier?.reference?.resolve() as SlintComponent? ?: return properties
return properties + inherit.inheritsProperties()
}

View File

@@ -0,0 +1,69 @@
package me.zhouxi.slint.lang.psi.extension
import com.intellij.codeInsight.lookup.LookupElementBuilder
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.PsiFile
import com.intellij.psi.util.parentOfType
import me.zhouxi.slint.lang.psi.*
fun SlintComponent.inheritsProperties(): List<SlintProperty> {
val properties = this.propertyList
val inherit =
this.inheritDeclaration?.componentRef?.resolve() ?: return properties
if (inherit == this) {
return properties
}
return properties + inherit.inheritsProperties()
}
fun SlintComponent.relativePathOf(element: PsiElement): String? {
return relativePathOf(element.containingFile)
}
fun SlintComponent.relativePathOf(psiFile: PsiFile): String? {
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()
}
/**
* 导入的有效的名字,
* psiElement可能是identifier或者internalName
*/
fun SlintImport.availableNames(): List<PsiElement> {
return this.importElement?.importSpecifierList?.map {
it.importAlias?.identifier ?: it.componentRef
} ?: emptyList()
}
fun SlintSubComponent.resolve(): SlintComponent? {
return this.componentRef.reference?.resolve() as SlintComponent?
}
fun SlintProperty.toLookupElement(): LookupElementBuilder {
return LookupElementBuilder.create(this)
.withTypeText(this.parentOfType<SlintComponent>()?.identifier?.text)
.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

@@ -5,8 +5,8 @@ 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.SlintPsiNamedElement
import me.zhouxi.slint.lang.psi.SlintTypes
/**
* @author zhouxi 2024/5/15
@@ -14,7 +14,7 @@ import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
abstract class SlintPsiNamedElementImpl(node: ASTNode) : SlintPsiElementImpl(node),
SlintPsiNamedElement {
override fun getNameIdentifier(): PsiElement? {
return findChildByClass(SlintNamed::class.java) ?: this
return findChildByType(SlintTypes.IDENTIFIER) ?: this
}
override fun getName(): String? {

View File

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

View File

@@ -1,15 +1,18 @@
package me.zhouxi.slint.lang.psi.stubs.index
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex
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> {
val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: GlobalSearchScope.projectScope(project)
val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: projectScope(project)
return StubIndex.getElements(
StubIndexKeys.Component,
key,
@@ -17,8 +20,29 @@ fun searchComponent(key: String, project: Project, psiFile: PsiFile? = null): Co
SlintComponent::class.java
)
}
fun searchProperty(key: String,project: Project,psiElement: PsiElement){
// GlobalSearchScope;
// StubIndex.getInstance().getAllKeys()
/**
* 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

@@ -1,7 +1,5 @@
package me.zhouxi.slint.lang.psi.stubs.stub.impl
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.stubs.StubBase
import com.intellij.psi.stubs.StubElement
import com.intellij.util.BitUtil
import me.zhouxi.slint.lang.psi.SlintProperty

View File

@@ -1,6 +1,5 @@
package me.zhouxi.slint.lang.psi.stubs.types
import com.intellij.lang.ASTNode
import com.intellij.lang.LighterAST
import com.intellij.lang.LighterASTNode
import com.intellij.lang.LighterASTTokenNode
@@ -10,9 +9,9 @@ import me.zhouxi.slint.lang.SlintLanguage
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintTypes.*
import me.zhouxi.slint.lang.psi.impl.SlintComponentImpl
import me.zhouxi.slint.lang.psi.stubs.stub.impl.SlintComponentStubImpl
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys
import me.zhouxi.slint.lang.psi.stubs.stub.SlintComponentStub
import me.zhouxi.slint.lang.psi.stubs.stub.impl.SlintComponentStubImpl
import java.io.IOException
/**
@@ -24,15 +23,11 @@ object SlintComponentElementType :
return SlintComponentImpl(stub, this)
}
fun createPsi(node: ASTNode): SlintComponent {
return SlintComponentImpl(node)
}
override fun createStub(psi: SlintComponent, parentStub: StubElement<out PsiElement?>): SlintComponentStub {
return SlintComponentStubImpl(
parentStub,
psi.exportKeyword != null,
psi.componentName!!.text
false,
psi.identifier!!.text
)
}
@@ -54,11 +49,13 @@ object SlintComponentElementType :
}
override fun createStub(tree: LighterAST, node: LighterASTNode, parentStub: StubElement<*>): SlintComponentStub {
val exported = tree.getChildren(node).any { it.tokenType == ExportKeyword }
val identifier = tree.getChildren(node).first { it.tokenType == ComponentName }
val token = tree.getChildren(identifier)[0] as LighterASTTokenNode
val token = tree.getChildren(node).find { it.tokenType == IDENTIFIER }
?.let {
val text = it as LighterASTTokenNode
tree.charTable.intern(text.text)
}
return SlintComponentStubImpl(parentStub, exported, tree.charTable.intern(token.text).toString())
return SlintComponentStubImpl(parentStub, false, token.toString())
}
override fun indexStub(stub: SlintComponentStub, sink: IndexSink) {

View File

@@ -25,16 +25,18 @@ object SlintImportSpecifierElementType :
): SlintImportSpecifierStub {
val children = tree.getChildren(node);
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
tree.charTable.intern(token.text).toString()
}
val alias = run {
val aliasNode = children.firstOrNull { it.tokenType == ImportAlias }
aliasNode?.let {
val nameNode = tree.getChildren(aliasNode).firstOrNull { it.tokenType == InternalName }
val token = nameNode?.let { tree.getChildren(nameNode) }?.first() as LighterASTTokenNode
tree.charTable.intern(token.text).toString()
val nameNode = tree.getChildren(aliasNode).firstOrNull { it.tokenType == IDENTIFIER }
val token = nameNode?.let { tree.getChildren(nameNode) }?.firstOrNull() as LighterASTTokenNode?
token?.let {
tree.charTable.intern(it.text).toString()
}
}
}
return SlintImportSpecifierStubImpl(parentStub, name, alias)
@@ -46,8 +48,8 @@ object SlintImportSpecifierElementType :
): SlintImportSpecifierStub {
return SlintImportSpecifierStubImpl(
parentStub,
psi.referenceIdentifier.text,
psi.importAlias?.internalName?.text
psi.componentRef.text,
psi.importAlias?.identifier?.text
)
}

View File

@@ -19,15 +19,8 @@ object SlintPropertyElementType :
ILightStubElementType<SlintPropertyStub, SlintProperty?>("SlintProperty", SlintLanguage.INSTANCE) {
override fun createStub(tree: LighterAST, node: LighterASTNode, parentStub: StubElement<*>): SlintPropertyStub {
val children = tree.getChildren(node)
val modifierNode = children.firstOrNull { it.tokenType == PropertyModifier }?.let {
when (tree.getChildren(it)[0].tokenType) {
InKeyword -> 1
OutKeyword -> 2
InOutKeyword -> 3
else -> 0
}
} ?: 0
val name = tree.getChildren(children.first { it.tokenType == PropertyName })[0] as LighterASTTokenNode
val modifierNode = 0
val name = children.first { it.tokenType == IDENTIFIER } as LighterASTTokenNode
return SlintPropertyStubImpl(
parentStub,
modifierNode.toShort(),
@@ -47,11 +40,8 @@ object SlintPropertyElementType :
psi: SlintProperty,
parentStub: StubElement<out PsiElement?>
): SlintPropertyStub {
val modifier = psi.propertyModifier
val isIn = modifier?.inKeyword != null || modifier?.inOutKeyword != null
val isOut = modifier?.outKeyword != null || modifier?.inOutKeyword != null
val name = psi.propertyName!!.text
val flag = SlintPropertyStubImpl.pack(isIn, isOut)
val name = psi.identifier.text
val flag: Short = 0
return SlintPropertyStubImpl(parentStub, flag, name)
}

View File

@@ -1,118 +0,0 @@
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<SlintProperty>
): SlintProperty? {
if (component is SlintSubComponent) {
return searchProperty(component, predicate)
}
if (component is SlintComponent) {
return searchElementInParents(component, predicate) { it.propertyList }
}
return null
}
fun searchProperty(
subComponent: SlintSubComponent?,
predicate: Predicate<SlintProperty>
): SlintProperty? {
val component = resolveComponent(subComponent?.referenceIdentifier) ?: return null
return searchElementInParents(component, predicate) { it.propertyList }
}
fun searchCallback(
subComponent: SlintSubComponent?,
predicate: Predicate<SlintCallback>
): SlintCallback? {
val component = subComponent?.referenceIdentifier?.reference?.resolve() ?: return null
return searchElementInParents(
component as SlintComponent,
predicate
) { it.callbackList }
}
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)
}

View File

@@ -0,0 +1,15 @@
package me.zhouxi.slint.lang.psi.utils
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import me.zhouxi.slint.lang.psi.SlintTypes
val braces = TokenSet.create(
SlintTypes.LBrace,
SlintTypes.LParent,
SlintTypes.LBracket
)
val expressions = TokenSet.create(*SlintTypes::class.java.declaredFields
.filter { it.name.endsWith("Expression") }
.map { it.get(null) as IElementType }.toTypedArray()
)

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

@@ -1,12 +1,11 @@
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!!)
// val manager = RunManager.getInstance(e.project!!)
// e.si
// manager.createConfiguration()

View File

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

View File

@@ -10,17 +10,17 @@ import java.nio.file.Paths
object SlintViewer {
private val executable = if (SystemInfo.isWindows) {
"slint-viewer-windows.exe"
"slint-lsp-windows.exe"
} else if (SystemInfo.isLinux) {
"slint-viewer-linux"
"slint-lsp-linux"
} else if (SystemInfo.isMac) {
"slint-viewer-macos"
"slint-lsp-macos"
} else {
throw RuntimeException("Unsupported OS")
}
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) {
//component Reference
registrar.registerReferenceProvider(
psiElement(ReferenceIdentifier)
.withParent(
or(
psiElement(SubComponent),
psiElement(Component),
psiElement(InheritDeclaration),
psiElement(ImportSpecifier)
)
),
psiElement(ComponentRef),
ComponentReferenceProvider
)
//moduleLocation Reference
registrar.registerReferenceProvider(
psiElement(ModuleLocation),
psiElement(ModuleRef),
ModuleLocationReferenceProvider()
)
//property binding
registrar.registerReferenceProvider(
psiElement().withParent(
or(
psiElement(PropertyBinding),
psiElement(Component),
psiElement(SubComponent)
)
),
psiElement(PropertyRef),
PropertyReferenceProvider
)
}

View File

@@ -15,27 +15,27 @@ import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
*/
object ComponentReferenceProvider : PsiReferenceProvider() {
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? {
val file = element.containingFile as SlintFile
file.childrenOfType<SlintComponent>()
.find { it.componentName?.textMatches(element) == true }
.find { it.identifier?.textMatches(element) == true }
?.let { return it }
val specifier = if (element.parent is SlintImportSpecifier) {
element.parent as SlintImportSpecifier
} else {
file.childrenOfType<SlintImport>()
.flatMap { it.importElement?.importSpecifierList ?: arrayListOf() }
.find { (it.importAlias?.internalName ?: it.referenceIdentifier).textMatches(element) }
.find { (it.importAlias?.identifier ?: it.componentRef).textMatches(element) }
?: return null
}
val componentName = specifier.referenceIdentifier.text
val location = specifier.parentOfType<SlintImportElement>()?.moduleLocation ?: return null
val componentName = specifier.componentRef.text
val location = specifier.parentOfType<SlintImportElement>()?.moduleRef ?: return null
val targetFile = location.reference?.resolve()?.containingFile ?: return null
val components = searchComponent(componentName, element.project, targetFile)
return components.firstOrNull()

View File

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

View File

@@ -5,13 +5,11 @@ 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.extension.inheritsProperties
import me.zhouxi.slint.lang.psi.utils.searchProperty
import me.zhouxi.slint.lang.psi.extension.resolve
object PropertyReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
@@ -22,11 +20,11 @@ object PropertyReferenceProvider : PsiReferenceProvider() {
override fun resolve(): PsiElement? {
val subComponent = element.parentOfType<SlintSubComponent>()
if (subComponent != null) {
val component = subComponent.referenceIdentifier.reference?.resolve() as SlintComponent? ?: return null
return component.inheritsProperties().find { it.propertyName?.textMatches(element) == true }
val component = subComponent.resolve() ?: return null
return component.inheritsProperties().find { it.identifier.textMatches(element) }
}
val component = element.parentOfType<SlintComponent>() ?: return null
return component.inheritsProperties().find { it.propertyName?.textMatches(element) == true }
return component.inheritsProperties().find { it.identifier.textMatches(element) }
}
}
}

View File

@@ -11,6 +11,12 @@
<depends>com.intellij.modules.platform</depends>
<extensions defaultExtensionNs="com.intellij">
<platform.lsp.serverSupportProvider implementation="me.zhouxi.slint.lsp.SlintLspServerSupportProvider"/>
<typedHandler implementation="me.zhouxi.slint.completion.SlintCompletionAutoPopupHandler"
id="completionAutoPopup"
order="first"/>
<fileType name="Slint File"
implementationClass="me.zhouxi.slint.lang.psi.SlintFileType"
fieldName="INSTANCE"
@@ -26,7 +32,7 @@
<lang.braceMatcher language="Slint"
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"/>
<completion.contributor language="Slint"
@@ -55,6 +61,8 @@
<stubIndex implementation="me.zhouxi.slint.lang.psi.stubs.index.ImportNameIndex"/>
<lang.formatter language="Slint" implementationClass="me.zhouxi.slint.formatter.SlintFormatterModelBuilder"/>
</extensions>
<actions>
<action id="slint.preview" class="me.zhouxi.slint.preview.PreviewAction"

View File

@@ -1,7 +1,6 @@
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;
@@ -16,7 +15,6 @@ import java.util.concurrent.TimeUnit;
/**
* @author zhouxi 2024/4/30
*/
@Slf4j
public class LexerTest extends LexerTestCase {
@Override
@@ -44,12 +42,12 @@ public class LexerTest extends LexerTestCase {
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));
// 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));
// 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 {
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++) {
var source = """
component App{
aaa;
property name;
}
""";
doCodeTest(source);
}
started.stop();
System.out.println(started.getNanoTime()/50000);
}
}

View File

@@ -1,5 +1,10 @@
/* dsadas */
/* dsadas */
"dasdasd\""
"dada"
"
import {AboutSlint} from "std-widgets.slint";
component export component App inherits Window {
property <int> name;
function name1() {
}
adc:=AboutSlint { }
}
export { App as Aap }