fix: 语法问题

This commit is contained in:
me
2024-05-31 17:58:39 +08:00
parent ada52875ad
commit 39a81fcb5d
5 changed files with 206 additions and 171 deletions

View File

@@ -2,11 +2,7 @@ package me.zhouxi.slint.completion
import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionProvider
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.openapi.util.Pair
import com.intellij.patterns.ElementPattern
import com.intellij.psi.PsiElement
import me.zhouxi.slint.completion.provider.*
class SlintCompletionContributor : CompletionContributor() {
@@ -14,8 +10,8 @@ class SlintCompletionContributor : CompletionContributor() {
extend(TopKeywordProvider)
extend(BasicTypeProvider)
extend(AtChildrenCompletionProvider)
extend(ElementKeywordProvider)
extend(ComponentProvider)
extend(ComponentElementKeywordProvider)
extend(ComponentNameProvider)
extend(PropertyBindingProvider)
extend(InheritsCompletionProvider)
}

View File

@@ -1,32 +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.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component
object ElementKeywordProvider : AbstractSlintCompletionProvider<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", "function"
).map { LookupElementBuilder.create(it) }
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(psiElement(Component))
.andNot(AtChildrenCompletionProvider.pattern())
}
}
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<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", "function"
).map { LookupElementBuilder.create(it) }
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withParent(psiElement(Component))
.andNot(AtChildrenCompletionProvider.pattern())
}
}

View File

@@ -1,125 +1,124 @@
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementDecorator.withInsertHandler
import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.childrenOfType
import com.intellij.psi.util.parentOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.createComma
import me.zhouxi.slint.lang.createImport
import me.zhouxi.slint.lang.createImportSpecifier
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintInheritDeclaration
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration
import me.zhouxi.slint.lang.psi.extension.importNames
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys
object ComponentProvider : AbstractSlintCompletionProvider<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
val currentFile = parameters.position.containingFile.originalFile.virtualFile
val path = VfsUtil.findRelativePath(currentFile, targetFile.virtualFile, '/')
val builder = LookupElementBuilder.create(it).withTypeText(path).withIcon(AllIcons.Nodes.Class)
withInsertHandler(builder, ComponentInsertHandler(targetFile, path))
}
result.addAllElements(lookups)
}
class ComponentInsertHandler(
private val targetFile: PsiFile,
private val path: String?
) : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
//insert '{}'
val caretOffset: Int = context.editor.caretModel.offset
val elementAt = context.file.findElementAt(caretOffset)
if (elementAt?.prevSibling !is SlintInheritDeclaration) {
context.document.insertString(caretOffset, "{}")
context.editor.caretModel.moveToOffset(caretOffset + 1)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
}
val component = item.psiElement as SlintComponent
if (component.containingFile.isEquivalentTo(context.file)) {
return
}
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
val target = import.importElement?.moduleLocation?.reference?.resolve()
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
}
if (targetImport == null) {
//不存在对指定文件的Import插入
val import = createImport(context.project, component.componentName!!.text, path!!)
context.file.addBefore(import, context.file.firstChild)
return
}
//如果导入的Name里面包含了这个ComponentName
if (targetImport.importNames().any { it.textMatches(component.componentName!!) }) {
return
}
//引入的文件存在,但是没有导入对应组件
val importElement = targetImport.importElement!!
val specifier = createImportSpecifier(context.project, component.componentName!!.text)
val last = importElement.importSpecifierList.lastOrNull()
//不存在前置节点
if (last == null) {
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
val element = PsiTreeUtil.nextVisibleLeaf(last)
if (!element!!.textMatches(",")) {
val comma = importElement.addAfter(createComma(context.project), last)
importElement.addAfter(specifier, comma)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
importElement.addAfter(specifier, element)
}
}
override fun pattern(): ElementPattern<out PsiElement> {
return or(
psiElement().withParent(psiElement(Component)).andNot(AtChildrenCompletionProvider.pattern()),
psiElement().withSuperParent(2, psiElement(InheritDeclaration))
)
}
}
package me.zhouxi.slint.completion.provider
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementDecorator.withInsertHandler
import com.intellij.icons.AllIcons
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.patterns.StandardPatterns.or
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.search.GlobalSearchScope.projectScope
import com.intellij.psi.stubs.StubIndex
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.childrenOfType
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.createComma
import me.zhouxi.slint.lang.createImport
import me.zhouxi.slint.lang.createImportSpecifier
import me.zhouxi.slint.lang.psi.SlintComponent
import me.zhouxi.slint.lang.psi.SlintImport
import me.zhouxi.slint.lang.psi.SlintInheritDeclaration
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.SlintTypes.InheritDeclaration
import me.zhouxi.slint.lang.psi.extension.importNames
import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys
object ComponentNameProvider : AbstractSlintCompletionProvider<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
val currentFile = parameters.position.containingFile.originalFile.virtualFile
val path = VfsUtil.findRelativePath(currentFile, targetFile.virtualFile, '/')
val builder = LookupElementBuilder.create(it).withTypeText(path).withIcon(AllIcons.Nodes.Class)
withInsertHandler(builder, ComponentInsertHandler(targetFile, path))
}
result.addAllElements(lookups)
}
class ComponentInsertHandler(
private val targetFile: PsiFile,
private val path: String?
) : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
//insert '{}'
val caretOffset: Int = context.editor.caretModel.offset
val elementAt = context.file.findElementAt(caretOffset)
if (elementAt?.prevSibling !is SlintInheritDeclaration) {
context.document.insertString(caretOffset, " { }")
context.editor.caretModel.moveToOffset(caretOffset + 3)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
}
val component = item.psiElement as SlintComponent
if (component.containingFile.isEquivalentTo(context.file)) {
return
}
val targetImport = context.file.childrenOfType<SlintImport>().find { import ->
val target = import.importElement?.moduleLocation?.reference?.resolve()
PsiManager.getInstance(context.project).areElementsEquivalent(target, targetFile)
}
if (targetImport == null) {
//不存在对指定文件的Import插入
val import = createImport(context.project, component.componentName!!.text, path!!)
context.file.addBefore(import, context.file.firstChild)
return
}
//如果导入的Name里面包含了这个ComponentName
if (targetImport.importNames().any { it.textMatches(component.componentName!!) }) {
return
}
//引入的文件存在,但是没有导入对应组件
val importElement = targetImport.importElement!!
val specifier = createImportSpecifier(context.project, component.componentName!!.text)
val last = importElement.importSpecifierList.lastOrNull()
//不存在前置节点
if (last == null) {
importElement.addAfter(specifier, importElement.childrenOfType<LeafPsiElement>()[0])
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
val element = PsiTreeUtil.nextVisibleLeaf(last)
if (!element!!.textMatches(",")) {
val comma = importElement.addAfter(createComma(context.project), last)
importElement.addAfter(specifier, comma)
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
return
}
PsiDocumentManager.getInstance(context.project).commitDocument(context.document)
importElement.addAfter(specifier, element)
}
}
override fun pattern(): ElementPattern<out PsiElement> {
return or(
psiElement().withParent(psiElement(Component)).andNot(AtChildrenCompletionProvider.pattern()),
psiElement().withSuperParent(2, psiElement(InheritDeclaration))
)
}
}

