From 8da4c9a969459d939ddf43d61751f598f404a217 Mon Sep 17 00:00:00 2001
From: pietervdvn <pietervdvn@posteo.net>
Date: Fri, 8 Apr 2022 17:56:56 +0200
Subject: [PATCH] Fix line numbers in error messages

---
 examples/someClass.ts   |  5 +++++
 src/ExtractComments.ts  |  2 +-
 src/ScriptExtraction.ts | 42 +++++++++++++++++++++++++----------------
 src/UnitTest.ts         |  5 +++--
 4 files changed, 35 insertions(+), 19 deletions(-)

diff --git a/examples/someClass.ts b/examples/someClass.ts
index a671254..b4d35cd 100644
--- a/examples/someClass.ts
+++ b/examples/someClass.ts
@@ -13,6 +13,11 @@ export default class SomeClass {
      * SomeClass.get() + 1 // => 43
      * 
      * new OtherClass().doSomething(new SomeClass()) // => 5
+     *
+     * const abc = 42
+     * SomeClass.get() // => abc
+     * SomeClass.get() + 1 // => abc + 1
+     * 
      */
     private static get() : number{
         // a comment
diff --git a/src/ExtractComments.ts b/src/ExtractComments.ts
index 76ba1be..9e5f835 100644
--- a/src/ExtractComments.ts
+++ b/src/ExtractComments.ts
@@ -26,7 +26,7 @@ export default class ExtractComments {
     }
 
     /**
-     * Gets the actual comments
+     * Gets the actual comments and their context.
      */
     public getComments(): { comment: string, context: Context }[] {
         return this.results
diff --git a/src/ScriptExtraction.ts b/src/ScriptExtraction.ts
index a741400..4290767 100644
--- a/src/ScriptExtraction.ts
+++ b/src/ScriptExtraction.ts
@@ -25,15 +25,24 @@ export class ScriptExtraction {
         return docstring.split("\n").filter(s => s.startsWith("import "));
     }
 
-    public static extractScripts(docstring: string): { script: Script, name?: string, line: number }[] {
-        const out = [] as { script: Script, name?: string, line: number }[]
-        
+    /**
+     * Extracts the scripts from a comment
+     *
+     * // should have correct line numbers
+     * const extr = ScriptExtraction.extractScripts("some actual comment\n\n42 // => 42\n\n43 // => 43\n44 // => 44\n\n // should be named\n45 // => 45\n")
+     * extr[0] // => {script: [{lhs: "42", rhs: "42", tag: '==', line: 2}]}
+     * extr[1] // => {script: [{lhs: "43", rhs: "43", tag: '==', line: 4},{lhs: "44", rhs: "44", tag: '==', line: 5}]}
+     * extr[2] // => {name: "should be named", script: [{lhs: "45", rhs: "45", tag: '==', line: 8}]}
+     */
+    public static extractScripts(docstring: string): { script: Script, name?: string }[] {
+        const out = [] as { script: Script, name?: string }[]
+
         function lineIndexOf(part: string): number {
             const index = docstring.indexOf(part)
             const before = docstring.slice(0, index)
             return before.split(/\r\n|\r|\n/).length - 1
         }
-        
+
         for (const s of docstring.split(/\n\n+/m)) {
 
             if (!ScriptExtraction.is_doctest(s)) {
@@ -42,12 +51,13 @@ export class ScriptExtraction {
 
             const line = lineIndexOf(s)
             const script = ScriptExtraction.extractScript(s, line)
-            let name = undefined
             const match = s.match(/^[ \t]*\/\/([^\n]*)/)
             if (match !== null) {
-                name = match[1].trim()
+                const name = match[1].trim()
+                out.push({script, name})
+            } else {
+                out.push({script})
             }
-            out.push({script, name, line})
         }
         return out
     }
@@ -55,25 +65,25 @@ export class ScriptExtraction {
     /**
      * ScriptExtraction.extractScript('s', 0) // => [{tag: 'Statement', stmt: 's;'}]
      * ScriptExtraction.extractScript('e // => 1', 0) // => [{tag: '==', lhs: 'e', rhs: '1', line: 0}]
-     * ScriptExtraction.extractScript('s; e // => 1', 0) // => [{tag: 'Statement', stmt: 's;'}, {tag: '==', lhs: 'e', rhs: '1', line: 1}]
+     * ScriptExtraction.extractScript('s;\n e // => 1', 0) // => [{tag: 'Statement', stmt: 's;'}, {tag: '==', lhs: 'e', rhs: '1', line: 1}]
+     * ScriptExtraction.extractScript('s0\n e0 // => 0 \n e // => 1', 42) // => [{tag: 'Statement', stmt: 's0;'}, {tag: '==', lhs: 'e0', rhs: '0', line: 43}, {tag: '==', lhs: 'e', rhs: '1', line: 44}]
      */
     private static extractScript(s: string, linestart: number): Script {
         function getLineNumber(pos: number) {
             let line = 0;
-            for (let i = 0; i < pos; i++) {
-                if (s === "\n") {
+            for (let i = 0; i <= pos; i++) {
+                if (s[i] === "\n") {
                     line++
                 }
             }
             return line;
         }
-        
+
         const pwoc = ts.createPrinter({removeComments: true})
         const ast = ts.createSourceFile('_.ts', s, ts.ScriptTarget.Latest)
         return ast.statements.map((stmt, i): Statement | Equality => {
-            
-            
-            
+
+
             if (ts.isExpressionStatement(stmt)) {
                 const next = ast.statements[i + 1] // zip with next
                 const [a, z] = next ? [next.pos, next.end] : [stmt.end, ast.end]
@@ -83,8 +93,8 @@ export class ScriptExtraction {
                     const lhs = pwoc.printNode(ts.EmitHint.Expression, stmt.expression, ast)
                     const rhs = m[1].trim()
 
-                    
-                    return {tag: '==', lhs, rhs, line: linestart + getLineNumber(stmt.pos)}
+
+                    return {tag: '==', lhs, rhs, line: linestart + getLineNumber(a)}
                 }
             }
 
diff --git a/src/UnitTest.ts b/src/UnitTest.ts
index a0981aa..9cb1e20 100644
--- a/src/UnitTest.ts
+++ b/src/UnitTest.ts
@@ -33,7 +33,8 @@ export default class UnitTest {
 
     public static FromComment(comment: string, context: Context): UnitTest[] {
         const imports = ScriptExtraction.extractImports(comment)
-        return ScriptExtraction.extractScripts(comment).map(({script, line, name}, i) =>
+        return ScriptExtraction.extractScripts(comment).map(
+            ({script, name}, i) =>
             new UnitTest(script, {
                 ...context,
                 linenumber: (context.linenumber ?? 0) ,
@@ -63,7 +64,7 @@ export default class UnitTest {
                 if (s.tag == 'Statement') {
                     return s.stmt
                 } else {
-                    return `__expect(${s.lhs}, "failed at ${this.context.functionname} (${this.context.filepath}:${s.line + (this.context.linenumber ?? 0)}:1)").to.deep.equal(${s.rhs})`
+                    return `__expect(${s.lhs}, "failed at ${this.context.functionname} (${this.context.filepath}:${1 + s.line + (this.context.linenumber ?? 0)}:1)").to.deep.equal(${s.rhs})`
                 }
             })
             .map(x => '\n        ' + x)