diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index db697cfb1ab0e..c9adc3427ee94 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -67,10 +67,8 @@ namespace ts.server { } const lm = LineIndex.linesFromText(insertedText); const lines = lm.lines; - if (lines.length > 1) { - if (lines[lines.length - 1] === "") { - lines.pop(); - } + if (lines.length > 1 && lines[lines.length - 1] === "") { + lines.pop(); } let branchParent: LineNode | undefined; let lastZeroCount: LineCollection | undefined; @@ -683,8 +681,12 @@ namespace ts.server { } // Skipped all children - const { leaf } = this.lineNumberToInfo(this.lineCount(), 0); - return { oneBasedLine: this.lineCount(), zeroBasedColumn: leaf ? leaf.charCount() : 0, lineText: undefined }; + const lineCount = this.lineCount(); + if (lineCount === 0) { // it's empty! (and lineNumberToInfo expects a one-based line) + return { oneBasedLine: 1, zeroBasedColumn: 0, lineText: undefined }; + } + const leaf = Debug.checkDefined(this.lineNumberToInfo(lineCount, 0).leaf); + return { oneBasedLine: lineCount, zeroBasedColumn: leaf.charCount(), lineText: undefined }; } /** diff --git a/src/testRunner/unittests/tsserver/versionCache.ts b/src/testRunner/unittests/tsserver/versionCache.ts index 60eaa6ca42e7b..d779ba1fdd243 100644 --- a/src/testRunner/unittests/tsserver/versionCache.ts +++ b/src/testRunner/unittests/tsserver/versionCache.ts @@ -52,6 +52,18 @@ var q:Point=p;`; assert.deepEqual(lineIndex.positionToLineOffset(0), { line: 1, offset: 1 }); }); + it("handles emptying whole file (GH#44518)", () => { + // See below for the main thing that this tests; it would be better to have a test + // that uses `ScriptInfo.positionToLineOffset` but I couldn't find away to do that + const { lines } = server.LineIndex.linesFromText("function foo() {\n\ndsa\n\n}\n\nfo(dsa\n\n\n "); + const lineIndex = new server.LineIndex(); + lineIndex.load(lines); + const snapshot = lineIndex.edit(0, 39); + assert.equal(snapshot.getText(0, snapshot.getLength()), ""); + // line must always be >=1, otherwise the failIfInvalidLocation(location) assertion in ScriptInfo.positionToLineOffset will fail + assert.deepEqual(snapshot.positionToLineOffset(0), { line: 1, offset: 1 }); + }); + it(`change 9 1 0 1 {"y"}`, () => { validateEditAtLineCharIndex(9, 1, 0, "y"); });