diff --git a/src/main/kotlin/me/zhouxi/slint/completion/SlintCompletionContributor.kt b/src/main/kotlin/me/zhouxi/slint/completion/SlintCompletionContributor.kt index b326f3f..4b57341 100644 --- a/src/main/kotlin/me/zhouxi/slint/completion/SlintCompletionContributor.kt +++ b/src/main/kotlin/me/zhouxi/slint/completion/SlintCompletionContributor.kt @@ -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) } diff --git a/src/main/kotlin/me/zhouxi/slint/completion/provider/ElementKeywordProvider.kt b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentElementKeywordProvider.kt similarity index 85% rename from src/main/kotlin/me/zhouxi/slint/completion/provider/ElementKeywordProvider.kt rename to src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentElementKeywordProvider.kt index 832444c..e585748 100644 --- a/src/main/kotlin/me/zhouxi/slint/completion/provider/ElementKeywordProvider.kt +++ b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentElementKeywordProvider.kt @@ -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() { - 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 { - 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() { + 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 { + return psiElement().withParent(psiElement(Component)) + .andNot(AtChildrenCompletionProvider.pattern()) + } +} diff --git a/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentNameProvider.kt similarity index 94% rename from src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt rename to src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentNameProvider.kt index 501f23a..58a2a55 100644 --- a/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentProvider.kt +++ b/src/main/kotlin/me/zhouxi/slint/completion/provider/ComponentNameProvider.kt @@ -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() { - override fun addCompletions( - parameters: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet - ) { - val project = parameters.position.project - val components = arrayListOf() - 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 { - 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().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) - } - } - - override fun pattern(): ElementPattern { - 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() { + override fun addCompletions( + parameters: CompletionParameters, + context: ProcessingContext, + result: CompletionResultSet + ) { + val project = parameters.position.project + val components = arrayListOf() + 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 { + 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().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) + } + } + + override fun pattern(): ElementPattern { + return or( + psiElement().withParent(psiElement(Component)).andNot(AtChildrenCompletionProvider.pattern()), + psiElement().withSuperParent(2, psiElement(InheritDeclaration)) + ) + } +} diff --git a/src/main/kotlin/me/zhouxi/slint/completion/provider/TopKeywordProvider.kt b/src/main/kotlin/me/zhouxi/slint/completion/provider/TopKeywordProvider.kt index a259b6e..9540810 100644 --- a/src/main/kotlin/me/zhouxi/slint/completion/provider/TopKeywordProvider.kt +++ b/src/main/kotlin/me/zhouxi/slint/completion/provider/TopKeywordProvider.kt @@ -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() { @@ -15,13 +24,42 @@ object TopKeywordProvider : AbstractSlintCompletionProvider { - return psiElement().withSuperParent(1, psiElement(SlintFileElementType)) + return psiElement().withSuperParent(2, psiElement(SlintFileElementType)) + .andNot(psiElement().withParent(psiElement(Component))) + } + + class MyInsertHandler : InsertHandler { + 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); + } + }) + } + } } diff --git a/src/main/kotlin/me/zhouxi/slint/lang/psi/stubs/types/SlintComponentElementType.kt b/src/main/kotlin/me/zhouxi/slint/lang/psi/stubs/types/SlintComponentElementType.kt index 2d32e60..2ee1e25 100644 --- a/src/main/kotlin/me/zhouxi/slint/lang/psi/stubs/types/SlintComponentElementType.kt +++ b/src/main/kotlin/me/zhouxi/slint/lang/psi/stubs/types/SlintComponentElementType.kt @@ -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) {