Compare commits
10 Commits
6fd71775c7
...
578f8d4661
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
578f8d4661 | ||
|
|
e0c006cb94 | ||
|
|
1b9faa6f09 | ||
|
|
39a81fcb5d | ||
|
|
ada52875ad | ||
|
|
03a336bac3 | ||
|
|
ad35338f49 | ||
|
|
c6f45b3368 | ||
|
|
06cf13484d | ||
|
|
088dbd5661 |
@@ -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)
|
||||
|
||||
@@ -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,57 +11,55 @@
|
||||
psiImplClassSuffix="Impl"
|
||||
psiPackage="me.zhouxi.slint.lang.psi"
|
||||
psiImplPackage="me.zhouxi.slint.lang.psi.impl"
|
||||
tokens=[
|
||||
Comma = ","
|
||||
FatArrow = "=>"
|
||||
DoubleArrow = "<=>"
|
||||
PlusEqual = "+="
|
||||
MinusEqual = "-="
|
||||
StarEqual = "*="
|
||||
DivEqual = "/="
|
||||
LessEqual = "<="
|
||||
GreaterEqual = ">="
|
||||
EqualEqual = "=="
|
||||
NotEqual = "!="
|
||||
ColonEqual = ":="
|
||||
Arrow = "->"
|
||||
OrOr = "||"
|
||||
AndAnd = "&&"
|
||||
LBrace = "{"
|
||||
RBrace = "}"
|
||||
LParent = "("
|
||||
RParent = ")"
|
||||
LAngle = "<"
|
||||
RAngle = ">"
|
||||
LBracket = "["
|
||||
RBracket = "]"
|
||||
Plus = "+"
|
||||
Minus = "-"
|
||||
Star = "*"
|
||||
Div = "/"
|
||||
Equal = "="
|
||||
Colon = ":"
|
||||
Comma = ","
|
||||
Semicolon = ";"
|
||||
Bang = "!"
|
||||
Dot = "."
|
||||
Question = "?"
|
||||
Dollar = "$"
|
||||
At = "@"
|
||||
Pipe = "|"
|
||||
Percent = "%"
|
||||
Whitespace = "regexp:(\s+)"
|
||||
NumberLiteral = "regexp:\d+(\.(\d+)?)?([a-z]+|%)?"
|
||||
Identifier = "regexp:^[a-zA-Z_][A-Za-z0-9\-_]*"
|
||||
ColorLiteral = "regexp:#([a-zA-Z0-9]+)"
|
||||
StringLiteral = 'regexp:(^"[^"\r\n]*")'
|
||||
LineComment = 'regexp:^//[^\r\n]*'
|
||||
BlockComment = 'regexp:/\*[\s\S]*?\*/'
|
||||
|
||||
]
|
||||
extends(".*Expression")=Expression
|
||||
implements(".*Keyword")=["me.zhouxi.slint.lang.psi.keyword.SlintPsiKeywordIdentifier"]
|
||||
mixin(".*Keyword")="me.zhouxi.slint.lang.psi.keyword.SlintPsiKeywordIdentifierImpl"
|
||||
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 ::= 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
|
||||
|
||||
@@ -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)
|
||||
@@ -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) {
|
||||
|
||||
12
src/main/java/me/zhouxi/slint/lang/psi/SlintParserUtil.java
Normal file
12
src/main/java/me/zhouxi/slint/lang/psi/SlintParserUtil.java
Normal 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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 + ")"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package me.zhouxi.slint.lang.psi.keyword;
|
||||
|
||||
import me.zhouxi.slint.lang.psi.SlintPsiElement;
|
||||
|
||||
public interface SlintPsiKeywordIdentifier extends SlintPsiElement {
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
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 ElementKeywordProvider : CompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(elementKeywords)
|
||||
}
|
||||
|
||||
private val elementKeywords = arrayOf(
|
||||
"in", "out", "in-out", "callback",
|
||||
"property", "private", "changed",
|
||||
"states", "transitions", "@children"
|
||||
).map { LookupElementBuilder.create(it) }
|
||||
}
|
||||
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.psiElement
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.Component
|
||||
|
||||
object ComponentElementKeywordProvider : AbstractSlintCompletionProvider() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(elementKeywords)
|
||||
}
|
||||
|
||||
private val elementKeywords = arrayOf(
|
||||
"in", "out", "in-out", "callback",
|
||||
"property", "private", "changed",
|
||||
"states", "transitions", "function"
|
||||
).map { LookupElementBuilder.create(it).withBoldness(true) }
|
||||
|
||||
override fun pattern(): ElementPattern<out PsiElement> {
|
||||
return psiElement().withParent(psiElement(Component))
|
||||
.andNot(AtChildrenCompletionProvider.pattern())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.zhouxi.slint.completion.provider
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/6/6
|
||||
*/
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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) }
|
||||
}
|
||||
95
src/main/kotlin/me/zhouxi/slint/formatter/FormattingBlock.kt
Normal file
95
src/main/kotlin/me/zhouxi/slint/formatter/FormattingBlock.kt
Normal 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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
58
src/main/kotlin/me/zhouxi/slint/formatter/LeafBlock.kt
Normal file
58
src/main/kotlin/me/zhouxi/slint/formatter/LeafBlock.kt
Normal 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>()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
51
src/main/kotlin/me/zhouxi/slint/formatter/SyntheticBlock.kt
Normal file
51
src/main/kotlin/me/zhouxi/slint/formatter/SyntheticBlock.kt
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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?
|
||||
}
|
||||
@@ -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? {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package me.zhouxi.slint.lang.psi.stubs.index;
|
||||
|
||||
public class ExportedNameIndex {
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user