mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-03 21:36:36 +00:00
Is there a "current execution context" available from extensions? #700
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @deanebarker on GitHub (Oct 23, 2024).
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.ToHtmlshould 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 theWritemethod has available is:this, which is theAtRefsRendereritself -- this is created inside a private method calledGetRendererInstanceinBaseRenderer; I can't see where I could have get access to thisHtmlRenderer-- this comes from a method calledRentHtmlRendereron the pipeline; I can't see where I could get access to thisAtRefobject -- this is formed when it's parsedI thought about somehow passing it in on that last one -- adding it to the
AtRefwhen I parse it. But, that would require the macro to somehow be defined inside the parsed Markdown, which has the same problem: theMatchmethod ofInlineParserdoesn't seem to have an execution context either.This is kind of what I want:
Then, inside any extension , the
MarkdownContextwould 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
MarkdownDocumentitself:(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?
@xoofx commented on GitHub (Oct 24, 2024):
There is indeed no current execution context available across the board.
There is currently one
MarkdownParserContextavailable 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.
@deanebarker commented on GitHub (Oct 24, 2024):
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
ObjectRenderersonRendererBase, but I don't understand how that gets populated in relation to a single execution.(Note: I also like the
ThreadStaticoption, but let's pull on the above thread (ha!) first...)@deanebarker commented on GitHub (Oct 24, 2024):
Note that I did get it working using the
ThreadStaticmethod (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:whateverin the document finds the_macro:deanesection, parses the content as a Liquid template, then passeswhateverto it, and renders it.Again, this works and proves the theory, but I'd love something more elegant.
@xoofx commented on GitHub (Oct 24, 2024):
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.
@deanebarker commented on GitHub (Oct 24, 2024):
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:
I created a local field in
AtRefsExtensionto hold them. So, at this point, the extension itself is holding on to them.Then, in both the
Setupmethods, I pass them into theAtRefsInlineParserandAtRefsRendererinstances that those two methods generate and return.Is that basically right?
@xoofx commented on GitHub (Oct 25, 2024):
Yep 😊