Skip to content

Commit

Permalink
Parse table caption, add table numbering tests
Browse files Browse the repository at this point in the history
  • Loading branch information
iamgio committed Nov 2, 2024
1 parent 664df91 commit 9357ba2
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import eu.iamgio.quarkdown.visitor.node.NodeVisitor
* A table, consisting of columns, each of which has a header and multiple cells.
* A table is location-trackable since, if requested by the user, it may show a caption displaying its location-based label.
* @param columns columns of the table. Each column has a header and multiple cells
* @param caption optional caption of the table (Quarkdown extension)
*/
class Table(
val columns: List<Column>,
override val caption: String? = "Test caption" /*null*/,
override val caption: String? = null,
) : NestableNode, LocationTrackableNode, CaptionableNode {
// Exposing all the cell contents as this table's direct children
// allows visiting them during a tree traversal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ class ImageFigure(val image: Image) : NestableNode, LocationTrackableNode, Capti
*/
override val caption: String? = image.link.title

/**
* Whether the figure is numerable and labelable.
* This is true if the figure has a [caption].
*/
val isLabelable: Boolean
get() = caption != null

/**
* A singleton list containing [image].
* This is needed to allow the image to be traversed by a tree iterator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ import eu.iamgio.quarkdown.document.numbering.NumberingFormat
*/
class LocationAwareLabelStorerHook(private val context: MutableContext) : AstIteratorHook {
override fun attach(iterator: ObservableAstIterator) {
updateLabels<ImageFigure>(DocumentNumbering::figures, iterator, filter = { it.isLabelable })
updateLabels<Table>(DocumentNumbering::tables, iterator)
updateLabels<ImageFigure>(DocumentNumbering::figures, iterator, filter = { it.caption != null })
updateLabels<Table>(DocumentNumbering::tables, iterator, filter = { it.caption != null })
}

/**
Expand All @@ -63,7 +63,7 @@ class LocationAwareLabelStorerHook(private val context: MutableContext) : AstIte
// Gets the needed numbering format from the global numbering settings.
val format = formatSupplier(context.documentInfo.numberingOrDefault ?: return)

if (format == null) return
if (format == null || format.isNonCounting) return

// Stores the number of elements encountered at each location.
val countAtLocation = mutableMapOf<SectionLocation, Int>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ data class NumberingFormat(
val accuracy: Int
get() = counterSymbolCount

/**
* Whether the format does not contain any counting symbols.
*/
val isNonCounting: Boolean
get() = counterSymbolCount <= 0

/**
* Converts the numbering format into a string.
* For example, the [NumberingFormat] `1.A.a` would format the levels `1, 1, 0` as `2.B.a`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ open class BaseMarkdownInlineTokenRegexPatterns {
RegexBuilder("\\[(label)\\]\\(\\s*(href)(?:\\s+(title))?\\s*\\)")
.withReference("label", LABEL_HELPER)
.withReference("href", "<(?:\\\\.|[^\\n<>\\\\])+>|[^\\s\\x00-\\x1f]*")
.withReference(
"title",
"\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)",
)
.withReference("title", DELIMITED_TITLE_HELPER)
.build(),
)

Expand Down Expand Up @@ -331,6 +328,10 @@ private const val LABEL_HELPER = "(?:\\[(?:\\\\.|[^\\[\\]\\\\])*\\]|\\\\.|`[^`]*

private const val BLOCK_LABEL_HELPER = "(?!\\s*\\])(?:\\\\.|[^\\[\\]\\\\])+"

// "This is a title", 'This is a title', (This is a title)
internal const val DELIMITED_TITLE_HELPER =
"\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)"

private const val COMMENT_TAG_HELPER =
"comment" +
"|^</[a-zA-Z][\\w:-]*\\s*>" + // self-closing tag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import eu.iamgio.quarkdown.function.value.factory.ValueFactory
import eu.iamgio.quarkdown.lexer.Lexer
import eu.iamgio.quarkdown.lexer.Token
import eu.iamgio.quarkdown.lexer.acceptAll
import eu.iamgio.quarkdown.lexer.patterns.DELIMITED_TITLE_HELPER
import eu.iamgio.quarkdown.lexer.tokens.BlockCodeToken
import eu.iamgio.quarkdown.lexer.tokens.BlockQuoteToken
import eu.iamgio.quarkdown.lexer.tokens.BlockTextToken
Expand Down Expand Up @@ -328,9 +329,24 @@ class BlockTokenParser(private val context: MutableContext) : BlockTokenVisitor<
}
}

// Quarkdown extension: a table may have a caption.
// A caption is located at the end of the table, after a line break,
// wrapped by a delimiter, the same way as a link/image title.
// "This is a caption", 'This is a caption', (This is a caption)
val captionRegex = Regex("^\\s*($DELIMITED_TITLE_HELPER)\\s*$")
// The found caption of the table, if any.
var caption: String? = null

// Other rows.
groups.next().lineSequence()
.filterNot { it.isBlank() }
.onEach { row ->
// Extract the caption if this is the caption row.
captionRegex.find(row)?.let { captionMatch ->
caption = captionMatch.groupValues.getOrNull(1)?.trimDelimiters()
}
}
.filterNot { caption != null } // The caption row is at the end of the table and not part of the table itself.
.forEach { row ->
var cellCount = 0
// Push cell.
Expand All @@ -346,6 +362,7 @@ class BlockTokenParser(private val context: MutableContext) : BlockTokenVisitor<

return Table(
columns = columns.map { Table.Column(it.alignment, it.header, it.cells) },
caption,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class QuarkdownHtmlNodeRenderer(context: Context) : BaseHtmlNodeRenderer(context

node.caption?.let { caption ->
+buildTag(captionTagName) {
+caption
+escapeCriticalContent(caption)
// The label is set as an attribute for styling.
optionalAttribute("data-element-label", label)
// Localized name of the element (e.g. `figure` -> `Figure` for English locale).
Expand Down Expand Up @@ -413,6 +413,7 @@ class QuarkdownHtmlNodeRenderer(context: Context) : BaseHtmlNodeRenderer(context
}
}

// Quarkdown introduces table captions, also numerated.
override fun visit(node: Table) =
super.tableBuilder(node).apply {
numberedCaption(node, "caption", kindLocalizationKey = "table")
Expand Down
22 changes: 22 additions & 0 deletions core/src/test/kotlin/eu/iamgio/quarkdown/BlockParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,28 @@ class BlockParserTest {
assertFalse(hasNext())
}

repeat(2) {
with(nodes.next()) {
assertEquals("Table caption", caption)

val columns = columns.iterator()
with(columns.next()) {
assertEquals(Table.Alignment.NONE, alignment)
assertEquals(2, cells.size)
assertNodeEquals(Text("G H I"), cells[0].text.first())
assertNodeEquals(Text("M N O"), cells[1].text.first())
}
with(columns.next()) {
assertEquals(Table.Alignment.NONE, alignment)
assertEquals(2, cells.size)
assertNodeEquals(Text("J K L"), cells[0].text.first())
assertNodeEquals(Text("P Q R"), cells[1].text.first())
}

assertFalse(columns.hasNext())
}
}

assertFalse(nodes.hasNext())
}

Expand Down
27 changes: 27 additions & 0 deletions core/src/test/kotlin/eu/iamgio/quarkdown/HtmlNodeRendererTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,33 @@ class HtmlNodeRendererTest {
),
).render(),
)

assertEquals(
out.next(),
Table(
listOf(
Table.Column(
Table.Alignment.NONE,
header = Table.Cell(listOf(Text("A"))),
cells =
listOf(
Table.Cell(listOf(Text("C"))),
Table.Cell(listOf(Text("E"))),
),
),
Table.Column(
Table.Alignment.NONE,
header = Table.Cell(listOf(Text("B"))),
cells =
listOf(
Table.Cell(listOf(Text("D"))),
Table.Cell(listOf(Text("F"))),
),
),
),
caption = "Table 'caption'.",
).render(),
)
}

// Quarkdown
Expand Down
15 changes: 14 additions & 1 deletion core/src/test/resources/parsing/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,17 @@ bar | baz
| | A | B |
|:------|----|---:|
| **C** | AC | BC |
| **D** | AD | BD |
| **D** | AD | BD |

| A B C | D E F |
|-------|-------|
| G H I | J K L |
| M N O | P Q R |
"Table caption"

| A B C | D E F |
|-------|-------|
| G H I | J K L |
| M N O | P Q R |
"Table caption"
| S T U | V W X |
36 changes: 36 additions & 0 deletions core/src/test/resources/rendering/block/table.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,40 @@
</td>
</tr>
</tbody>
</table>

---

<table>
<thead>
<tr>
<th>
A
</th>
<th>
B
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
C
</td>
<td>
D
</td>
</tr>
<tr>
<td>
E
</td>
<td>
F
</td>
</tr>
</tbody>
<caption>
Table &#39;caption&#39;.
</caption>
</table>
Loading

0 comments on commit 9357ba2

Please sign in to comment.