Skip to content

Commit fe02380

Browse files
committed
feat: support markdown tables (closes #1186)
1 parent b501ffa commit fe02380

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ResponseNodeRenderer.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import com.vladsch.flexmark.ast.Link;
1010
import com.vladsch.flexmark.ast.OrderedListItem;
1111
import com.vladsch.flexmark.ast.Paragraph;
12+
import com.vladsch.flexmark.ext.tables.TableBlock;
13+
import com.vladsch.flexmark.ext.tables.TableCell;
14+
import com.vladsch.flexmark.ext.tables.TableHead;
15+
import com.vladsch.flexmark.ext.tables.TableRow;
1216
import com.vladsch.flexmark.html.HtmlWriter;
1317
import com.vladsch.flexmark.html.renderer.NodeRenderer;
1418
import com.vladsch.flexmark.html.renderer.NodeRendererContext;
@@ -28,10 +32,49 @@ public Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
2832
new NodeRenderingHandler<>(CodeBlock.class, this::renderCodeBlock),
2933
new NodeRenderingHandler<>(BulletListItem.class, this::renderBulletListItem),
3034
new NodeRenderingHandler<>(Heading.class, this::renderHeading),
31-
new NodeRenderingHandler<>(OrderedListItem.class, this::renderOrderedListItem)
35+
new NodeRenderingHandler<>(OrderedListItem.class, this::renderOrderedListItem),
36+
new NodeRenderingHandler<>(TableBlock.class, this::renderTable),
37+
new NodeRenderingHandler<>(TableRow.class, this::renderTableRow),
38+
new NodeRenderingHandler<>(TableCell.class, this::renderTableCell)
3239
);
3340
}
3441

42+
private void renderTable(TableBlock node, NodeRendererContext context, HtmlWriter html) {
43+
var borderColor = ColorUtil.toHex(new JBColor(0xD0D0D0, 0x3C3F47));
44+
html.attr("style",
45+
"border-collapse: collapse; width: 100%; margin: 8px 0; border-top: 1px solid "
46+
+ borderColor);
47+
context.delegateRender();
48+
}
49+
50+
private void renderTableRow(TableRow node, NodeRendererContext context, HtmlWriter html) {
51+
html.attr("style",
52+
"border-bottom: 1px solid " + ColorUtil.toHex(new JBColor(0xE3E3E3, 0x2D2F35)) + ";");
53+
context.delegateRender();
54+
}
55+
56+
private void renderTableCell(TableCell node, NodeRendererContext context, HtmlWriter html) {
57+
TableRow row = (TableRow) node.getParent();
58+
var isHeaderCell = row != null && row.getParent() instanceof TableHead;
59+
var tag = isHeaderCell ? "th" : "td";
60+
61+
var styleBuilder = new StringBuilder();
62+
styleBuilder.append("padding: 8px 12px; text-align: left; vertical-align: middle;");
63+
64+
if (isHeaderCell) {
65+
var bgColor = ColorUtil.toHex(new JBColor(0xF2F3F5, 0x3A3D41));
66+
styleBuilder.append(" font-weight: 600; background-color: ").append(bgColor).append("; color: white; min-width: 200px;");
67+
}
68+
69+
html.attr("style", styleBuilder.toString().trim());
70+
if (isHeaderCell) {
71+
html.attr("scope", "col");
72+
}
73+
html.withAttr().tag(tag);
74+
context.renderChildren(node);
75+
html.tag("/" + tag);
76+
}
77+
3578
private void renderCodeBlock(CodeBlock node, NodeRendererContext context, HtmlWriter html) {
3679
html.attr("style", "white-space: pre-wrap;");
3780
context.delegateRender();

src/main/kotlin/ee/carlrobert/codegpt/util/MarkdownUtil.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ee.carlrobert.codegpt.util
22

3+
import com.vladsch.flexmark.ext.tables.TablesExtension
34
import com.vladsch.flexmark.html.HtmlRenderer
45
import com.vladsch.flexmark.parser.Parser
56
import com.vladsch.flexmark.util.data.MutableDataSet
@@ -37,6 +38,7 @@ object MarkdownUtil {
3738
@JvmStatic
3839
fun convertMdToHtml(message: String): String {
3940
val options = MutableDataSet()
41+
options.set(Parser.EXTENSIONS, listOf(TablesExtension.create()))
4042
options.set(HtmlRenderer.SOFT_BREAK, "<br/>")
4143

4244
val document = Parser.builder(options).build().parse(message)

src/test/kotlin/ee/carlrobert/codegpt/util/MarkdownUtilTest.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,40 @@ import org.junit.Test
55

66
class MarkdownUtilTest {
77

8+
@Test
9+
fun shouldRenderTableCorrectly() {
10+
val markdown = """
11+
<think>thinking content</think>
12+
Here's a basic fruits table:
13+
14+
| Fruit | Color | Price ($/kg) | Season | Calories (per 100g) |
15+
|-------|-------|--------------|--------|---------------------|
16+
| Apple | Red | 3.50 | Fall | 52 |
17+
| Banana | Yellow | 2.80 | Year-round | 89 |
18+
| Orange | Orange | 4.20 | Winter | 47 |
19+
| Strawberry | Red | 6.00 | Spring | 32 |
20+
| Grape | Purple | 5.50 | Summer | 69 |
21+
| Mango | Orange | 7.00 | Summer | 60 |
22+
| Watermelon | Green | 1.90 | Summer | 30 |
23+
| Kiwi | Brown | 4.80 | Winter | 61 |
24+
| Pineapple | Yellow | 5.00 | Year-round | 50 |
25+
| Blueberry | Blue | 8.50 | Summer | 57 |
26+
""".trimIndent()
27+
28+
val html = MarkdownUtil.convertMdToHtml(markdown)
29+
30+
assertThat(html).contains("thead")
31+
assertThat(html).contains("/thead")
32+
assertThat(html).contains("tbody")
33+
assertThat(html).contains("/tbody")
34+
assertThat(html).contains("th")
35+
assertThat(html).contains("Fruit")
36+
assertThat(html).contains("td")
37+
assertThat(html).contains("Apple")
38+
val tableCount = html.split("<table").size - 1
39+
assertThat(tableCount).isEqualTo(1)
40+
}
41+
842
@Test
943
fun shouldExtractMarkdownCodeBlocks() {
1044
val testInput = """

0 commit comments

Comments
 (0)