diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/MarkdownReader.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/MarkdownReader.kt
new file mode 100644
index 0000000..25ae7f0
--- /dev/null
+++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/MarkdownReader.kt
@@ -0,0 +1,94 @@
+package space.kscience.snark.html
+
+import kotlinx.io.Source
+import kotlinx.io.readString
+import org.intellij.markdown.IElementType
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.ast.ASTNode
+import org.intellij.markdown.ast.findChildOfType
+import org.intellij.markdown.ast.getTextInNode
+import org.intellij.markdown.flavours.space.SFMFlavourDescriptor
+import org.intellij.markdown.html.*
+import org.intellij.markdown.parser.LinkMap
+import org.intellij.markdown.parser.MarkdownParser
+import space.kscience.snark.SnarkReader
+
+private class SnarkInlineLinkGeneratingProvider(
+ baseURI: URI?,
+ resolveAnchors: Boolean = false,
+) : LinkGeneratingProvider(baseURI, resolveAnchors) {
+
+ override fun getRenderInfo(text: String, node: ASTNode): RenderInfo? {
+ return RenderInfo(
+ label = node.findChildOfType(MarkdownElementTypes.LINK_TEXT) ?: return null,
+ destination = node.findChildOfType(MarkdownElementTypes.LINK_DESTINATION)?.getTextInNode(text)?.let { raw ->
+ val processedLink = WebPageTextProcessor.functionRegex.replace(raw) { match ->
+ when (match.groups["target"]?.value) {
+ "homeRef" -> "snark://homeRef"
+ "resolveRef" -> "snark://ref/${match.groups["name"]?.value ?: ""}"
+ "resolvePageRef" -> "snark://page/${match.groups["name"]?.value ?: ""}"
+ "pageMeta.get" -> "snark://meta/${match.groups["name"]?.value ?: ""}"
+ else -> match.value
+ }
+ }
+ LinkMap.normalizeDestination(processedLink, true)
+ } ?: "",
+ title = node.findChildOfType(MarkdownElementTypes.LINK_TITLE)?.getTextInNode(text)?.let {
+ LinkMap.normalizeTitle(it)
+ }
+ )
+ }
+}
+
+private class SnarkImageGeneratingProvider(
+ linkMap: LinkMap,
+ baseURI: URI?,
+) : ImageGeneratingProvider(linkMap, baseURI) {
+
+ val snarkInlineLinkProvider = SnarkInlineLinkGeneratingProvider(baseURI)
+ override fun getRenderInfo(text: String, node: ASTNode): RenderInfo? {
+ node.findChildOfType(MarkdownElementTypes.INLINE_LINK)?.let { linkNode ->
+ return snarkInlineLinkProvider.getRenderInfo(text, linkNode)
+ }
+ (node.findChildOfType(MarkdownElementTypes.FULL_REFERENCE_LINK)
+ ?: node.findChildOfType(MarkdownElementTypes.SHORT_REFERENCE_LINK))
+ ?.let { linkNode ->
+ return referenceLinkProvider.getRenderInfo(text, linkNode)
+ }
+ return null
+ }
+}
+
+public object SnarkFlavorDescriptor : SFMFlavourDescriptor(false) {
+ override fun createHtmlGeneratingProviders(linkMap: LinkMap, baseURI: URI?): Map {
+ return super.createHtmlGeneratingProviders(linkMap, baseURI) + mapOf(
+ MarkdownElementTypes.INLINE_LINK to SnarkInlineLinkGeneratingProvider(baseURI, absolutizeAnchorLinks)
+ .makeXssSafe(useSafeLinks),
+ MarkdownElementTypes.IMAGE to SnarkImageGeneratingProvider(linkMap, baseURI)
+ .makeXssSafe(useSafeLinks),
+ )
+ }
+}
+
+public object MarkdownReader : SnarkHtmlReader {
+ override val types: Set = setOf("text/markdown", "md", "markdown")
+
+ override fun readFrom(source: String): PageFragment = PageFragment {
+ val parsedTree = markdownParser.parse(IElementType("ROOT"), source)
+// markdownParser.buildMarkdownTreeFromString(source)
+ val htmlString = HtmlGenerator(source, parsedTree, markdownFlavor).generateHtml()
+
+ consumer.onTagContentUnsafe {
+ +htmlString
+ }
+
+ }
+
+ private val markdownFlavor = SnarkFlavorDescriptor//SFMFlavourDescriptor(false)
+ private val markdownParser = MarkdownParser(markdownFlavor)
+
+ override fun readFrom(source: Source): PageFragment = readFrom(source.readString())
+
+ public val snarkReader: SnarkReader = SnarkReader(this, "text/markdown")
+
+}
\ No newline at end of file
diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/Postprocessor.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/Postprocessor.kt
index c77c61f..223206d 100644
--- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/Postprocessor.kt
+++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/Postprocessor.kt
@@ -4,9 +4,9 @@ import kotlinx.html.*
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.parseAsName
import space.kscience.snark.TextProcessor
+import java.net.URI
public class WebPageTextProcessor(private val page: PageContext) : TextProcessor {
- private val regex = """\$\{([\w.]*)(?>\("(.*)"\))?}""".toRegex()
/**
* A basic [TextProcessor] that replaces `${...}` expressions in text. The following expressions are recognised:
@@ -16,30 +16,44 @@ public class WebPageTextProcessor(private val page: PageContext) : TextProcessor
* * `pageMeta.get("...") -> [PageContext.pageMeta] get string method
* Otherwise return unchanged string
*/
- override fun process(text: CharSequence): String = text.replace(regex) { match ->
- when (match.groups[1]!!.value) {
+ override fun process(text: CharSequence): String = text.replace(functionRegex) { match ->
+ when (match.groups["target"]?.value) {
"homeRef" -> page.homeRef
"resolveRef" -> {
- val refString = match.groups[2]?.value ?: error("resolveRef requires a string (quoted) argument")
+ val refString = match.groups["name"]?.value ?: error("resolveRef requires a string (quoted) argument")
page.resolveRef(refString)
}
"resolvePageRef" -> {
- val refString = match.groups[2]?.value
+ val refString = match.groups["name"]?.value
?: error("resolvePageRef requires a string (quoted) argument")
page.localisedPageRef(refString.parseAsName())
}
"pageMeta.get" -> {
- val nameString = match.groups[2]?.value
+ val nameString = match.groups["name"]?.value
?: error("resolvePageRef requires a string (quoted) argument")
page.pageMeta[nameString.parseAsName()].string ?: "@null"
}
else -> match.value
}
+ }.replace(attributeRegex){ match->
+ val uri = URI(match.groups["uri"]!!.value)
+ val snarkUrl = when(uri.authority){
+ "homeRef"->page.homeRef
+ "ref" -> page.resolveRef(uri.path)
+ "page" -> page.localisedPageRef(uri.path.parseAsName())
+ "meta" -> page.pageMeta[uri.path.parseAsName()].string ?: "@null"
+ else -> match.value
+ }
+ "=\"$snarkUrl\""
}
+ public companion object {
+ internal val functionRegex = """\$\{(?[\w.]*)(?:\((?:"|")(?.*)(?:"|")\))?\}""".toRegex()
+ private val attributeRegex = """="(?snark://([^"]*))"""".toRegex()
+ }
}
/**
@@ -49,26 +63,28 @@ public class WebPageTextProcessor(private val page: PageContext) : TextProcessor
public class Postprocessor(
public val page: PageContext,
private val consumer: TagConsumer,
- private val processor: TextProcessor = WebPageTextProcessor(page),
+ private val textProcessor: TextProcessor,
) : TagConsumer by consumer {
override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) {
if (tag is A && attribute == "href" && value != null) {
- consumer.onTagAttributeChange(tag, attribute, processor.process(value))
+ consumer.onTagAttributeChange(tag, attribute, textProcessor.process(value))
+ } else if (tag is IMG && attribute == "src" && value != null) {
+ consumer.onTagAttributeChange(tag, attribute, textProcessor.process(value))
} else {
consumer.onTagAttributeChange(tag, attribute, value)
}
}
override fun onTagContent(content: CharSequence) {
- consumer.onTagContent(processor.process(content))
+ consumer.onTagContent(textProcessor.process(content))
}
override fun onTagContentUnsafe(block: Unsafe.() -> Unit) {
val proxy = object : Unsafe {
override fun String.unaryPlus() {
consumer.onTagContentUnsafe {
- processor.process(this@unaryPlus).unaryPlus()
+ textProcessor.process(this@unaryPlus).unaryPlus()
}
}
}
diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/SnarkHtmlReader.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/SnarkHtmlReader.kt
new file mode 100644
index 0000000..924ef89
--- /dev/null
+++ b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/SnarkHtmlReader.kt
@@ -0,0 +1,23 @@
+package space.kscience.snark.html
+
+import kotlinx.html.div
+import kotlinx.html.unsafe
+import kotlinx.io.Source
+import kotlinx.io.readString
+import space.kscience.snark.SnarkReader
+
+
+public interface SnarkHtmlReader : SnarkReader
+
+public object HtmlReader : SnarkHtmlReader {
+ override val types: Set = setOf("html")
+
+ override fun readFrom(source: String): PageFragment = PageFragment {
+ div {
+ unsafe { +source }
+ }
+ }
+
+ override fun readFrom(source: Source): PageFragment = readFrom(source.readString())
+}
+
diff --git a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/readers.kt b/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/readers.kt
deleted file mode 100644
index 940d9d9..0000000
--- a/snark-html/src/jvmMain/kotlin/space/kscience/snark/html/readers.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package space.kscience.snark.html
-
-import kotlinx.html.div
-import kotlinx.html.unsafe
-import kotlinx.io.Source
-import kotlinx.io.readString
-import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
-import org.intellij.markdown.html.HtmlGenerator
-import org.intellij.markdown.parser.MarkdownParser
-import space.kscience.snark.SnarkReader
-
-
-public interface SnarkHtmlReader: SnarkReader
-
-public object HtmlReader : SnarkHtmlReader {
- override val types: Set = setOf("html")
-
- override fun readFrom(source: String): PageFragment = PageFragment {
- div {
- unsafe { +source }
- }
- }
-
- override fun readFrom(source: Source): PageFragment = readFrom(source.readString())
-}
-
-public object MarkdownReader : SnarkHtmlReader {
- override val types: Set = setOf("text/markdown", "md", "markdown")
-
- override fun readFrom(source: String): PageFragment = PageFragment {
- val parsedTree = markdownParser.buildMarkdownTreeFromString(source)
- val htmlString = HtmlGenerator(source, parsedTree, markdownFlavor).generateHtml()
-
- div {
- unsafe {
- +htmlString
- }
- }
- }
-
- private val markdownFlavor = CommonMarkFlavourDescriptor()
- private val markdownParser = MarkdownParser(markdownFlavor)
-
- override fun readFrom(source: Source): PageFragment = readFrom(source.readString())
-
- public val snarkReader: SnarkReader = SnarkReader(this, "text/markdown")
-
-}
-