Skip to content

Commit 54fc12f

Browse files
committed
feat: support escaping delimiters with a backslash
1 parent 074f7f0 commit 54fc12f

File tree

3 files changed

+25
-8
lines changed

3 files changed

+25
-8
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ md.use(kbd);
1313

1414
This plugin can also be used together with [`markdown-it-attrs`](https://github.com/arve0/markdown-it-attrs/).
1515

16-
## Parsing notes
16+
## Syntax notes
1717

1818
The end tag `]]` must be on the same line as the start tag `[[`.
1919

20-
The combinations`[[`” and “`]]`” are not allowed within keystroke tags.
21-
If you need to use them, use the HTML escape sequence “`[[` for `[[` or `]]` for `]]`.
20+
The characters`[`” and “`]`” are not allowed within keystroke tags.
21+
If you need to use them, escape them with a backslash (i.e. `\[` or `\]`) or use HTML escape sequences (`&#91` for `[` or `]` for `]`).

src/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import StateInline from 'markdown-it/lib/rules_inline/state_inline'
33

44
const MARKER_OPEN = '['
55
const MARKER_CLOSE = ']'
6+
const ESCAPE_CHARACTER = '\\'
67
const TAG = 'kbd'
78

89
/*
@@ -26,25 +27,31 @@ function tokenize(state: StateInline, silent: boolean) {
2627
// Find the end sequence
2728
let openTagCount = 1
2829
let end = -1
29-
nextChar = state.src.charAt(start + 2)
30+
let skipNext = false
3031
for (let i = start + 1; i < max && end === -1; i++) {
31-
momChar = state.src.charAt(i) // do not copy from nextChar because we sometimes skip over indices
32+
momChar = nextChar
3233
nextChar = state.src.charAt(i + 1)
34+
if (skipNext) {
35+
skipNext = false
36+
continue
37+
}
3338
if (momChar === MARKER_CLOSE && nextChar === MARKER_CLOSE) {
3439
openTagCount -= 1
3540
if (openTagCount == 0) {
3641
// Found the end!
3742
end = i
3843
}
3944
// Skip second marker char, it is already counted.
40-
i += 1
45+
skipNext = true
4146
} else if (momChar === MARKER_OPEN && nextChar === MARKER_OPEN) {
4247
openTagCount += 1
4348
// Skip second marker char, it is already counted.
44-
i += 1
49+
skipNext = true
4550
} else if (momChar === '\n') {
4651
// Found end of line before the end sequence. Thus, ignore our start sequence!
4752
return false
53+
} else if (momChar === ESCAPE_CHARACTER) {
54+
skipNext = true
4855
}
4956
}
5057

test/test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ describe('markdown-it-kbd', () => {
2828
`))
2929
})
3030

31+
it.each([
32+
['[[\\[]]', '<kbd>[</kbd>'],
33+
['[[\\]]]', '<kbd>]</kbd>'],
34+
['[[\\[\\[]]', '<kbd>[[</kbd>'],
35+
['[[\\]\\]]]', '<kbd>]]</kbd>']
36+
])('supports escaped delimiters: %s', (input, expected) => {
37+
expect(md.render(input)).toBe(`<p>${expected}</p>\n`)
38+
})
39+
3140
it('supports deep nesting and markup in nested tags', () => {
3241
expect(md.render(trimmed(`
3342
[[[[[[Shift]]\`+\`[[_long[[x]]_]]]]-Ctrl]]+[[F4]]
@@ -73,7 +82,8 @@ describe('markdown-it-kbd', () => {
7382
['[[[x]]', '[<kbd>x</kbd>'],
7483
['[[[x]]]', '[<kbd>x</kbd>]'],
7584
['[[*test*', '[[<em>test</em>'],
76-
['[[[[Shift]]+[[F3]]]', '[[<kbd>Shift</kbd>+<kbd>F3</kbd>]']
85+
['[[[[Shift]]+[[F3]]]', '[[<kbd>Shift</kbd>+<kbd>F3</kbd>]'],
86+
['[[\\\\]]', '<kbd>\\</kbd>']
7787
])('renders correctly: %s', (input, expected) => {
7888
expect(md.render(input)).toBe(`<p>${expected}</p>\n`)
7989
})

0 commit comments

Comments
 (0)