View File

@@ -2,11 +2,20 @@ 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.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder.*
import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.impl.ConstantNode
import com.intellij.icons.AllIcons
import com.intellij.patterns.ElementPattern
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import me.zhouxi.slint.lang.psi.SlintTypes.Component
import me.zhouxi.slint.lang.psi.stubs.types.SlintFileElementType
object TopKeywordProvider : AbstractSlintCompletionProvider<CompletionParameters>() {
@@ -15,13 +24,42 @@ object TopKeywordProvider : AbstractSlintCompletionProvider<CompletionParameters
context: ProcessingContext,
result: CompletionResultSet
) {
result.addAllElements(topKeywords)
result.addAllElements(completion)
result.addElement(create("component"))
}
private val topKeywords =
arrayOf("export", "component", "global", "enum", "struct").map { LookupElementBuilder.create(it) }
val completion = arrayListOf(create("component "), create("struct "), create("enum "))
.flatMap {
arrayListOf(
it,
it.withPresentableText(it.lookupString)
.withTailText("\$Name$ {...}")
.withInsertHandler(MyInsertHandler()),
create("export ${it.lookupString}"),
create("export ${it.lookupString}")
.withTailText("\$Name$ {...}")
.withInsertHandler(MyInsertHandler()),
)
}
override fun pattern(): ElementPattern<out PsiElement> {
return psiElement().withSuperParent(1, psiElement(SlintFileElementType))
return psiElement().withSuperParent(2, psiElement(SlintFileElementType))
.andNot(psiElement().withParent(psiElement(Component)))
}
class MyInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val manager = TemplateManager.getInstance(context.project)
val template = manager.createTemplate("", "", " \$Name$ { \n }")
template.isToReformat = true
template.addVariable("Name", ConstantNode(""), true)
manager.startTemplate(context.editor, template, object : TemplateEditingAdapter() {
override fun templateFinished(template: Template, brokenOff: Boolean) {
context.editor.caretModel.moveToOffset(context.tailOffset - 2);
}
})
}
}
}

View File

@@ -50,10 +50,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 == ComponentName }
?.let {
val text = tree.getChildren(it)[0] as LighterASTTokenNode
tree.charTable.intern(text.text)
}
return SlintComponentStubImpl(parentStub, exported, tree.charTable.intern(token.text).toString())
return SlintComponentStubImpl(parentStub, exported, token.toString())
}
override fun indexStub(stub: SlintComponentStub, sink: IndexSink) {