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:
parent
891eaa74fa
commit
38836e5c61
6 changed files with 55 additions and 24 deletions
|
@ -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 /* `[` */ ||
|
||||||
|
|
|
@ -184,7 +184,6 @@ function tokenizeLabel(effects, ok, nok) {
|
||||||
'directiveContainerLabel',
|
'directiveContainerLabel',
|
||||||
'directiveContainerLabelMarker',
|
'directiveContainerLabelMarker',
|
||||||
'directiveContainerLabelString',
|
'directiveContainerLabelString',
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,6 @@ function tokenizeLabel(effects, ok, nok) {
|
||||||
'directiveLeafLabel',
|
'directiveLeafLabel',
|
||||||
'directiveLeafLabelMarker',
|
'directiveLeafLabelMarker',
|
||||||
'directiveLeafLabelString',
|
'directiveLeafLabelString',
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,7 @@ function tokenizeLabel(effects, ok, nok) {
|
||||||
nok,
|
nok,
|
||||||
'directiveTextLabel',
|
'directiveTextLabel',
|
||||||
'directiveTextLabelMarker',
|
'directiveTextLabelMarker',
|
||||||
'directiveTextLabelString',
|
'directiveTextLabelString'
|
||||||
true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 it’s not allowed in links either
|
attributes (~~`:a[] {}`~~) — because it’s 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 it’s not allowed in fenced code either
|
(~~`:::a:::`~~) — because it’s 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
30
test.js
|
@ -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>',
|
||||||
|
|
Loading…
Add table
Reference in a new issue