Initial work on md references provider

#146277
This commit is contained in:
Matt Bierner
2022-03-29 17:14:39 -07:00
parent 9b6435af76
commit 338ae07ccb
10 changed files with 357 additions and 131 deletions

View File

@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as vscode from 'vscode';
import { MdWorkspaceContents } from '../workspaceContents';
export class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents {
private readonly _documents = new Map<string, vscode.TextDocument>();
constructor(documents: vscode.TextDocument[]) {
for (const doc of documents) {
this._documents.set(doc.fileName, doc);
}
}
async getAllMarkdownDocuments() {
return Array.from(this._documents.values());
}
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;
private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocumentEmitter.event;
private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.Uri>();
public onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocumentEmitter.event;
public updateDocument(document: vscode.TextDocument) {
this._documents.set(document.fileName, document);
this._onDidChangeMarkdownDocumentEmitter.fire(document);
}
public createDocument(document: vscode.TextDocument) {
assert.ok(!this._documents.has(document.uri.fsPath));
this._documents.set(document.uri.fsPath, document);
this._onDidCreateMarkdownDocumentEmitter.fire(document);
}
public deleteDocument(resource: vscode.Uri) {
this._documents.delete(resource.fsPath);
this._onDidDeleteMarkdownDocumentEmitter.fire(resource);
}
}

View File

@@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import { MdLinkProvider } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { MdWorkspaceContents } from '../workspaceContents';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from './inMemoryDocument';
import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace';
import { joinLines, noopToken, workspaceFile } from './util';
function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) {
const engine = createNewMarkdownEngine();
const linkProvider = new MdLinkProvider(engine);
const provider = new MdReferencesProvider(linkProvider, workspaceContents, engine);
return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
}
suite.only('markdown header references', () => {
test('Should not return references when not on header', async () => {
const doc = new InMemoryDocument(workspaceFile('doc.md'), joinLines(
`# abc`,
``,
`[link 1](#abc)`,
`text`,
));
{
const refs = await getReferences(doc, new vscode.Position(1, 0), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs, undefined);
}
{
const refs = await getReferences(doc, new vscode.Position(3, 2), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs, undefined);
}
});
test('Should find simple references within same file', async () => {
const doc = new InMemoryDocument(workspaceFile('doc.md'), joinLines(
`# abc`,
``,
`[link 1](#abc)`,
`[not link](#noabc)`,
`[link 2](#abc)`,
));
const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs!.length, 2);
{
const ref1 = refs![0];
assert.deepStrictEqual(ref1.range.start.line, 2);
}
{
const ref2 = refs![1];
assert.deepStrictEqual(ref2.range.start.line, 4);
}
});
test('Should find simple references across files', async () => {
const docUri = workspaceFile('doc.md');
const other1Uri = workspaceFile('sub', 'other.md');
const other2Uri = workspaceFile('other2.md');
const doc = new InMemoryDocument(docUri, joinLines(
`# abc`,
``,
`[link 1](#abc)`,
));
const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryWorkspaceMarkdownDocuments([
doc,
new InMemoryDocument(other1Uri, joinLines(
`[not link](#abc)`,
`[not link](/doc.md#abz)`,
`[link](/doc.md#abc)`,
)),
new InMemoryDocument(other2Uri, joinLines(
`[not link](#abc)`,
`[not link](./doc.md#abz)`,
`[link](./doc.md#abc)`,
))
]));
assert.deepStrictEqual(refs!.length, 3);
{
const ref1 = refs![0];
assert.deepStrictEqual(ref1.uri.toString(), docUri.toString());
assert.deepStrictEqual(ref1.range.start.line, 2);
}
{
const ref2 = refs![1];
assert.deepStrictEqual(ref2.uri.toString(), other1Uri.toString());
assert.deepStrictEqual(ref2.range.start.line, 2);
}
{
const ref3 = refs![2];
assert.deepStrictEqual(ref3.uri.toString(), other2Uri.toString());
assert.deepStrictEqual(ref3.range.start.line, 2);
}
});
});

View File

@@ -31,3 +31,7 @@ export function getCursorPositions(contents: string, doc: InMemoryDocument): vsc
}
return positions;
}
export function workspaceFile(...segments: string[]): vscode.Uri {
return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments);
}

View File

@@ -8,9 +8,9 @@ import 'mocha';
import * as vscode from 'vscode';
import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider';
import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbolProvider';
import { MdWorkspaceContents } from '../workspaceContents';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from './inMemoryDocument';
import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace';
const symbolProvider = new MdDocumentSymbolProvider(createNewMarkdownEngine());
@@ -100,44 +100,3 @@ suite('markdown.WorkspaceSymbolProvider', () => {
assert.strictEqual(newSymbols.length, 3);
});
});
class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents {
private readonly _documents = new Map<string, vscode.TextDocument>();
constructor(documents: vscode.TextDocument[]) {
for (const doc of documents) {
this._documents.set(doc.fileName, doc);
}
}
async getAllMarkdownDocuments() {
return Array.from(this._documents.values());
}
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;
private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocumentEmitter.event;
private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.Uri>();
public onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocumentEmitter.event;
public updateDocument(document: vscode.TextDocument) {
this._documents.set(document.fileName, document);
this._onDidChangeMarkdownDocumentEmitter.fire(document);
}
public createDocument(document: vscode.TextDocument) {
assert.ok(!this._documents.has(document.uri.fsPath));
this._documents.set(document.uri.fsPath, document);
this._onDidCreateMarkdownDocumentEmitter.fire(document);
}
public deleteDocument(resource: vscode.Uri) {
this._documents.delete(resource.fsPath);
this._onDidDeleteMarkdownDocumentEmitter.fire(resource);
}
}