diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index b8d4d24807..9eedfeefb5 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -342,12 +342,29 @@ namespace ts { } /* @internal */ - export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray, line: number, character: number, debugText?: string): number { + export function getPositionOfLineAndCharacterWithEdits(sourceFile: SourceFileLike, line: number, character: number): number { + return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text, /*allowEdits*/ true); + } + + /* @internal */ + export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray, line: number, character: number, debugText?: string, allowEdits?: true): number { if (line < 0 || line >= lineStarts.length) { - Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`); + if (allowEdits) { + // Clamp line to nearest allowable value + line = line < 0 ? 0 : line >= lineStarts.length ? lineStarts.length - 1 : line; + } + else { + Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`); + } } const res = lineStarts[line] + character; + if (allowEdits) { + // Clamp to nearest allowable values to allow the underlying to be edited without crashing (accuracy is lost, instead) + // TODO: Somehow track edits between file as it was during the creation of sourcemap we have and the current file and + // apply them to the computed position to improve accuracy + return res > lineStarts[line + 1] ? lineStarts[line + 1] : typeof debugText === "string" && res > debugText.length ? debugText.length : res; + } if (line < lineStarts.length - 1) { Debug.assert(res < lineStarts[line + 1]); } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 76cf24e51d..9b74fb22d5 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -608,7 +608,7 @@ namespace ts { function processMapping(mapping: Mapping): MappedPosition { const generatedPosition = generatedFile !== undefined - ? getPositionOfLineAndCharacter(generatedFile, mapping.generatedLine, mapping.generatedCharacter) + ? getPositionOfLineAndCharacterWithEdits(generatedFile, mapping.generatedLine, mapping.generatedCharacter) : -1; let source: string | undefined; let sourcePosition: number | undefined; @@ -617,7 +617,7 @@ namespace ts { const sourceFile = host.getSourceFileLike(sourceFilePath); source = map.sources[mapping.sourceIndex]; sourcePosition = sourceFile !== undefined - ? getPositionOfLineAndCharacter(sourceFile, mapping.sourceLine, mapping.sourceCharacter) + ? getPositionOfLineAndCharacterWithEdits(sourceFile, mapping.sourceLine, mapping.sourceCharacter) : -1; } return { diff --git a/tests/cases/fourslash/server/declarationMapsOutOfDateMapping.ts b/tests/cases/fourslash/server/declarationMapsOutOfDateMapping.ts new file mode 100644 index 0000000000..2768e0d962 --- /dev/null +++ b/tests/cases/fourslash/server/declarationMapsOutOfDateMapping.ts @@ -0,0 +1,32 @@ +/// + +// @Filename: /node_modules/a/dist/index.d.ts +////export declare class Foo { +//// bar: any; +////} +//////# sourceMappingURL=index.d.ts.map + +// @Filename: /node_modules/a/dist/index.d.ts.map +////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,GAAG,MAAC;CACP"} + +// @Filename: /node_modules/a/src/index.ts +////export class /*2*/Foo { +////} +//// + +// @Filename: /node_modules/a/package.json +////{ +//// "name": "a", +//// "version": "0.0.0", +//// "private": true, +//// "main": "dist", +//// "types": "dist" +////} + +// @Filename: /index.ts +////import { Foo/*1*/ } from "a"; + +goTo.file("/index.ts"); + +goTo.marker("1"); +verify.goToDefinitionIs("2"); // getDefinitionAndBoundSpan