File tree 2 files changed +58
-0
lines changed
src/utils/custom-highlight-lang
2 files changed +58
-0
lines changed Original file line number Diff line number Diff line change @@ -41,5 +41,40 @@ export default function swiftOverride(hljs) {
41
41
} ;
42
42
}
43
43
44
+ // Checks if a given language sub-mode matches the "ESCAPED_NEWLINE" from the
45
+ // built-in Swift parser from hljs
46
+ const isEscapedNewlineMode = ( mode ) => {
47
+ const { className, match } = mode ;
48
+ if ( className !== 'subst' || ! match ) {
49
+ return false ;
50
+ }
51
+
52
+ const matchStr = match . toString ( ) ;
53
+ return matchStr . startsWith ( '\\' ) && matchStr . endsWith ( '[\\t ]*(?:[\\r\\n]|\\r\\n)' ) ;
54
+ } ;
55
+
56
+ // replace the "ESCAPED_NEWLINE" sub-mode in the multiline string literal mode
57
+ // variants so that it doesn't include the actual newline characters in the
58
+ // span token that it generates, because this causes issues with our
59
+ // line-number + multi-line string literal logic when the span for the
60
+ // backslash token is split across multiple lines
61
+ const strIndex = language . contains . findIndex ( ( { className } ) => className === 'string' ) ;
62
+ language . contains [ strIndex ] = {
63
+ ...language . contains [ strIndex ] ,
64
+ variants : language . contains [ strIndex ] . variants . map ( variant => ( {
65
+ ...variant ,
66
+ contains : variant . contains . map ( mode => ( isEscapedNewlineMode ( mode ) ? ( {
67
+ className : 'subst' ,
68
+ begin : / \\ # { 0 , 3 } / ,
69
+ end : / [ \t ] * (?: [ \r \n ] | \r \n ) / ,
70
+ // same match as the original one but with an explicit start/end match so
71
+ // that the end one can be excluded from the resulting span
72
+ excludeEnd : true ,
73
+ } ) : (
74
+ mode
75
+ ) ) ) ,
76
+ } ) ) ,
77
+ } ;
78
+
44
79
return language ;
45
80
}
Original file line number Diff line number Diff line change @@ -136,6 +136,29 @@ describe("syntax-highlight", () => {
136
136
` ) ;
137
137
} ) ;
138
138
139
+ it ( 'keeps escaped newline tokens on the same line for multiline string literals' , async ( ) => {
140
+ const content = [
141
+ 'let multiline = """' ,
142
+ 'a \\' ,
143
+ 'b' ,
144
+ '' ,
145
+ 'c \\' ,
146
+ 'd' ,
147
+ '"""' ,
148
+ ] ;
149
+ const { highlightedCode, sanitizedCode } = await prepare ( content , 'swift' ) ;
150
+ expect ( sanitizedCode ) . not . toEqual ( highlightedCode ) ;
151
+ expect ( sanitizedCode ) . toMatchInlineSnapshot ( `
152
+ <span class="syntax-keyword">let</span> multiline <span class="syntax-operator">=</span> <span class="syntax-string">"""</span>
153
+ <span class="syntax-string">a <span class="syntax-subst">\\</span></span>
154
+ <span class="syntax-string">b</span>
155
+ <span class="syntax-string"></span>
156
+ <span class="syntax-string">c <span class="syntax-subst">\\</span></span>
157
+ <span class="syntax-string">d</span>
158
+ <span class="syntax-string">"""</span>
159
+ ` ) ;
160
+ } ) ;
161
+
139
162
it ( "wraps multiline nested html elements" , ( ) => {
140
163
const code = document . createElement ( "CODE" ) ;
141
164
code . innerHTML = `<span class="syntax-function">function <span class="syntax-title function_">someName</span>(<span class="syntax-params">foo,
You can’t perform that action at this time.
0 commit comments