Skip to content

Commit 99eac16

Browse files
authored
fix: reach non-children nodes in tree traversal (#411)
<!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/iamgio/quarkdown/pull/411" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end -->
2 parents d3021b8 + 4246b3e commit 99eac16

19 files changed

Lines changed: 215 additions & 38 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,14 @@ Thanks @szy1840!
135135

136136
When editing long paged documents with live preview, the scroll position could sometimes be restored only partially because of long paged.js load times. The swap now reliably waits for the content to be fully loaded.
137137

138-
#### Fixed Mermaid diagrams breaking page breaks
138+
#### Fixed Mermaid diagrams preventing page breaks
139139

140140
Fixed an issue that caused Mermaid diagrams in `paged` documents to cause subsequent content to overflow instead of being pushed to the next page.
141141

142+
#### Fixed tree traversal not reaching non-body nodes
143+
144+
Fixed an issue that caused tree traversal-dependent features, such as cross-references, to not work in titles of `.box` and `.collapse`, and in block quote attributions.
145+
142146
#### Improved lexer performance
143147

144148
The lexer has been optimized to reduce regex builds to a minimum, resulting in significantly improved performance for large documents.

quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/BlockQuote.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,23 @@ import com.quarkdown.core.ast.NestableNode
55
import com.quarkdown.core.ast.Node
66
import com.quarkdown.core.rendering.representable.RenderRepresentable
77
import com.quarkdown.core.rendering.representable.RenderRepresentableVisitor
8+
import com.quarkdown.core.util.node.group
89
import com.quarkdown.core.visitor.node.NodeVisitor
910

1011
/**
1112
* A block quote.
1213
* @param type information type. If `null`, the quote does not have a particular type
1314
* @param attribution additional author or source of the quote
14-
* @param children content
15+
* @param content body content of the quote
1516
*/
1617
class BlockQuote(
1718
val type: Type? = null,
1819
val attribution: InlineContent? = null,
19-
override val children: List<Node>,
20+
val content: List<Node>,
2021
) : NestableNode {
22+
override val children: List<Node>
23+
get() = content + attribution.group()
24+
2125
override fun <T> accept(visitor: NodeVisitor<T>) = visitor.visit(this)
2226

2327
/**

quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/Table.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.quarkdown.core.ast.quarkdown.CaptionableNode
1010
import com.quarkdown.core.ast.quarkdown.reference.CrossReferenceableNode
1111
import com.quarkdown.core.rendering.representable.RenderRepresentable
1212
import com.quarkdown.core.rendering.representable.RenderRepresentableVisitor
13+
import com.quarkdown.core.util.node.group
1314
import com.quarkdown.core.visitor.node.NodeVisitor
1415

1516
/**
@@ -31,16 +32,17 @@ class Table(
3132
override val kindLocalizationKey: String
3233
get() = LocalizedKindKeys.TABLE
3334

34-
// Exposing all the cell contents as this table's direct children
35-
// allows visiting them during a tree traversal.
36-
// If they were isolated, they would be unreachable.
35+
/**
36+
* Exposes all the cell contents and caption as this table's direct children
37+
* allowing visiting them during a tree traversal. If they were isolated, they would be unreachable.
38+
*/
3739
override val children: List<Node>
3840
get() =
3941
columns
4042
.asSequence()
4143
.flatMap { it.cells + it.header }
4244
.flatMap { it.text }
43-
.toList()
45+
.toList() + caption.group()
4446

4547
/**
4648
* A column of a table.
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
package com.quarkdown.core.ast.quarkdown
22

33
import com.quarkdown.core.ast.InlineContent
4+
import com.quarkdown.core.ast.NestableNode
45
import com.quarkdown.core.ast.Node
6+
import com.quarkdown.core.util.node.group
57

68
/**
79
* A node that may have a caption, such as a [com.quarkdown.core.ast.base.block.Table] or a [com.quarkdown.core.ast.quarkdown.block.ImageFigure].
810
* The caption is a sequence of inline nodes, which accepts further inline formatting (e.g. emphasis, links).
11+
*
12+
* Extends [NestableNode] so that the caption content is reachable by AST tree traversals.
913
*/
10-
interface CaptionableNode : Node {
14+
interface CaptionableNode : NestableNode {
1115
/**
1216
* The optional caption, as inline content. If `null`, this node has no caption.
1317
*/
1418
val caption: InlineContent?
19+
20+
/**
21+
* Caption nodes grouped into a single container, making them visible to tree traversals.
22+
* Subclasses that define their own [children] must include this in their override.
23+
*/
24+
override val children: List<Node>
25+
get() = listOf(caption.group())
1526
}

quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/block/Box.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.quarkdown.core.document.size.Size
1111
import com.quarkdown.core.misc.color.Color
1212
import com.quarkdown.core.rendering.representable.RenderRepresentable
1313
import com.quarkdown.core.rendering.representable.RenderRepresentableVisitor
14+
import com.quarkdown.core.util.node.group
1415
import com.quarkdown.core.util.takeLines
1516
import com.quarkdown.core.visitor.node.NodeVisitor
1617

@@ -26,16 +27,19 @@ private const val ERROR_MAX_SOURCE_TEXT_LINES = 10
2627
* @param padding padding of the box. If `null`, the box uses the default value
2728
* @param backgroundColor background color of the box. If `null`, the box uses the default value
2829
* @param foregroundColor foreground color of the box. If `null`, the box uses the default value
29-
* @param children content of the box
30+
* @param content body content of the box
3031
*/
3132
class Box(
3233
val title: InlineContent?,
3334
val type: Type,
3435
val padding: Size? = null,
3536
val backgroundColor: Color? = null,
3637
val foregroundColor: Color? = null,
37-
override val children: List<Node>,
38+
val content: List<Node>,
3839
) : NestableNode {
40+
override val children: List<Node>
41+
get() = content + title.group()
42+
3943
override fun <T> accept(visitor: NodeVisitor<T>) = visitor.visit(this)
4044

4145
/**
@@ -87,7 +91,7 @@ class Box(
8791
text("Error" + if (title != null) ": $title" else "")
8892
},
8993
type = Type.ERROR,
90-
children = content,
94+
content = content,
9195
)
9296

9397
/**

quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/block/Collapse.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@ package com.quarkdown.core.ast.quarkdown.block
33
import com.quarkdown.core.ast.InlineContent
44
import com.quarkdown.core.ast.NestableNode
55
import com.quarkdown.core.ast.Node
6+
import com.quarkdown.core.util.node.group
67
import com.quarkdown.core.visitor.node.NodeVisitor
78

89
/**
910
* A collapsible block, whose content can be hidden or shown by interacting with it.
1011
* @param title title of the block
1112
* @param isOpen whether the block is open at the beginning
13+
* @param content body content of the block
1214
*/
1315
class Collapse(
1416
val title: InlineContent,
1517
val isOpen: Boolean,
16-
override val children: List<Node>,
18+
val content: List<Node>,
1719
) : NestableNode {
20+
override val children: List<Node>
21+
get() = content + title.group()
22+
1823
override fun <T> accept(visitor: NodeVisitor<T>): T = visitor.visit(this)
1924
}

quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/block/Figure.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.quarkdown.core.ast.attributes.location.LocationTrackableNode
99
import com.quarkdown.core.ast.base.inline.Image
1010
import com.quarkdown.core.ast.quarkdown.CaptionableNode
1111
import com.quarkdown.core.ast.quarkdown.reference.CrossReferenceableNode
12+
import com.quarkdown.core.util.node.group
1213
import com.quarkdown.core.visitor.node.NodeVisitor
1314

1415
/**
@@ -28,6 +29,9 @@ open class Figure<T : Node>(
2829
CrossReferenceableNode,
2930
CaptionableNode,
3031
LocalizedKind {
32+
override val children: List<Node>
33+
get() = listOf(child, caption.group())
34+
3135
override val kindLocalizationKey: String
3236
get() = LocalizedKindKeys.FIGURE
3337

quarkdown-core/src/main/kotlin/com/quarkdown/core/util/node/NodeUtils.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import com.quarkdown.core.ast.dsl.buildInline
1010
import com.quarkdown.core.ast.quarkdown.inline.TextSymbol
1111
import com.quarkdown.core.visitor.node.NodeVisitor
1212

13+
/**
14+
* Groups nodes into an [AstRoot] container.
15+
* @return an empty [AstRoot] if `null`
16+
*/
17+
fun List<Node>?.group(): AstRoot = AstRoot(this.orEmpty())
18+
1319
/**
1420
* Returns a sequence of all nodes in the tree, where [this] is the root node.
1521
* The sequence is generated by traversing the tree in depth-first order.

quarkdown-core/src/test/kotlin/com/quarkdown/core/AstDslTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class AstDslTest {
8989
),
9090
),
9191
BlockQuote(
92-
children =
92+
content =
9393
listOf(
9494
Paragraph(
9595
listOf(

quarkdown-core/src/test/kotlin/com/quarkdown/core/BlockParserTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,10 @@ class BlockParserTest {
399399
}
400400

401401
with(nodes.next()) {
402-
assertEquals(1, children.size)
403-
children.first().let { inner ->
402+
assertEquals(1, content.size)
403+
content.first().let { inner ->
404404
assertIs<BlockQuote>(inner)
405-
assertEquals("You miss 100% of the shots you don’t take.", inner.children.toPlainText())
405+
assertEquals("You miss 100% of the shots you don’t take.", inner.content.toPlainText())
406406
assertNodeEquals(Text("Wayne Gretzky"), inner.attribution!!.single())
407407
}
408408
assertNodeEquals(Emphasis(listOf(Text("Michael Scott"))), attribution!!.single())

0 commit comments

Comments
 (0)