Skip to content

Commit 8df5a50

Browse files
authored
Merge pull request #261 from iamgio/feat/subdocument-function
feat(stdlib): add `.subdocument`
2 parents fcd60a5 + 12a2a1c commit 8df5a50

File tree

3 files changed

+140
-37
lines changed

3 files changed

+140
-37
lines changed

quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Ecosystem.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.quarkdown.stdlib
22

3+
import com.quarkdown.core.ast.InlineMarkdownContent
4+
import com.quarkdown.core.ast.base.inline.Link
5+
import com.quarkdown.core.ast.base.inline.SubdocumentLink
36
import com.quarkdown.core.context.Context
47
import com.quarkdown.core.context.MutableContext
58
import com.quarkdown.core.function.library.module.QuarkdownModule
@@ -14,6 +17,7 @@ import com.quarkdown.core.function.value.OutputValue
1417
import com.quarkdown.core.function.value.Value
1518
import com.quarkdown.core.function.value.VoidValue
1619
import com.quarkdown.core.function.value.factory.ValueFactory
20+
import com.quarkdown.core.function.value.wrappedAsValue
1721
import com.quarkdown.stdlib.internal.asString
1822
import java.io.Reader
1923

@@ -25,6 +29,7 @@ val Ecosystem: QuarkdownModule =
2529
moduleOf(
2630
::include,
2731
::includeAll,
32+
::subdocument,
2833
)
2934

3035
/**
@@ -86,3 +91,28 @@ fun includeAll(
8691
paths
8792
.map { include(context, it.asString()) }
8893
.let(::GeneralCollectionValue)
94+
95+
/**
96+
* Creates a link to a subdocument located at the given [path].
97+
*
98+
* This is an alias to the link syntax, `[Label](path)`, with more freedom:
99+
* - Function calls are supported, whereas the link syntax only supports static paths.
100+
* - The link syntax recognizes subdocuments only by their file extension (`.qd` or `.md`).
101+
*
102+
* @param path path to the subdocument
103+
* @param label optional label for the link.
104+
* If not provided, this function will just add the subdocument to the document graph, without displaying a link.
105+
* @return a [SubdocumentLink] node, which may be hidden if [label] is not provided
106+
* @wiki Subdocuments
107+
*/
108+
fun subdocument(
109+
path: String,
110+
label: InlineMarkdownContent? = null,
111+
): NodeValue =
112+
SubdocumentLink(
113+
Link(
114+
label = label?.children ?: emptyList(),
115+
url = path,
116+
title = null,
117+
),
118+
).wrappedAsValue()

quarkdown-test/src/test/kotlin/com/quarkdown/test/EcosystemTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.quarkdown.test.util.execute
55
import kotlin.test.Test
66
import kotlin.test.assertEquals
77
import kotlin.test.assertFails
8+
import kotlin.test.assertTrue
89

910
private const val OUTPUT_1 = "<h1>Title</h1><p>Some <em>text</em>.</p>"
1011
private const val OUTPUT_3 = "<h2>Included</h2><pre><code>code\ncode</code></pre>"
@@ -204,4 +205,16 @@ class EcosystemTest {
204205
)
205206
}
206207
}
208+
209+
@Test
210+
fun `bulk-include all from directory`() {
211+
execute(
212+
"""
213+
.noautopagebreak
214+
.includeall {.listfiles {include} sortby:{name}}
215+
""".trimIndent(),
216+
) {
217+
assertTrue(it.startsWith(OUTPUT_1 + OUTPUT_3))
218+
}
219+
}
207220
}

