Compare normalized fragments for md references

For #146277
This commit is contained in:
Matt Bierner 2022-03-30 14:51:27 -07:00
parent 767178d1b9
commit f68db8f6ba
No known key found for this signature in database
GPG key ID: 099C331567E11888
3 changed files with 41 additions and 8 deletions

View file

@ -67,7 +67,7 @@ function registerMarkdownLanguageFeatures(
vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)),
vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)),
vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)),
vscode.languages.registerReferenceProvider(selector, new MdReferencesProvider(linkProvider, workspaceContents, engine)),
vscode.languages.registerReferenceProvider(selector, new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier)),
MdPathCompletionProvider.register(selector, engine, linkProvider),
);
}

View file

@ -5,6 +5,7 @@
import * as vscode from 'vscode';
import * as uri from 'vscode-uri';
import { MarkdownEngine } from '../markdownEngine';
import { Slugifier } from '../slugify';
import { TableOfContents, TocEntry } from '../tableOfContents';
import { Disposable } from '../util/dispose';
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
@ -12,10 +13,10 @@ import { InternalLinkTarget, LinkData, LinkTarget, MdLinkProvider } from './docu
import { MdWorkspaceCache } from './workspaceCache';
function isLinkToHeader(target: LinkTarget, header: TocEntry, headerDocument: vscode.Uri): target is InternalLinkTarget {
function isLinkToHeader(target: LinkTarget, header: TocEntry, headerDocument: vscode.Uri, slugifier: Slugifier): target is InternalLinkTarget {
return target.kind === 'internal'
&& target.path.fsPath === headerDocument.fsPath
&& target.fragment === header.slug.value;
&& slugifier.fromHeading(target.fragment).value === header.slug.value;
}
export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider {
@ -26,6 +27,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
private readonly linkProvider: MdLinkProvider,
private readonly workspaceContents: MdWorkspaceContents,
private readonly engine: MarkdownEngine,
private readonly slugifier: Slugifier,
) {
super();
@ -57,9 +59,9 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
for (const link of links) {
if (isLinkToHeader(link.target, header, document.uri)) {
if (isLinkToHeader(link.target, header, document.uri, this.slugifier)) {
references.push(new vscode.Location(link.target.fromResource, link.sourceRange));
} else if (link.target.kind === 'definition' && isLinkToHeader(link.target.target, header, document.uri)) {
} else if (link.target.kind === 'definition' && isLinkToHeader(link.target.target, header, document.uri, this.slugifier)) {
references.push(new vscode.Location(link.target.target.fromResource, link.sourceRange));
}
}
@ -129,7 +131,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
}
if (sourceLink.target.fragment) {
if (link.target.fragment === sourceLink.target.fragment) {
if (this.slugifier.fromHeading(link.target.fragment).equals(this.slugifier.fromHeading(sourceLink.target.fragment))) {
references.push(new vscode.Location(link.target.fromResource, link.sourceRange));
}
} else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments

View file

@ -8,9 +8,10 @@ import 'mocha';
import * as vscode from 'vscode';
import { MdLinkProvider } from '../languageFeatures/documentLinkProvider';
import { MdReferencesProvider } from '../languageFeatures/references';
import { githubSlugifier } from '../slugify';
import { InMemoryDocument } from '../util/inMemoryDocument';
import { MdWorkspaceContents } from '../workspaceContents';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from '../util/inMemoryDocument';
import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace';
import { joinLines, noopToken, workspaceFile } from './util';
@ -18,7 +19,7 @@ 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);
const provider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier);
return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken);
}
@ -67,6 +68,36 @@ suite('markdown: find all references', () => {
}
});
test('Should find references using normalized slug', async () => {
const doc = new InMemoryDocument(workspaceFile('doc.md'), joinLines(
`# a B c`,
`[simple](#a-b-c)`,
`[start underscore](#_a-b-c)`,
`[different case](#a-B-C)`,
));
{
// Trigger header
const refs = await getReferences(doc, new vscode.Position(0, 0), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs!.length, 4);
}
{
// Trigger on line 1
const refs = await getReferences(doc, new vscode.Position(1, 12), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs!.length, 4);
}
{
// Trigger on line 2
const refs = await getReferences(doc, new vscode.Position(2, 24), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs!.length, 4);
}
{
// Trigger on line 3
const refs = await getReferences(doc, new vscode.Position(3, 20), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.deepStrictEqual(refs!.length, 4);
}
});
test('Should find references from header across files', async () => {
const docUri = workspaceFile('doc.md');
const other1Uri = workspaceFile('sub', 'other.md');