Remove reliance on document.lineAt (#154191)

* Remove reliance on document.lineAt

This helps aligning more with the LSP types: https://github.com/microsoft/vscode-languageserver-node/issues/146

* Strip newline
This commit is contained in:
Matt Bierner 2022-07-05 11:52:47 -07:00 committed by GitHub
parent 510a74fc2c
commit fc0bd9d377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 40 additions and 47 deletions

View file

@ -9,7 +9,7 @@ import * as uri from 'vscode-uri';
import { OpenDocumentLinkCommand } from '../commands/openDocumentLink';
import { ILogger } from '../logging';
import { IMdParser } from '../markdownEngine';
import { ITextDocument } from '../types/textDocument';
import { getLine, ITextDocument } from '../types/textDocument';
import { coalesce } from '../util/arrays';
import { noopToken } from '../util/cancellation';
import { Disposable } from '../util/dispose';
@ -422,9 +422,9 @@ export class MdLinkComputer {
reference = match[5];
const offset = ((match.index ?? 0) + match[1].length) + 1;
hrefStart = document.positionAt(offset);
const line = document.lineAt(hrefStart.line);
const line = getLine(document, hrefStart.line);
// See if link looks like a checkbox
const checkboxMatch = line.text.match(/^\s*[\-\*]\s*\[x\]/i);
const checkboxMatch = line.match(/^\s*[\-\*]\s*\[x\]/i);
if (checkboxMatch && hrefStart.character <= checkboxMatch[0].length) {
continue;
}

View file

@ -7,7 +7,8 @@ import type Token = require('markdown-it/lib/token');
import * as vscode from 'vscode';
import { IMdParser } from '../markdownEngine';
import { MdTableOfContentsProvider } from '../tableOfContents';
import { ITextDocument } from '../types/textDocument';
import { getLine, ITextDocument } from '../types/textDocument';
import { isEmptyOrWhitespace } from '../util/string';
const rangeLimit = 5000;
@ -59,7 +60,7 @@ export class MdFoldingProvider implements vscode.FoldingRangeProvider {
const toc = await this.tocProvide.getForDocument(document);
return toc.entries.map(entry => {
let endLine = entry.sectionLocation.range.end.line;
if (document.lineAt(endLine).isEmptyOrWhitespace && endLine >= entry.line + 1) {
if (isEmptyOrWhitespace(getLine(document, endLine)) && endLine >= entry.line + 1) {
endLine = endLine - 1;
}
return new vscode.FoldingRange(entry.line, endLine);
@ -72,7 +73,7 @@ export class MdFoldingProvider implements vscode.FoldingRangeProvider {
return multiLineListItems.map(listItem => {
const start = listItem.map[0];
let end = listItem.map[1] - 1;
if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) {
if (isEmptyOrWhitespace(getLine(document, end)) && end >= start + 1) {
end = end - 1;
}
return new vscode.FoldingRange(start, end, this.getFoldingRangeKind(listItem));

View file

@ -7,7 +7,7 @@ import { dirname, resolve } from 'path';
import * as vscode from 'vscode';
import { IMdParser } from '../markdownEngine';
import { TableOfContents } from '../tableOfContents';
import { ITextDocument } from '../types/textDocument';
import { getLine, ITextDocument } from '../types/textDocument';
import { resolveUriToMarkdownFile } from '../util/openDocumentLink';
import { Schemes } from '../util/schemes';
import { IMdWorkspace } from '../workspace';
@ -167,7 +167,7 @@ export class MdVsCodePathCompletionProvider implements vscode.CompletionItemProv
private readonly definitionPattern = /^\s*\[[\w\-]+\]:\s*([^\s]*)$/m;
private getPathCompletionContext(document: ITextDocument, position: vscode.Position): CompletionContext | undefined {
const line = document.lineAt(position.line).text;
const line = getLine(document, position.line);
const linePrefixText = line.slice(0, position.character);
const lineSuffixText = line.slice(position.character);

View file

@ -6,7 +6,8 @@ import Token = require('markdown-it/lib/token');
import * as vscode from 'vscode';
import { IMdParser } from '../markdownEngine';
import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents';
import { ITextDocument } from '../types/textDocument';
import { getLine, ITextDocument } from '../types/textDocument';
import { isEmptyOrWhitespace } from '../util/string';
interface MarkdownItTokenWithMap extends Token {
map: [number, number];
@ -111,14 +112,14 @@ function createBlockRange(block: MarkdownItTokenWithMap, document: ITextDocument
if (block.type === 'fence') {
return createFencedRange(block, cursorLine, document, parent);
} else {
let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0];
let startLine = isEmptyOrWhitespace(getLine(document, block.map[0])) ? block.map[0] + 1 : block.map[0];
let endLine = startLine === block.map[1] ? block.map[1] : block.map[1] - 1;
if (block.type === 'paragraph_open' && block.map[1] - block.map[0] === 2) {
startLine = endLine = cursorLine;
} else if (isList(block) && document.lineAt(endLine).isEmptyOrWhitespace) {
} else if (isList(block) && isEmptyOrWhitespace(getLine(document, endLine))) {
endLine = endLine - 1;
}
const range = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text?.length ?? 0);
const range = new vscode.Range(startLine, 0, endLine, getLine(document, endLine).length);
if (parent?.range.contains(range) && !parent.range.isEqual(range)) {
return new vscode.SelectionRange(range, parent);
} else if (parent?.range.isEqual(range)) {
@ -130,7 +131,7 @@ function createBlockRange(block: MarkdownItTokenWithMap, document: ITextDocument
}
function createInlineRange(document: ITextDocument, cursorPosition: vscode.Position, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined {
const lineText = document.lineAt(cursorPosition.line).text;
const lineText = getLine(document, cursorPosition.line);
const boldSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, parent);
const italicSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, parent);
let comboSelection: vscode.SelectionRange | undefined;
@ -150,8 +151,8 @@ function createFencedRange(token: MarkdownItTokenWithMap, cursorLine: number, do
const startLine = token.map[0];
const endLine = token.map[1] - 1;
const onFenceLine = cursorLine === startLine || cursorLine === endLine;
const fenceRange = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text.length);
const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(startLine + 1, 0, endLine - 1, document.lineAt(endLine - 1).text.length) : undefined;
const fenceRange = new vscode.Range(startLine, 0, endLine, getLine(document, endLine).length);
const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(startLine + 1, 0, endLine - 1, getLine(document, endLine - 1).length) : undefined;
if (contentRange) {
return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent));
} else {
@ -242,7 +243,7 @@ function getFirstChildHeader(document: ITextDocument, header?: TocEntry, toc?: r
const children = toc.filter(t => header.sectionLocation.range.contains(t.sectionLocation.range) && t.sectionLocation.range.start.line > header.sectionLocation.range.start.line).sort((t1, t2) => t1.line - t2.line);
if (children.length > 0) {
childRange = children[0].sectionLocation.range.start;
const lineText = document.lineAt(childRange.line - 1).text;
const lineText = getLine(document, childRange.line - 1);
return childRange ? childRange.translate(-1, lineText.length) : undefined;
}
}

View file

@ -7,7 +7,7 @@ import * as vscode from 'vscode';
import { ILogger } from './logging';
import { IMdParser } from './markdownEngine';
import { githubSlugifier, Slug, Slugifier } from './slugify';
import { ITextDocument } from './types/textDocument';
import { getLine, ITextDocument } from './types/textDocument';
import { Disposable } from './util/dispose';
import { isMarkdownFile } from './util/file';
import { Schemes } from './util/schemes';
@ -108,9 +108,9 @@ export class TableOfContents {
}
const lineNumber = heading.map[0];
const line = document.lineAt(lineNumber);
const line = getLine(document, lineNumber);
let slug = parser.slugifier.fromHeading(line.text);
let slug = parser.slugifier.fromHeading(line);
const existingSlugEntry = existingSlugEntries.get(slug.value);
if (existingSlugEntry) {
++existingSlugEntry.count;
@ -120,14 +120,14 @@ export class TableOfContents {
}
const headerLocation = new vscode.Location(document.uri,
new vscode.Range(lineNumber, 0, lineNumber, line.text.length));
new vscode.Range(lineNumber, 0, lineNumber, line.length));
const headerTextLocation = new vscode.Location(document.uri,
new vscode.Range(lineNumber, line.text.match(/^#+\s*/)?.[0].length ?? 0, lineNumber, line.text.length - (line.text.match(/\s*#*$/)?.[0].length ?? 0)));
new vscode.Range(lineNumber, line.match(/^#+\s*/)?.[0].length ?? 0, lineNumber, line.length - (line.match(/\s*#*$/)?.[0].length ?? 0)));
toc.push({
slug,
text: TableOfContents.getHeaderText(line.text),
text: TableOfContents.getHeaderText(line),
level: TableOfContents.getHeaderLevel(heading.markup),
line: lineNumber,
sectionLocation: headerLocation, // Populated in next steps
@ -151,7 +151,7 @@ export class TableOfContents {
sectionLocation: new vscode.Location(document.uri,
new vscode.Range(
entry.sectionLocation.range.start,
new vscode.Position(endLine, document.lineAt(endLine).text.length)))
new vscode.Position(endLine, getLine(document, endLine).length)))
};
});
}

View file

@ -15,7 +15,7 @@ import { nulLogger } from './nulLogging';
import { assertRangeEqual, joinLines, workspacePath } from './util';
suite.only('Markdown: MdLinkComputer', () => {
suite('Markdown: MdLinkComputer', () => {
function getLinksForFile(fileContents: string): Promise<MdLink[]> {
const doc = new InMemoryDocument(workspacePath('x.md'), fileContents);

View file

@ -3,15 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as vscode from 'vscode';
/**
* Minimal version of {@link vscode.TextLine}.
*/
export interface ITextLine {
readonly text: string;
readonly isEmptyOrWhitespace: boolean;
}
import * as vscode from 'vscode';
/**
* Minimal version of {@link vscode.TextDocument}.
@ -22,6 +14,9 @@ export interface ITextDocument {
readonly lineCount: number;
getText(range?: vscode.Range): string;
lineAt(line: number): ITextLine;
positionAt(offset: number): vscode.Position;
}
export function getLine(doc: ITextDocument, line: number): string {
return doc.getText(new vscode.Range(line, 0, line, Number.MAX_VALUE)).replace(/\r?\n$/, '');
}

View file

@ -5,14 +5,12 @@
import * as vscode from 'vscode';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { ITextDocument, ITextLine } from '../types/textDocument';
import { ITextDocument } from '../types/textDocument';
export class InMemoryDocument implements ITextDocument {
private readonly _doc: TextDocument;
private lines: ITextLine[] | undefined;
constructor(
public readonly uri: vscode.Uri, contents: string,
public readonly version = 0,
@ -25,16 +23,6 @@ export class InMemoryDocument implements ITextDocument {
return this._doc.lineCount;
}
lineAt(index: any): ITextLine {
if (!this.lines) {
this.lines = this._doc.getText().split(/\r?\n/).map(text => ({
text,
get isEmptyOrWhitespace() { return /^\s*$/.test(text); }
}));
}
return this.lines[index];
}
positionAt(offset: number): vscode.Position {
const pos = this._doc.positionAt(offset);
return new vscode.Position(pos.line, pos.character);

View file

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function isEmptyOrWhitespace(str: string): boolean {
return /^\s*$/.test(str);
}