|
| 1 | +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. |
1 | 2 | package cc.unitmesh.vue.provider
|
2 | 3 |
|
3 | 4 | import cc.unitmesh.devti.provider.RelatedClassesProvider
|
| 5 | +import com.intellij.javascript.nodejs.PackageJsonData |
| 6 | +import com.intellij.lang.ecmascript6.psi.* |
| 7 | +import com.intellij.lang.ecmascript6.psi.impl.ES6ImportPsiUtil.ES6_IMPORT_DECLARATION |
| 8 | +import com.intellij.lang.ecmascript6.resolve.ES6PsiUtil |
| 9 | +import com.intellij.lang.ecmascript6.resolve.JSFileReferencesUtil |
| 10 | +import com.intellij.lang.javascript.buildTools.npm.PackageJsonUtil |
| 11 | +import com.intellij.lang.javascript.psi.ecma6.TypeScriptPropertySignature |
| 12 | +import com.intellij.lang.javascript.psi.resolve.JSResolveUtil |
| 13 | +import com.intellij.openapi.diagnostic.logger |
| 14 | +import com.intellij.openapi.util.text.StringUtil |
4 | 15 | import com.intellij.psi.PsiElement
|
5 | 16 | import com.intellij.psi.PsiFile
|
6 |
| -import org.jetbrains.vuejs.lang.html.VueLanguage |
| 17 | +import com.intellij.psi.impl.source.html.HtmlFileImpl |
| 18 | +import com.intellij.util.asSafely |
| 19 | +import com.intellij.webSymbols.SymbolKind |
| 20 | +import org.jetbrains.vuejs.index.findModule |
| 21 | +import org.jetbrains.vuejs.index.findScriptTag |
| 22 | +import org.jetbrains.vuejs.lang.html.VueFile |
| 23 | +import java.util.* |
| 24 | + |
7 | 25 |
|
8 | 26 | /**
|
9 | 27 | * AutoDev Modular for Vue
|
10 | 28 | */
|
11 | 29 | class VueRelatedClassProvider : RelatedClassesProvider {
|
12 |
| - override fun lookup(element: PsiElement): MutableList<PsiElement> { |
13 |
| - if (element.language !is VueLanguage) return mutableListOf<PsiElement>() |
| 30 | + override fun lookup(element: PsiElement): List<PsiElement> { |
| 31 | + if (element !is VueFile) return emptyList() |
| 32 | + |
| 33 | + return emptyList() |
| 34 | + } |
| 35 | + |
| 36 | + override fun lookup(psiFile: PsiFile): List<PsiElement> { |
| 37 | + if (psiFile !is VueFile) return emptyList() |
| 38 | + |
| 39 | + val scriptTag = findScriptTag(psiFile, true) ?: findScriptTag(psiFile, false) ?: return emptyList() |
| 40 | +// val jsContent = PsiTreeUtil.getChildOfType(scriptTag, JSEmbeddedContent::class.java) |
| 41 | +// ?: return mutableListOf<PsiElement>() |
| 42 | + val kind = "" |
| 43 | + val localImports = sequenceOf(findModule(scriptTag, false), findModule(scriptTag, true)) |
| 44 | + .flatMap { |
| 45 | + JSResolveUtil.getStubbedChildren(it, ES6_IMPORT_DECLARATION).asSequence() |
| 46 | + } |
| 47 | + .filterIsInstance<ES6ImportDeclaration>() |
| 48 | + .map { |
| 49 | + it.children.mapNotNull { source -> |
| 50 | + when (source) { |
| 51 | + is ES6ImportSpecifierAlias -> symbolLocationsFromSpecifier(source.findSpecifierElement() as? ES6ImportSpecifier, kind) |
| 52 | + is ES6ImportSpecifier -> symbolLocationsFromSpecifier(source, kind) |
| 53 | + is ES6ImportedBinding -> symbolLocationsForModule(source, source.declaration?.fromClause?.referenceText, "default", kind) |
| 54 | + is TypeScriptPropertySignature -> symbolLocationFromPropertySignature(source, kind)?.let { listOf(it) } |
| 55 | + is ES6ExportDefaultAssignment, is HtmlFileImpl -> source.containingFile.virtualFile?.url?.let { |
| 56 | + listOf(WebTypesSymbolLocation(it, "default", kind)) |
| 57 | + } |
| 58 | + else -> null |
| 59 | + } |
| 60 | + |
| 61 | + } |
| 62 | + }.flatten() |
| 63 | + |
| 64 | + |
| 65 | + logger<VueRelatedClassProvider>().info("imports: $localImports") |
| 66 | + /// resolve file in local |
| 67 | + return emptyList() |
| 68 | + } |
14 | 69 |
|
15 |
| - return mutableListOf<PsiElement>() |
| 70 | + private fun symbolLocationsFromSpecifier(specifier: ES6ImportSpecifier?, symbolKind: String): List<WebTypesSymbolLocation> { |
| 71 | + if (specifier?.specifierKind == ES6ImportExportSpecifier.ImportExportSpecifierKind.IMPORT) { |
| 72 | + val symbolName = if (specifier.isDefault) "default" else specifier.referenceName |
| 73 | + val moduleName = specifier.declaration?.fromClause?.referenceText |
| 74 | + return symbolLocationsForModule(specifier, moduleName, symbolName, symbolKind) |
| 75 | + } |
| 76 | + return emptyList() |
16 | 77 | }
|
17 | 78 |
|
18 |
| - override fun lookup(element: PsiFile): MutableList<PsiElement> { |
19 |
| - return mutableListOf<PsiElement>() |
| 79 | + private fun symbolLocationsForModule(context: PsiElement, |
| 80 | + moduleName: String?, |
| 81 | + symbolName: String?, |
| 82 | + symbolKind: String): List<WebTypesSymbolLocation> = |
| 83 | + if (symbolName != null && moduleName != null) { |
| 84 | + val result = mutableListOf<WebTypesSymbolLocation>() |
| 85 | + val unquotedModule = StringUtil.unquoteString(moduleName) |
| 86 | + if (!unquotedModule.startsWith(".")) { |
| 87 | + result.add(WebTypesSymbolLocation(unquotedModule.lowercase(Locale.US), symbolName, symbolKind)) |
| 88 | + } |
| 89 | + |
| 90 | + if (unquotedModule.contains('/')) { |
| 91 | + val modules = JSFileReferencesUtil.resolveModuleReference(context, unquotedModule) |
| 92 | + modules.mapNotNullTo(result) { |
| 93 | + it.containingFile?.originalFile?.virtualFile?.url?.let { url -> |
| 94 | + WebTypesSymbolLocation(url, symbolName, symbolKind) |
| 95 | + } |
| 96 | + } |
| 97 | + // A workaround to avoid full resolution in case of components in subpackages |
| 98 | + if (symbolName == "default" |
| 99 | + && !unquotedModule.startsWith(".") |
| 100 | + && unquotedModule.count { it == '/' } == 1) { |
| 101 | + |
| 102 | + modules.mapNotNullTo(result) { |
| 103 | + ES6PsiUtil.findDefaultExport(it) |
| 104 | + ?.asSafely<ES6ExportDefaultAssignment>() |
| 105 | + ?.initializerReference |
| 106 | + ?.let { symbolName -> |
| 107 | + WebTypesSymbolLocation(unquotedModule.takeWhile { it != '/' }, symbolName, symbolKind) |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + result |
| 113 | + } |
| 114 | + else emptyList() |
| 115 | + |
| 116 | + private fun symbolLocationFromPropertySignature(property: TypeScriptPropertySignature, kind: SymbolKind): WebTypesSymbolLocation? { |
| 117 | + if (!property.isValid) return null |
| 118 | + |
| 119 | + // TypeScript GlobalComponents definition |
| 120 | + val symbolName = property.memberName.takeIf { it.isNotEmpty() } |
| 121 | + ?: return null |
| 122 | + |
| 123 | + // Locate module |
| 124 | + val packageName = property.containingFile?.originalFile?.virtualFile?.let { PackageJsonUtil.findUpPackageJson(it) } |
| 125 | + ?.let { PackageJsonData.getOrCreate(it) } |
| 126 | + ?.name |
| 127 | + ?: return null |
| 128 | + |
| 129 | + return WebTypesSymbolLocation(packageName.lowercase(Locale.US), symbolName, kind) |
20 | 130 | }
|
| 131 | + |
| 132 | + private data class WebTypesSymbolLocation( |
| 133 | + val moduleName: String, |
| 134 | + val symbolName: String, |
| 135 | + val symbolKind: String, |
| 136 | + ) |
21 | 137 | }
|
0 commit comments