From 38836e5c61e1f7263ec323bc4efac175ceb12d26 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 22 Feb 2021 14:41:52 +0100 Subject: [PATCH] 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. --- lib/factory-label.js | 42 ++++++++++++++++------------- lib/tokenize-directive-container.js | 1 - lib/tokenize-directive-leaf.js | 1 - lib/tokenize-directive-text.js | 3 +-- readme.md | 2 -- test.js | 30 +++++++++++++++++++++ 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/lib/factory-label.js b/lib/factory-label.js index d6cfa44..458fa5c 100644 --- a/lib/factory-label.js +++ b/lib/factory-label.js @@ -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: // -// 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) || /* */ 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) || /* */ 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 /* `[` */ || diff --git a/lib/tokenize-directive-container.js b/lib/tokenize-directive-container.js index e288da7..0cea1d8 100644 --- a/lib/tokenize-directive-container.js +++ b/lib/tokenize-directive-container.js @@ -184,7 +184,6 @@ function tokenizeLabel(effects, ok, nok) { 'directiveContainerLabel', 'directiveContainerLabelMarker', 'directiveContainerLabelString', - true, true ) } diff --git a/lib/tokenize-directive-leaf.js b/lib/tokenize-directive-leaf.js index 07c3295..fcbac8d 100644 --- a/lib/tokenize-directive-leaf.js +++ b/lib/tokenize-directive-leaf.js @@ -69,7 +69,6 @@ function tokenizeLabel(effects, ok, nok) { 'directiveLeafLabel', 'directiveLeafLabelMarker', 'directiveLeafLabelString', - true, true ) } diff --git a/lib/tokenize-directive-text.js b/lib/tokenize-directive-text.js index 00aff94..ffffba8 100644 --- a/lib/tokenize-directive-text.js +++ b/lib/tokenize-directive-text.js @@ -65,8 +65,7 @@ function tokenizeLabel(effects, ok, nok) { nok, 'directiveTextLabel', 'directiveTextLabelMarker', - 'directiveTextLabelString', - true + 'directiveTextLabelString' ) } diff --git a/readme.md b/readme.md index be23acb..2239867 100644 --- a/readme.md +++ b/readme.md @@ -188,8 +188,6 @@ this implementation mimics CommonMark as closely as possible: label (~~`:a []`~~), name and attributes (~~`:a {}`~~), or label and 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 (~~`:::a:::`~~) — because it’s not allowed in fenced code either * The label and attributes in a leaf or container cannot include line endings diff --git a/test.js b/test.js index 111d2c2..4dbab6c 100644 --- a/test.js +++ b/test.js @@ -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()), + '

a g

', + 'should support a directive in an label' + ) + t.equal( micromark(':a[]asd', options()), '

asd

', @@ -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})), + '

y

', + '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})), + '

x[y]z

', + 'should support balanced brackets in a label' + ) + + t.equal( + micromark(':abbr[a[b[c[d]e]f]g]h', options({abbr: abbr})), + '

a[b[c[d]e]f]gh

', + '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})), + '

[a[b[c[d[e]f]g]h]i]j

', + 'should *not* support balanced brackets in a label, four levels deep' + ) + t.equal( micromark(':abbr[a\nb\rc]', options({abbr: abbr})), '

a\nb\rc

',