Clamp calculated sourcemap positions rather than throwing (#28583)

* Clamp calculated sourcemap positions rather than throwing, to allow the underlying file to drift out of date with the sourcemap without a crash

* Clamp line as well
This commit is contained in:
Wesley Wigham 2018-11-16 16:05:07 -08:00 committed by GitHub
parent 12f3d0d54c
commit cd08a22ef5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 4 deletions

View file

@ -342,12 +342,29 @@ namespace ts {
}
/* @internal */
export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray<number>, 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<number>, 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]);
}

View file

@ -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 {

View file

@ -0,0 +1,32 @@
/// <reference path="../fourslash.ts" />
// @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