Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/linkify/vscode-node/inlineCodeSymbolLinkifier.ts
13399 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import { collapseRangeToStart } from '../../../util/common/range';
8
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
9
import { CancellationError } from '../../../util/vs/base/common/errors';
10
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
11
import { SymbolInformation } from '../../../vscodeTypes';
12
import { LinkifiedPart, LinkifiedText, LinkifySymbolAnchor } from '../common/linkifiedText';
13
import { IContributedLinkifier, LinkifierContext } from '../common/linkifyService';
14
import { resolveSymbolFromReferences } from './commands';
15
import { ReferencesSymbolResolver } from './findWord';
16
17
export const inlineCodeRegexp = /(?<!\[)`([^`\n]+)`(?!\])/g;
18
19
const maxPotentialWordMatches = 8;
20
21
/**
22
* Linkifies symbol names that appear as inline code.
23
*/
24
export class InlineCodeSymbolLinkifier implements IContributedLinkifier {
25
private readonly resolver: ReferencesSymbolResolver;
26
27
constructor(
28
@IInstantiationService instantiationService: IInstantiationService,
29
) {
30
this.resolver = instantiationService.createInstance(ReferencesSymbolResolver, { symbolMatchesOnly: true, maxResultCount: maxPotentialWordMatches });
31
}
32
33
async linkify(text: string, context: LinkifierContext, token: CancellationToken): Promise<LinkifiedText | undefined> {
34
if (!context.references.length || vscode.version.startsWith('1.94')) {
35
return;
36
}
37
38
// Collect all inline code matches first
39
const matches = [...text.matchAll(inlineCodeRegexp)];
40
if (!matches.length) {
41
return;
42
}
43
44
// Resolve unique symbol texts in parallel, then map results back to each match
45
const uniqueSymbols = [...new Set(matches.map(m => m[1]))];
46
const resolvedMap = new Map<string, readonly vscode.Location[] | undefined>();
47
const results = await Promise.all(
48
uniqueSymbols.map(sym => this.tryResolveSymbol(sym, context, token))
49
);
50
for (let i = 0; i < uniqueSymbols.length; i++) {
51
resolvedMap.set(uniqueSymbols[i], results[i]);
52
}
53
54
if (token.isCancellationRequested) {
55
throw new CancellationError();
56
}
57
58
// Build output using resolution results
59
const out: LinkifiedPart[] = [];
60
let endLastMatch = 0;
61
for (let i = 0; i < matches.length; i++) {
62
const match = matches[i];
63
const prefix = text.slice(endLastMatch, match.index);
64
if (prefix) {
65
out.push(prefix);
66
}
67
68
const symbolText = match[1];
69
const loc = resolvedMap.get(symbolText);
70
71
if (loc?.length) {
72
const info: SymbolInformation = {
73
name: symbolText,
74
containerName: '',
75
kind: vscode.SymbolKind.Variable,
76
location: loc[0]
77
};
78
79
out.push(new LinkifySymbolAnchor(info, async (token) => {
80
const dest = await resolveSymbolFromReferences(loc.map(l => ({ uri: l.uri, pos: l.range.start })), symbolText, token);
81
if (dest) {
82
const selectionRange = dest.loc.targetSelectionRange ?? dest.loc.targetRange;
83
info.location = new vscode.Location(dest.loc.targetUri, collapseRangeToStart(selectionRange));
84
85
// TODO: Figure out how to get the actual symbol kind here and update it
86
}
87
88
return info;
89
}));
90
} else {
91
out.push(match[0]);
92
}
93
94
endLastMatch = match.index + match[0].length;
95
}
96
97
const suffix = text.slice(endLastMatch);
98
if (suffix) {
99
out.push(suffix);
100
}
101
102
return { parts: out };
103
}
104
105
private async tryResolveSymbol(symbolText: string, context: LinkifierContext, token: CancellationToken): Promise<vscode.Location[] | undefined> {
106
if (/^https?:\/\//i.test(symbolText)) {
107
return;
108
}
109
110
return this.resolver.resolve(symbolText, context.references, token);
111
}
112
}
113
114