Skip to content

Commit cfe4699

Browse files
committed
feat(language): add JavaShireQLInterpreter and JavaSymbolProvider for enhanced Java support #379
1 parent 603aaae commit cfe4699

File tree

16 files changed

+798
-11
lines changed

16 files changed

+798
-11
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ project(":java") {
498498
}
499499

500500
implementation(project(":core"))
501+
implementation(project(":exts:devins-lang"))
501502
}
502503
}
503504

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package cc.unitmesh.devti.devins
2+
3+
enum class JvmShireQLFuncType(val methodName: String, val description: String) {
4+
GET_NAME("getName", "Get class name"),
5+
NAME("name", "Get class name"),
6+
EXTENDS("extends", "Get class extends"),
7+
IMPLEMENTS("implements", "Get class implements"),
8+
METHOD_CODE_BY_NAME("methodCodeByName", "Get method code by name"),
9+
FIELD_CODE_BY_NAME("fieldCodeByName", "Get field code by name"),
10+
11+
SUBCLASSES_OF("subclassesOf", "Get subclasses of class"),
12+
ANNOTATED_OF("annotatedOf", "Get annotated classes"),
13+
SUPERCLASS_OF("superclassOf", "Get superclass of class"),
14+
IMPLEMENTS_OF("implementsOf", "Get implemented interfaces of class"),
15+
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/ast/FunctionStatementProcessor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ open class FunctionStatementProcessor(override val myProject: Project, override
480480
is FrontMatterType.DATE,
481481
is FrontMatterType.IDENTIFIER,
482482
is FrontMatterType.STRING,
483-
-> {
483+
-> {
484484
type.value
485485
}
486486

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package cc.unitmesh.devti.language.navigation
2+
3+
import cc.unitmesh.devti.language.ast.action.PatternActionFuncDef
4+
import cc.unitmesh.devti.language.middleware.post.PostProcessorType
5+
import cc.unitmesh.devti.language.psi.DevInFrontMatterEntry
6+
import cc.unitmesh.devti.language.psi.DevInFrontMatterHeader
7+
import cc.unitmesh.devti.language.psi.DevInFuncCall
8+
import cc.unitmesh.devti.language.psi.DevInFuncName
9+
import cc.unitmesh.devti.language.psi.DevInTypes
10+
import cc.unitmesh.devti.language.utils.findFile
11+
import cc.unitmesh.devti.language.utils.lookupFile
12+
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
13+
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandlerBase
14+
import com.intellij.openapi.application.runInEdt
15+
import com.intellij.openapi.editor.Editor
16+
import com.intellij.openapi.fileEditor.FileEditorManager
17+
import com.intellij.openapi.project.Project
18+
import com.intellij.psi.PsiElement
19+
import com.intellij.psi.impl.source.tree.LeafPsiElement
20+
import com.intellij.psi.util.PsiTreeUtil.getChildrenOfTypeAsList
21+
import com.intellij.psi.util.elementType
22+
23+
24+
class ShireGotoDeclarationHandler : GotoDeclarationHandlerBase(), GotoDeclarationHandler {
25+
private val validFunctionNames = setOf(
26+
PatternActionFuncDef.EXECUTE.funcName,
27+
PatternActionFuncDef.THREAD.funcName,
28+
PatternActionFuncDef.BATCH.funcName,
29+
PostProcessorType.SaveFile.handleName,
30+
PatternActionFuncDef.APPROVAL_EXECUTE.funcName,
31+
"mock"
32+
)
33+
34+
override fun getGotoDeclarationTarget(element: PsiElement?, editor: Editor?): PsiElement? {
35+
if (element !is LeafPsiElement) return null
36+
val project = element.project
37+
38+
gotoSourceFile(element, project)
39+
40+
return gotoToFunctionDecl(element)
41+
}
42+
43+
private fun gotoToFunctionDecl(element: LeafPsiElement): DevInFrontMatterEntry? {
44+
val psiFile = element.containingFile
45+
// handle for foreign function
46+
val func = element.parent as? DevInFuncName ?: return null
47+
val header = getChildrenOfTypeAsList(psiFile, DevInFrontMatterHeader::class.java).firstOrNull()
48+
val functionsNode = header?.frontMatterEntries?.frontMatterEntryList?.firstOrNull {
49+
it.firstChild.text == "functions"
50+
} ?: return null
51+
52+
val functionEntries: List<DevInFrontMatterEntry> =
53+
functionsNode.frontMatterValue?.objectKeyValue?.keyValueList?.filter {
54+
it.frontMatterEntry.foreignFunction != null
55+
}?.map {
56+
it.frontMatterEntry
57+
} ?: return null
58+
59+
val funcName = func.text
60+
val foreignFunc = functionEntries.find { it.frontMatterKey?.text == funcName } ?: return null
61+
return foreignFunc
62+
}
63+
64+
private fun gotoSourceFile(
65+
element: LeafPsiElement,
66+
project: Project,
67+
) {
68+
if (element.elementType != DevInTypes.QUOTE_STRING) return
69+
if (element.parent?.elementType != DevInTypes.PIPELINE_ARG) return
70+
71+
val funcCall = element.parent?.parent?.parent as? DevInFuncCall ?: return
72+
val funcName = funcCall.funcName.text
73+
74+
if (funcName !in validFunctionNames) return
75+
76+
val fileName = element.text.removeSurrounding("\"")
77+
val file = project.lookupFile(fileName) ?: project.findFile(fileName) ?: return
78+
79+
runInEdt {
80+
FileEditorManager.getInstance(project).openFile(file, true)
81+
}
82+
}
83+
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/parser/DevInParserDefinition.kt

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ internal class DevInParserDefinition : ParserDefinition {
2222
@NotNull
2323
override fun createLexer(project: Project?): Lexer = DevInLexerAdapter()
2424

25-
@NotNull
26-
override fun getCommentTokens(): TokenSet = TokenSet.EMPTY
25+
override fun getCommentTokens(): TokenSet = ShireTokenTypeSets.SHIRE_COMMENTS
2726

2827
@NotNull
2928
override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY
3029

30+
override fun getWhitespaceTokens(): TokenSet = ShireTokenTypeSets.WHITESPACES
31+
3132
@NotNull
3233
override fun createParser(project: Project?): PsiParser = DevInParser()
3334

@@ -39,16 +40,33 @@ internal class DevInParserDefinition : ParserDefinition {
3940

4041
@NotNull
4142
override fun createElement(node: ASTNode?): PsiElement {
42-
val elementType = node!!.elementType
43-
if (elementType == DevInTypes.CODE) {
44-
return CodeBlockElement(node)
45-
}
43+
return when (node!!.elementType) {
44+
DevInTypes.CODE -> {
45+
CodeBlockElement(node)
46+
}
4647

47-
if (elementType == DevInTypes.CODE_CONTENTS) {
48-
return ASTWrapperPsiElement(node)
49-
}
48+
DevInTypes.PATTERN -> {
49+
PatternElement(node)
50+
}
51+
DevInTypes.FUNC_CALL -> {
52+
when (node.firstChildNode.text) {
53+
"grep" -> {
54+
ShireGrepFuncCall(node)
55+
}
56+
"sed" -> {
57+
ShireSedFuncCall(node)
58+
}
59+
else -> {
60+
DevInTypes.Factory.createElement(node)
61+
}
62+
}
63+
}
64+
DevInTypes.CODE_CONTENTS -> {
65+
ASTWrapperPsiElement(node)
66+
}
5067

51-
return DevInTypes.Factory.createElement(node)
68+
else -> DevInTypes.Factory.createElement(node)
69+
}
5270
}
5371

5472
companion object {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package cc.unitmesh.devti.language.parser
2+
3+
import com.intellij.extapi.psi.ASTWrapperPsiElement
4+
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.ElementManipulators
6+
import com.intellij.psi.LiteralTextEscaper
7+
import com.intellij.psi.PsiLanguageInjectionHost
8+
import com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor
9+
10+
class PatternElement(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost,
11+
InjectionBackgroundSuppressor {
12+
override fun isValidHost(): Boolean = true
13+
14+
override fun updateText(text: String): PsiLanguageInjectionHost {
15+
return ElementManipulators.handleContentChange(this, text)
16+
}
17+
18+
override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {
19+
return LiteralTextEscaper.createSimple(this, false)
20+
}
21+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package cc.unitmesh.devti.language.parser
2+
3+
import cc.unitmesh.devti.language.psi.impl.DevInFuncCallImpl
4+
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.ElementManipulators
6+
import com.intellij.psi.LiteralTextEscaper
7+
import com.intellij.psi.PsiLanguageInjectionHost
8+
import com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor
9+
10+
class ShireGrepFuncCall(node: ASTNode) : DevInFuncCallImpl(node), PsiLanguageInjectionHost,
11+
InjectionBackgroundSuppressor {
12+
override fun isValidHost(): Boolean = true
13+
14+
override fun updateText(text: String): PsiLanguageInjectionHost {
15+
return ElementManipulators.handleContentChange(this, text)
16+
}
17+
18+
override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {
19+
return LiteralTextEscaper.createSimple(this, false)
20+
}
21+
}
22+
23+
class ShireSedFuncCall(node: ASTNode) : DevInFuncCallImpl(node), PsiLanguageInjectionHost,
24+
InjectionBackgroundSuppressor {
25+
override fun isValidHost(): Boolean = true
26+
27+
override fun updateText(text: String): PsiLanguageInjectionHost {
28+
return ElementManipulators.handleContentChange(this, text)
29+
}
30+
31+
override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {
32+
return LiteralTextEscaper.createSimple(this, false)
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package cc.unitmesh.devti.language.parser
2+
3+
import cc.unitmesh.devti.language.psi.DevInTypes
4+
import com.intellij.psi.TokenType
5+
import com.intellij.psi.tree.TokenSet
6+
7+
object ShireTokenTypeSets {
8+
// DevInTypes.NEWLINE
9+
val WHITESPACES: TokenSet = TokenSet.create(TokenType.WHITE_SPACE)
10+
11+
val SHIRE_COMMENTS = TokenSet.create(DevInTypes.CONTENT_COMMENTS, DevInTypes.COMMENTS, DevInTypes.BLOCK_COMMENT)
12+
}

exts/devins-lang/src/main/resources/cc.unitmesh.devti.language.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
<typedHandler implementation="cc.unitmesh.devti.language.DevInTypedHandler"/>
3232

33+
<gotoDeclarationHandler implementation="cc.unitmesh.devti.language.navigation.ShireGotoDeclarationHandler"/>
34+
3335
<completion.contributor language="DevIn"
3436
id="devInCompletionContributor"
3537
order="last"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package cc.unitmesh.devti.language.regression
2+
3+
import cc.unitmesh.devti.language.provider.ShireSymbolProvider
4+
import com.intellij.codeInsight.completion.CompletionParameters
5+
import com.intellij.codeInsight.completion.CompletionResultSet
6+
import com.intellij.codeInsight.lookup.LookupElement
7+
import com.intellij.openapi.fileTypes.PlainTextFileType
8+
import com.intellij.openapi.project.Project
9+
import com.intellij.psi.PsiElement
10+
import com.intellij.psi.PsiManager
11+
import com.intellij.psi.PsiNamedElement
12+
import com.intellij.psi.search.FileTypeIndex
13+
import com.intellij.psi.search.ProjectScope
14+
15+
class DefaultShireSymbolProvider : ShireSymbolProvider {
16+
override val language: String = "Default"
17+
18+
override fun lookupSymbol(
19+
project: Project,
20+
parameters: CompletionParameters,
21+
result: CompletionResultSet,
22+
): List<LookupElement> {
23+
return emptyList()
24+
}
25+
26+
override fun lookupElementByName(project: Project, name: String): List<PsiElement>? {
27+
val searchScope = ProjectScope.getProjectScope(project)
28+
val virtualFiles = FileTypeIndex.getFiles(PlainTextFileType.INSTANCE, searchScope)
29+
30+
return when (name) {
31+
"String" -> {
32+
emptyList()
33+
}
34+
35+
"File" -> {
36+
virtualFiles.mapNotNull { PsiManager.getInstance(project).findFile(it) }.toList()
37+
}
38+
39+
else -> {
40+
emptyList()
41+
}
42+
}
43+
}
44+
45+
override fun resolveSymbol(project: Project, symbol: String): List<PsiNamedElement> {
46+
return emptyList()
47+
}
48+
}

0 commit comments

Comments
 (0)