Add support for balanced brackets in labels

Previously, brackets were only allowed in labels when escaped,
making it impossible to use media with resources (links, images),
or nested directives.

This adds support for up to three levels of nesting in a label.

This also removes a hidden flag that was not used anymore.

Closes GH-7.
This commit is contained in:
Titus Wormer 2021-02-22 14:41:52 +01:00
parent 891eaa74fa
commit 38836e5c61
No known key found for this signature in database
GPG key ID: E6E581152ED04E2E
6 changed files with 55 additions and 24 deletions

View file

@ -1,12 +1,11 @@
module.exports = createLabel
var markdownLineEnding = require('micromark/dist/character/markdown-line-ending')
var markdownSpace = require('micromark/dist/character/markdown-space')
// This is a fork of:
// <https://github.com/micromark/micromark/blob/bf53bf9/lib/tokenize/factory-label.js>
// to allow empty, support text instead of strings, and optionally w/o EOLs,
// labels.
// to allow empty labels, balanced brackets (such as for nested directives),
// text instead of strings, and optionally disallows EOLs.
// eslint-disable-next-line max-params
function createLabel(
@ -16,11 +15,10 @@ function createLabel(
type,
markerType,
stringType,
allowEmpty,
disallowEol
) {
var size = 0
var data
var balance = 0
return start
@ -35,7 +33,7 @@ function createLabel(
}
function afterStart(code) {
if (code === 93 /* `]` */ && allowEmpty) {
if (code === 93 /* `]` */) {
effects.enter(markerType)
effects.consume(code)
effects.exit(markerType)
@ -50,21 +48,14 @@ function createLabel(
function atBreak(code) {
if (
code === null /* EOF */ ||
code === 91 /* `[` */ ||
(code === 93 /* `]` */ && !data && !allowEmpty) ||
/* <https://github.com/micromark/micromark/blob/bf53bf9/lib/constant/constants.js#L34> */
size > 999
) {
return nok(code)
}
if (code === 93 /* `]` */) {
effects.exit(stringType)
effects.enter(markerType)
effects.consume(code)
effects.exit(markerType)
effects.exit(type)
return ok
if (code === 93 /* `]` */ && !balance--) {
return atClosingBrace(code)
}
if (markdownLineEnding(code)) {
@ -85,8 +76,6 @@ function createLabel(
function label(code) {
if (
code === null /* EOF */ ||
code === 91 /* `[` */ ||
code === 93 /* `]` */ ||
markdownLineEnding(code) ||
/* <https://github.com/micromark/micromark/blob/bf53bf9/lib/constant/constants.js#L34> */
size > 999
@ -95,11 +84,28 @@ function createLabel(
return atBreak(code)
}
if (code === 91 /* `[` */ && ++balance > 3) {
return nok(code)
}
if (code === 93 /* `]` */ && !balance--) {
effects.exit('chunkText')
return atClosingBrace(code)
}
effects.consume(code)
data = data || !markdownSpace(code)
return code === 92 /* `\` */ ? labelEscape : label
}
function atClosingBrace(code) {
effects.exit(stringType)
effects.enter(markerType)
effects.consume(code)
effects.exit(markerType)
effects.exit(type)
return ok
}
function labelEscape(code) {
if (
code === 91 /* `[` */ ||

View file

@ -184,7 +184,6 @@ function tokenizeLabel(effects, ok, nok) {
'directiveContainerLabel',
'directiveContainerLabelMarker',
'directiveContainerLabelString',
true,
true
)
}

View file

@ -69,7 +69,6 @@ function tokenizeLabel(effects, ok, nok) {
'directiveLeafLabel',
'directiveLeafLabelMarker',
'directiveLeafLabelString',
true,
true
)
}

View file

@ -65,8 +65,7 @@ function tokenizeLabel(effects, ok, nok) {
nok,
'directiveTextLabel',
'directiveTextLabelMarker',
'directiveTextLabelString',
true
'directiveTextLabelString'
)
}

View file

@ -188,8 +188,6 @@ this implementation mimics CommonMark as closely as possible:
label (~~`:a []`~~), name and attributes (~~`:a {}`~~), or label and
attributes (~~`:a[] {}`~~) — because its not allowed in links either
(~~`[] ()`~~)
* Only escaped brackets are allow in the label (~~`:a[b[c]d]`~~) — because
links in links are not allowed either
* No trailing colons allowed on the opening fence of a container
(~~`:::a:::`~~) — because its not allowed in fenced code either
* The label and attributes in a leaf or container cannot include line endings

30
test.js
View file

@ -114,6 +114,12 @@ test('micromark-extension-directive (syntax)', function (t) {
'should support markdown in an label'
)
t.equal(
micromark('a :b[c :d[e] f] g', options()),
'<p>a g</p>',
'should support a directive in an label'
)
t.equal(
micromark(':a[]asd', options()),
'<p>asd</p>',
@ -1120,6 +1126,12 @@ test('micromark-extension-directive (compile)', function (t) {
'should support fall through directives (`*`)'
)
t.equal(
micromark(':a[:img{src="x" alt=y}]{href="z"}', options({'*': h})),
'<p><a href="z"><img src="x" alt="y"></a></p>',
'should support fall through directives (`*`)'
)
t.end()
})
@ -1136,6 +1148,24 @@ test('content', function (t) {
'should support escaped brackets in a label'
)
t.equal(
micromark(':abbr[x[y]z]', options({abbr: abbr})),
'<p><abbr>x[y]z</abbr></p>',
'should support balanced brackets in a label'
)
t.equal(
micromark(':abbr[a[b[c[d]e]f]g]h', options({abbr: abbr})),
'<p><abbr>a[b[c[d]e]f]g</abbr>h</p>',
'should support balanced brackets in a label, three levels deep'
)
t.equal(
micromark(':abbr[a[b[c[d[e]f]g]h]i]j', options({abbr: abbr})),
'<p><abbr></abbr>[a[b[c[d[e]f]g]h]i]j</p>',
'should *not* support balanced brackets in a label, four levels deep'
)
t.equal(
micromark(':abbr[a\nb\rc]', options({abbr: abbr})),
'<p><abbr>a\nb\rc</abbr></p>',