Alternative way for link passing and substitution in markdown
This commit is contained in:
parent
0c4ae405b8
commit
324afe8fd5
@ -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<IElementType, GeneratingProvider> {
|
||||
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<String> = 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<PageFragment> = SnarkReader(this, "text/markdown")
|
||||
|
||||
}
|
@ -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 = """\$\{(?<target>[\w.]*)(?:\((?:"|")(?<name>.*)(?:"|")\))?\}""".toRegex()
|
||||
private val attributeRegex = """="(?<uri>snark://([^"]*))"""".toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,26 +63,28 @@ public class WebPageTextProcessor(private val page: PageContext) : TextProcessor
|
||||
public class Postprocessor<out R>(
|
||||
public val page: PageContext,
|
||||
private val consumer: TagConsumer<R>,
|
||||
private val processor: TextProcessor = WebPageTextProcessor(page),
|
||||
private val textProcessor: TextProcessor,
|
||||
) : TagConsumer<R> 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PageFragment>
|
||||
|
||||
public object HtmlReader : SnarkHtmlReader {
|
||||
override val types: Set<String> = setOf("html")
|
||||
|
||||
override fun readFrom(source: String): PageFragment = PageFragment {
|
||||
div {
|
||||
unsafe { +source }
|
||||
}
|
||||
}
|
||||
|
||||
override fun readFrom(source: Source): PageFragment = readFrom(source.readString())
|
||||
}
|
||||
|
@ -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<PageFragment>
|
||||
|
||||
public object HtmlReader : SnarkHtmlReader {
|
||||
override val types: Set<String> = 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<String> = 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<PageFragment> = SnarkReader(this, "text/markdown")
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user