Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Implement <xref:reference.api.i-code-block-preprocessor> as a sealed class. `Try
examples/ExtensibilityLabExample/LineCountPreprocessor.cs
```

The returned `CodeBlockPreprocessResult` carries the pre-rendered HTML, the `BaseLanguage` CSS class Pennington stamps on the block, and `SkipTransform`. Set `SkipTransform` to `true` when the output is final and the `[!code ...]` annotation pass should not re-process it.
The returned `CodeBlockPreprocessResult` carries the pre-rendered HTML, the `BaseLanguage` CSS class Pennington stamps on the block, and two opt-out flags. Set `SkipTransform` to `true` when the output is final and the `[!code ...]` annotation pass should not re-process it. Set `SkipChrome` to `true` when the output is not a code block at all — a rendered diagram, a chart, an embedded widget — and the standard wrapper markup (the `code-highlight-wrapper` div, the language head bar, and the container divs) should not surround it; the HTML is emitted verbatim. A `SkipChrome` block also stops registering as a code fence in the per-page Markdown twins: without the wrapper's `data-language` attribute, the HTML→markdown converter treats the output as ordinary markup instead of reconstructing a fence around it.

## Pick a Priority value

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace Pennington.Markdown.Extensions;
/// <see cref="ICodeBlockPreprocessor"/>s first, falls back to
/// <see cref="HighlightingService"/> on the base language, applies
/// <see cref="CodeTransformer"/>, and wraps the result via
/// <see cref="CodeBlockHtmlBuilder"/>. Used by both the Markdig renderer
/// <see cref="CodeBlockHtmlBuilder"/> (unless a preprocessor opts out with
/// <see cref="CodeBlockPreprocessResult.SkipChrome"/>). Used by both the Markdig renderer
/// and the <c>&lt;CodeBlock&gt;</c> Razor component so markdown fences and
/// Razor usages produce identical HTML.
/// </summary>
Expand Down Expand Up @@ -47,7 +48,9 @@ public string Render(
var html = result.SkipTransform
? result.HighlightedHtml
: CodeTransformer.Transform(result.HighlightedHtml);
return CodeBlockHtmlBuilder.BuildHtml(html, effectiveOptions, isInTabGroup, languageId);
return result.SkipChrome
? html
: CodeBlockHtmlBuilder.BuildHtml(html, effectiveOptions, isInTabGroup, languageId);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/Pennington/Markdown/Extensions/ICodeBlockPreprocessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ public interface ICodeBlockPreprocessor
/// <param name="HighlightedHtml">Fully highlighted HTML (wrapped in pre/code tags).</param>
/// <param name="BaseLanguage">The base language for CSS class purposes.</param>
/// <param name="SkipTransform">If true, skip CodeTransformer on the output.</param>
/// <param name="SkipChrome">If true, emit <paramref name="HighlightedHtml"/> as-is with none of the standard
/// code-block chrome (outer <c>code-highlight-wrapper</c>, language head bar, container divs) — the preprocessor
/// owns the entire output. Use for fences that render to something other than a code block, such as a diagram.</param>
public record CodeBlockPreprocessResult(
string HighlightedHtml,
string BaseLanguage,
bool SkipTransform = false);
bool SkipTransform = false,
bool SkipChrome = false);
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ public void SkipTransform_PreventsCodeTransformer()
result.ShouldNotContain("<span class=\"line\">");
}

[Fact]
public void SkipChrome_OmitsCodeBlockWrapper()
{
// With SkipChrome=true the preprocessor owns the entire output: no
// code-highlight-wrapper, no language head bar, no data-language attribute.
var preprocessor = new TestPreprocessor("test:owned",
new CodeBlockPreprocessResult(
"<div class=\"owned-output\"><svg></svg></div>",
"test",
SkipTransform: true,
SkipChrome: true));

var markdown = "```test:owned\nsome code\n```";

var result = RenderMarkdown(markdown, [preprocessor]);

result.ShouldContain("owned-output");
result.ShouldNotContain("code-highlight-wrapper");
result.ShouldNotContain("codeblock-head");
result.ShouldNotContain("data-language");
}

private class TestPreprocessor(
string matchLanguageId,
CodeBlockPreprocessResult? result,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,28 @@ public void Render_PreprocessorPriority_HighestRunsFirst()
html.ShouldNotContain(">low<");
}

[Fact]
public void Render_SkipChrome_EmitsPreprocessorHtmlUnwrapped()
{
// A preprocessor whose output is not a code block (a rendered diagram) opts out
// of the wrapper chrome entirely — the result is its HTML verbatim.
var preprocessor = new StubPreprocessor(
matchLanguageId: "beck",
result: new CodeBlockPreprocessResult(
HighlightedHtml: "<div class=\"beck-embed\"><svg></svg></div>",
BaseLanguage: "beck",
SkipTransform: true,
SkipChrome: true));

var service = new CodeBlockRenderingService(
new HighlightingService([]),
[preprocessor]);

var html = service.Render(code: "nodes: []", languageId: "beck");

html.ShouldBe("<div class=\"beck-embed\"><svg></svg></div>");
}

[Fact]
public void Render_NoPreprocessors_FallsBackToHighlighter()
{
Expand Down
Loading