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)