From 06cf13484d75af1ff0384c8eee46142fe47d190a Mon Sep 17 00:00:00 2001 From: me Date: Mon, 27 May 2024 19:17:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E6=8F=92?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/grammar/main.bnf | 2 +- .../psi/impl/SlintStubBasedPsiElementImpl.kt | 4 + .../completion/provider/ComponentProvider.kt | 83 ++++++++++++------- .../zhouxi/slint/lang/SlintElementFactory.kt | 7 ++ .../slint/lang/psi/extension/SlintImportEx.kt | 20 +++++ 5 files changed, 87 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/me/zhouxi/slint/lang/psi/extension/SlintImportEx.kt diff --git a/src/main/grammar/main.bnf b/src/main/grammar/main.bnf index ad3b0b8..aacd081 100644 --- a/src/main/grammar/main.bnf +++ b/src/main/grammar/main.bnf @@ -81,7 +81,7 @@ Import ::= ImportKeyword (ImportElement|ImportResource)';'{ extends="me.zhouxi.slint.lang.psi.impl.SlintStubBasedPsiElementImpl" } -ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* ','? '}' FromKeyword ModuleLocation{ +ImportElement ::= '{' ImportSpecifier (',' ImportSpecifier)* '}' FromKeyword ModuleLocation{ pin=1 } ImportResource ::= ModuleLocation diff --git a/src/main/java/me/zhouxi/slint/lang/psi/impl/SlintStubBasedPsiElementImpl.kt b/src/main/java/me/zhouxi/slint/lang/psi/impl/SlintStubBasedPsiElementImpl.kt index 85d294e..85efd04 100644 --- a/src/main/java/me/zhouxi/slint/lang/psi/impl/SlintStubBasedPsiElementImpl.kt +++ b/src/main/java/me/zhouxi/slint/lang/psi/impl/SlintStubBasedPsiElementImpl.kt @@ -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> : constructor(stub: T, nodeType: IStubElementType<*, *>) : super(stub, nodeType) + override fun toString(): String { + return StringUtil.trimEnd(this::class.java.simpleName, "Impl") + "(" + elementType + ")" + } } diff --git a/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt index f3c4c4d..d08c6a8 100644 --- a/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt +++ b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt @@ -1,24 +1,28 @@ 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.completion.* +import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.lookup.LookupElementDecorator +import com.intellij.codeInsight.lookup.LookupElementDecorator.withInsertHandler import com.intellij.icons.AllIcons import com.intellij.openapi.vfs.VfsUtil import com.intellij.psi.PsiDocumentManager +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.refactoring.extractMethod.newImpl.ExtractMethodHelper.addSiblingAfter -import com.intellij.testFramework.utils.editor.commitToPsi +import com.intellij.psi.util.parentOfTypes 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.extension.importNames import me.zhouxi.slint.lang.psi.stubs.index.StubIndexKeys object ComponentProvider : CompletionProvider() { @@ -44,31 +48,54 @@ object ComponentProvider : CompletionProvider() { } val lookups = components.map { val targetFile = it.containingFile.originalFile - val currentFile = parameters.position.containingFile.originalFile - val path = VfsUtil.findRelativePath(currentFile.virtualFile, targetFile.virtualFile, '/') + 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) - LookupElementDecorator.withInsertHandler(builder) { context, item -> -// val caretOffset: Int = context.editor.caretModel.offset -// context.document.insertString(caretOffset, "{}") -// context.editor.caretModel.moveToOffset(caretOffset + 1) - val currentFile = PsiDocumentManager.getInstance(project).getPsiFile(context.document)!! - val element = item.psiElement as SlintComponent - val targetImport = currentFile.childrenOfType().find { import -> - val target = import.importElement?.moduleLocation?.reference?.resolve() - PsiManager.getInstance(project).areElementsEquivalent(target, targetFile) - } - if (targetImport == null) { - currentFile.addBefore( - createImport(project, element.componentName!!.text, path!!), - currentFile.firstChild - ) - } else { - val importElement = targetImport.importElement!! - val specifier = createImportSpecifier(project, element.componentName!!.text) - importElement.addAfter(specifier,importElement.importSpecifierList.last()) - } - } + withInsertHandler(builder, ComponentInsertHandler(targetFile, path)) } result.addAllElements(lookups) } + + + class ComponentInsertHandler( + private val targetFile: PsiFile, + private val path: String? + ) : InsertHandler { + override fun handleInsert(context: InsertionContext, item: LookupElement) { + val component = item.psiElement as SlintComponent + val targetImport = context.file.childrenOfType().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()[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) + } + } } diff --git a/src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt b/src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt index 4f5c553..95ffbc6 100644 --- a/src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt +++ b/src/main/kotlin/me/zhouxi/slint/lang/SlintElementFactory.kt @@ -4,6 +4,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiFileFactory import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.nextLeafs import me.zhouxi.slint.lang.psi.SlintComponent import me.zhouxi.slint.lang.psi.SlintFile import me.zhouxi.slint.lang.psi.SlintImportSpecifier @@ -31,4 +32,10 @@ fun createImport(project: Project?, text: String, location: String): PsiElement 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] } \ No newline at end of file diff --git a/src/main/kotlin/me/zhouxi/slint/lang/psi/extension/SlintImportEx.kt b/src/main/kotlin/me/zhouxi/slint/lang/psi/extension/SlintImportEx.kt new file mode 100644 index 0000000..bc79e8f --- /dev/null +++ b/src/main/kotlin/me/zhouxi/slint/lang/psi/extension/SlintImportEx.kt @@ -0,0 +1,20 @@ +package me.zhouxi.slint.lang.psi.extension + +import com.intellij.psi.PsiElement +import me.zhouxi.slint.lang.psi.SlintImport +import me.zhouxi.slint.lang.psi.SlintReferenceIdentifier + + +fun SlintImport.importNames(): List { + return this.importElement?.importSpecifierList?.map { it.referenceIdentifier } ?: emptyList() +} + +/** + * 导入的有效的名字, + * psiElement可能是identifier或者internalName + */ +fun SlintImport.availableNames(): List { + return this.importElement?.importSpecifierList?.map { + it.importAlias?.internalName ?: it.referenceIdentifier + } ?: emptyList() +} \ No newline at end of file