Skip to content

Commit 8e70b21

Browse files
committed
feat(java): add related symbol resolution and command #257
- Introduce `RelatedSymbolInsCommand` for resolving related symbols in Java. - Enhance `JavaCustomDevInsSymbolProvider` with `resolveElement` for better symbol resolution. - Add `RELATED` builtin command for fetching related content. - Improve `JavaTypeUtil` to handle type resolution more robustly.
1 parent 1e6956b commit 8e70b21

File tree

10 files changed

+129
-7
lines changed

10 files changed

+129
-7
lines changed

core/src/main/kotlin/cc/unitmesh/devti/provider/devins/DevInsSymbolProvider.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.intellij.codeInsight.completion.CompletionResultSet
55
import com.intellij.codeInsight.lookup.LookupElement
66
import com.intellij.openapi.extensions.ExtensionPointName
77
import com.intellij.openapi.project.Project
8+
import com.intellij.psi.PsiElement
89

910
/**
1011
* The symbol provider for DevIns completion and execution
@@ -24,6 +25,8 @@ interface DevInsSymbolProvider {
2425
result: CompletionResultSet
2526
): List<LookupElement>
2627

28+
fun resolveElement(project: Project, symbol: String): List<PsiElement>
29+
2730
/**
2831
* Resolves the symbol for different programming languages.
2932
* For example, in Java:

core/src/main/kotlin/cc/unitmesh/devti/sketch/run/SketchRunContext.kt renamed to core/src/main/kotlin/cc/unitmesh/devti/sketch/SketchRunContext.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package cc.unitmesh.devti.sketch.run
1+
package cc.unitmesh.devti.sketch
22

33
import com.intellij.openapi.vfs.VirtualFile
44

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/DevInsCompiler.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ class DevInsCompiler(
253253
LocalSearchInsCommand(myProject, prop, shireCode)
254254
}
255255

256+
257+
BuiltinCommand.RELATED -> {
258+
result.isLocalCommand = true
259+
RelatedSymbolInsCommand(myProject, prop)
260+
}
261+
256262
else -> {
257263
PrintInsCommand("/" + commandNode.commandName + ":" + prop)
258264
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/BrowseInsCommand.kt renamed to exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/exec/BrowseInsCommand.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
package cc.unitmesh.devti.language.compiler
1+
package cc.unitmesh.devti.language.compiler.exec
22

33
import cc.unitmesh.devti.language.agenttool.browse.BrowseTool
4-
import cc.unitmesh.devti.language.compiler.exec.InsCommand
54
import com.intellij.openapi.application.runInEdt
65
import com.intellij.openapi.project.Project
76

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cc.unitmesh.devti.language.compiler.exec
2+
3+
import cc.unitmesh.devti.provider.RelatedClassesProvider
4+
import cc.unitmesh.devti.provider.devins.DevInsSymbolProvider
5+
import com.intellij.openapi.project.Project
6+
7+
class RelatedSymbolInsCommand(val myProject: Project, private val symbol: String) : InsCommand {
8+
override suspend fun execute(): String? {
9+
val elements = DevInsSymbolProvider.all().map {
10+
it.resolveElement(myProject, symbol)
11+
}.flatten()
12+
13+
if (elements.isEmpty()) return null
14+
15+
val psiElements = elements.mapNotNull {
16+
RelatedClassesProvider.provide(it.language)?.lookup(it)
17+
}.flatten()
18+
19+
if (psiElements.isEmpty()) return null
20+
21+
return psiElements.joinToString("\n") { it.text }
22+
}
23+
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/completion/dataprovider/BuiltinCommand.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ enum class BuiltinCommand(
6565
true,
6666
true
6767
),
68+
RELATED(
69+
"related",
70+
"Get related content by the current file",
71+
AllIcons.Actions.Find,
72+
true,
73+
true
74+
),
6875
;
6976

7077
companion object {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/related:cc.unitmesh.devti.language.psi

java/src/main/kotlin/cc/unitmesh/idea/provider/JavaCustomDevInsSymbolProvider.kt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
77
import com.intellij.ide.highlighter.JavaFileType
88
import com.intellij.lang.java.JavaLanguage
99
import com.intellij.openapi.project.Project
10+
import com.intellij.psi.PsiElement
1011
import com.intellij.psi.PsiManager
1112
import com.intellij.psi.PsiPackageStatement
1213
import com.intellij.psi.impl.file.impl.JavaFileManagerImpl
@@ -52,7 +53,7 @@ class JavaCustomDevInsSymbolProvider : DevInsSymbolProvider {
5253
}
5354

5455
override fun resolveSymbol(project: Project, symbol: String): List<String> {
55-
val scope = GlobalSearchScope.allScope(project)
56+
val scope = ProjectScope.getProjectScope(project)
5657

5758
if (symbol.isEmpty()) return emptyList()
5859

@@ -94,6 +95,51 @@ class JavaCustomDevInsSymbolProvider : DevInsSymbolProvider {
9495
return emptyList()
9596
}
9697

98+
99+
override fun resolveElement(project: Project, symbol: String): List<PsiElement> {
100+
val scope = ProjectScope.getProjectScope(project)
101+
102+
if (symbol.isEmpty()) return emptyList()
103+
104+
// className only, like `String` not Dot
105+
if (symbol.contains(".").not()) {
106+
val psiClasses = PsiShortNamesCache.getInstance(project).getClassesByName(symbol, scope)
107+
if (psiClasses.isNotEmpty()) {
108+
return psiClasses.toList()
109+
}
110+
}
111+
112+
// for package name only, like `cc.unitmesh`
113+
JavaFileManagerImpl(project).findPackage(symbol)?.let { pkg ->
114+
return pkg.classes.toList()
115+
}
116+
117+
// for single class, with function name, like `cc.unitmesh.idea.provider.JavaCustomDevInsSymbolProvider`
118+
val clazz = JavaFileManagerImpl(project).findClass(symbol, scope)
119+
if (clazz != null) {
120+
return listOf(clazz)
121+
}
122+
123+
// for lookup for method
124+
val split = symbol.split("#")
125+
if (split.size == 2) {
126+
val clazzName = split[0]
127+
val methodName = split[1]
128+
return lookupElementWithMethodName(project, clazzName, scope, methodName)
129+
}
130+
131+
// may by not our format, like <package>.<class>.<method> split last
132+
val lastDotIndex = symbol.lastIndexOf(".")
133+
if (lastDotIndex != -1) {
134+
val clazzName = symbol.substring(0, lastDotIndex)
135+
val methodName = symbol.substring(lastDotIndex + 1)
136+
return lookupElementWithMethodName(project, clazzName, scope, methodName)
137+
}
138+
139+
return emptyList()
140+
}
141+
142+
97143
private fun lookupWithMethodName(
98144
project: Project,
99145
clazzName: String,
@@ -110,4 +156,21 @@ class JavaCustomDevInsSymbolProvider : DevInsSymbolProvider {
110156

111157
return emptyList()
112158
}
159+
160+
private fun lookupElementWithMethodName(
161+
project: Project,
162+
clazzName: String,
163+
scope: GlobalSearchScope,
164+
methodName: String
165+
): List<PsiElement> {
166+
val psiClass = JavaFileManagerImpl(project).findClass(clazzName, scope)
167+
if (psiClass != null) {
168+
val psiMethod = psiClass.findMethodsByName(methodName, true).firstOrNull()
169+
if (psiMethod != null) {
170+
return listOf(psiMethod)
171+
}
172+
}
173+
174+
return emptyList()
175+
}
113176
}

java/src/main/kotlin/cc/unitmesh/idea/provider/JavaRelatedClassesProvider.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cc.unitmesh.idea.provider
22

33
import cc.unitmesh.devti.provider.RelatedClassesProvider
44
import cc.unitmesh.idea.context.JavaContextCollection
5+
import cc.unitmesh.idea.service.JavaTypeUtil.resolveByType
56
import com.intellij.openapi.application.ApplicationManager
67
import com.intellij.openapi.application.runReadAction
78
import com.intellij.openapi.roots.ProjectFileIndex
@@ -16,6 +17,7 @@ class JavaRelatedClassesProvider : RelatedClassesProvider {
1617
.flatMap { findSuperClasses(it) }
1718
.map { cleanUp(it) }
1819
.toList()
20+
.distinct()
1921

2022
is PsiClass -> findRelatedClasses(element)
2123
else -> emptyList()
@@ -83,13 +85,20 @@ class JavaRelatedClassesProvider : RelatedClassesProvider {
8385
val parameterTypes = parameters.map { it.type }
8486

8587
val genericTypes = parameters.flatMap { (it.type as? PsiClassType)?.parameters?.toList() ?: emptyList() }
88+
89+
val returnType = if (method.returnTypeElement?.type != null) {
90+
resolveByType(method.returnTypeElement?.type!!).values
91+
} else {
92+
emptyList()
93+
}
94+
8695
val mentionedTypes = parameterTypes + genericTypes
8796

8897
val filterIsInstance = mentionedTypes.filterIsInstance<PsiClassType>()
8998
.distinct()
9099

91100
return@runReadAction ApplicationManager.getApplication().executeOnPooledThread<List<PsiClass>> {
92-
return@executeOnPooledThread filterIsInstance
101+
return@executeOnPooledThread returnType + filterIsInstance
93102
.mapNotNull { runReadAction { it.resolve() } }
94103
.filter { isProjectContent(it) }
95104
.toList()

java/src/main/kotlin/cc/unitmesh/idea/service/JavaTypeUtil.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,20 @@ object JavaTypeUtil {
1111
val resolvedClasses = mutableMapOf<String, PsiClass>()
1212
if (outputType is PsiClassReferenceType) {
1313
val resolveClz = outputType.resolve()
14+
1415
outputType.parameters.filterIsInstance<PsiClassReferenceType>().forEach {
15-
if (resolveClz != null) {
16-
resolvedClasses[it.canonicalText] = resolveClz
16+
val resolve = it.resolve()
17+
if (resolve != null) {
18+
resolvedClasses[it.canonicalText] = resolve
19+
}
20+
21+
it.typeArguments().map { argType ->
22+
if (argType is PsiClassReferenceType) {
23+
val resolvedArgType = argType.resolve()
24+
if (resolvedArgType != null) {
25+
resolvedClasses[argType.canonicalText] = resolvedArgType
26+
}
27+
}
1728
}
1829
}
1930

0 commit comments

Comments
 (0)