Skip to content

Commit

Permalink
Fix nesting md containers output missing extra colons
Browse files Browse the repository at this point in the history
  • Loading branch information
Trinovantes committed Aug 10, 2024
1 parent cfe247e commit b97f059
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 49 deletions.
11 changes: 11 additions & 0 deletions src/Generator/RstGeneratorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ export type RstGeneratorOptions = {
transformers: Array<ShikiTransformer>
highlighter: Awaited<ReturnType<typeof getHighlighter>>
}

/**
* Directives that will output markdown containers as defined by `markdown-it-container`
*
* ::: warning
* Hello World
* :::
*/
directivesWillOutputMdContainers: Array<string>
}

export function createDefaultGeneratorOptions(opts?: Partial<RstGeneratorOptions>): RstGeneratorOptions {
Expand Down Expand Up @@ -158,6 +167,8 @@ export function createDefaultGeneratorOptions(opts?: Partial<RstGeneratorOptions

defaultLiteralBlockLanguage: 'txt',
defaultSyntaxLanguage: '',

directivesWillOutputMdContainers: [],
}

return merge(defaultOpts, opts)
Expand Down
29 changes: 29 additions & 0 deletions src/Generator/RstGeneratorState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,35 @@ export class RstGeneratorState {
this.writeText('\n')
}

// VitePress uses `markdown-it-container` for custom block-level container
// When nesting containers, we need the outer blocks to have more colons than the inner containers
//
// :::::: outer
// ::: inner
// Content
// :::
// ::::::
//
writeLineMdContainer(label: string, root: RstNode, visitChildren: VisitChildren): void {
const getMaxMdContainerDepth = (node: RstNode): number => {
const selfIsMdContainer = node instanceof RstDirective && this.opts.directivesWillOutputMdContainers.includes(node.directive)

let maxChildMdContainerDepth = 0
for (const child of node.children) {
maxChildMdContainerDepth = Math.max(maxChildMdContainerDepth, getMaxMdContainerDepth(child))
}

return maxChildMdContainerDepth + (selfIsMdContainer ? 1 : 0)
}

const numNestedContainers = getMaxMdContainerDepth(root)
const dots = ':::'.repeat(numNestedContainers)

this.writeLine(`${dots} ${label}`)
visitChildren()
this.writeLine(dots)
}

writeLineHtmlTag(tag: string, node: RstNode | null, visitChildren?: VisitChildren): void {
this.writeLineHtmlTagWithAttr(tag, node, new HtmlAttributeStore(), visitChildren)
}
Expand Down
46 changes: 27 additions & 19 deletions src/Plugins/Admonition/AdmonitionPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ import { normalizeSimpleName } from '@/SimpleName.js'
// MARK: Directive
// ----------------------------------------------------------------------------

const RST_ADMONITION_DIRECTIVES = [
'info',
'attention',
'caution',
'danger',
'error',
'hint',
'important',
'seealso',
'note',
'tip',
'warning',
]

export const specificAdmonitionDirectiveGenerators = createDirectiveGenerators(
[
'info',
'attention',
'caution',
'danger',
'error',
'hint',
'important',
'seealso',
'note',
'tip',
'warning',
],
RST_ADMONITION_DIRECTIVES,

(generatorState, node) => {
const attrs = new HtmlAttributeStore()
Expand Down Expand Up @@ -59,9 +61,9 @@ export const specificAdmonitionDirectiveGenerators = createDirectiveGenerators(
}
})()

generatorState.writeLine(`::: ${containerType}`)
generatorState.visitNodes(node.children)
generatorState.writeLine(':::')
generatorState.writeLineMdContainer(containerType, node, () => {
generatorState.visitNodes(node.children)
})
},
)

Expand All @@ -85,9 +87,10 @@ export const genericAdmonitionDirectiveGenerators = createDirectiveGenerators(
},

(generatorState, node) => {
generatorState.writeLine(`::: ${normalizeSimpleName(node.initContentText)}`)
generatorState.visitNodes(node.children)
generatorState.writeLine(':::')
const admonition = normalizeSimpleName(node.initContentText)
generatorState.writeLineMdContainer(admonition, node, () => {
generatorState.visitNodes(node.children)
})
},
)

Expand All @@ -104,4 +107,9 @@ export const admonitionDirectivePlugins = createRstCompilerPlugins({
onBeforeParse: (parserOption) => {
parserOption.directivesWithInitContent.push(GENERIC_ADMONITION_DIRECTIVE)
},

onBeforeGenerate: (generatorOptions) => {
generatorOptions.directivesWillOutputMdContainers.push(GENERIC_ADMONITION_DIRECTIVE)
generatorOptions.directivesWillOutputMdContainers.push(...RST_ADMONITION_DIRECTIVES)
},
})
55 changes: 29 additions & 26 deletions src/Plugins/Tabs/TabsPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,6 @@ export enum TabsClientConstant {
LOCAL_STORAGE_KEY = 'rst-compiler:tabs-plugin',
}

const tabsClientScript = (() => {
let output = `
<script type="text/javascript">
(() => {
${browserCode}
})()
</script>`

for (const [origText, replacement] of Object.entries(TabsClientConstant)) {
output = output.replaceAll(origText, replacement)
}

return output
})()

