]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler): Addressed infinite loop in compiler (#3992)
authorAustin Keener <keeneraustin@yahoo.com>
Thu, 15 Jul 2021 18:57:47 +0000 (14:57 -0400)
committerGitHub <noreply@github.com>
Thu, 15 Jul 2021 18:57:47 +0000 (14:57 -0400)
close #3987

packages/shared/__tests__/__snapshots__/codeframe.spec.ts.snap
packages/shared/__tests__/codeframe.spec.ts
packages/shared/src/codeframe.ts

index 89a4c2635459f9ad19cc1d084d32813c02bc40d1..579a4507d6ef695f8704ece81b0a05e1bd3ee98a 100644 (file)
@@ -35,3 +35,29 @@ exports[`compiler: codeframe multi-line highlights 1`] = `
 4  |  \\">
    |  ^"
 `;
+
+exports[`compiler: codeframe newline sequences - unix 1`] = `
+"8  |          <input name=\\"email\\" type=\\"text\\"/>
+9  |        </div>
+10 |        <div id=\\"hook\\">
+   |        ^^^^^^^^^^^^^^^
+11 |          <label for=\\"password\\">Password</label>
+   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+12 |          <input name=\\"password\\" type=\\"password\\"/>
+   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+13 |        </div>
+   |  ^^^^^^^^^^^^"
+`;
+
+exports[`compiler: codeframe newline sequences - windows 1`] = `
+"8  |          <input name=\\"email\\" type=\\"text\\"/>
+9  |        </div>
+10 |        <div id=\\"hook\\">
+   |        ^^^^^^^^^^^^^^^
+11 |          <label for=\\"password\\">Password</label>
+   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+12 |          <input name=\\"password\\" type=\\"password\\"/>
+   |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+13 |        </div>
+   |  ^^^^^^^^^^^^"
+`;
index eb86a67844ca3eb76ed064015bfe6c800b1d966a..5083100485d32febaf9709bc7f30c52aab5d7b3d 100644 (file)
@@ -43,4 +43,49 @@ attr
     const attrEnd = source.indexOf(`">`) + 1
     expect(generateCodeFrame(source, attrStart, attrEnd)).toMatchSnapshot()
   })
+
+  {
+    const source = `
+<template>
+  <div>
+    <h1>Sign In</h1>
+    <form>
+      <div>
+        <label for="email">Email</label>
+        <input name="email" type="text"/>
+      </div>
+      <div id="hook">
+        <label for="password">Password</label>
+        <input name="password" type="password"/>
+      </div>
+    </form>
+  </div>
+</template>
+`
+    const startToken = '<div id="hook">'
+    const endToken = '</div>'
+
+    // 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()
+    })
+  }
 })
index f11432cd92dfdfaed55eb0a31d60ab9a9ffc786c..82b38bfb33085d3f2684c1956a40c9af293ef8fe 100644 (file)
@@ -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