initial commit

This commit is contained in:
me
2024-05-23 15:45:59 +08:00
commit 70275b14e9
55 changed files with 2661 additions and 0 deletions

View 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
}

View File

@@ -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)
)
}
}

View File

@@ -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) }

View File

@@ -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)
)
}
}

View 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)
}

View File

@@ -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()
}
}
}

View File

@@ -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
}
}

View 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
}

View File

@@ -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
}
}

View File

@@ -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() }
}

View File

@@ -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()
}

View 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()
}

View 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()
}
}

View File

@@ -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")
}
}

View File

@@ -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
}

View File

@@ -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
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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"))
}
}
}
}
}

View 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()
}
}

View File

@@ -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()
)
}
}

View File

@@ -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
}
}
}

View File

@@ -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) }
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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 }
}
}
}