// ----------------------------------------------------------------------------
// MARK: Container Directive
// ----------------------------------------------------------------------------
Expand All @@ -65,25 +50,23 @@ export const tabContainerDirectiveGenerators = createDirectiveGenerators(
const attrs = new HtmlAttributeStore()
attrs.append(TabsClientConstant.ATTR_TAB_GROUP_NAME, getTabsGroupKey(generatorState, node))

generatorState.registerGlobalHeader(GENERATOR_GLOBAL_HEADER_KEY, tabsClientScript)
generatorState.registerGlobalHeader(GENERATOR_GLOBAL_HEADER_KEY, getTabsClientScript())
generatorState.writeLineHtmlTagWithAttr(TabsClientConstant.ELEMENT_CONTAINER, node, attrs, () => {
generatorState.visitNodes(node.children)
})
},

(generatorState, node) => {
const groupKey = getTabsGroupKey(generatorState, node)
if (groupKey) {
generatorState.writeLine(`:::tabs key:${groupKey}`)
} else {
generatorState.writeLine(':::tabs')
}

generatorState.useNoLineBreaksBetweenBlocks(() => {
generatorState.visitNodes(node.children)
const containerLabel = groupKey
? `tabs key:${groupKey}`
: 'tabs'

generatorState.writeLineMdContainer(containerLabel, node, () => {
generatorState.useNoLineBreaksBetweenBlocks(() => {
generatorState.visitNodes(node.children)
})
})

generatorState.writeLine(':::')
},
)

Expand Down Expand Up @@ -204,6 +187,10 @@ export const tabsDirectivePlugins = createRstCompilerPlugins({
parserOption.directivesWithInitContent.push(TAB_PANEL_GROUP_DIRECTIVE)
parserOption.directivesWithInitContent.push(TAB_PANEL_CODEGROUP_DIRECTIVE)
},

onBeforeGenerate: (generatorOptions) => {
generatorOptions.directivesWillOutputMdContainers.push(TAB_CONTAINER_DIRECTIVE)
},
})

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -233,3 +220,19 @@ function getCodeTabInfo(node: RstDirective) {
label: origLabel,
}
}

function getTabsClientScript() {
let output = `
<script type="text/javascript">
(() => {
${browserCode}
})()
</script>
`

for (const [origText, replacement] of Object.entries(TabsClientConstant)) {
output = output.replaceAll(origText, replacement)
}

return output
}
129 changes: 129 additions & 0 deletions tests/unit/Plugins/Admonition/AdmonitionPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,132 @@ describe.each([
:::
`)
})

describe('nested admonitions', () => {
const input = `
.. info:: Outer 1
.. warning:: Inner 1
.. error:: Hello World
.. warning:: Inner 2
.. info:: Outer 2
`

testParser(input, [
{
type: RstNodeType.Directive,
data: {
directive: 'info',
},
children: [
{
type: RstNodeType.Paragraph,
text: 'Outer 1',
},
{
type: RstNodeType.Directive,
data: {
directive: 'warning',
},
children: [
{
type: RstNodeType.Paragraph,
text: 'Inner 1',
},
{
type: RstNodeType.Directive,
data: {
directive: 'error',
},
children: [
{
type: RstNodeType.Paragraph,
text: 'Hello World',
},
],
},
],
},
{
type: RstNodeType.Directive,
data: {
directive: 'warning',
},
children: [
{
type: RstNodeType.Paragraph,
text: 'Inner 2',
},
],
},
],
},
{
type: RstNodeType.Directive,
data: {
directive: 'info',
},
children: [
{
type: RstNodeType.Paragraph,
text: 'Outer 2',
},
],
},
])

testGenerator(input, `
<div class="admonition info">
<p>
Outer 1
</p>
<div class="admonition warning">
<p>
Inner 1
</p>
<div class="admonition error">
<p>
Hello World
</p>
</div>
</div>
<div class="admonition warning">
<p>
Inner 2
</p>
</div>
</div>
<div class="admonition info">
<p>
Outer 2
</p>
</div>
`, `
::::::::: info
Outer 1
:::::: warning
Inner 1
::: danger
Hello World
:::
::::::
::: warning
Inner 2
:::
:::::::::
::: info
Outer 2
:::
`)
})
8 changes: 4 additions & 4 deletions tests/unit/Plugins/Tabs/TabsPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('normal tabs', () => {
</rst-tab-panel>
</rst-tabs-container>
`, `
:::tabs
::: tabs
== Label A
Text A
== Label B
Expand Down Expand Up @@ -174,7 +174,7 @@ describe('group tabs', () => {
</rst-tab-panel>
</rst-tabs-container>
`, `
:::tabs key:group-tab
::: tabs key:group-tab
== Label A
Text A
== Label B
Expand Down Expand Up @@ -250,7 +250,7 @@ describe('group tabs (code)', () => {
</rst-tab-panel>
</rst-tabs-container>
`, `
:::tabs key:code-tab
::: tabs key:code-tab
== JavaScript
\`\`\`js
Text A
Expand Down Expand Up @@ -330,7 +330,7 @@ describe('when code-group does not have a valid language label, it generates mar
</rst-tab-panel>
</rst-tabs-container>
`, `
:::tabs key:code-tab
::: tabs key:code-tab
== Label A
\`\`\`txt
Text A
Expand Down

0 comments on commit b97f059

Please sign in to comment.