fix: 语法问题
This commit is contained in:
@@ -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-2024.1")
|
||||
sandboxDir.set("idea-sandbox")
|
||||
plugins.set(listOf("java","Kotlin"))
|
||||
plugins.set(listOf("java"))
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
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]*'
|
||||
@@ -70,26 +70,25 @@ private recoverTopElement ::= !('component' | 'struct' | 'enum' | 'global'| 'exp
|
||||
private DocumentElement ::= Import | Struct | Enum | GlobalSingleton | Component | Export {
|
||||
recoverWhile=recoverTopElement
|
||||
}
|
||||
GlobalSingleton ::= ExportKeyword? GlobalKeyword ComponentName '{' ComponentElement* '}' {
|
||||
GlobalSingleton ::= ExportKeyword? GlobalKeyword 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 ::= ImportKeyword (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)* '}' FromKeyword 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"
|
||||
@@ -97,21 +96,21 @@ ImportSpecifier ::= ReferenceIdentifier ImportAlias?{
|
||||
}
|
||||
private AliasNameRecover::=!(','|'}'|';')
|
||||
|
||||
ImportAlias ::= AsKeyword InternalName {
|
||||
ImportAlias ::= AsKeyword 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 ::= ExportKeyword? StructKeyword Identifier (':=')? StructBody {
|
||||
pin=2
|
||||
}
|
||||
private StructBody ::= '{' FieldDeclarations? '}'{
|
||||
pin=1
|
||||
}
|
||||
//EnumDeclaration
|
||||
Enum ::= ExportKeyword? EnumKeyword EnumName '{' (EnumValue (','EnumValue)*','? )? '}'{
|
||||
Enum ::= ExportKeyword? EnumKeyword Identifier '{' (Identifier (','Identifier)*','? )? '}'{
|
||||
pin=2
|
||||
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
|
||||
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
|
||||
@@ -125,19 +124,19 @@ Export ::= ExportKeyword (ExportType | ExportModule) {
|
||||
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 ::= AsKeyword Identifier{
|
||||
pin=1
|
||||
}
|
||||
ExportModule ::= '*' FromKeyword ModuleLocation ';'{
|
||||
ExportModule ::= '*' FromKeyword ModuleRef ';'{
|
||||
pin=1
|
||||
}
|
||||
|
||||
Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration? '{' ComponentElement* '}' {
|
||||
Component ::= ExportKeyword? ComponentKeyword Identifier InheritDeclaration? '{' ComponentElement* '}' {
|
||||
pin=2
|
||||
implements=[
|
||||
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
|
||||
@@ -148,7 +147,7 @@ Component ::= ExportKeyword? ComponentKeyword ComponentName InheritDeclaration?
|
||||
}
|
||||
//组件定义
|
||||
//private LegacyComponent ::= (ComponentName|NamedIdentifier ':=' ) ComponentBody
|
||||
InheritDeclaration ::= InheritsKeyword ReferenceIdentifier {
|
||||
InheritDeclaration ::= InheritsKeyword ComponentRef {
|
||||
pin=1
|
||||
recoverWhile=recoverInherit
|
||||
}
|
||||
@@ -157,18 +156,18 @@ private recoverInherit::=!('{')
|
||||
private ComponentElement ::=ChildrenPlaceholder| Property | Callback
|
||||
| Function | PropertyAnimation | CallbackConnection | Transitions
|
||||
| PropertyChanged
|
||||
| States | TwoWayBinding|PropertyBinding | ConditionalElement
|
||||
| RepetitionElement | SubComponent {
|
||||
recoverWhile(".*")=recoverWhileForComponentBody
|
||||
| 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|';'){
|
||||
Property ::= PropertyModifier? PropertyKeyword ('<' Type '>')? Identifier (PropertyValue|PropertyTwoWayBindingValue|';'){
|
||||
pin=2
|
||||
implements=[
|
||||
"me.zhouxi.slint.lang.psi.SlintPsiNamedElement"
|
||||
@@ -181,7 +180,7 @@ PropertyModifier ::= InKeyword|OutKeyword|InOutKeyword|PrivateKeyword
|
||||
private PropertyValue::= ':' BindingStatement {
|
||||
pin=1
|
||||
}
|
||||
private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyNameReference ';' {
|
||||
private PropertyTwoWayBindingValue ::= '<=>' QualifiedPropertyRef ';' {
|
||||
pin=1
|
||||
}
|
||||
//--------------------------------PropertyChanged Start----------------------------------------------------
|
||||
@@ -190,29 +189,29 @@ PropertyChanged ::= ChangedKeyword LocalVariable '=>' CodeBlock{
|
||||
}
|
||||
//--------------------------------CallbackDeclaration Start----------------------------------------------------
|
||||
// 回调定义 pure callback abc()->int; callback abc; callback(..);callback()->type;
|
||||
Callback ::= PureKeyword? CallbackKeyword FunctionName CallbackBinding? ';'{
|
||||
Callback ::= PureKeyword? CallbackKeyword Identifier 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){
|
||||
private ReturnType ::= '->' (QualifiedTypeRef|ArrayType){
|
||||
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{
|
||||
Function ::= FunctionModifiers? FunctionKeyword Identifier FunctionArguments ReturnType? CodeBlock{
|
||||
pin=2
|
||||
implements=["me.zhouxi.slint.lang.psi.SlintPsiNamedElement"]
|
||||
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
|
||||
@@ -225,7 +224,7 @@ private FunctionArguments ::= '(' FieldDeclarations* ')'{
|
||||
|
||||
//--------------------------------CallbackConnectionDeclaration Start---------------------------------------------------
|
||||
//回调绑定定义 abc()=> {}
|
||||
CallbackConnection ::= FunctionReference CallbackConnectionArguments? '=>' CodeBlock ';'?{
|
||||
CallbackConnection ::= FunctionRef CallbackConnectionArguments? '=>' CodeBlock ';'?{
|
||||
pin=3
|
||||
}
|
||||
private CallbackConnectionArguments ::= '(' (LocalVariable (',' LocalVariable)* ','?)? ')'
|
||||
@@ -245,10 +244,11 @@ RepetitionIndex ::= '[' LocalVariable ']'{
|
||||
}
|
||||
//--------------------------------SubElementDeclaration Start---------------------------------------------------
|
||||
//子组件结构元素定义
|
||||
SubComponent ::= (PropertyName ':=')? ReferenceIdentifier '{' ComponentElement* '}'{
|
||||
SubComponent ::= (Identifier ':=')? ComponentRef '{' ComponentElement* '}'{
|
||||
pin=3
|
||||
recoverWhile=recoverWhileForComponentBody
|
||||
// recoverWhile=recoverForBrace
|
||||
}
|
||||
//private recoverForBrace::=!'}'
|
||||
|
||||
//--------------------------------TransitionsDeclaration Start---------------------------------------------------
|
||||
//过渡绑定
|
||||
@@ -272,7 +272,7 @@ State ::= LocalVariable StateCondition? ':' '{' StateItem* '}' {
|
||||
extends="me.zhouxi.slint.lang.psi.impl.SlintPsiNamedElementImpl"
|
||||
recoverWhile=recoverForRBracket
|
||||
}
|
||||
private recoverForRBracket::=!(']'|'}'|';'|GenericIdentifier)
|
||||
private recoverForRBracket::=!(']'|'}'|';'|Identifier)
|
||||
StateCondition ::= WhenKeyword Expression {
|
||||
pin=1
|
||||
}
|
||||
@@ -283,12 +283,11 @@ StateTransition ::= (InKeyword|OutKeyword) ':' '{' PropertyAnimation* '}'{
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
//类型定义
|
||||
Type ::= NamedType | UnnamedType | ArrayType
|
||||
private NamedType ::= QualifiedTypeNameReference
|
||||
Type ::= QualifiedTypeRef | UnnamedType | ArrayType
|
||||
ArrayType ::= '[' Type ']'
|
||||
UnnamedType ::= '{' FieldDeclarations* '}'
|
||||
private FieldDeclarations ::= FieldDeclaration (',' FieldDeclaration)* ','?
|
||||
private FieldDeclaration ::= PropertyName ':' Type
|
||||
private FieldDeclaration ::= Identifier ':' Type
|
||||
|
||||
|
||||
//代码块
|
||||
@@ -320,18 +319,17 @@ ElseStatement ::= ElseKeyword CodeBlock {
|
||||
pin=1
|
||||
}
|
||||
//动画定义
|
||||
PropertyAnimation ::= AnimateKeyword ('*'| (QualifiedPropertyNameReference (',' QualifiedPropertyNameReference)*)) '{' QualifiedNamePropertyBinding*'}'{
|
||||
PropertyAnimation ::= AnimateKeyword ('*'| (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不是':'的情况下才是代码块
|
||||
//所以优先判断对象创建判断,然后进行代码块判断,最后进行表达式
|
||||
//代码块分号可选
|
||||
@@ -365,7 +363,7 @@ Expression ::= AtExpression
|
||||
{
|
||||
recoverWhile=recoverForExpression
|
||||
}
|
||||
private recoverForExpression ::= !(Expression|GenericIdentifier|';')
|
||||
private recoverForExpression ::= !(Expression|Identifier|';')
|
||||
|
||||
private BinaryExpression ::= SelfAssignExpression | ComparisonExpression | BooleanExpression
|
||||
//加法表达式
|
||||
@@ -446,7 +444,7 @@ ParenthesizedExpression ::= '(' Expression ')'{
|
||||
pin=2
|
||||
}
|
||||
//fake PropertySuffixExpression::= Expression? '.' GenericIdentifier
|
||||
PropertyExpression ::= Expression '.' GenericIdentifier {
|
||||
PropertyExpression ::= Expression '.' Identifier {
|
||||
// pin=2 extends=PropertySuffixExpression elementType=PropertySuffixExpression
|
||||
}
|
||||
FunctionInvocationExpression ::= Expression '(' InvocationArguments? ')'{
|
||||
@@ -465,7 +463,7 @@ ArrayCreationExpression ::= '[' ArrayElements* ']'{
|
||||
private ArrayElements ::= Expression (','Expression)* ','?
|
||||
|
||||
|
||||
LiteralExpression ::= Numbers | RawStringLiteral | GenericIdentifier | RawColorLiteral
|
||||
LiteralExpression ::= Numbers | RawStringLiteral | Identifier | RawColorLiteral
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
@@ -477,7 +475,7 @@ ObjectCreationExpression ::= '{' ObjectPropertyBindings '}'{
|
||||
private ObjectPropertyBindings ::= ObjectPropertyBinding (','ObjectPropertyBinding)* ','?{
|
||||
pin=1
|
||||
}
|
||||
private ObjectPropertyBinding ::= PropertyName ':' Expression{
|
||||
private ObjectPropertyBinding ::= Identifier ':' Expression{
|
||||
pin=2
|
||||
recoverWhile=recover
|
||||
}
|
||||
@@ -513,55 +511,35 @@ PrivateKeyword::='private'
|
||||
ReturnKeyword::='return'
|
||||
//---------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
|
||||
PropertyRef ::= Identifier{
|
||||
extends=Referred
|
||||
}
|
||||
TypeNameReference::=GenericIdentifier
|
||||
|
||||
QualifiedTypeNameReference ::= TypeNameReference ('.' TypeNameReference)*{
|
||||
extends=TypeNameReference
|
||||
QualifiedTypeRef::= TypeRef ('.' TypeRef)*{
|
||||
extends=TypeRef
|
||||
}
|
||||
|
||||
ModuleLocation ::= RawStringLiteral{
|
||||
extends="me.zhouxi.slint.lang.psi.impl.SlintReferencedIdentifierImpl"
|
||||
TypeRef::=Identifier{
|
||||
extends=Referred
|
||||
}
|
||||
FunctionRef::=Identifier{
|
||||
extends=Referred
|
||||
}
|
||||
ModuleRef::=RawStringLiteral{
|
||||
extends=Referred
|
||||
}
|
||||
//
|
||||
//noinspection BnfSuspiciousToken
|
||||
@@ -571,7 +549,7 @@ private RawStringLiteral ::= StringLiteral
|
||||
private Numbers ::= NumberLiteral
|
||||
|
||||
//noinspection BnfSuspiciousToken
|
||||
private GenericIdentifier::= Identifier
|
||||
private Identifier::= IDENTIFIER
|
||||
|
||||
//noinspection BnfSuspiciousToken
|
||||
private RawColorLiteral ::= ColorLiteral
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.zhouxi.slint.lang.psi;
|
||||
|
||||
public interface SlintPsiRefIdentifier extends SlintPsiElement {
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package me.zhouxi.slint.lang.psi;
|
||||
|
||||
public interface SlintPsiReferencedIdentifier extends SlintPsiElement {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,11 @@ class SlintPairedBraceMatcher : PairedBraceMatcher {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Pair: Array<BracePair> = arrayOf(
|
||||
BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true),
|
||||
BracePair(SlintTypes.LParent, SlintTypes.RParent, true),
|
||||
BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true),
|
||||
BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val Pair: Array<BracePair> = arrayOf(
|
||||
BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true),
|
||||
BracePair(SlintTypes.LParent, SlintTypes.RParent, true),
|
||||
BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true),
|
||||
BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true)
|
||||
)
|
||||
@@ -19,12 +19,12 @@ 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.getCompletionPhase()
|
||||
val phase = CompletionServiceImpl.completionPhase
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("checkAutoPopup: character=$charTyped;")
|
||||
LOG.debug("phase=$phase")
|
||||
LOG.debug("lookup=$lookup")
|
||||
LOG.debug("currentCompletion=" + CompletionServiceImpl.getCompletionService().currentCompletion)
|
||||
LOG.debug("currentCompletion=" + CompletionServiceImpl.completionService.currentCompletion)
|
||||
}
|
||||
|
||||
if (lookup != null) {
|
||||
|
||||
@@ -4,16 +4,20 @@ import com.intellij.codeInsight.completion.CompletionContributor
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import me.zhouxi.slint.completion.provider.*
|
||||
import me.zhouxi.slint.completion.provider.ComponentNameProvider
|
||||
import me.zhouxi.slint.completion.provider.SubComponentNameProvider
|
||||
|
||||
class SlintCompletionContributor : CompletionContributor() {
|
||||
init {
|
||||
extend(TopKeywordProvider)
|
||||
extend(TopElementProvider)
|
||||
extend(BasicTypeProvider)
|
||||
extend(AtChildrenCompletionProvider)
|
||||
extend(ComponentElementKeywordProvider)
|
||||
extend(ComponentNameProvider)
|
||||
extend(PropertyBindingProvider)
|
||||
extend(InheritsCompletionProvider)
|
||||
extend(ExportElementProvider)
|
||||
extend(SubComponentNameProvider)
|
||||
}
|
||||
|
||||
private fun extend(
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.psi.PsiElement
|
||||
/**
|
||||
* @author zhouxi 2024/5/29
|
||||
*/
|
||||
abstract class AbstractSlintCompletionProvider<V : CompletionParameters?> : CompletionProvider<V>() {
|
||||
abstract class AbstractSlintCompletionProvider<V : CompletionParameters> : CompletionProvider<V>() {
|
||||
|
||||
abstract fun pattern(): ElementPattern<out PsiElement>
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ object ComponentElementKeywordProvider : AbstractSlintCompletionProvider<Complet
|
||||
"in", "out", "in-out", "callback",
|
||||
"property", "private", "changed",
|
||||
"states", "transitions", "function"
|
||||
).map { LookupElementBuilder.create(it) }
|
||||
).map { LookupElementBuilder.create(it).withBoldness(true) }
|
||||
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return psiElement().withParent(psiElement(Component))
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
package me.zhouxi.slint.completion.provider
|
||||
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator.withInsertHandler
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
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.search.GlobalSearchScope.projectScope
|
||||
import com.intellij.psi.stubs.StubIndex
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import com.intellij.util.ProcessingContext
|
||||
import 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.SlintInheritDeclaration
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.Component
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.ExportSpecifier
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration
|
||||
import me.zhouxi.slint.lang.psi.extension.importNames
|
||||
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys
|
||||
import me.zhouxi.slint.lang.psi.extension.relativePathOf
|
||||
import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
|
||||
|
||||
object ComponentNameProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
@@ -37,88 +21,21 @@ object ComponentNameProvider : AbstractSlintCompletionProvider<CompletionParamet
|
||||
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 components = searchComponent(project) { result.prefixMatcher.prefixMatches(it) }
|
||||
val lookups = components.map {
|
||||
val targetFile = it.containingFile.originalFile
|
||||
val currentFile = parameters.position.containingFile.originalFile.virtualFile
|
||||
val path = VfsUtil.findRelativePath(currentFile, targetFile.virtualFile, '/')
|
||||
val builder = LookupElementBuilder.create(it).withTypeText(path).withIcon(AllIcons.Nodes.Class)
|
||||
withInsertHandler(builder, ComponentInsertHandler(targetFile, path))
|
||||
LookupElementBuilder.create(it).withTypeText(it.relativePathOf(currentFile)).withIcon(AllIcons.Nodes.Class)
|
||||
}
|
||||
result.addAllElements(lookups)
|
||||
}
|
||||
|
||||
|
||||
class ComponentInsertHandler(
|
||||
private val targetFile: PsiFile,
|
||||
private val path: String?
|
||||
) : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
//insert '{}'
|
||||
val caretOffset: Int = context.editor.caretModel.offset
|
||||
val elementAt = context.file.findElementAt(caretOffset)
|
||||
if (elementAt?.prevSibling !is SlintInheritDeclaration) {
|
||||
context.document.insertString(caretOffset, " { }")
|
||||
context.editor.caretModel.moveToOffset(caretOffset + 3)
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
}
|
||||
val component = item.psiElement as SlintComponent
|
||||
if (component.containingFile.isEquivalentTo(context.file)) {
|
||||
return
|
||||
}
|
||||
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
|
||||
val target = import.importElement?.moduleLocation?.reference?.resolve()
|
||||
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
|
||||
}
|
||||
if (targetImport == null) {
|
||||
//不存在对指定文件的Import,插入
|
||||
val import = createImport(context.project, component.componentName!!.text, path!!)
|
||||
context.file.addBefore(import, context.file.firstChild)
|
||||
return
|
||||
}
|
||||
//如果导入的Name里面包含了这个ComponentName
|
||||
if (targetImport.importNames().any { it.textMatches(component.componentName!!) }) {
|
||||
return
|
||||
}
|
||||
//引入的文件存在,但是没有导入对应组件
|
||||
val importElement = targetImport.importElement!!
|
||||
val specifier = createImportSpecifier(context.project, component.componentName!!.text)
|
||||
val last = importElement.importSpecifierList.lastOrNull()
|
||||
//不存在前置节点
|
||||
if (last == null) {
|
||||
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
return
|
||||
}
|
||||
val element = PsiTreeUtil.nextVisibleLeaf(last)
|
||||
if (!element!!.textMatches(",")) {
|
||||
val comma = importElement.addAfter(createComma(context.project), last)
|
||||
importElement.addAfter(specifier, comma)
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
return
|
||||
}
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
importElement.addAfter(specifier, element)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return or(
|
||||
psiElement().withParent(psiElement(Component)).andNot(AtChildrenCompletionProvider.pattern()),
|
||||
psiElement().withSuperParent(2, psiElement(InheritDeclaration))
|
||||
)
|
||||
return psiElement()
|
||||
.withSuperParent(
|
||||
2, or(
|
||||
psiElement(InheritDeclaration),
|
||||
psiElement(ExportSpecifier)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package me.zhouxi.slint.completion.provider
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder.create
|
||||
import com.intellij.codeInsight.template.Template
|
||||
import com.intellij.codeInsight.template.TemplateEditingAdapter
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode
|
||||
import com.intellij.patterns.ElementPattern
|
||||
import com.intellij.patterns.PlatformPatterns.psiElement
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.Component
|
||||
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/6/6
|
||||
*/
|
||||
object ExportElementProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
|
||||
|
||||
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return psiElement().withSuperParent(2, psiElement(SlintFileElementType))
|
||||
.andNot(psiElement().withParent(psiElement(Component)))
|
||||
}
|
||||
|
||||
val lookup = arrayOf(
|
||||
create("export "),
|
||||
create("export ").withTailText(" {... }").withInsertHandler(MyInsertHandler())
|
||||
).map { it.withBoldness(true) }
|
||||
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(lookup)
|
||||
}
|
||||
|
||||
|
||||
class MyInsertHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val manager = TemplateManager.getInstance(context.project)
|
||||
val template = manager.createTemplate("", "", "{ \$Name$ }").apply {
|
||||
isToReformat = true
|
||||
addVariable("Name", ConstantNode(""), true)
|
||||
}
|
||||
manager.startTemplate(context.editor, template, object : TemplateEditingAdapter() {
|
||||
override fun templateFinished(template: Template, brokenOff: Boolean) {
|
||||
context.editor.caretModel.moveToOffset(context.tailOffset - 2);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.zhouxi.slint.completion.provider
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/6/6
|
||||
*/
|
||||
@@ -15,7 +15,7 @@ import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
object InheritsCompletionProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return PlatformPatterns.psiElement()
|
||||
.afterLeaf(PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(SlintTypes.ComponentName)))
|
||||
.afterLeaf(PlatformPatterns.psiElement().withParent(PlatformPatterns.psiElement(SlintTypes.IDENTIFIER)))
|
||||
}
|
||||
|
||||
override fun addCompletions(
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package me.zhouxi.slint.completion.provider
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.patterns.ElementPattern
|
||||
import com.intellij.patterns.PlatformPatterns.psiElement
|
||||
import com.intellij.patterns.StandardPatterns.or
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.createComma
|
||||
import me.zhouxi.slint.lang.createImport
|
||||
import me.zhouxi.slint.lang.createImportSpecifier
|
||||
import me.zhouxi.slint.lang.psi.SlintComponent
|
||||
import me.zhouxi.slint.lang.psi.SlintImport
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.Component
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.SubComponent
|
||||
import me.zhouxi.slint.lang.psi.extension.importNames
|
||||
import me.zhouxi.slint.lang.psi.extension.relativePathOf
|
||||
import me.zhouxi.slint.lang.psi.stubs.index.searchComponent
|
||||
|
||||
object SubComponentNameProvider
|
||||
: AbstractSlintCompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
val project = parameters.position.project
|
||||
val components = searchComponent(project) { result.prefixMatcher.prefixMatches(it) }
|
||||
val lookups = components.map {
|
||||
val targetFile = it.containingFile.originalFile
|
||||
val path = it.relativePathOf(parameters.position) ?: targetFile.name
|
||||
LookupElementBuilder.create(it)
|
||||
.withInsertHandler(ImportComponentInsertHandler(targetFile, path))
|
||||
.withTypeText(path)
|
||||
.withIcon(AllIcons.Nodes.Class)
|
||||
}
|
||||
result.addAllElements(lookups)
|
||||
}
|
||||
|
||||
|
||||
class ImportComponentInsertHandler(
|
||||
private val targetFile: PsiFile,
|
||||
private val path: String
|
||||
) : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
//insert '{}'
|
||||
val caretOffset: Int = context.editor.caretModel.offset
|
||||
context.document.insertString(caretOffset, " { }")
|
||||
context.editor.caretModel.moveToOffset(caretOffset + 3)
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
val component = item.psiElement as SlintComponent
|
||||
if (component.containingFile.isEquivalentTo(context.file)) {
|
||||
return
|
||||
}
|
||||
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
|
||||
val target = import.importElement?.moduleRef?.reference?.resolve()
|
||||
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
|
||||
}
|
||||
if (targetImport == null) {
|
||||
//不存在对指定文件的Import,插入
|
||||
val import = createImport(context.project, component.identifier!!.text, path)
|
||||
context.file.addBefore(import, context.file.firstChild)
|
||||
return
|
||||
}
|
||||
//如果导入的Name里面包含了这个ComponentName
|
||||
if (targetImport.importNames().any { it.textMatches(component.identifier!!) }) {
|
||||
return
|
||||
}
|
||||
//引入的文件存在,但是没有导入对应组件
|
||||
val importElement = targetImport.importElement!!
|
||||
val specifier = createImportSpecifier(context.project, component.identifier!!.text)
|
||||
val last = importElement.importSpecifierList.lastOrNull()
|
||||
//不存在前置节点
|
||||
if (last == null) {
|
||||
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
return
|
||||
}
|
||||
val element = PsiTreeUtil.nextVisibleLeaf(last)
|
||||
if (!element!!.textMatches(",")) {
|
||||
val comma = importElement.addAfter(createComma(context.project), last)
|
||||
importElement.addAfter(specifier, comma)
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
return
|
||||
}
|
||||
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
|
||||
importElement.addAfter(specifier, element)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return psiElement()
|
||||
.withParent(or(psiElement(Component), psiElement(SubComponent)))
|
||||
.andNot(AtChildrenCompletionProvider.pattern())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +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.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
|
||||
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
|
||||
|
||||
object TopKeywordProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(completion)
|
||||
result.addElement(create("component"))
|
||||
}
|
||||
|
||||
val completion = arrayListOf(create("component "), create("struct "), create("enum "))
|
||||
.flatMap {
|
||||
arrayListOf(
|
||||
it,
|
||||
it.withPresentableText(it.lookupString)
|
||||
.withTailText("\$Name$ {...}")
|
||||
.withInsertHandler(MyInsertHandler()),
|
||||
create("export ${it.lookupString}"),
|
||||
create("export ${it.lookupString}")
|
||||
.withTailText("\$Name$ {...}")
|
||||
.withInsertHandler(MyInsertHandler()),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
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 }")
|
||||
template.isToReformat = true
|
||||
template.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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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<CompletionParameters>() {
|
||||
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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.intellij.psi.tree.TokenSet
|
||||
import me.zhouxi.slint.lang.psi.SlintPsiKeyword
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.*
|
||||
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
|
||||
import me.zhouxi.slint.lang.psi.utils.braces
|
||||
import me.zhouxi.slint.lang.psi.utils.expressions
|
||||
import me.zhouxi.slint.lang.psi.utils.keywords
|
||||
@@ -35,6 +36,10 @@ class FormattingBlock(
|
||||
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
|
||||
@@ -53,6 +58,13 @@ class FormattingBlock(
|
||||
|
||||
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)
|
||||
@@ -65,21 +77,23 @@ class FormattingBlock(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val Comment = TokenSet.create(BlockComment, LineComment)
|
||||
val leafTokens =
|
||||
TokenSet.create(
|
||||
ReferenceIdentifier,
|
||||
ComponentName,
|
||||
InternalName,
|
||||
ComponentRef,
|
||||
PropertyRef,
|
||||
FunctionRef,
|
||||
TypeRef,
|
||||
LocalVariable,
|
||||
PropertyName,
|
||||
PropertyModifier,
|
||||
SlintPsiKeyword.ElementType.TYPE
|
||||
)
|
||||
|
||||
private val syntheticParentTokens = TokenSet.create(
|
||||
SubComponent,
|
||||
Component,
|
||||
GlobalSingleton
|
||||
)
|
||||
}
|
||||
|
||||
private val syntheticParentTokens = TokenSet.create(
|
||||
SubComponent,
|
||||
Component,
|
||||
GlobalSingleton
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ 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,
|
||||
|
||||
@@ -9,6 +9,7 @@ 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.SlintTypes.*
|
||||
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
|
||||
import me.zhouxi.slint.lang.psi.utils.keywords
|
||||
|
||||
/**
|
||||
@@ -45,7 +46,7 @@ class SlintFormatterModelBuilder : FormattingModelBuilder {
|
||||
.spaces(0)
|
||||
.before(Semicolon)
|
||||
.spacing(0, 0, 0, false, 0)
|
||||
.after(Semicolon)
|
||||
.after(TokenSet.create(Semicolon))
|
||||
.lineBreakInCode()
|
||||
.after(Colon)
|
||||
.spaces(1)
|
||||
@@ -57,6 +58,8 @@ class SlintFormatterModelBuilder : FormattingModelBuilder {
|
||||
.spaces(1)
|
||||
.after(RAngle)
|
||||
.spaces(1)
|
||||
.afterInside(TokenSet.ANY, SlintFileElementType)
|
||||
.lineBreakInCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,11 @@ class SyntheticBlock(
|
||||
) {
|
||||
return Spacing.createSpacing(1, 1, 0, false, 0)
|
||||
}
|
||||
return null
|
||||
return Spacing.createSpacing(0, 1, 1, true, 1)
|
||||
}
|
||||
|
||||
override fun getChildAttributes(newChildIndex: Int) = ChildAttributes(null, subBlocks[0].alignment)
|
||||
override fun getChildAttributes(newChildIndex: Int) =
|
||||
ChildAttributes(Indent.getNormalIndent(), subBlocks[0].alignment)
|
||||
|
||||
override fun isIncomplete() = false
|
||||
|
||||
|
||||
@@ -24,26 +24,42 @@ class KeywordHighlightAnnotator : Annotator {
|
||||
.create()
|
||||
return
|
||||
}
|
||||
if (element is SlintTypeNameReference && SlintPsiUtils.isInternalType(element)) {
|
||||
if (element is SlintTypeRef && 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)
|
||||
|
||||
@@ -16,7 +16,7 @@ import me.zhouxi.slint.lang.psi.SlintImportSpecifier
|
||||
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 {
|
||||
|
||||
@@ -2,7 +2,10 @@ 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.*
|
||||
|
||||
@@ -10,16 +13,29 @@ import me.zhouxi.slint.lang.psi.*
|
||||
fun SlintComponent.inheritsProperties(): List<SlintProperty> {
|
||||
val properties = this.propertyList
|
||||
val inherit =
|
||||
this.inheritDeclaration?.referenceIdentifier?.reference?.resolve() as SlintComponent? ?: return properties
|
||||
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 SlintImport.importNames(): List<SlintReferenceIdentifier> {
|
||||
return this.importElement?.importSpecifierList?.map { it.referenceIdentifier } ?: emptyList()
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,18 +44,26 @@ fun SlintImport.importNames(): List<SlintReferenceIdentifier> {
|
||||
*/
|
||||
fun SlintImport.availableNames(): List<PsiElement> {
|
||||
return this.importElement?.importSpecifierList?.map {
|
||||
it.importAlias?.internalName ?: it.referenceIdentifier
|
||||
it.importAlias?.identifier ?: it.componentRef
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
|
||||
fun SlintSubComponent.resolve(): SlintComponent? {
|
||||
return this.referenceIdentifier.reference?.resolve() as SlintComponent?
|
||||
return this.componentRef.reference?.resolve() as SlintComponent?
|
||||
}
|
||||
|
||||
|
||||
fun SlintProperty.toLookupElement(): LookupElementBuilder {
|
||||
return LookupElementBuilder.create(this)
|
||||
.withTypeText(this.parentOfType<SlintComponent>()?.componentName?.text)
|
||||
.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?
|
||||
}
|
||||
@@ -7,6 +7,7 @@ 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 +15,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? {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package me.zhouxi.slint.lang.psi.stubs.index;
|
||||
|
||||
public class ExportedNameIndex {
|
||||
}
|
||||
@@ -3,16 +3,46 @@ package me.zhouxi.slint.lang.psi.stubs.index
|
||||
import com.intellij.openapi.project.Project
|
||||
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,
|
||||
project, scope,
|
||||
SlintComponent::class.java
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Component With predicate
|
||||
*/
|
||||
fun searchComponent(
|
||||
project: Project,
|
||||
psiFile: PsiFile? = null,
|
||||
predicate: Predicate<String>
|
||||
): Collection<SlintComponent> {
|
||||
val scope = psiFile?.let { GlobalSearchScope.fileScope(it) } ?: projectScope(project)
|
||||
val components = arrayListOf<SlintComponent>()
|
||||
StubIndex.getInstance().processAllKeys(StubIndexKeys.Component, project) {
|
||||
if (predicate.test(it)) {
|
||||
val elements = StubIndex.getElements(
|
||||
StubIndexKeys.Component,
|
||||
it,
|
||||
project,
|
||||
scope,
|
||||
SlintComponent::class.java
|
||||
)
|
||||
components.addAll(elements)
|
||||
}
|
||||
true
|
||||
}
|
||||
return components
|
||||
}
|
||||
@@ -27,7 +27,7 @@ object SlintComponentElementType :
|
||||
return SlintComponentStubImpl(
|
||||
parentStub,
|
||||
psi.exportKeyword != null,
|
||||
psi.componentName!!.text
|
||||
psi.identifier!!.text
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ object SlintComponentElementType :
|
||||
|
||||
override fun createStub(tree: LighterAST, node: LighterASTNode, parentStub: StubElement<*>): SlintComponentStub {
|
||||
val exported = tree.getChildren(node).any { it.tokenType == ExportKeyword }
|
||||
val token = tree.getChildren(node).find { it.tokenType == ComponentName }
|
||||
val token = tree.getChildren(node).find { it.tokenType == IDENTIFIER }
|
||||
?.let {
|
||||
val text = tree.getChildren(it)[0] as LighterASTTokenNode
|
||||
val text = it as LighterASTTokenNode
|
||||
tree.charTable.intern(text.text)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ 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 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()
|
||||
@@ -48,8 +48,8 @@ object SlintImportSpecifierElementType :
|
||||
): SlintImportSpecifierStub {
|
||||
return SlintImportSpecifierStubImpl(
|
||||
parentStub,
|
||||
psi.referenceIdentifier.text,
|
||||
psi.importAlias?.internalName?.text
|
||||
psi.componentRef.text,
|
||||
psi.importAlias?.identifier?.text
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ object SlintPropertyElementType :
|
||||
else -> 0
|
||||
}
|
||||
} ?: 0
|
||||
val name = tree.getChildren(children.first { it.tokenType == PropertyName })[0] as LighterASTTokenNode
|
||||
val name = children.first { it.tokenType == IDENTIFIER } as LighterASTTokenNode
|
||||
return SlintPropertyStubImpl(
|
||||
parentStub,
|
||||
modifierNode.toShort(),
|
||||
@@ -50,7 +50,7 @@ object SlintPropertyElementType :
|
||||
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 name = psi.identifier!!.text
|
||||
val flag = SlintPropertyStubImpl.pack(isIn, isOut)
|
||||
return SlintPropertyStubImpl(parentStub, flag, name)
|
||||
}
|
||||
|
||||
16
src/main/kotlin/me/zhouxi/slint/lsp/SlintLspClient.kt
Normal file
16
src/main/kotlin/me/zhouxi/slint/lsp/SlintLspClient.kt
Normal 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")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ 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.extension.inheritsProperties
|
||||
import me.zhouxi.slint.lang.psi.extension.resolve
|
||||
|
||||
object PropertyReferenceProvider : PsiReferenceProvider() {
|
||||
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
|
||||
@@ -19,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) == true }
|
||||
}
|
||||
val component = element.parentOfType<SlintComponent>() ?: return null
|
||||
return component.inheritsProperties().find { it.propertyName?.textMatches(element) == true }
|
||||
return component.inheritsProperties().find { it.identifier?.textMatches(element) == true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<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"/>
|
||||
@@ -29,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"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
doCodeTest(source);
|
||||
}
|
||||
started.stop();
|
||||
System.out.println(started.getNanoTime()/50000);
|
||||
var source = """
|
||||
component App{
|
||||
aaa;
|
||||
property name;
|
||||
}
|
||||
""";
|
||||
doCodeTest(source);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user