Path: blob/main/extensions/copilot/src/extension/linkify/vscode-node/inlineCodeSymbolLinkifier.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { collapseRangeToStart } from '../../../util/common/range';7import { CancellationToken } from '../../../util/vs/base/common/cancellation';8import { CancellationError } from '../../../util/vs/base/common/errors';9import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';10import { SymbolInformation } from '../../../vscodeTypes';11import { LinkifiedPart, LinkifiedText, LinkifySymbolAnchor } from '../common/linkifiedText';12import { IContributedLinkifier, LinkifierContext } from '../common/linkifyService';13import { resolveSymbolFromReferences } from './commands';14import { ReferencesSymbolResolver } from './findWord';1516export const inlineCodeRegexp = /(?<!\[)`([^`\n]+)`(?!\])/g;1718const maxPotentialWordMatches = 8;1920/**21* Linkifies symbol names that appear as inline code.22*/23export class InlineCodeSymbolLinkifier implements IContributedLinkifier {24private readonly resolver: ReferencesSymbolResolver;2526constructor(27@IInstantiationService instantiationService: IInstantiationService,28) {29this.resolver = instantiationService.createInstance(ReferencesSymbolResolver, { symbolMatchesOnly: true, maxResultCount: maxPotentialWordMatches });30}3132async linkify(text: string, context: LinkifierContext, token: CancellationToken): Promise<LinkifiedText | undefined> {33if (!context.references.length || vscode.version.startsWith('1.94')) {34return;35}3637// Collect all inline code matches first38const matches = [...text.matchAll(inlineCodeRegexp)];39if (!matches.length) {40return;41}4243// Resolve unique symbol texts in parallel, then map results back to each match44const uniqueSymbols = [...new Set(matches.map(m => m[1]))];45const resolvedMap = new Map<string, readonly vscode.Location[] | undefined>();46const results = await Promise.all(47uniqueSymbols.map(sym => this.tryResolveSymbol(sym, context, token))48);49for (let i = 0; i < uniqueSymbols.length; i++) {50resolvedMap.set(uniqueSymbols[i], results[i]);51}5253if (token.isCancellationRequested) {54throw new CancellationError();55}5657// Build output using resolution results58const out: LinkifiedPart[] = [];59let endLastMatch = 0;60for (let i = 0; i < matches.length; i++) {61const match = matches[i];62const prefix = text.slice(endLastMatch, match.index);63if (prefix) {64out.push(prefix);65}6667const symbolText = match[1];68const loc = resolvedMap.get(symbolText);6970if (loc?.length) {71const info: SymbolInformation = {72name: symbolText,73containerName: '',74kind: vscode.SymbolKind.Variable,75location: loc[0]76};7778out.push(new LinkifySymbolAnchor(info, async (token) => {79const dest = await resolveSymbolFromReferences(loc.map(l => ({ uri: l.uri, pos: l.range.start })), symbolText, token);80if (dest) {81const selectionRange = dest.loc.targetSelectionRange ?? dest.loc.targetRange;82info.location = new vscode.Location(dest.loc.targetUri, collapseRangeToStart(selectionRange));8384// TODO: Figure out how to get the actual symbol kind here and update it85}8687return info;88}));89} else {90out.push(match[0]);91}9293endLastMatch = match.index + match[0].length;94}9596const suffix = text.slice(endLastMatch);97if (suffix) {98out.push(suffix);99}100101return { parts: out };102}103104private async tryResolveSymbol(symbolText: string, context: LinkifierContext, token: CancellationToken): Promise<vscode.Location[] | undefined> {105if (/^https?:\/\//i.test(symbolText)) {106return;107}108109return this.resolver.resolve(symbolText, context.references, token);110}111}112113114