Skip to content

Commit cf5dbd7

Browse files
committed
Fix grammars depending on empty lines
1 parent eb28bed commit cf5dbd7

2 files changed

Lines changed: 44 additions & 27 deletions

File tree

lib/parse.js

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import {transparent, classes, grandparents} from './theme.js'
99

1010
// Source: <https://github.com/microsoft/vscode-textmate/blob/9157c7f/src/metadata.ts#L33-L35>
11+
// Also: <https://github.com/microsoft/vscode/blob/8ca37ee/src/vs/editor/common/encodedTokenAttributes.ts#L71>
1112
const FONT_STYLE_MASK = 0b0000_0000_0000_0000_0111_1000_0000_0000
1213
const FOREGROUND_MASK = 0b0000_0000_1111_1111_1000_0000_0000_0000
1314
const BACKGROUND_MASK = 0b1111_1111_0000_0000_0000_0000_0000_0000
1415

1516
// Source: <https://github.com/microsoft/vscode-textmate/blob/9157c7f/src/metadata.ts#L37-L42>
17+
// Also: <https://github.com/microsoft/vscode/blob/8ca37ee/src/vs/editor/common/encodedTokenAttributes.ts#L92-L94>
1618
const FONT_STYLE_OFFSET = 11
1719
const FOREGROUND_OFFSET = 15
1820
const BACKGROUND_OFFSET = 24
@@ -28,42 +30,41 @@ export function parse(value, grammar, colors) {
2830
/** @type {Root} */
2931
const tree = {type: 'root', children: []}
3032
const search = /\r?\n|\r/g
31-
/** @type {StateStack|null} */
33+
/** @type {StateStack | null} */
3234
let stack = null
3335
let start = 0
3436

3537
while (start < value.length) {
3638
const match = search.exec(value)
3739
const end = match ? match.index : value.length
3840

39-
// Empty lines could still match `source` and be turned into a span.
40-
// Ignore those.
41-
if (start !== end) {
42-
const {tokens, ruleStack} = grammar.tokenizeLine2(
43-
value.slice(start, end),
44-
stack
45-
)
46-
let index = 0
47-
48-
while (index < tokens.length) {
49-
const tokenStart = start + tokens[index++]
50-
const metadata = tokens[index++]
51-
const tokenEnd = index < tokens.length ? start + tokens[index] : end
52-
// Source: <https://github.com/microsoft/vscode-textmate/blob/9157c7f/src/metadata.ts#L71-L93>
53-
const fg = (metadata & FOREGROUND_MASK) >>> FOREGROUND_OFFSET
54-
const bg = (metadata & BACKGROUND_MASK) >>> BACKGROUND_OFFSET
55-
const fs = (metadata & FONT_STYLE_MASK) >>> FONT_STYLE_OFFSET
56-
/** @type {Root|Element} */
57-
let scope = tree
58-
scope = delveIfClassName(scope, fontStyleToClass(fs))
59-
scope = delveIfClassName(scope, colorToClass(colors[bg]))
60-
scope = delveIfClassName(scope, colorToClass(colors[fg]))
61-
appendText(scope, value.slice(tokenStart, tokenEnd))
62-
}
63-
64-
stack = ruleStack
41+
// > 👉 **Important**: empty lines have to be tokenized, as some patterns
42+
// > look for them.
43+
const {tokens, ruleStack} = grammar.tokenizeLine2(
44+
value.slice(start, end),
45+
stack
46+
)
47+
let index = 0
48+
49+
while (index < tokens.length) {
50+
const tokenStart = start + tokens[index++]
51+
const metadata = tokens[index++]
52+
const tokenEnd = index < tokens.length ? start + tokens[index] : end
53+
// Source: <https://github.com/microsoft/vscode-textmate/blob/9157c7f/src/metadata.ts#L71-L93>
54+
const fg = (metadata & FOREGROUND_MASK) >>> FOREGROUND_OFFSET
55+
const bg = (metadata & BACKGROUND_MASK) >>> BACKGROUND_OFFSET
56+
const fs = (metadata & FONT_STYLE_MASK) >>> FONT_STYLE_OFFSET
57+
58+
/** @type {Root|Element} */
59+
let scope = tree
60+
scope = delveIfClassName(scope, fontStyleToClass(fs))
61+
scope = delveIfClassName(scope, colorToClass(colors[bg]))
62+
scope = delveIfClassName(scope, colorToClass(colors[fg]))
63+
appendText(scope, value.slice(tokenStart, tokenEnd))
6564
}
6665

66+
stack = ruleStack
67+
6768
start = end
6869

6970
if (match) {

test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,20 @@ class Country(val name : String)`,
168168
'<span class="pl-k">package</span> <span class="pl-en">addressbook</span>\n\n<span class="pl-k">class</span> <span class="pl-en">Country</span>(<span class="pl-k">val</span> <span class="pl-smi">name</span> <span class="pl-k">:</span> <span class="pl-c1">String</span>)',
169169
'should highlight some kotlin'
170170
)
171+
172+
// Real world example of this test case:
173+
// <https://github.com/microsoft/vscode-markdown-tm-grammar/blob/eed230887a39da1ecf5bfc914e00a1e1813c0fdb/markdown.tmLanguage.base.yaml#L125>
174+
const starryNightBlankLines = await createStarryNight([
175+
{
176+
names: [],
177+
extensions: [],
178+
scopeName: 'x',
179+
patterns: [{begin: '^a$', end: '^[\\t ]*$', name: 'invalid.illegal'}]
180+
}
181+
])
182+
assert.equal(
183+
toHtml(starryNightBlankLines.highlight('a\n\nb', 'x')),
184+
'<span class="pl-ii">a</span>\n\nb',
185+
'should be able to match on empty lines'
186+
)
171187
})

0 commit comments

Comments
 (0)