Skip to content

Commit 10ceea3

Browse files
authored
Merge pull request #22 from phodal/refactor/code-review-components
refactor: extract shared renderer models and utils to mpp-core
2 parents 909c92f + 272614a commit 10ceea3

File tree

18 files changed

+768
-492
lines changed

18 files changed

+768
-492
lines changed
Lines changed: 254 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,262 @@
11
package cc.unitmesh.agent.tool.impl
22

3+
import cc.unitmesh.codegraph.model.*
34
import cc.unitmesh.codegraph.parser.CodeParser
4-
import cc.unitmesh.codegraph.parser.ios.IosCodeParser
5+
import cc.unitmesh.codegraph.parser.Language
56

67
/**
7-
* Android implementation of CodeParser factory
8-
* Uses the same JVM-based implementation as regular JVM
8+
* Android implementation of CodeParser factory.
9+
*
10+
* Note: Android cannot access jvmMain code directly, so we provide a simplified
11+
* regex-based implementation similar to iOS. For full TreeSitter functionality,
12+
* consider using server-side parsing.
913
*/
1014
actual fun createCodeParser(): CodeParser {
11-
// Android uses JVM backend, but IosCodeParser is a fallback
12-
// In practice, we should use JvmCodeParser but it's not accessible from androidMain
13-
// For now, use the simplified iOS implementation
14-
return IosCodeParser()
15+
return AndroidCodeParser()
16+
}
17+
18+
/**
19+
* Simplified CodeParser for Android platform.
20+
* Uses regex-based parsing to extract basic code structure information.
21+
*/
22+
private class AndroidCodeParser : CodeParser {
23+
24+
override suspend fun parseNodes(
25+
sourceCode: String,
26+
filePath: String,
27+
language: Language
28+
): List<CodeNode> {
29+
return when (language) {
30+
Language.JAVA, Language.KOTLIN -> parseOOPNodes(sourceCode, filePath, language)
31+
Language.JAVASCRIPT, Language.TYPESCRIPT -> parseJSNodes(sourceCode, filePath, language)
32+
Language.PYTHON -> parsePythonNodes(sourceCode, filePath, language)
33+
else -> emptyList()
34+
}
35+
}
36+
37+
override suspend fun parseNodesAndRelationships(
38+
sourceCode: String,
39+
filePath: String,
40+
language: Language
41+
): Pair<List<CodeNode>, List<CodeRelationship>> {
42+
val nodes = parseNodes(sourceCode, filePath, language)
43+
val relationships = buildRelationships(nodes)
44+
return Pair(nodes, relationships)
45+
}
46+
47+
override suspend fun parseCodeGraph(
48+
files: Map<String, String>,
49+
language: Language
50+
): CodeGraph {
51+
val allNodes = mutableListOf<CodeNode>()
52+
val allRelationships = mutableListOf<CodeRelationship>()
53+
54+
for ((filePath, sourceCode) in files) {
55+
val (nodes, relationships) = parseNodesAndRelationships(sourceCode, filePath, language)
56+
allNodes.addAll(nodes)
57+
allRelationships.addAll(relationships)
58+
}
59+
60+
return CodeGraph(
61+
nodes = allNodes,
62+
relationships = allRelationships,
63+
metadata = mapOf(
64+
"language" to language.name,
65+
"fileCount" to files.size.toString(),
66+
"platform" to "Android"
67+
)
68+
)
69+
}
70+
71+
override suspend fun parseImports(
72+
sourceCode: String,
73+
filePath: String,
74+
language: Language
75+
): List<ImportInfo> {
76+
return when (language) {
77+
Language.JAVA, Language.KOTLIN -> extractJvmImports(sourceCode, filePath)
78+
Language.PYTHON -> extractPythonImports(sourceCode, filePath)
79+
Language.JAVASCRIPT, Language.TYPESCRIPT -> extractJsImports(sourceCode, filePath)
80+
else -> emptyList()
81+
}
82+
}
83+
84+
private fun extractJvmImports(content: String, filePath: String): List<ImportInfo> {
85+
val importRegex = Regex("""import\s+(static\s+)?([a-zA-Z_][\w.]*[\w*])""")
86+
return importRegex.findAll(content).map { match ->
87+
// Calculate actual line number from match position
88+
val lineNumber = content.substring(0, match.range.first).count { it == '\n' } + 1
89+
ImportInfo(
90+
path = match.groupValues[2].removeSuffix(".*"),
91+
type = ImportType.MODULE,
92+
filePath = filePath,
93+
startLine = lineNumber,
94+
endLine = lineNumber
95+
)
96+
}.toList()
97+
}
98+
99+
private fun extractPythonImports(content: String, filePath: String): List<ImportInfo> {
100+
val imports = mutableListOf<ImportInfo>()
101+
102+
val fromImportRegex = Regex("""from\s+([\w.]+)\s+import""")
103+
fromImportRegex.findAll(content).forEach { match ->
104+
// Calculate actual line number from match position
105+
val lineNumber = content.substring(0, match.range.first).count { it == '\n' } + 1
106+
imports.add(ImportInfo(
107+
path = match.groupValues[1],
108+
type = ImportType.MODULE,
109+
filePath = filePath,
110+
startLine = lineNumber,
111+
endLine = lineNumber
112+
))
113+
}
114+
115+
val importRegex = Regex("""^import\s+([\w.]+)""", RegexOption.MULTILINE)
116+
importRegex.findAll(content).forEach { match ->
117+
// Calculate actual line number from match position
118+
val lineNumber = content.substring(0, match.range.first).count { it == '\n' } + 1
119+
imports.add(ImportInfo(
120+
path = match.groupValues[1],
121+
type = ImportType.MODULE,
122+
filePath = filePath,
123+
startLine = lineNumber,
124+
endLine = lineNumber
125+
))
126+
}
127+
128+
return imports
129+
}
130+
131+
private fun extractJsImports(content: String, filePath: String): List<ImportInfo> {
132+
val imports = mutableListOf<ImportInfo>()
133+
134+
val es6ImportRegex = Regex("""import\s+(?:.+\s+from\s+)?['"]([@\w./-]+)['"]""")
135+
es6ImportRegex.findAll(content).forEach { match ->
136+
// Calculate actual line number from match position
137+
val lineNumber = content.substring(0, match.range.first).count { it == '\n' } + 1
138+
imports.add(ImportInfo(
139+
path = match.groupValues[1],
140+
type = ImportType.MODULE,
141+
filePath = filePath,
142+
startLine = lineNumber,
143+
endLine = lineNumber
144+
))
145+
}
146+
147+
return imports
148+
}
149+
150+
private fun parseOOPNodes(
151+
sourceCode: String,
152+
filePath: String,
153+
language: Language
154+
): List<CodeNode> {
155+
val nodes = mutableListOf<CodeNode>()
156+
val lines = sourceCode.lines()
157+
val packageName = extractPackageName(sourceCode)
158+
159+
val classPattern = Regex("""(class|interface|enum|object)\s+(\w+)""")
160+
161+
for ((index, line) in lines.withIndex()) {
162+
val currentLine = index + 1
163+
164+
classPattern.find(line)?.let { match ->
165+
val type = when (match.groupValues[1]) {
166+
"class", "object" -> CodeElementType.CLASS
167+
"interface" -> CodeElementType.INTERFACE
168+
"enum" -> CodeElementType.ENUM
169+
else -> CodeElementType.CLASS
170+
}
171+
val name = match.groupValues[2]
172+
nodes.add(createCodeNode(name, type, packageName, filePath, currentLine, language))
173+
}
174+
}
175+
176+
return nodes
177+
}
178+
179+
private fun parseJSNodes(
180+
sourceCode: String,
181+
filePath: String,
182+
language: Language
183+
): List<CodeNode> {
184+
val nodes = mutableListOf<CodeNode>()
185+
val lines = sourceCode.lines()
186+
187+
val classPattern = Regex("""class\s+(\w+)""")
188+
189+
for ((index, line) in lines.withIndex()) {
190+
val currentLine = index + 1
191+
192+
classPattern.find(line)?.let { match ->
193+
val name = match.groupValues[1]
194+
nodes.add(createCodeNode(name, CodeElementType.CLASS, "", filePath, currentLine, language))
195+
}
196+
}
197+
198+
return nodes
199+
}
200+
201+
private fun parsePythonNodes(
202+
sourceCode: String,
203+
filePath: String,
204+
language: Language
205+
): List<CodeNode> {
206+
val nodes = mutableListOf<CodeNode>()
207+
val lines = sourceCode.lines()
208+
209+
val classPattern = Regex("""class\s+(\w+)""")
210+
211+
for ((index, line) in lines.withIndex()) {
212+
val currentLine = index + 1
213+
214+
classPattern.find(line)?.let { match ->
215+
val name = match.groupValues[1]
216+
nodes.add(createCodeNode(name, CodeElementType.CLASS, "", filePath, currentLine, language))
217+
}
218+
}
219+
220+
return nodes
221+
}
222+
223+
private fun extractPackageName(sourceCode: String): String {
224+
val packagePattern = Regex("""package\s+([\w.]+)""")
225+
return packagePattern.find(sourceCode)?.groupValues?.get(1) ?: ""
226+
}
227+
228+
private fun createCodeNode(
229+
name: String,
230+
type: CodeElementType,
231+
packageName: String,
232+
filePath: String,
233+
startLine: Int,
234+
language: Language
235+
): CodeNode {
236+
val qualifiedName = if (packageName.isNotEmpty()) "$packageName.$name" else name
237+
// Use deterministic composite ID to avoid collisions
238+
val id = "$filePath:$startLine:$qualifiedName"
239+
240+
return CodeNode(
241+
id = id,
242+
type = type,
243+
name = name,
244+
packageName = packageName,
245+
filePath = filePath,
246+
startLine = startLine,
247+
// Approximate end line: regex parsing cannot determine actual end line,
248+
// so we use a reasonable default. For accurate end lines, use TreeSitter-based parsing.
249+
endLine = startLine + 10,
250+
startColumn = 0,
251+
endColumn = 0,
252+
qualifiedName = qualifiedName,
253+
content = "",
254+
metadata = mapOf("language" to language.name, "platform" to "Android")
255+
)
256+
}
257+
258+
private fun buildRelationships(nodes: List<CodeNode>): List<CodeRelationship> {
259+
// Simplified: no relationships for basic parsing
260+
return emptyList()
261+
}
15262
}

0 commit comments

Comments
 (0)