From e00aa56658ec207d45aae6eb23f0267b9e1c55e2 Mon Sep 17 00:00:00 2001 From: Austin Keener Date: Thu, 15 Jul 2021 14:57:47 -0400 Subject: [PATCH] fix(compiler): Addressed infinite loop in compiler (#3992) close #3987 --- .../__snapshots__/codeframe.spec.ts.snap | 26 +++++++++++ packages/shared/__tests__/codeframe.spec.ts | 45 +++++++++++++++++++ packages/shared/src/codeframe.ts | 23 ++++++++-- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/shared/__tests__/__snapshots__/codeframe.spec.ts.snap b/packages/shared/__tests__/__snapshots__/codeframe.spec.ts.snap index 89a4c2635..579a4507d 100644 --- a/packages/shared/__tests__/__snapshots__/codeframe.spec.ts.snap +++ b/packages/shared/__tests__/__snapshots__/codeframe.spec.ts.snap @@ -35,3 +35,29 @@ exports[`compiler: codeframe multi-line highlights 1`] = ` 4 | \\"> | ^" `; + +exports[`compiler: codeframe newline sequences - unix 1`] = ` +"8 | +9 | +10 |
+ | ^^^^^^^^^^^^^^^ +11 | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +12 | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +13 |
+ | ^^^^^^^^^^^^" +`; + +exports[`compiler: codeframe newline sequences - windows 1`] = ` +"8 | +9 | +10 |
+ | ^^^^^^^^^^^^^^^ +11 | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +12 | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +13 |
+ | ^^^^^^^^^^^^" +`; diff --git a/packages/shared/__tests__/codeframe.spec.ts b/packages/shared/__tests__/codeframe.spec.ts index eb86a6784..508310048 100644 --- a/packages/shared/__tests__/codeframe.spec.ts +++ b/packages/shared/__tests__/codeframe.spec.ts @@ -43,4 +43,49 @@ attr const attrEnd = source.indexOf(`">`) + 1 expect(generateCodeFrame(source, attrStart, attrEnd)).toMatchSnapshot() }) + + { + const source = ` + +` + const startToken = '
' + const endToken = '
' + + // Explicitly ensure the line-ending for the platform instead of assuming + // the newline sequences used in the source above. + const unixNewlineSource = source.replace(/\r\n/g, '\n') + const windowsNewLineSource = unixNewlineSource.replace(/\n/g, '\r\n') + + test('newline sequences - windows', () => { + const keyStart = windowsNewLineSource.indexOf(startToken) + const keyEnd = + windowsNewLineSource.indexOf(endToken, keyStart) + endToken.length + expect( + generateCodeFrame(windowsNewLineSource, keyStart, keyEnd) + ).toMatchSnapshot() + }) + + test('newline sequences - unix', () => { + const keyStart = unixNewlineSource.indexOf(startToken) + const keyEnd = + unixNewlineSource.indexOf(endToken, keyStart) + endToken.length + expect( + generateCodeFrame(unixNewlineSource, keyStart, keyEnd) + ).toMatchSnapshot() + }) + } }) diff --git a/packages/shared/src/codeframe.ts b/packages/shared/src/codeframe.ts index f11432cd9..82b38bfb3 100644 --- a/packages/shared/src/codeframe.ts +++ b/packages/shared/src/codeframe.ts @@ -5,11 +5,22 @@ export function generateCodeFrame( start = 0, end = source.length ): string { - const lines = source.split(/\r?\n/) + // Split the content into individual lines but capture the newline sequence + // that separated each line. This is important because the actual sequence is + // needed to properly take into account the full line length for offset + // comparison + let lines = source.split(/(\r?\n)/) + + // Separate the lines and newline sequences into separate arrays for easier referencing + const newlineSequences = lines.filter((_, idx) => idx % 2 === 1) + lines = lines.filter((_, idx) => idx % 2 === 0) + let count = 0 const res: string[] = [] for (let i = 0; i < lines.length; i++) { - count += lines[i].length + 1 + count += + lines[i].length + + ((newlineSequences[i] && newlineSequences[i].length) || 0) if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue @@ -20,9 +31,12 @@ export function generateCodeFrame( }` ) const lineLength = lines[j].length + const newLineSeqLength = + (newlineSequences[j] && newlineSequences[j].length) || 0 + if (j === i) { // push underline - const pad = start - (count - lineLength) + 1 + const pad = start - (count - (lineLength + newLineSeqLength)) const length = Math.max( 1, end > count ? lineLength - pad : end - start @@ -33,7 +47,8 @@ export function generateCodeFrame( const length = Math.max(Math.min(end - count, lineLength), 1) res.push(` | ` + '^'.repeat(length)) } - count += lineLength + 1 + + count += lineLength + newLineSeqLength } } break