mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 04:09:28 +00:00
Enable find all references and rename of auto links and http(s) links in markdown
For #146777, #146277
This commit is contained in:
@@ -170,6 +170,11 @@ const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*
|
||||
*/
|
||||
const referenceLinkPattern = /(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?![\:\(])/g;
|
||||
|
||||
/**
|
||||
* Matches `<http://example.com>`
|
||||
*/
|
||||
const autoLinkPattern = /\<(\w+:[^\>\s]+)\>/g;
|
||||
|
||||
/**
|
||||
* Matches `[text]: link`
|
||||
*/
|
||||
@@ -256,6 +261,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
...(await this.getInlineLinks(document)),
|
||||
...this.getReferenceLinks(document),
|
||||
...this.getLinkDefinitions(document),
|
||||
...this.getAutoLinks(document),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -277,6 +283,30 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
|
||||
return results;
|
||||
}
|
||||
|
||||
private *getAutoLinks(document: SkinnyTextDocument): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
|
||||
for (const match of text.matchAll(autoLinkPattern)) {
|
||||
const link = match[1];
|
||||
const linkTarget = parseLink(document, link);
|
||||
if (linkTarget) {
|
||||
const offset = (match.index ?? 0) + 1;
|
||||
const linkStart = document.positionAt(offset);
|
||||
const linkEnd = document.positionAt(offset + link.length);
|
||||
yield {
|
||||
kind: 'link',
|
||||
href: linkTarget,
|
||||
source: {
|
||||
text: link,
|
||||
resource: document.uri,
|
||||
hrefRange: new vscode.Range(linkStart, linkEnd),
|
||||
fragmentRange: getFragmentRange(link, linkStart, linkEnd),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private *getReferenceLinks(document: SkinnyTextDocument): Iterable<MdLink> {
|
||||
const text = document.getText();
|
||||
for (const match of text.matchAll(referenceLinkPattern)) {
|
||||
|
||||
@@ -158,8 +158,22 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
return Array.from(this.getReferencesToLinkReference(allLinksInWorkspace, sourceLink.href.ref, { resource: sourceLink.source.resource, range: sourceLink.source.hrefRange }));
|
||||
}
|
||||
|
||||
if (sourceLink.href.kind !== 'internal') {
|
||||
return [];
|
||||
if (sourceLink.href.kind === 'external') {
|
||||
const references: MdReference[] = [];
|
||||
|
||||
for (const link of allLinksInWorkspace) {
|
||||
if (link.href.kind === 'external' && link.href.uri.scheme === sourceLink.href.uri.scheme && link.href.uri.path === sourceLink.href.uri.path) {
|
||||
const isTriggerLocation = sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange);
|
||||
references.push({
|
||||
kind: 'link',
|
||||
isTriggerLocation,
|
||||
isDefinition: false,
|
||||
link,
|
||||
location: new vscode.Location(link.source.resource, link.source.hrefRange),
|
||||
});
|
||||
}
|
||||
}
|
||||
return references;
|
||||
}
|
||||
|
||||
let targetDoc = await this.workspaceContents.getMarkdownDocument(sourceLink.href.path);
|
||||
@@ -190,9 +204,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
headerTextLocation: entry.headerTextLocation
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceLink.href.fragment) {
|
||||
for (const link of allLinksInWorkspace) {
|
||||
if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, targetDoc.uri)) {
|
||||
continue;
|
||||
@@ -226,7 +238,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined));
|
||||
}
|
||||
|
||||
private *findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
|
||||
private * findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable<MdReference> {
|
||||
for (const link of allLinksInWorkspace) {
|
||||
if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) {
|
||||
continue;
|
||||
@@ -248,7 +260,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference
|
||||
}
|
||||
}
|
||||
|
||||
private *getReferencesToLinkReference(allLinks: Iterable<MdLink>, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable<MdReference> {
|
||||
private * getReferencesToLinkReference(allLinks: Iterable<MdLink>, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable<MdReference> {
|
||||
for (const link of allLinks) {
|
||||
let ref: string;
|
||||
if (link.kind === 'definition') {
|
||||
|
||||
@@ -52,6 +52,10 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerRef.link.href.kind === 'external') {
|
||||
return { range: triggerRef.link.source.hrefRange, placeholder: document.getText(triggerRef.link.source.hrefRange) };
|
||||
}
|
||||
|
||||
const { fragmentRange } = triggerRef.link.source;
|
||||
if (fragmentRange) {
|
||||
const declaration = this.findHeaderDeclaration(allRefsInfo.references);
|
||||
@@ -98,7 +102,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide
|
||||
continue;
|
||||
}
|
||||
}
|
||||
edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, isRefRename && !ref.link.source.fragmentRange ? newName : slug);
|
||||
edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, isRefRename && !ref.link.source.fragmentRange || ref.link.href.kind === 'external' ? newName : slug);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,4 +239,12 @@ suite('markdown.DocumentLinkProvider', () => {
|
||||
const links = await getLinksForFile(text);
|
||||
assert.strictEqual(links.length, 1);
|
||||
});
|
||||
|
||||
test('Should find autolinks', async () => {
|
||||
const links = await getLinksForFile('pre <http://example.com> post');
|
||||
assert.strictEqual(links.length, 1);
|
||||
|
||||
const link = links[0];
|
||||
assertRangeEqual(link.range, new vscode.Range(0, 5, 0, 23));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -344,6 +344,75 @@ suite('markdown: find all references', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Should support finding references to http uri', async () => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`[no](https://example.com)`,
|
||||
`[2](http://example.com)`,
|
||||
`[3]: http://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc]));
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 2 },
|
||||
{ uri, line: 3 },
|
||||
);
|
||||
});
|
||||
|
||||
test('Should consider paths when finding references to http uri', async () => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com/cat)`,
|
||||
`[2](http://example.com)`,
|
||||
`[3](http://example.com/dog)`,
|
||||
`[4](http://example.com/cat/looong)`,
|
||||
`[5](http://example.com/cat)`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc]));
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 4 },
|
||||
);
|
||||
});
|
||||
|
||||
test('Should support finding references to http uri across files', async () => {
|
||||
const uri1 = workspacePath('doc.md');
|
||||
const uri2 = workspacePath('doc2.md');
|
||||
const doc = new InMemoryDocument(uri1, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`[3]: http://example.com`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([
|
||||
doc,
|
||||
new InMemoryDocument(uri2, joinLines(
|
||||
`[other](http://example.com)`,
|
||||
))
|
||||
]));
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri: uri1, line: 0 },
|
||||
{ uri: uri1, line: 1 },
|
||||
{ uri: uri2, line: 0 },
|
||||
);
|
||||
});
|
||||
|
||||
test('Should support finding references to autolinked http links', async () => {
|
||||
const uri = workspacePath('doc.md');
|
||||
const doc = new InMemoryDocument(uri, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`<http://example.com>`,
|
||||
));
|
||||
|
||||
const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc]));
|
||||
assertReferencesEqual(refs!,
|
||||
{ uri, line: 0 },
|
||||
{ uri, line: 1 },
|
||||
);
|
||||
});
|
||||
|
||||
suite('Reference links', () => {
|
||||
test('Should find reference links within file from link', async () => {
|
||||
const docUri = workspacePath('doc.md');
|
||||
|
||||
@@ -356,4 +356,32 @@ suite('markdown: rename', () => {
|
||||
assert.strictEqual(info!.placeholder, 'a B c');
|
||||
assertRangeEqual(info!.range, new vscode.Range(1, 8, 1, 13));
|
||||
});
|
||||
|
||||
test('Rename on http uri should work ', async () => {
|
||||
const uri1 = workspacePath('doc.md');
|
||||
const uri2 = workspacePath('doc2.md');
|
||||
const doc = new InMemoryDocument(uri1, joinLines(
|
||||
`[1](http://example.com)`,
|
||||
`[2]: http://example.com`,
|
||||
`<http://example.com>`,
|
||||
));
|
||||
|
||||
const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "https://example.com/sub", new InMemoryWorkspaceMarkdownDocuments([
|
||||
doc,
|
||||
new InMemoryDocument(uri2, joinLines(
|
||||
`[4](http://example.com)`,
|
||||
))
|
||||
]));
|
||||
assertEditsEqual(edit!, {
|
||||
uri: uri1, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'),
|
||||
new vscode.TextEdit(new vscode.Range(1, 5, 1, 23), 'https://example.com/sub'),
|
||||
new vscode.TextEdit(new vscode.Range(2, 1, 2, 19), 'https://example.com/sub'),
|
||||
]
|
||||
}, {
|
||||
uri: uri2, edits: [
|
||||
new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'),
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user