Skip to content

Commit ad7919a

Browse files
committed
refactor(parser): replace XML tags with markdown code blocks #331
- Replace `<THOUGHT>` and `<PLAN>` XML tags with markdown code blocks using `plan` language. - Remove XML-specific parsing logic and simplify code fence handling. - Update tests to reflect changes in code block parsing.
1 parent 78c2ad2 commit ad7919a

File tree

6 files changed

+30
-185
lines changed

6 files changed

+30
-185
lines changed

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/code/MarkdownViewer.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ object MarkdownViewer {
2121
val editorFontSize = EditorColorsManager.getInstance().schemeForCurrentUITheme.editorFontSize
2222

2323
val fontFamilyAndSize = "font-family:'" + editorFontName + "'; font-size:" + editorFontSize + "pt;"
24-
val backgroundColorCss = "background-color: #" + ColorUtil.toHex(backgroundColor) + ";"
25-
htmlEditorKit.getStyleSheet().addRule("code { $backgroundColorCss$fontFamilyAndSize}")
26-
htmlEditorKit.getStyleSheet().addRule("code.language-markdown { background-color: #ffffff; $fontFamilyAndSize}")
24+
htmlEditorKit.getStyleSheet().addRule("code { $fontFamilyAndSize }")
2725
htmlEditorKit.getStyleSheet().addRule("p { margin-top: 1px }")
2826

2927
jEditorPane.also {

core/src/main/kotlin/cc/unitmesh/devti/util/parser/CodeFence.kt

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ class CodeFence(
1414
private var lastTxtBlock: CodeFence? = null
1515
val devinStartRegex = Regex("<devin>")
1616
val devinEndRegex = Regex("</devin>")
17-
val thoughtStartRegex = Regex("<THOUGHT>")
18-
val thoughtEndRegex = Regex("</THOUGHT>")
19-
val planStartRegex = Regex("<PLAN>")
20-
val planEndRegex = Regex("</PLAN>")
2117

2218
fun parse(content: String): CodeFence {
2319
val languageRegex = Regex("\\s*```([\\w#+ ]*)")
@@ -28,7 +24,6 @@ class CodeFence(
2824
val endMatch = devinEndRegex.find(content)
2925
val isComplete = endMatch != null
3026

31-
// 提取内容:如果有结束标签就截取中间内容,没有就取整个后续内容
3227
val devinContent = if (isComplete) {
3328
content.substring(startMatch.range.last + 1, endMatch!!.range.first).trim()
3429
} else {
@@ -38,7 +33,6 @@ class CodeFence(
3833
return CodeFence(findLanguage("DevIn"), devinContent, isComplete, "devin", "DevIn")
3934
}
4035

41-
// 原有的 Markdown 代码块解析逻辑
4236
var codeStarted = false
4337
var codeClosed = false
4438
var languageId: String? = null
@@ -87,8 +81,6 @@ class CodeFence(
8781
content = preProcessDevinBlock(content)
8882
}
8983

90-
content = preProcessXmlBlocks(content)
91-
9284
val startMatches = devinStartRegex.findAll(content)
9385
for (startMatch in startMatches) {
9486
if (startMatch.range.first > currentIndex) {
@@ -124,38 +116,6 @@ class CodeFence(
124116
return codeFences.filter { it.text.isNotEmpty() }
125117
}
126118

127-
private fun preProcessXmlBlocks(content: String): String {
128-
var currentContent = content
129-
130-
// 处理<THOUGHT>标签
131-
val thoughtMatches = Regex("(?<=^|\\n)<THOUGHT>([\\s\\S]*?)</THOUGHT>\\n?").findAll(content).toList()
132-
for (match in thoughtMatches) {
133-
val thoughtContent = match.groups[1]?.value ?: ""
134-
// 检查是否有内部的<PLAN>标签
135-
val planMatch = Regex("<PLAN>([\\s\\S]*?)</PLAN>").find(thoughtContent)
136-
137-
if (planMatch != null) {
138-
val planContent = planMatch.groups[1]?.value?.trim() ?: ""
139-
// 将<PLAN>内容替换为Markdown代码块格式
140-
val processedContent = thoughtContent.replace(planMatch.value, "\n```plan\n$planContent\n```\n")
141-
currentContent = currentContent.replace(match.value, processedContent)
142-
} else {
143-
// 如果没有内部PLAN标签,保持原样
144-
currentContent = currentContent
145-
}
146-
}
147-
148-
// 直接处理独立的<PLAN>标签
149-
val planMatches = Regex("(?<=^|\\n)<PLAN>([\\s\\S]*?)</PLAN>\\n?").findAll(currentContent).toList()
150-
for (match in planMatches) {
151-
val planContent = match.groups[1]?.value?.trim() ?: ""
152-
val replacement = "\n```plan\n$planContent\n```\n"
153-
currentContent = currentContent.replace(match.value, replacement)
154-
}
155-
156-
return currentContent
157-
}
158-
159119
val devinRegexBlock = Regex("(?<=^|\\n)```devin\\n([\\s\\S]*?)\\n```\\n")
160120
val normalCodeBlock = Regex("\\s*```([\\w#+ ]*)\\n")
161121

@@ -272,7 +232,6 @@ class CodeFence(
272232
"bash" -> "Shell Script"
273233
"http" -> "HTTP Request"
274234
"plan" -> "Plain Text"
275-
"thought" -> "Plain Text"
276235
else -> languageName
277236
}
278237

@@ -318,8 +277,6 @@ class CodeFence(
318277
"shell script" -> "sh"
319278
"bash" -> "sh"
320279
"devin" -> "devin"
321-
"plan" -> "plan"
322-
"thought" -> "thought"
323280
else -> languageId
324281
}
325282
}
@@ -349,8 +306,6 @@ class CodeFence(
349306
"scala" -> "Scala"
350307
"rs" -> "Rust"
351308
"http" -> "HTTP Request"
352-
"plan" -> "PLAN"
353-
"thought" -> "THOUGHT"
354309
else -> extension
355310
}
356311
}

core/src/main/resources/genius/en/code/plan.devin

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ $context.toolList
3434
instead of saying 'I need to use the edit file tool to edit your file', just
3535
say 'I will edit your file'.
3636
5. Before calling each tool, first explain to the USER why you are calling it.
37-
6. Ensure that every write operation (such as `patch`) is enclosed within its own `<devin></devin>` tag.
3837
</tool_calling>
3938

4039
Here is the rule you should follow:
4140

4241
1. Thoroughly review `<user.question>`. Create an initial plan that includes all the necessary steps to
4342
resolve `<user.question>`, using the recommended steps provided below, and incorporating any requirements from
44-
the `<user.question>`. Place your plan inside the XML tag `<THOUGHT>` within the sub-tag `<PLAN>`.
43+
the `<user.question>`. Place your plan inside the XML tag `<THOUGHT>` within the code language `plan`.
4544
2. Review the project’s codebase, examining not only its structure but also the specific implementation details, to
4645
identify all segments that may contribute to or help resolve the issue described in `<user.question>`.
4746
3. If `<user.question>` describes an error, create a script to reproduce it and run the script to confirm the error.
@@ -60,14 +59,14 @@ first. Be thorough in your thinking process, so it's okay if it is lengthy.
6059

6160
For each step, document your reasoning process inside `<THOUGHT>` tags. Include the following information, enclosed within XML tags:
6261

63-
1. `<PLAN>`: An updated plan incorporating the outcomes from the previous step. Mark progress by adding `✓` after each task in the plan that was fully completed before this step during the **current session**. Use the symbol `!` for tasks that have a latest status as failed, and use `*` for tasks that are currently in progress. If there are sub-tasks, mark their progress statuses as well. Ensure all progress statuses are marked accurately and appropriately reflect the hierarchical relationships of statuses between tasks and sub-tasks. For example, if all sub-tasks are completed, the parent task should also be marked as completed.
62+
1. `plan`: An updated plan incorporating the outcomes from the previous step. Mark progress by adding `✓` after each task in the plan that was fully completed before this step during the **current session**. Use the symbol `!` for tasks that have a latest status as failed, and use `*` for tasks that are currently in progress. If there are sub-tasks, mark their progress statuses as well. Ensure all progress statuses are marked accurately and appropriately reflect the hierarchical relationships of statuses between tasks and sub-tasks. For example, if all sub-tasks are completed, the parent task should also be marked as completed.
6463

6564
For example:
6665

6766
<THOUGHT>
68-
<PLAN>
67+
```plan
6968
Some plan
70-
</PLAN>
69+
```plan
7170
</THOUGHT>
7271

7372
Here is user.question:

core/src/main/resources/genius/zh/code/plan.devin

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@ $context.toolList
3434
instead of saying 'I need to use the edit file tool to edit your file', just
3535
say 'I will edit your file'.
3636
5. Before calling each tool, first explain to the USER why you are calling it.
37-
6. Ensure that every write operation (such as `patch`) is enclosed within its own `<devin></devin>` tag.
3837
</tool_calling>
3938

4039
Here is the rule you should follow:
4140

4241
1. Thoroughly review `<user.question>`. Create an initial plan that includes all the necessary steps to
4342
resolve `<user.question>`, using the recommended steps provided below, and incorporating any requirements from
44-
the `<user.question>`. Place your plan inside the XML tag `<THOUGHT>` within the sub-tag `<PLAN>`.
43+
the `<user.question>`. Place your plan inside the XML tag `<THOUGHT>` within the code language `plan`.
4544
2. Review the project’s codebase, examining not only its structure but also the specific implementation details, to
4645
identify all segments that may contribute to or help resolve the issue described in `<user.question>`.
4746
3. If `<user.question>` describes an error, create a script to reproduce it and run the script to confirm the error.
@@ -60,15 +59,14 @@ first. Be thorough in your thinking process, so it's okay if it is lengthy.
6059

6160
For each step, document your reasoning process inside `<THOUGHT>` tags. Include the following information, enclosed within XML tags:
6261

63-
1. `<PLAN>`: An updated plan incorporating the outcomes from the previous step. Mark progress by adding `✓` after each task in the plan that was fully completed before this step during the **current session**. Use the symbol `!` for tasks that have a latest status as failed, and use `*` for tasks that are currently in progress. If there are sub-tasks, mark their progress statuses as well. Ensure all progress statuses are marked accurately and appropriately reflect the hierarchical relationships of statuses between tasks and sub-tasks. For example, if all sub-tasks are completed, the parent task should also be marked as completed.
64-
62+
1. `plan`: An updated plan incorporating the outcomes from the previous step. Mark progress by adding `✓` after each task in the plan that was fully completed before this step during the **current session**. Use the symbol `!` for tasks that have a latest status as failed, and use `*` for tasks that are currently in progress. If there are sub-tasks, mark their progress statuses as well. Ensure all progress statuses are marked accurately and appropriately reflect the hierarchical relationships of statuses between tasks and sub-tasks. For example, if all sub-tasks are completed, the parent task should also be marked as completed.
6563

6664
For example:
6765

6866
<THOUGHT>
69-
<PLAN>
70-
1.
71-
</PLAN>
67+
```plan
68+
Some plan
69+
```plan
7270
</THOUGHT>
7371

7472
Here is user.question:

core/src/test/kotlin/cc/unitmesh/devti/parser/CodeFenceTest.kt

Lines changed: 17 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -557,109 +557,18 @@ Index: src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java
557557
assertEquals(codeFences.size, 1)
558558
}
559559

560-
fun testShouldParsePlanTag() {
561-
val content = """
562-
<PLAN>
563-
1. 领域模型重构:
564-
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
565-
- 添加领域行为方法(发布、审核、评论等)
566-
- 引入值对象(BlogId、Content等)
567-
</PLAN>
568-
""".trimIndent()
569-
570-
val code = CodeFence.parse(content)
571-
assertEquals(
572-
code.text,
573-
"""
574-
1. 领域模型重构:
575-
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
576-
- 添加领域行为方法(发布、审核、评论等)
577-
- 引入值对象(BlogId、Content等)
578-
""".trimIndent()
579-
)
580-
assertTrue(code.isComplete)
581-
assertEquals("plan", code.extension)
582-
assertEquals("plan", code.originLanguage)
583-
}
584-
585-
fun testShouldParseThoughtTag() {
586-
val content = """
587-
<THOUGHT>
588-
我需要实现博客系统的领域模型重构,主要包括以下步骤:
589-
1. 将BlogPost合并到Blog聚合根
590-
2. 添加领域行为
591-
</THOUGHT>
592-
""".trimIndent()
593-
594-
val code = CodeFence.parse(content)
595-
assertEquals(
596-
code.text,
597-
"""
598-
我需要实现博客系统的领域模型重构,主要包括以下步骤:
599-
1. 将BlogPost合并到Blog聚合根
600-
2. 添加领域行为
601-
""".trimIndent()
602-
)
603-
assertTrue(code.isComplete)
604-
assertEquals("thought", code.extension)
605-
assertEquals("thought", code.originLanguage)
606-
}
607-
608-
fun testShouldParseNestedPlanInThought() {
609-
val content = """
610-
<THOUGHT>
611-
我需要对系统进行重构,首先列出计划:
612-
613-
<PLAN>
614-
1. 领域模型重构:
615-
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
616-
- 添加领域行为方法(发布、审核、评论等)
617-
618-
2. 分层结构调整:
619-
- 清理entity层冗余对象,建立清晰的domain层
620-
- 实现领域服务与基础设施层分离
621-
</PLAN>
622-
623-
然后按照计划实施重构。
624-
</THOUGHT>
625-
""".trimIndent()
626-
627-
val code = CodeFence.parse(content)
628-
assertEquals(
629-
code.text,
630-
"""
631-
1. 领域模型重构:
632-
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
633-
- 添加领域行为方法(发布、审核、评论等)
634-
635-
2. 分层结构调整:
636-
- 清理entity层冗余对象,建立清晰的domain层
637-
- 实现领域服务与基础设施层分离
638-
""".trimIndent()
639-
)
640-
assertTrue(code.isComplete)
641-
assertEquals("plan", code.extension)
642-
assertEquals("plan", code.originLanguage)
643-
}
644-
645560
fun testShouldParseAllWithXmlTags() {
646561
val content = """
647562
首先,我需要思考重构的步骤:
648563
649-
<THOUGHT>
650-
系统重构需要考虑领域驱动设计原则,确保聚合根的完整性。
651-
652-
<PLAN>
564+
```plan
653565
1. 领域模型重构:
654566
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
655567
- 添加领域行为方法(发布、审核、评论等)
656568
657569
2. 分层结构调整:
658570
- 清理entity层冗余对象
659-
</PLAN>
660-
661-
这样的重构可以提高系统的内聚性。
662-
</THOUGHT>
571+
```
663572
664573
然后,我们可以开始代码实现:
665574
@@ -678,29 +587,13 @@ Index: src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java
678587
<devin>
679588
/patch
680589
```patch
681-
Index: src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java
682-
--- src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java (revision 1)
683-
+++ src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java (revision 2)
684-
```
685-
</devin>
686-
687-
<PLAN>
688-
3. 战术模式实现:
689-
- 使用工厂模式处理复杂对象创建
690-
- 实现仓储接口与领域层的依赖倒置
691-
</PLAN>
692590
""".trimIndent()
693591

694592
val codeFences = CodeFence.parseAll(content)
695-
assertEquals(6, codeFences.size)
593+
assertEquals(5, codeFences.size)
696594

697-
// 检查第一个代码段是Markdown内容
698-
assertEquals("首先,我需要思考重构的步骤:\n" +
699-
"\n" +
700-
"\n" +
701-
"系统重构需要考虑领域驱动设计原则,确保聚合根的完整性。", codeFences[0].text)
595+
assertEquals("首先,我需要思考重构的步骤:", codeFences[0].text)
702596

703-
// 检查第二个代码段是plan内容(嵌套在THOUGHT中的PLAN)
704597
assertEquals(
705598
"""
706599
1. 领域模型重构:
@@ -714,18 +607,22 @@ Index: src/main/java/cc/unitmesh/untitled/demo/repository/BlogRepository.java
714607
)
715608
assertEquals("plan", codeFences[1].extension)
716609

717-
// 检查第三个代码段是Java代码
718-
assertEquals("这样的重构可以提高系统的内聚性。\n\n然后,我们可以开始代码实现:", codeFences[2].text)
719-
720-
// 检查第四个代码段是单独的PLAN标签内容
610+
assertEquals("然后,我们可以开始代码实现:", codeFences[2].text)
721611
assertEquals(
722612
"""
723-
3. 战术模式实现:
724-
- 使用工厂模式处理复杂对象创建
725-
- 实现仓储接口与领域层的依赖倒置
726-
""".trimIndent(),
613+
public class Blog {
614+
private BlogId id;
615+
private String title;
616+
private String content;
617+
618+
public void publish() {
619+
// 实现发布逻辑
620+
}
621+
}""".trimIndent(),
727622
codeFences[3].text
728623
)
729-
assertEquals("plan", codeFences[3].extension)
624+
assertEquals("java", codeFences[3].extension)
625+
626+
assertEquals("DevIn", codeFences[4].originLanguage)
730627
}
731628
}

core/src/test/kotlin/cc/unitmesh/devti/util/parser/MarkdownToHtmlConverterTest.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ class MarkdownConverterTest {
1111
fun should_convert_simple_markdown_to_html() {
1212
// Given
1313
val markdownText = """
14-
<THOUGHT>
15-
<PLAN>
14+
```
1615
1. 领域模型重构:
1716
- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象
1817
- 添加领域行为方法(发布、审核、评论等)
@@ -31,10 +30,9 @@ class MarkdownConverterTest {
3130
4. 测试保障:
3231
- 重构单元测试,验证领域模型行为
3332
- 添加聚合根不变性约束测试
34-
</PLAN>
35-
</THOUGHT>
33+
```
3634
""".trimIndent()
37-
val expectedHtml = "<pre><code class=\"language-markdown\">\n" +
35+
val expectedHtml = "<pre><code class=\"language-plan\">\n" +
3836
"1. 领域模型重构:\n" +
3937
"- 将BlogPost实体合并到Blog聚合根,建立完整的领域对象\n" +
4038
"- 添加领域行为方法(发布、审核、评论等)\n" +

0 commit comments

Comments
 (0)