initial commit
This commit is contained in:
12
src/main/kotlin/me/zhouxi/slint/Plugin.kt
Normal file
12
src/main/kotlin/me/zhouxi/slint/Plugin.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package me.zhouxi.slint
|
||||
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import java.nio.file.Path
|
||||
|
||||
object Plugin {
|
||||
val Id = PluginId.findId("me.zhouxi.intellij-slint")
|
||||
|
||||
val Path: Path = PluginManagerCore.getPlugin(Id)!!.pluginPath
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.zhouxi.slint.brace
|
||||
|
||||
import com.intellij.lang.BracePair
|
||||
import com.intellij.lang.PairedBraceMatcher
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
|
||||
class SlintPairedBraceMatcher : PairedBraceMatcher {
|
||||
override fun getPairs(): Array<BracePair> {
|
||||
return Pair
|
||||
}
|
||||
|
||||
override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getCodeConstructStart(file: PsiFile, openingBraceOffset: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Pair: Array<BracePair> = arrayOf(
|
||||
BracePair(SlintTypes.LBrace, SlintTypes.RBrace, true),
|
||||
BracePair(SlintTypes.LParent, SlintTypes.RParent, true),
|
||||
BracePair(SlintTypes.LBracket, SlintTypes.RBracket, true),
|
||||
BracePair(SlintTypes.LAngle, SlintTypes.RAngle, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package me.zhouxi.slint.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.patterns.PlatformPatterns
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.SlintParserDefinition
|
||||
import me.zhouxi.slint.lang.psi.SlintComponent
|
||||
import me.zhouxi.slint.lang.psi.SlintFile
|
||||
import me.zhouxi.slint.lang.psi.SlintImport
|
||||
import me.zhouxi.slint.lang.psi.SlintPsiUtils.InternalTypes
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
import me.zhouxi.slint.lang.psi.utils.exportedElements
|
||||
import me.zhouxi.slint.lang.psi.utils.inheritDeclaredElements
|
||||
|
||||
class SlintCompletionContributor : CompletionContributor() {
|
||||
init {
|
||||
//文件级别
|
||||
extend(
|
||||
CompletionType.BASIC,
|
||||
PlatformPatterns.psiElement().withAncestor(4, PlatformPatterns.psiElement(SlintParserDefinition.FileType)),
|
||||
object : CompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(TopKeywords)
|
||||
}
|
||||
})
|
||||
|
||||
//类型定义
|
||||
extend(
|
||||
CompletionType.BASIC,
|
||||
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintTypes.Type)),
|
||||
object : CompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(BasicTypes)
|
||||
}
|
||||
})
|
||||
|
||||
//componentBody
|
||||
extend(
|
||||
CompletionType.BASIC,
|
||||
PlatformPatterns.psiElement().withAncestor(3, PlatformPatterns.psiElement(SlintTypes.ComponentBody)),
|
||||
object : CompletionProvider<CompletionParameters>() {
|
||||
override fun addCompletions(
|
||||
parameters: CompletionParameters,
|
||||
context: ProcessingContext,
|
||||
result: CompletionResultSet
|
||||
) {
|
||||
result.addAllElements(ElementKeywords)
|
||||
val component = parameters.position.parentOfType<SlintComponent>() ?: return
|
||||
val elements = component.inheritDeclaredElements()
|
||||
for (element in elements) {
|
||||
result.addElement(LookupElementBuilder.create(element).withIcon(AllIcons.Nodes.Property))
|
||||
}
|
||||
val file = component.containingFile as SlintFile
|
||||
val array = file.childrenOfType<SlintImport>()
|
||||
.mapNotNull { it.moduleLocation?.reference?.resolve() as SlintFile? }
|
||||
.flatMap { it.exportedElements() }
|
||||
for (element in array) {
|
||||
result.addElement(LookupElementBuilder.create(element).withIcon(AllIcons.Nodes.Class))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val BasicTypes = InternalTypes.map { LookupElementBuilder.create(it) }
|
||||
val TopKeywords = arrayOf("export", "component", "global", "enum", "struct").map { LookupElementBuilder.create(it) }
|
||||
val ElementKeywords = arrayOf(
|
||||
"in", "out", "in-out", "callback",
|
||||
"property", "private", "changed",
|
||||
"states", "transitions", "@children"
|
||||
).map { LookupElementBuilder.create(it) }
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.zhouxi.slint.formatter
|
||||
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.psi.TokenType
|
||||
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition.SyntaxElement
|
||||
import com.intellij.psi.impl.source.codeStyle.lineIndent.JavaLikeLangLineIndentProvider
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import me.zhouxi.slint.lang.Slint
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
|
||||
class SlintLineIndentProvider : JavaLikeLangLineIndentProvider() {
|
||||
|
||||
override fun mapType(tokenType: IElementType): SyntaxElement? {
|
||||
return SyntaxMap[tokenType]
|
||||
}
|
||||
|
||||
|
||||
override fun isSuitableForLanguage(language: Language): Boolean {
|
||||
return language.isKindOf(Slint.INSTANCE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SyntaxMap: Map<IElementType, SyntaxElement> = java.util.Map.ofEntries<IElementType, SyntaxElement>(
|
||||
java.util.Map.entry(SlintTypes.LBracket, JavaLikeElement.BlockOpeningBrace),
|
||||
java.util.Map.entry(SlintTypes.RBracket, JavaLikeElement.BlockClosingBrace),
|
||||
java.util.Map.entry(SlintTypes.LBrace, JavaLikeElement.BlockOpeningBrace),
|
||||
java.util.Map.entry(SlintTypes.RBrace, JavaLikeElement.BlockClosingBrace),
|
||||
java.util.Map.entry(TokenType.WHITE_SPACE, JavaLikeElement.Whitespace),
|
||||
java.util.Map.entry(SlintTypes.Semicolon, JavaLikeElement.Semicolon),
|
||||
java.util.Map.entry(SlintTypes.LineComment, JavaLikeElement.LineComment),
|
||||
java.util.Map.entry(SlintTypes.BlockComment, JavaLikeElement.BlockComment),
|
||||
java.util.Map.entry(SlintTypes.Colon, JavaLikeElement.Colon),
|
||||
java.util.Map.entry(SlintTypes.Comma, JavaLikeElement.Comma),
|
||||
java.util.Map.entry(SlintTypes.LParent, JavaLikeElement.LeftParenthesis),
|
||||
java.util.Map.entry(SlintTypes.RParent, JavaLikeElement.RightParenthesis)
|
||||
)
|
||||
}
|
||||
}
|
||||
58
src/main/kotlin/me/zhouxi/slint/highlight/Definitions.kt
Normal file
58
src/main/kotlin/me/zhouxi/slint/highlight/Definitions.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package me.zhouxi.slint.highlight
|
||||
|
||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
|
||||
import com.intellij.openapi.editor.HighlighterColors
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
|
||||
object Definitions {
|
||||
@JvmField
|
||||
val _KeyWord: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("_KeyWord", DefaultLanguageHighlighterColors.KEYWORD)
|
||||
|
||||
val _StringLiteral: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("_StringLiteral", DefaultLanguageHighlighterColors.STRING)
|
||||
|
||||
val _Comment: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("_Comment", DefaultLanguageHighlighterColors.LINE_COMMENT)
|
||||
|
||||
val _DeclaredIdentifier: TextAttributesKey = TextAttributesKey.createTextAttributesKey(
|
||||
"_DeclaredIdentifier", DefaultLanguageHighlighterColors.INSTANCE_FIELD
|
||||
)
|
||||
|
||||
val _BadCharacter: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("BadCharacter", HighlighterColors.BAD_CHARACTER)
|
||||
|
||||
val _NumberLiteral: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("_NumberLiteral", DefaultLanguageHighlighterColors.NUMBER)
|
||||
|
||||
val _SemiColon: TextAttributesKey =
|
||||
TextAttributesKey.createTextAttributesKey("_SemiColon", DefaultLanguageHighlighterColors.SEMICOLON)
|
||||
val _Error: TextAttributesKey = TextAttributesKey.createTextAttributesKey("_Error", HighlighterColors.BAD_CHARACTER)
|
||||
|
||||
|
||||
private val BadCharacter = arrayOf(_BadCharacter)
|
||||
|
||||
@JvmField
|
||||
val KeyWord: Array<TextAttributesKey> = arrayOf(_KeyWord)
|
||||
|
||||
@JvmField
|
||||
val StringLiteral: Array<TextAttributesKey> = arrayOf(_StringLiteral)
|
||||
|
||||
val DeclaredIdentifier: Array<TextAttributesKey> = arrayOf(_DeclaredIdentifier)
|
||||
|
||||
@JvmField
|
||||
val Comment: Array<TextAttributesKey> = arrayOf(_Comment)
|
||||
|
||||
val Empty: Array<TextAttributesKey?> = arrayOfNulls(0)
|
||||
|
||||
@JvmField
|
||||
val NumberLiteral: Array<TextAttributesKey> = arrayOf(_NumberLiteral)
|
||||
|
||||
@JvmField
|
||||
val Brace: Array<TextAttributesKey> = arrayOf(DefaultLanguageHighlighterColors.BRACES)
|
||||
|
||||
@JvmField
|
||||
val SemiColon: Array<TextAttributesKey> = arrayOf(_SemiColon)
|
||||
|
||||
val Error: Array<TextAttributesKey> = arrayOf(_Error)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.zhouxi.slint.highlight
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
import com.intellij.lang.annotation.Annotator
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
|
||||
import com.intellij.psi.PsiElement
|
||||
import me.zhouxi.slint.lang.psi.*
|
||||
|
||||
class KeywordHighlightAnnotator : Annotator {
|
||||
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
|
||||
if (element is SlintPsiKeywordIdentifier) {
|
||||
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
|
||||
.range(element)
|
||||
.textAttributes(Definitions._KeyWord)
|
||||
.create()
|
||||
return
|
||||
}
|
||||
if (element is SlintTypeNameReference && SlintPsiUtils.isInternalType(element)) {
|
||||
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
|
||||
.range(element)
|
||||
.textAttributes(Definitions._KeyWord)
|
||||
.create()
|
||||
return
|
||||
}
|
||||
if (element is SlintPropertyName) {
|
||||
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
|
||||
.range(element)
|
||||
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
|
||||
.create()
|
||||
}
|
||||
if (element is SlintPropertyBinding) {
|
||||
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
|
||||
.range(element.getReferenceIdentifier())
|
||||
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_FIELD)
|
||||
.create()
|
||||
}
|
||||
if (element is SlintFunctionName || element is SlintFunctionReference) {
|
||||
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
|
||||
.range(element)
|
||||
.textAttributes(DefaultLanguageHighlighterColors.INSTANCE_METHOD)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.zhouxi.slint.highlight
|
||||
|
||||
import com.intellij.lexer.FlexAdapter
|
||||
import com.intellij.lexer.Lexer
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import me.zhouxi.slint.lang.lexer.SlintLexer
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes
|
||||
|
||||
class SlintSyntaxHighlighter : SyntaxHighlighterBase() {
|
||||
override fun getHighlightingLexer(): Lexer {
|
||||
return FlexAdapter(SlintLexer())
|
||||
}
|
||||
|
||||
override fun getTokenHighlights(tokenType: IElementType): Array<TextAttributesKey> {
|
||||
if (tokenType === SlintTypes.StringLiteral) {
|
||||
return Definitions.StringLiteral
|
||||
}
|
||||
if (tokenType === SlintTypes.LineComment || tokenType === SlintTypes.BlockComment) {
|
||||
return Definitions.Comment
|
||||
}
|
||||
if (tokenType === SlintTypes.NumberLiteral) {
|
||||
return Definitions.NumberLiteral
|
||||
}
|
||||
if (tokenType === SlintTypes.LBrace || tokenType === SlintTypes.RBrace) {
|
||||
return Definitions.Brace
|
||||
}
|
||||
if (tokenType === SlintTypes.DoubleArrow) {
|
||||
return Definitions.KeyWord
|
||||
}
|
||||
if (tokenType === SlintTypes.Semicolon) {
|
||||
return Definitions.SemiColon
|
||||
}
|
||||
return TextAttributesKey.EMPTY_ARRAY
|
||||
}
|
||||
}
|
||||
18
src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt
Normal file
18
src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package me.zhouxi.slint.lang
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import me.zhouxi.slint.lang.psi.SlintComponent
|
||||
import me.zhouxi.slint.lang.psi.SlintFile
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/8
|
||||
*/
|
||||
|
||||
fun createIdentifier(project: Project?, text: String): PsiElement {
|
||||
val factory = PsiFileFactory.getInstance(project)
|
||||
val file = factory.createFileFromText("dummy.slint", Slint.INSTANCE, "component $text{}") as SlintFile
|
||||
return file.findChildByClass(SlintComponent::class.java)!!.componentName!!.identifier
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.zhouxi.slint.lang.psi.impl
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import me.zhouxi.slint.lang.createIdentifier
|
||||
import me.zhouxi.slint.lang.psi.SlintNamed
|
||||
import me.zhouxi.slint.lang.psi.SlintPsiElementImpl
|
||||
import me.zhouxi.slint.lang.psi.SlintPsiNamedElement
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/15
|
||||
*/
|
||||
abstract class SlintPsiNamedElementMixinImpl(node: ASTNode) : SlintPsiElementImpl(node),
|
||||
SlintPsiNamedElement {
|
||||
override fun getNameIdentifier(): PsiElement? {
|
||||
return findChildByClass(SlintNamed::class.java)
|
||||
}
|
||||
|
||||
override fun getName(): String? {
|
||||
return nameIdentifier?.text ?: this.text
|
||||
}
|
||||
|
||||
override fun canNavigate(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getTextOffset(): Int {
|
||||
return nameIdentifier?.textOffset ?: super.getTextOffset()
|
||||
}
|
||||
|
||||
@Throws(IncorrectOperationException::class)
|
||||
override fun setName(name: @NlsSafe String): PsiElement {
|
||||
val element = nameIdentifier ?: throw IncorrectOperationException()
|
||||
element.replace(createIdentifier(project, name))
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.zhouxi.slint.lang.psi.utils
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import me.zhouxi.slint.lang.psi.*
|
||||
|
||||
|
||||
fun SlintExport.exportedElements(): List<SlintPsiNamedElement> {
|
||||
val list = mutableListOf<SlintPsiNamedElement>()
|
||||
this.exportType?.exportIdentifierList?.forEach {
|
||||
if (it.externalName == null) {
|
||||
val component = it.referenceIdentifier.reference?.resolve() as SlintComponent?
|
||||
component?.let { list.add(component) }
|
||||
} else {
|
||||
list.add(it.externalName!!)
|
||||
}
|
||||
}
|
||||
this.component?.let { list.add(it) }
|
||||
this.globalSingleton?.let { list.add(it) }
|
||||
val file = this.exportModule?.moduleLocation?.reference?.resolve() as SlintFile? ?: return list
|
||||
val exports = file.childrenOfType<SlintExport>()
|
||||
//TODO recursion error
|
||||
exports.forEach { list.addAll(it.exportedElements()) }
|
||||
return list
|
||||
}
|
||||
|
||||
fun SlintImport.importedElements(): List<PsiElement> {
|
||||
val list = mutableListOf<PsiElement>()
|
||||
this.importedIdentifierList.forEach { identifier ->
|
||||
list.add(identifier.referenceIdentifier)
|
||||
identifier.internalName?.let { list.add(it) }
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun SlintFile.importedElements(): List<PsiElement> {
|
||||
return this.childrenOfType<SlintImport>().flatMap { it.importedElements() }
|
||||
}
|
||||
|
||||
fun SlintFile.exportedElements(): List<SlintPsiNamedElement> {
|
||||
return this.childrenOfType<SlintExport>().flatMap { it.exportedElements() }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package me.zhouxi.slint.lang.psi.utils
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import me.zhouxi.slint.lang.psi.*
|
||||
import java.util.function.Function
|
||||
import java.util.function.Predicate
|
||||
|
||||
|
||||
fun resolveComponent(element: SlintReferenceIdentifier?): SlintComponent? {
|
||||
if (element == null) {
|
||||
return null
|
||||
}
|
||||
val maybeComponent = element.reference?.resolve()
|
||||
if (maybeComponent is SlintComponent) {
|
||||
return maybeComponent
|
||||
}
|
||||
return resolveReferencedComponent(maybeComponent ?: return null)
|
||||
}
|
||||
|
||||
fun resolveComponent(element: SlintInternalName): SlintComponent? {
|
||||
//内部名字解析引用
|
||||
val resolve = element.reference?.resolve()
|
||||
if (resolve is SlintComponent) {
|
||||
return resolve
|
||||
}
|
||||
//InternalName解析不到东西,换成External试一下
|
||||
if (resolve == null) {
|
||||
val externalName = PsiTreeUtil.getPrevSiblingOfType(element, SlintExternalName::class.java)
|
||||
if (externalName == null) {
|
||||
val externalName1 = PsiTreeUtil.getNextSiblingOfType(element, SlintExternalName::class.java) ?: return null
|
||||
return resolveReferencedComponent(externalName1)
|
||||
}
|
||||
return resolveReferencedComponent(externalName)
|
||||
}
|
||||
return resolveReferencedComponent(resolve)
|
||||
}
|
||||
|
||||
fun resolveComponent(element: SlintExternalName): SlintComponent? {
|
||||
val resolve = element.reference?.resolve()
|
||||
if (resolve is SlintComponent) {
|
||||
return resolve
|
||||
}
|
||||
//InternalName解析不到东西,换成External试一下
|
||||
if (resolve == null) {
|
||||
val internalName = PsiTreeUtil.getPrevSiblingOfType(element, SlintInternalName::class.java)
|
||||
if (internalName == null) {
|
||||
val internalName1 = PsiTreeUtil.getNextSiblingOfType(element, SlintInternalName::class.java) ?: return null
|
||||
return resolveReferencedComponent(internalName1)
|
||||
}
|
||||
return resolveReferencedComponent(internalName)
|
||||
}
|
||||
return resolveReferencedComponent(resolve)
|
||||
}
|
||||
|
||||
fun resolveReferencedComponent(element: PsiElement?): SlintComponent? {
|
||||
if (element == null) {
|
||||
return null
|
||||
}
|
||||
when (element) {
|
||||
is SlintComponentName -> return element.parent as SlintComponent?
|
||||
is SlintComponent -> return element
|
||||
is SlintReferenceIdentifier -> return resolveComponent(element)
|
||||
is SlintInternalName -> return resolveComponent(element)
|
||||
is SlintExternalName -> return resolveComponent(element)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun searchProperty(
|
||||
component: PsiElement?,
|
||||
predicate: Predicate<SlintPropertyDeclaration>
|
||||
): SlintPropertyDeclaration? {
|
||||
if (component is SlintSubComponent) {
|
||||
return searchProperty(component, predicate)
|
||||
}
|
||||
if (component is SlintComponent) {
|
||||
return searchElementInParents(component, predicate) { it.componentBody?.propertyDeclarationList }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun searchProperty(
|
||||
subComponent: SlintSubComponent?,
|
||||
predicate: Predicate<SlintPropertyDeclaration>
|
||||
): SlintPropertyDeclaration? {
|
||||
val component = resolveComponent(subComponent?.referenceIdentifier) ?: return null
|
||||
return searchElementInParents(component, predicate) { it.componentBody?.propertyDeclarationList }
|
||||
}
|
||||
|
||||
fun searchCallback(
|
||||
subComponent: SlintSubComponent?,
|
||||
predicate: Predicate<SlintCallbackDeclaration>
|
||||
): SlintCallbackDeclaration? {
|
||||
val component = subComponent?.referenceIdentifier?.reference?.resolve() ?: return null
|
||||
return searchElementInParents(
|
||||
component as SlintComponent,
|
||||
predicate
|
||||
) { it.componentBody?.callbackDeclarationList }
|
||||
}
|
||||
|
||||
fun <T> searchElementInParents(
|
||||
component: SlintComponent?,
|
||||
predicate: Predicate<T>,
|
||||
function: Function<SlintComponent, List<T>?>
|
||||
): T? {
|
||||
if (component == null) {
|
||||
return null
|
||||
}
|
||||
val properties = function.apply(component) ?: arrayListOf<T>()
|
||||
for (property in properties) {
|
||||
if (predicate.test(property)) {
|
||||
return property
|
||||
}
|
||||
}
|
||||
val inheritComponent = resolveReferencedComponent(component.inheritDeclaration?.referenceIdentifier)
|
||||
return searchElementInParents(inheritComponent, predicate, function)
|
||||
}
|
||||
|
||||
fun SlintComponent.currentDeclaredElements(): List<SlintPsiNamedElement> {
|
||||
val properties = this.componentBody?.propertyDeclarationList ?: emptyList()
|
||||
val callbacks = this.componentBody?.callbackDeclarationList ?: emptyList()
|
||||
return properties + callbacks
|
||||
}
|
||||
|
||||
fun SlintComponent.inheritDeclaredElements(): List<SlintPsiNamedElement> {
|
||||
val properties = this.componentBody?.propertyDeclarationList ?: emptyList()
|
||||
val callbacks = this.componentBody?.callbackDeclarationList ?: emptyList()
|
||||
val inheritedComponent = resolveReferencedComponent(this.inheritDeclaration?.referenceIdentifier)
|
||||
?: return properties + callbacks
|
||||
return properties + callbacks + inheritedComponent.inheritDeclaredElements()
|
||||
}
|
||||
11
src/main/kotlin/me/zhouxi/slint/preview/CommandOptions.kt
Normal file
11
src/main/kotlin/me/zhouxi/slint/preview/CommandOptions.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.configurations.LocatableRunConfigurationOptions
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class CommandOptions : LocatableRunConfigurationOptions() {
|
||||
|
||||
var arguments by string()
|
||||
}
|
||||
15
src/main/kotlin/me/zhouxi/slint/preview/PreviewAction.kt
Normal file
15
src/main/kotlin/me/zhouxi/slint/preview/PreviewAction.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
|
||||
class PreviewAction : AnAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val manager = RunManager.getInstance(e.project!!)
|
||||
// e.si
|
||||
// manager.createConfiguration()
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
import com.intellij.execution.configurations.ConfigurationType
|
||||
import com.intellij.execution.configurations.RunConfiguration
|
||||
import com.intellij.openapi.components.BaseState
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class PreviewConfigurationFactory(type: ConfigurationType) : ConfigurationFactory(type) {
|
||||
override fun getOptionsClass(): Class<out BaseState?> {
|
||||
return CommandOptions::class.java
|
||||
}
|
||||
|
||||
override fun getId(): String {
|
||||
return PreviewConfigurationType.id
|
||||
}
|
||||
|
||||
override fun createTemplateConfiguration(
|
||||
project: Project
|
||||
): RunConfiguration {
|
||||
return PreviewRunConfiguration(project, this, "Slint Viewer")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.configurations.ConfigurationTypeBase
|
||||
import com.intellij.openapi.util.NotNullLazyValue
|
||||
import me.zhouxi.slint.lang.Slint
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
object PreviewConfigurationType : ConfigurationTypeBase(
|
||||
"SlintViewerConfiguration",
|
||||
"Slint viewer",
|
||||
"Slint component preview",
|
||||
NotNullLazyValue.createValue { Slint.ICON }
|
||||
) {
|
||||
init {
|
||||
addFactory(PreviewConfigurationFactory(this))
|
||||
}
|
||||
|
||||
val factory: PreviewConfigurationFactory
|
||||
get() = this.configurationFactories[0] as PreviewConfigurationFactory
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.Executor
|
||||
import com.intellij.execution.configurations.*
|
||||
import com.intellij.execution.process.ColoredProcessHandler
|
||||
import com.intellij.execution.process.ProcessHandler
|
||||
import com.intellij.execution.process.ProcessTerminatedListener
|
||||
import com.intellij.execution.runners.ExecutionEnvironment
|
||||
import com.intellij.openapi.options.SettingsEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.InvalidDataException
|
||||
import com.intellij.util.io.BaseOutputReader
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.jdom.Element
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class PreviewRunConfiguration(project: Project, factory: ConfigurationFactory?, name: String?) :
|
||||
LocatableConfigurationBase<CommandOptions?>(project, factory!!, name) {
|
||||
public override fun getOptions(): CommandOptions {
|
||||
return super.getOptions() as CommandOptions
|
||||
}
|
||||
|
||||
override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration?> {
|
||||
return SlintSettingEditor()
|
||||
}
|
||||
|
||||
|
||||
override fun suggestedName(): String? {
|
||||
println(options.arguments)
|
||||
return suggestedName()
|
||||
}
|
||||
|
||||
@Throws(InvalidDataException::class)
|
||||
override fun readExternal(element: Element) {
|
||||
super.readExternal(element)
|
||||
val attribute = element.getAttributeValue("RunArguments")
|
||||
if (StringUtils.isNoneBlank(attribute)) {
|
||||
options.arguments = attribute
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeExternal(element: Element) {
|
||||
super.writeExternal(element)
|
||||
if (StringUtils.isNoneBlank(options.arguments)) {
|
||||
element.setAttribute("RunArguments", options.arguments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState {
|
||||
return object : CommandLineState(environment) {
|
||||
@Throws(ExecutionException::class)
|
||||
override fun startProcess(): ProcessHandler {
|
||||
val args = options.arguments?.split(" ")?.filter { it.isNotBlank() }?.toTypedArray() ?: arrayOf()
|
||||
val commandLine = GeneralCommandLine(
|
||||
SlintViewer.executable(), *args
|
||||
)
|
||||
val processHandler = object : ColoredProcessHandler(commandLine) {
|
||||
override fun readerOptions(): BaseOutputReader.Options {
|
||||
return BaseOutputReader.Options.forMostlySilentProcess()
|
||||
}
|
||||
}
|
||||
ProcessTerminatedListener.attach(processHandler)
|
||||
return processHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.actions.ConfigurationContext
|
||||
import com.intellij.execution.actions.LazyRunConfigurationProducer
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiElement
|
||||
import me.zhouxi.slint.lang.psi.SlintFile
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class PreviewRunConfigurationProducer : LazyRunConfigurationProducer<PreviewRunConfiguration>() {
|
||||
override fun getConfigurationFactory(): ConfigurationFactory {
|
||||
return PreviewConfigurationType.factory
|
||||
}
|
||||
|
||||
override fun setupConfigurationFromContext(
|
||||
configuration: PreviewRunConfiguration,
|
||||
context: ConfigurationContext,
|
||||
sourceElement: Ref<PsiElement>
|
||||
): Boolean {
|
||||
val file = sourceElement.get().containingFile
|
||||
if (file !is SlintFile) {
|
||||
return false
|
||||
}
|
||||
configuration.options.arguments = "${file.virtualFile.path} --auto-reload"
|
||||
configuration.name = file.name
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun isConfigurationFromContext(
|
||||
configuration: PreviewRunConfiguration,
|
||||
context: ConfigurationContext
|
||||
): Boolean {
|
||||
val path = context.psiLocation?.context?.containingFile?.virtualFile?.path ?: return false
|
||||
return configuration.options.arguments?.contains(path) == true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.execution.lineMarker.ExecutorAction
|
||||
import com.intellij.execution.lineMarker.RunLineMarkerContributor
|
||||
import com.intellij.psi.PsiElement
|
||||
import me.zhouxi.slint.lang.Slint
|
||||
import me.zhouxi.slint.lang.psi.SlintComponentName
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class PreviewRunLineMarkerContributor : RunLineMarkerContributor() {
|
||||
override fun getInfo(element: PsiElement): Info? {
|
||||
if (element.parent is SlintComponentName) {
|
||||
return Info(
|
||||
Slint.ICON, null,
|
||||
*ExecutorAction.getActions(1)
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.openapi.options.SettingsEditor
|
||||
import com.intellij.ui.components.JBTextField
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
import java.awt.Desktop
|
||||
import java.awt.Font
|
||||
import java.net.URI
|
||||
import javax.swing.JComponent
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/16
|
||||
*/
|
||||
class SlintSettingEditor : SettingsEditor<PreviewRunConfiguration>() {
|
||||
|
||||
private val commandInput = JBTextField()
|
||||
|
||||
override fun resetEditorFrom(config: PreviewRunConfiguration) {
|
||||
commandInput.text = config.options.arguments
|
||||
}
|
||||
|
||||
@Throws(ConfigurationException::class)
|
||||
override fun applyEditorTo(config: PreviewRunConfiguration) {
|
||||
config.options.arguments = commandInput.text
|
||||
}
|
||||
|
||||
override fun createEditor(): JComponent {
|
||||
return panel {
|
||||
separator()
|
||||
row {
|
||||
label("Slint viewer arguments").also {
|
||||
it.component.font = it.component.font.deriveFont(Font.BOLD)
|
||||
}
|
||||
}
|
||||
row {
|
||||
cell(commandInput).columns(COLUMNS_LARGE).align(Align.FILL)
|
||||
}
|
||||
row {
|
||||
link("Github document") {
|
||||
Desktop.getDesktop().browse(URI("https://github.com/slint-ui/slint/tree/master/tools/viewer"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/main/kotlin/me/zhouxi/slint/preview/SlintViewer.kt
Normal file
27
src/main/kotlin/me/zhouxi/slint/preview/SlintViewer.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package me.zhouxi.slint.preview
|
||||
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import me.zhouxi.slint.Plugin
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/17
|
||||
*/
|
||||
object SlintViewer {
|
||||
|
||||
private val executable = if (SystemInfo.isWindows) {
|
||||
"slint-viewer-windows.exe"
|
||||
} else if (SystemInfo.isLinux) {
|
||||
"slint-viewer-linux"
|
||||
} else if (SystemInfo.isMac) {
|
||||
"slint-viewer-macos"
|
||||
} else {
|
||||
throw RuntimeException("Unsupported OS")
|
||||
}
|
||||
|
||||
fun executable(): String {
|
||||
return Paths.get(Plugin.Path.toAbsolutePath().toString(), "slint-viewer", executable).toString()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package me.zhouxi.slint.reference
|
||||
|
||||
import com.intellij.patterns.PlatformPatterns.psiElement
|
||||
import com.intellij.patterns.StandardPatterns.or
|
||||
import com.intellij.psi.PsiReferenceContributor
|
||||
import com.intellij.psi.PsiReferenceRegistrar
|
||||
import me.zhouxi.slint.lang.psi.SlintTypes.*
|
||||
import me.zhouxi.slint.reference.provider.ComponentReferenceProvider
|
||||
import me.zhouxi.slint.reference.provider.ModuleLocationReferenceProvider
|
||||
import me.zhouxi.slint.reference.provider.PropertyReferenceProvider
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/17
|
||||
*/
|
||||
class SlintReferenceContributor : PsiReferenceContributor() {
|
||||
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
|
||||
//component Reference
|
||||
registrar.registerReferenceProvider(
|
||||
psiElement(ReferenceIdentifier)
|
||||
.withParent(
|
||||
or(
|
||||
psiElement(SubComponent),
|
||||
psiElement(Component),
|
||||
psiElement(InheritDeclaration),
|
||||
psiElement(ImportedIdentifier),
|
||||
psiElement(ExportIdentifier)
|
||||
)
|
||||
),
|
||||
ComponentReferenceProvider()
|
||||
)
|
||||
//moduleLocation Reference
|
||||
registrar.registerReferenceProvider(
|
||||
psiElement(ModuleLocation),
|
||||
ModuleLocationReferenceProvider()
|
||||
)
|
||||
//property binding
|
||||
registrar.registerReferenceProvider(
|
||||
psiElement().withParent(
|
||||
or(
|
||||
psiElement(PropertyBinding),
|
||||
psiElement(ComponentBody)
|
||||
)
|
||||
),
|
||||
PropertyReferenceProvider()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.zhouxi.slint.reference.provider
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.PsiReferenceBase
|
||||
import com.intellij.psi.PsiReferenceProvider
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.*
|
||||
import me.zhouxi.slint.lang.psi.utils.exportedElements
|
||||
import me.zhouxi.slint.lang.psi.utils.importedElements
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/17
|
||||
*/
|
||||
class ComponentReferenceProvider : PsiReferenceProvider() {
|
||||
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
|
||||
return arrayOf(SlintComponentNameReference(element as SlintReferenceIdentifier))
|
||||
}
|
||||
|
||||
class SlintComponentNameReference(element: SlintReferenceIdentifier) : PsiReferenceBase<PsiElement?>(element) {
|
||||
override fun resolve(): PsiElement? {
|
||||
val file = element.containingFile as SlintFile
|
||||
//优先查找当前文件内的组件定义
|
||||
val component = file.childrenOfType<SlintComponent>()
|
||||
.firstOrNull { it.componentName?.textMatches(element) == true }
|
||||
if (component != null) {
|
||||
return component
|
||||
}
|
||||
// TODO psiTreeUtils
|
||||
//然后是导出的组件 maybe component or externalName
|
||||
val externalElement = file.exportedElements().firstOrNull { it.textMatches(element) }
|
||||
if (externalElement != null) {
|
||||
return externalElement
|
||||
}
|
||||
//maybe internalName or referencedIdentifier;
|
||||
val element = file.importedElements().firstOrNull { it.textMatches(element) } ?: return null
|
||||
if (element is SlintPsiReferencedIdentifier) {
|
||||
val location = element.parentOfType<SlintImport>()?.moduleLocation ?: return null
|
||||
val targetFile = location.reference?.resolve() as SlintFile? ?: return null
|
||||
return targetFile.exportedElements()
|
||||
.firstOrNull { it.nameIdentifier?.textMatches(element) == true }
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
override fun getVariants(): Array<Any> {
|
||||
val file = element.containingFile as SlintFile
|
||||
val array: Array<Any> = file.childrenOfType<SlintImport>()
|
||||
.mapNotNull { it.moduleLocation?.reference?.resolve() as SlintFile? }
|
||||
.flatMap { it.exportedElements() }.toTypedArray()
|
||||
return array
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.zhouxi.slint.reference.provider
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.PsiReferenceBase
|
||||
import com.intellij.psi.PsiReferenceProvider
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.SlintFile
|
||||
import me.zhouxi.slint.lang.psi.SlintImport
|
||||
import me.zhouxi.slint.lang.psi.SlintInternalName
|
||||
import me.zhouxi.slint.lang.psi.utils.exportedElements
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/17
|
||||
*/
|
||||
class InternalNameReferenceProvider : PsiReferenceProvider() {
|
||||
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
|
||||
return arrayOf(InternalNameReference(element as SlintInternalName))
|
||||
}
|
||||
|
||||
class InternalNameReference(element: SlintInternalName) : PsiReferenceBase<SlintInternalName?>(element) {
|
||||
override fun resolve(): PsiElement? {
|
||||
val slintImport = element.parentOfType<SlintImport>() ?: return null
|
||||
val slintFile = slintImport.moduleLocation?.reference?.resolve() as? SlintFile ?: return null
|
||||
return slintFile.exportedElements().firstOrNull { it.textMatches(element) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.zhouxi.slint.reference.provider
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.SlintModuleLocation
|
||||
|
||||
/**
|
||||
* @author zhouxi 2024/5/17
|
||||
*/
|
||||
class ModuleLocationReferenceProvider : PsiReferenceProvider() {
|
||||
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
|
||||
return arrayOf(ModuleLocationReference(element))
|
||||
}
|
||||
|
||||
class ModuleLocationReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) {
|
||||
override fun resolve(): PsiElement? {
|
||||
val location = element as SlintModuleLocation
|
||||
val filename = location.stringLiteral.text
|
||||
if (filename.length < 3) return null
|
||||
val directory = element.containingFile.originalFile.virtualFile?.parent ?: return null
|
||||
val file = VfsUtil.findRelativeFile(directory, filename.substring(1, filename.length - 1)) ?: return null
|
||||
return PsiManager.getInstance(element.project).findFile(file)
|
||||
}
|
||||
|
||||
override fun calculateDefaultRangeInElement(): TextRange {
|
||||
return TextRange(1, element.textLength - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package me.zhouxi.slint.reference.provider
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.PsiReferenceBase
|
||||
import com.intellij.psi.PsiReferenceProvider
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import com.intellij.psi.util.parentOfTypes
|
||||
import com.intellij.util.ProcessingContext
|
||||
import me.zhouxi.slint.lang.psi.SlintComponent
|
||||
import me.zhouxi.slint.lang.psi.SlintPropertyBinding
|
||||
import me.zhouxi.slint.lang.psi.SlintSubComponent
|
||||
import me.zhouxi.slint.lang.psi.utils.searchProperty
|
||||
|
||||
class PropertyReferenceProvider : PsiReferenceProvider() {
|
||||
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
|
||||
return arrayOf(PropertyReference(element))
|
||||
}
|
||||
|
||||
class PropertyReference(element: PsiElement) : PsiReferenceBase<PsiElement?>(element) {
|
||||
override fun resolve(): PsiElement? {
|
||||
val binding = element.parentOfType<SlintPropertyBinding>() ?: return null
|
||||
val parent = binding.parentOfTypes(SlintSubComponent::class, SlintComponent::class) ?: return null
|
||||
return searchProperty(parent) { it.propertyName?.textMatches(element) == true }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user