Closed deanebarker closed 1 month ago
There is indeed no current execution context available across the board.
There is currently one MarkdownParserContext
available from parsers.
Otherwise for renderers, each renderer can have its own data/properties (e.g could contain mapping for your functions).
A workaround would be to use a ThreadStatic in your case to unblock if using properties on a custom renderer is not a good solution.
each renderer can have its own data/properties
I like this, but is a renderer instance specific to a single execution? And, if so, how do I get access to it? Where is it "born" in relation to the execution in which it's used?
I traced it back to a collection called ObjectRenderers
on RendererBase
, but I don't understand how that gets populated in relation to a single execution.
(Note: I also like the ThreadStatic
option, but let's pull on the above thread (ha!) first...)
Note that I did get it working using the ThreadStatic
method (see image), but it feels... wrong.
Also, ASP.NET will thread switch in the same request, but I don't know when, so it could conceivably switch between me defining the macro and MarkDig rendering. That's probably unlikely, but it could happen.
The "macros" are defined from sections appended to the same document the Markdown appears in (disregard the format; it's a proprietary thing). So @deane:whatever
in the document finds the _macro:deane
section, parses the content as a Liquid template, then passes whatever
to it, and renders it.
Again, this works and proves the theory, but I'd love something more elegant.
I like this, but is a renderer instance specific to a single execution? And, if so, how do I get access to it? Where is it "born" in relation to the execution in which it's used?
Renderers are configured via pipeline and pipeline builders. There are plenty of examples in the code base and provide custom rendering. For example https://github.com/xoofx/markdig/tree/master/src/Markdig/Extensions/Alerts has a renderer that can define an action per alert kind.
Ah, okay. I got it working, but I just want to confirm that I'm doing this right.
I created a new "Use" method, so in my pipeline builder, I do this:
var macros = GetMyMacros();
var builder = new MarkdownPipelineBuilder()
.UseAtRefs(macros)
I created a local field in AtRefsExtension
to hold them. So, at this point, the extension itself is holding on to them.
Then, in both the Setup
methods, I pass them into the AtRefsInlineParser
and AtRefsRenderer
instances that those two methods generate and return.
Is that basically right?
Is that basically right?
Yep 😊
Does Markdig have a concept of a "current execution context," meaning a common scope available to extensions in which you can store data that can be accessed for that transformation only? (Meaning, the boundaries of a single call to
Markdown.ToHtml
)I wrote an extension which allows for the identification of "tokens" (I call them "AtRefs", because of the syntax). They sort of allow the calling of very simple (one parameter) "functions."
For example:
In this case, the AtRef is "article" and the parameter is "/path/to/article". It will search the database for that path, and render a hyperlink using the title of the article it finds. I statically register a function on a static class in my extension to handle "article," because this a global scenario that I use all over my site -- every execution of
Markdown.ToHtml
should have access to this. My extension rendered can get access to that static class and method, so we're good.But sometimes I want an AtRef handler to only work for a specific execution. This is a handler that is specific to a single block of Markdown -- a "macro," if you will, for that scenario. So, what I need is some data construct accessible to my extension that lives and dies in the context of the single call to
Markdown.ToHtml
.Inside my extension renderer (extending from
HtmlObjectRenderer<AtRef>
), I cannot find anything exposed on which I could bring a custom function in. All theWrite
method has available is:this
, which is theAtRefsRenderer
itself -- this is created inside a private method calledGetRendererInstance
inBaseRenderer
; I can't see where I could have get access to thisHtmlRenderer
-- this comes from a method calledRentHtmlRenderer
on the pipeline; I can't see where I could get access to thisAtRef
object -- this is formed when it's parsedI thought about somehow passing it in on that last one -- adding it to the
AtRef
when I parse it. But, that would require the macro to somehow be defined inside the parsed Markdown, which has the same problem: theMatch
method ofInlineParser
doesn't seem to have an execution context either.This is kind of what I want:
Then, inside any extension , the
MarkdownContext
would be available for... whatever. It would only live in the context of that execution.Either that, or I'd like to be able to extend
MarkdownDocument
itself:(Of course, right now, extensions don't seem to have access to the larger document, so I'm not sure how this would help me. But it seems somehow... "correct"?)
Does some solution to my problem already exist? Am I overthinking this?