Clean up link provider

This commit is contained in:
Matt Bierner
2022-03-29 14:36:41 -07:00
parent 93aad560e1
commit 2783263582
4 changed files with 38 additions and 26 deletions
@@ -56,13 +56,15 @@ function registerMarkdownLanguageFeatures(
): vscode.Disposable {
const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' };
const linkProvider = new MdLinkProvider(engine);
return vscode.Disposable.from(
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
vscode.languages.registerDocumentLinkProvider(selector, new MdLinkProvider(engine)),
vscode.languages.registerDocumentLinkProvider(selector, linkProvider),
vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)),
vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)),
vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider)),
MdPathCompletionProvider.register(selector, engine),
MdPathCompletionProvider.register(selector, engine, linkProvider),
);
}
@@ -99,13 +99,25 @@ const angleBracketLinkRe = /^<(.*)>$/;
*
* <http://example.com> will be transformed to http://example.com
*/
export function stripAngleBrackets(link: string) {
function stripAngleBrackets(link: string) {
return link.replace(angleBracketLinkRe, '$1');
}
/**
* Matches `[text](link)`
*/
const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*(".*?")?\)/g;
/**
* Matches `[text][ref]`
*/
const referenceLinkPattern = /(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?!\:)/g;
/**
* Matches `[text]: link`
*/
const definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)([^<]\S*|<[^>]+>)/gm;
const inlineCodePattern = /(?:^|[^`])(`+)(?:.+?|.*?(?:(?:\r?\n).+?)*?)(?:\r?\n)?\1(?:$|[^`])/gm;
interface CodeInDocument {
@@ -149,15 +161,12 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
): Promise<vscode.DocumentLink[]> {
const text = document.getText();
return [
...(await this.providerInlineLinks(text, document)),
...this.provideReferenceLinks(text, document)
...(await this.getInlineLinks(text, document)),
...this.getReferenceLinks(text, document)
];
}
private async providerInlineLinks(
text: string,
document: vscode.TextDocument,
): Promise<vscode.DocumentLink[]> {
private async getInlineLinks(text: string, document: vscode.TextDocument): Promise<vscode.DocumentLink[]> {
const results: vscode.DocumentLink[] = [];
const codeInDocument = await findCode(document, this.engine);
for (const match of text.matchAll(linkPattern)) {
@@ -173,13 +182,8 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
return results;
}
private provideReferenceLinks(
text: string,
document: vscode.TextDocument,
): vscode.DocumentLink[] {
const results: vscode.DocumentLink[] = [];
const definitions = MdLinkProvider.getDefinitions(text, document);
public *getReferenceLinks(text: string, document: vscode.TextDocument): Iterable<vscode.DocumentLink> {
const definitions = this.getDefinitions(text, document);
for (const match of text.matchAll(referenceLinkPattern)) {
let linkStart: vscode.Position;
let linkEnd: vscode.Position;
@@ -201,9 +205,9 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
try {
const link = definitions.get(reference);
if (link) {
results.push(new vscode.DocumentLink(
yield new vscode.DocumentLink(
new vscode.Range(linkStart, linkEnd),
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([link.linkRange.start.line, link.linkRange.start.character]))}`)));
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([link.linkRange.start.line, link.linkRange.start.character]))}`));
}
} catch (e) {
// noop
@@ -214,17 +218,15 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
try {
const linkData = parseLink(document, definition.link);
if (linkData) {
results.push(new vscode.DocumentLink(definition.linkRange, linkData.uri));
yield new vscode.DocumentLink(definition.linkRange, linkData.uri);
}
} catch (e) {
// noop
}
}
return results;
}
public static getDefinitions(text: string, document: vscode.TextDocument) {
public getDefinitions(text: string, document: vscode.TextDocument): Map<string, { readonly link: string; readonly linkRange: vscode.Range }> {
const out = new Map<string, { link: string; linkRange: vscode.Range }>();
for (const match of text.matchAll(definitionPattern)) {
const pre = match[1];
@@ -77,12 +77,17 @@ function tryDecodeUriComponent(str: string): string {
export class MdPathCompletionProvider implements vscode.CompletionItemProvider {
public static register(selector: vscode.DocumentSelector, engine: MarkdownEngine): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine), '.', '/', '#');
public static register(
selector: vscode.DocumentSelector,
engine: MarkdownEngine,
linkProvider: MdLinkProvider,
): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkProvider), '.', '/', '#');
}
constructor(
private readonly engine: MarkdownEngine,
private readonly linkProvider: MdLinkProvider,
) { }
public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise<vscode.CompletionItem[]> {
@@ -230,7 +235,7 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider {
const insertionRange = new vscode.Range(context.linkTextStartPosition, position);
const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
const definitions = MdLinkProvider.getDefinitions(document.getText(), document);
const definitions = this.linkProvider.getDefinitions(document.getText(), document);
for (const def of definitions) {
yield {
kind: vscode.CompletionItemKind.Reference,
@@ -6,6 +6,7 @@
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { MdLinkProvider } from '../languageFeatures/documentLinkProvider';
import { MdPathCompletionProvider } from '../languageFeatures/pathCompletions';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from './inMemoryDocument';
@@ -18,7 +19,9 @@ function workspaceFile(...segments: string[]): vscode.Uri {
function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string) {
const doc = new InMemoryDocument(resource, fileContents);
const provider = new MdPathCompletionProvider(createNewMarkdownEngine());
const engine = createNewMarkdownEngine();
const linkProvider = new MdLinkProvider(engine);
const provider = new MdPathCompletionProvider(engine, linkProvider);
const cursorPositions = getCursorPositions(fileContents, doc);
return provider.provideCompletionItems(doc, cursorPositions[0], noopToken, {
triggerCharacter: undefined,