Fixing references on header when looking at file extensions

This commit is contained in:
Matt Bierner 2022-03-31 15:31:41 -07:00
parent 8a6a300216
commit 6b573340bd
No known key found for this signature in database
GPG key ID: 099C331567E11888
3 changed files with 100 additions and 21 deletions

View file

@ -9,17 +9,10 @@ import { Slugifier } from '../slugify';
import { TableOfContents, TocEntry } from '../tableOfContents';
import { Disposable } from '../util/dispose';
import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents';
import { InternalHref, LinkHref, MdLink, MdLinkProvider } from './documentLinkProvider';
import { InternalHref, MdLink, MdLinkProvider } from './documentLinkProvider';
import { MdWorkspaceCache } from './workspaceCache';
function isLinkToHeader(target: LinkHref, header: TocEntry, headerDocument: vscode.Uri, slugifier: Slugifier): target is InternalHref {
return target.kind === 'internal'
&& target.path.fsPath === headerDocument.fsPath
&& slugifier.fromHeading(target.fragment).value === header.slug.value;
}
/**
* A link in a markdown file.
*/
@ -121,15 +114,10 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
});
for (const link of links) {
if (isLinkToHeader(link.href, header, document.uri, this.slugifier)) {
references.push({
kind: 'link',
isTriggerLocation: false,
isDefinition: false,
location: new vscode.Location(link.sourceResource, link.sourceRange),
fragmentLocation: getFragmentLocation(link),
});
} else if (link.kind === 'definition' && isLinkToHeader(link.href, header, document.uri, this.slugifier)) {
if (link.href.kind === 'internal'
&& this.looksLikeLinkToDoc(link.href, document)
&& this.slugifier.fromHeading(link.href.fragment).value === header.slug.value
) {
references.push({
kind: 'link',
isTriggerLocation: false,
@ -194,10 +182,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
continue;
}
const matchesFilePart = link.href.path.fsPath === targetDoc.uri.fsPath
|| uri.Utils.extname(link.href.path) === '' && link.href.path.with({ path: link.href.path.path + '.md' }).fsPath === targetDoc.uri.fsPath;
if (!matchesFilePart) {
if (!this.looksLikeLinkToDoc(link.href, targetDoc)) {
continue;
}
@ -231,6 +216,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
return references;
}
private looksLikeLinkToDoc(href: InternalHref, targetDoc: SkinnyTextDocument) {
return href.path.fsPath === targetDoc.uri.fsPath
|| uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.uri.fsPath;
}
private * getReferencesToReferenceLink(allLinks: Iterable<MdLink>, sourceLink: MdLink): Iterable<MdReference> {
if (sourceLink.href.kind !== 'reference') {
return;

View file

@ -215,6 +215,37 @@ suite('markdown: find all references', () => {
);
});
test('Should find references without requiring file extensions', async () => {
const docUri = workspacePath('doc.md');
const other1Uri = workspacePath('other.md');
const doc = new InMemoryDocument(docUri, joinLines(
`# a B c`,
``,
`[link 1](#a-b-c)`,
));
const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryWorkspaceMarkdownDocuments([
doc,
new InMemoryDocument(other1Uri, joinLines(
`[not link](#a-b-c)`,
`[not link](/doc.md#a-b-z)`,
`[with ext](/doc.md#a-b-c)`,
`[without ext](/doc#a-b-c)`,
`[rel with ext](./doc.md#a-b-c)`,
`[rel without ext](./doc#a-b-c)`,
)),
]));
assertReferencesEqual(refs!,
{ uri: docUri, line: 0 }, // Header definition
{ uri: docUri, line: 2 },
{ uri: other1Uri, line: 2 }, // Other with ext
{ uri: other1Uri, line: 3 }, // Other without ext
{ uri: other1Uri, line: 4 }, // Other relative link with ext
{ uri: other1Uri, line: 5 }, // Other relative link without ext
);
});
test('Should find references from link across files when triggered on link without file extension', async () => {
const docUri = workspacePath('doc.md');
const other1Uri = workspacePath('sub', 'other.md');

View file

@ -149,4 +149,62 @@ suite('markdown: rename', () => {
]
});
});
test('Rename on header should pick up links across files', async () => {
const uri = workspacePath('doc.md');
const otherUri = workspacePath('other.md');
const doc = new InMemoryDocument(uri, joinLines(
`### A b C`, // rename here
`[text](#a-b-c)`,
));
const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryWorkspaceMarkdownDocuments([
doc,
new InMemoryDocument(otherUri, joinLines(
`[text](#a-b-c)`, // Should not find this
`[text](./doc.md#a-b-c)`, // But should find this
`[text](./doc#a-b-c)`, // And this
))
]));
assertEditsEqual(edit!, {
uri: uri, edits: [
new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'),
new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'),
]
}, {
uri: otherUri, edits: [
new vscode.TextEdit(new vscode.Range(1, 16, 1, 21), 'new-header'),
new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'),
]
});
});
test('Rename on link should pick up links across files', async () => {
const uri = workspacePath('doc.md');
const otherUri = workspacePath('other.md');
const doc = new InMemoryDocument(uri, joinLines(
`### A b C`,
`[text](#a-b-c)`, // rename here
));
const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "New Header", new InMemoryWorkspaceMarkdownDocuments([
doc,
new InMemoryDocument(otherUri, joinLines(
`[text](#a-b-c)`, // Should not find this
`[text](./doc.md#a-b-c)`, // But should find this
`[text](./doc#a-b-c)`, // And this
))
]));
assertEditsEqual(edit!, {
uri: uri, edits: [
new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'),
new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'),
]
}, {
uri: otherUri, edits: [
new vscode.TextEdit(new vscode.Range(1, 16, 1, 21), 'new-header'),
new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'),
]
});
});
});