quarkdown-test/src/test/kotlin/com/quarkdown/test/SubdocumentTest.kt

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.quarkdown.core.pipeline.output.OutputResource
99
import com.quarkdown.core.pipeline.output.TextOutputArtifact
1010
import com.quarkdown.test.util.execute
1111
import com.quarkdown.test.util.getSubResources
12+
import java.io.File
1213
import kotlin.test.Test
1314
import kotlin.test.assertContains
1415
import kotlin.test.assertEquals
@@ -55,7 +56,7 @@ class SubdocumentTest {
5556
@Test
5657
fun `root to subdocument`() {
5758
execute(
58-
source = "",
59+
"",
5960
subdocumentGraph = { it.addVertex(simpleSubdoc).addEdge(Subdocument.Root, simpleSubdoc) },
6061
outputResourceHook = { group ->
6162
val resource = getResource(group, simpleSubdoc, this)
@@ -70,7 +71,7 @@ class SubdocumentTest {
7071
@Test
7172
fun `collision-proof subdocument name`() {
7273
execute(
73-
source = "",
74+
"",
7475
subdocumentGraph = { it.addVertex(simpleSubdoc).addEdge(Subdocument.Root, simpleSubdoc) },
7576
minimizeSubdocumentCollisions = true,
7677
outputResourceHook = { group ->
@@ -84,13 +85,12 @@ class SubdocumentTest {
8485
@Test
8586
fun `context should be shared to subdocument`() {
8687
execute(
87-
source =
88-
"""
89-
.doctype {paged}
90-
91-
.function {$NON_EXISTENT_FUNCTION}
92-
hello
93-
""".trimIndent(),
88+
"""
89+
.doctype {paged}
90+
91+
.function {$NON_EXISTENT_FUNCTION}
92+
hello
93+
""".trimIndent(),
9494
subdocumentGraph = {
9595
it.addVertex(referenceToParentSubdoc).addEdge(Subdocument.Root, referenceToParentSubdoc)
9696
},
@@ -105,7 +105,7 @@ class SubdocumentTest {
105105
@Test
106106
fun `context should not be shared from subdocument to parent`() {
107107
execute(
108-
source = ".doctype {paged}",
108+
".doctype {paged}",
109109
subdocumentGraph = { it.addVertex(definitionSubdoc).addEdge(Subdocument.Root, definitionSubdoc) },
110110
outputResourceHook = {
111111
assertEquals(DocumentType.PAGED, documentInfo.type)
@@ -117,7 +117,7 @@ class SubdocumentTest {
117117
@Test
118118
fun `third-party presence should be shared from subdocument to parent`() {
119119
execute(
120-
source = "",
120+
"",
121121
subdocumentGraph = { it.addVertex(thirdPartySubdoc).addEdge(Subdocument.Root, thirdPartySubdoc) },
122122
outputResourceHook = {
123123
assertTrue(attributes.hasMermaidDiagram)
@@ -127,48 +127,108 @@ class SubdocumentTest {
127127

128128
@Test
129129
fun `simple subdocument from file`() {
130-
execute(
131-
source = "[1](subdoc/simple-1.qd)",
132-
outputResourceHook = {
133-
assertEquals(2, subdocumentGraph.vertices.size)
134-
assertEquals(2, getTextResourceCount(it))
135-
},
136-
) {
137-
if (subdocument == Subdocument.Root) {
138-
assertEquals("<p><a href=\"./simple-1.html\">1</a></p>", it)
130+
arrayOf(
131+
"The link is: [1](subdoc/simple-1.qd)",
132+
"The link is: .subdocument {subdoc/simple-1.qd} label:{1}",
133+
).forEach { source ->
134+
execute(
135+
source,
136+
outputResourceHook = {
137+
assertEquals(2, subdocumentGraph.vertices.size)
138+
assertEquals(2, getTextResourceCount(it))
139+
},
140+
) {
141+
if (subdocument == Subdocument.Root) {
142+
assertEquals("<p>The link is: <a href=\"./simple-1.html\">1</a></p>", it)
143+
}
144+
}
145+
}
146+
}
147+
148+
@Test
149+
fun `empty label subdocument from file`() {
150+
arrayOf(
151+
"The link is: [](subdoc/simple-1.qd)",
152+
"The link is: .subdocument {subdoc/simple-1.qd}",
153+
).forEach { source ->
154+
execute(
155+
source,
156+
outputResourceHook = {
157+
assertEquals(2, subdocumentGraph.vertices.size)
158+
assertEquals(2, getTextResourceCount(it))
159+
},
160+
) {
161+
if (subdocument == Subdocument.Root) {
162+
assertEquals("<p>The link is: <a href=\"./simple-1.html\"></a></p>", it)
163+
}
139164
}
140165
}
141166
}
142167

143168
@Test
144169
fun `root to gateway to 1 and 2`() {
145-
execute(
146-
source = "[Gateway](subdoc/gateway.qd)",
147-
outputResourceHook = {
148-
assertEquals(4, subdocumentGraph.vertices.size)
149-
assertEquals(4, getTextResourceCount(it))
150-
},
151-
) {}
170+
arrayOf(
171+
"[Gateway](subdoc/gateway.qd)",
172+
".subdocument {subdoc/gateway.qd} label:{Gateway}",
173+
).forEach { source ->
174+
execute(
175+
source,
176+
outputResourceHook = {
177+
assertEquals(4, subdocumentGraph.vertices.size)
178+
assertEquals(4, getTextResourceCount(it))
179+
},
180+
) {}
181+
}
152182
}
153183

154184
@Test
155185
fun `circular, root to 1 to 2 to 1`() {
156-
execute(
157-
source = "[1](subdoc/circular-1.qd)",
158-
outputResourceHook = {
159-
assertEquals(3, subdocumentGraph.vertices.size)
160-
assertEquals(3, getTextResourceCount(it))
161-
},
162-
) {}
186+
arrayOf(
187+
"[1](subdoc/circular-1.qd)",
188+
".subdocument {subdoc/circular-1.qd} label:{1}",
189+
).forEach { source ->
190+
execute(
191+
source,
192+
outputResourceHook = {
193+
assertEquals(3, subdocumentGraph.vertices.size)
194+
assertEquals(3, getTextResourceCount(it))
195+
},
196+
) {}
197+
}
163198
}
164199

165200
@Test
166201
fun `recursive, root to 1 recursively`() {
202+
arrayOf(
203+
"[1](subdoc/recursive.qd)",
204+
".subdocument {subdoc/recursive.qd} label:{1}",
205+
).forEach { source ->
206+
execute(
207+
source,
208+
outputResourceHook = {
209+
assertEquals(2, subdocumentGraph.vertices.size)
210+
assertEquals(2, getTextResourceCount(it))
211+
},
212+
) {}
213+
}
214+
}
215+
216+
@Test
217+
fun `all from directory`() {
167218
execute(
168-
source = "[1](subdoc/recursive.qd)",
219+
"""
220+
.foreach {.listfiles {subdoc} sortby:{name}}
221+
path:
222+
.path::subdocument label:{.path::filename extension:{no}}
223+
""".trimIndent(),
169224
outputResourceHook = {
170-
assertEquals(2, subdocumentGraph.vertices.size)
171-
assertEquals(2, getTextResourceCount(it))
225+
val files = File(fileSystem.workingDirectory, "subdoc").listFiles()!!
226+
assertEquals(files.size + 1, subdocumentGraph.vertices.size) // +1 for root
227+
228+
files.forEach { file ->
229+
val subdocName = file.nameWithoutExtension
230+
assertTrue(subdocumentGraph.vertices.any { vertex -> vertex.name.startsWith(subdocName) })
231+
}
172232
},
173233
) {}
174234
}

0 commit comments

Comments
 (0)