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

View file

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

View file

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

View file

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

View file

@ -188,8 +188,6 @@ this implementation mimics CommonMark as closely as possible:
label (~~`:a []`~~), name and attributes (~~`:a {}`~~), or label and label (~~`:a []`~~), name and attributes (~~`:a {}`~~), or label and
attributes (~~`:a[] {}`~~) — because its not allowed in links either 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 * No trailing colons allowed on the opening fence of a container
(~~`:::a:::`~~) — because its not allowed in fenced code either (~~`:::a:::`~~) — because its not allowed in fenced code either
* The label and attributes in a leaf or container cannot include line endings * 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' '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( t.equal(
micromark(':a[]asd', options()), micromark(':a[]asd', options()),
'<p>asd</p>', '<p>asd</p>',
@ -1120,6 +1126,12 @@ test('micromark-extension-directive (compile)', function (t) {
'should support fall through directives (`*`)' '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() t.end()
}) })
@ -1136,6 +1148,24 @@ test('content', function (t) {
'should support escaped brackets in a label' '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( t.equal(
micromark(':abbr[a\nb\rc]', options({abbr: abbr})), micromark(':abbr[a\nb\rc]', options({abbr: abbr})),
'<p><abbr>a\nb\rc</abbr></p>', '<p><abbr>a\nb\rc</abbr></p>',