File tree Expand file tree Collapse file tree 2 files changed +58
-0
lines changed
src/utils/custom-highlight-lang Expand file tree Collapse file tree 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) {
4141 } ;
4242 }
4343
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+
4479 return language ;
4580}
Original file line number Diff line number Diff line change @@ -136,6 +136,29 @@ describe("syntax-highlight", () => {
136136 ` ) ;
137137 } ) ;
138138
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+
139162 it ( "wraps multiline nested html elements" , ( ) => {
140163 const code = document . createElement ( "CODE" ) ;
141164 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