Не подтверждена Коммит 8ea83426 создал по автору Matt Bierner's avatar Matt Bierner Зафиксировано автором GitHub
Просмотр файлов

Merge pull request #274935 from mjbvz/dev/mjbvz/grubby-mite

Fix markdown setting scope links
владельцы 1b30f489 d67e575a
......@@ -599,7 +599,9 @@ function getDomSanitizerConfig(mdStrConfig: MdStrConfig, options: MarkdownSaniti
Schemas.vscodeFileResource,
Schemas.vscodeRemote,
Schemas.vscodeRemoteResource,
Schemas.vscodeNotebookCell
Schemas.vscodeNotebookCell,
// For links that are handled entirely by the action handler
Schemas.internal,
];
if (isTrusted) {
......
......@@ -206,9 +206,13 @@ export function parseHrefAndDimensions(href: string): { href: string; dimensions
return { href, dimensions };
}
export function markdownCommandLink(command: { title: string; id: string; arguments?: unknown[]; tooltip?: string }, escapeTokens = true): string {
export function createMarkdownLink(text: string, href: string, title?: string, escapeTokens = true): string {
return `[${escapeTokens ? escapeMarkdownSyntaxTokens(text) : text}](${href}${title ? ` "${escapeMarkdownSyntaxTokens(title)}"` : ''})`;
}
export function createMarkdownCommandLink(command: { title: string; id: string; arguments?: unknown[]; tooltip?: string }, escapeTokens = true): string {
const uri = createCommandUri(command.id, ...(command.arguments || [])).toString();
return `[${escapeTokens ? escapeMarkdownSyntaxTokens(command.title) : command.title}](${uri}${command.tooltip ? ` "${escapeMarkdownSyntaxTokens(command.tooltip)}"` : ''})`;
return createMarkdownLink(command.title, uri, command.tooltip, escapeTokens);
}
export function createCommandUri(commandId: string, ...commandArgs: unknown[]): URI {
......
......@@ -170,10 +170,15 @@ export class OpenerService implements IOpenerService {
}
async open(target: URI | string, options?: OpenOptions): Promise<boolean> {
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
// Internal schemes are not openable and must instead be handled in event listeners
if (targetURI.scheme === Schemas.internal) {
return false;
}
// check with contributed validators
if (!options?.skipValidation) {
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; // validate against the original URI that this URI resolves to, if one exists
for (const validator of this._validators) {
if (!(await validator.shouldOpen(validationTarget, options))) {
......
......@@ -5,7 +5,7 @@
import { assertNever } from '../../../../../base/common/assert.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { Emitter, Event } from '../../../../../base/common/event.js';
import { markdownCommandLink } from '../../../../../base/common/htmlContent.js';
import { createMarkdownCommandLink } from '../../../../../base/common/htmlContent.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import Severity from '../../../../../base/common/severity.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
......@@ -492,7 +492,7 @@ export async function showToolsPicker(
traverse(treePicker.itemTree);
if (count > toolLimit) {
treePicker.severity = Severity.Warning;
treePicker.validationMessage = localize('toolLimitExceeded', "{0} tools are enabled. You may experience degraded tool calling above {1} tools.", count, markdownCommandLink({ title: String(toolLimit), id: '_chat.toolPicker.closeAndOpenVirtualThreshold' }));
treePicker.validationMessage = localize('toolLimitExceeded', "{0} tools are enabled. You may experience degraded tool calling above {1} tools.", count, createMarkdownCommandLink({ title: String(toolLimit), id: '_chat.toolPicker.closeAndOpenVirtualThreshold' }));
} else {
treePicker.severity = Severity.Ignore;
treePicker.validationMessage = undefined;
......
......@@ -7,7 +7,7 @@ import * as dom from '../../../../../base/browser/dom.js';
import { RunOnceScheduler } from '../../../../../base/common/async.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { Emitter } from '../../../../../base/common/event.js';
import { escapeMarkdownSyntaxTokens, markdownCommandLink, MarkdownString } from '../../../../../base/common/htmlContent.js';
import { escapeMarkdownSyntaxTokens, createMarkdownCommandLink, MarkdownString } from '../../../../../base/common/htmlContent.js';
import { Lazy } from '../../../../../base/common/lazy.js';
import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
import { autorun } from '../../../../../base/common/observable.js';
......@@ -109,7 +109,7 @@ export class ChatMcpServersInteractionContentPart extends Disposable implements
}
private createServerCommandLinks(servers: Array<{ id: string; label: string }>): string {
return servers.map(s => markdownCommandLink({
return servers.map(s => createMarkdownCommandLink({
title: '`' + escapeMarkdownSyntaxTokens(s.label) + '`',
id: McpCommandIds.ServerOptions,
arguments: [s.id],
......@@ -117,7 +117,7 @@ export class ChatMcpServersInteractionContentPart extends Disposable implements
}
private updateDetailedProgress(state: IAutostartResult): void {
const skipText = markdownCommandLink({
const skipText = createMarkdownCommandLink({
title: localize('mcp.skip.link', 'Skip?'),
id: McpCommandIds.SkipCurrentAutostart,
});
......
......@@ -6,7 +6,7 @@
import { $, append } from '../../../../../base/browser/dom.js';
import { alert } from '../../../../../base/browser/ui/aria/aria.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { markdownCommandLink, MarkdownString, type IMarkdownString } from '../../../../../base/common/htmlContent.js';
import { createMarkdownCommandLink, MarkdownString, type IMarkdownString } from '../../../../../base/common/htmlContent.js';
import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { IMarkdownRenderer } from '../../../../../platform/markdown/browser/markdownRenderer.js';
......@@ -116,7 +116,7 @@ export class ChatProgressContentPart extends Disposable implements IChatContentP
let md: string;
switch (reason.type) {
case ToolConfirmKind.Setting:
md = localize('chat.autoapprove.setting', 'Auto approved by {0}', markdownCommandLink({ title: '`' + reason.id + '`', id: 'workbench.action.openSettings', arguments: [reason.id] }, false));
md = localize('chat.autoapprove.setting', 'Auto approved by {0}', createMarkdownCommandLink({ title: '`' + reason.id + '`', id: 'workbench.action.openSettings', arguments: [reason.id] }, false));
break;
case ToolConfirmKind.LmServicePerTool:
md = reason.scope === 'session'
......@@ -124,7 +124,7 @@ export class ChatProgressContentPart extends Disposable implements IChatContentP
: reason.scope === 'workspace'
? localize('chat.autoapprove.lmServicePerTool.workspace', 'Auto approved for this workspace')
: localize('chat.autoapprove.lmServicePerTool.profile', 'Auto approved for this profile');
md += ' (' + markdownCommandLink({ title: localize('edit', 'Edit'), id: 'workbench.action.chat.editToolApproval', arguments: [reason.scope] }) + ')';
md += ' (' + createMarkdownCommandLink({ title: localize('edit', 'Edit'), id: 'workbench.action.chat.editToolApproval', arguments: [reason.scope] }) + ')';
break;
case ToolConfirmKind.UserAction:
case ToolConfirmKind.Denied:
......
......@@ -5,7 +5,7 @@
import { ProgressBar } from '../../../../../../base/browser/ui/progressbar/progressbar.js';
import { decodeBase64 } from '../../../../../../base/common/buffer.js';
import { IMarkdownString, markdownCommandLink, MarkdownString } from '../../../../../../base/common/htmlContent.js';
import { IMarkdownString, createMarkdownCommandLink, MarkdownString } from '../../../../../../base/common/htmlContent.js';
import { Lazy } from '../../../../../../base/common/lazy.js';
import { toDisposable } from '../../../../../../base/common/lifecycle.js';
import { getExtensionForMimeType } from '../../../../../../base/common/mime.js';
......@@ -158,7 +158,7 @@ export class ChatInputOutputMarkdownProgressPart extends BaseChatToolInvocationS
let md: string;
switch (reason.type) {
case ToolConfirmKind.Setting:
md = localize('chat.autoapprove.setting', 'Auto approved by {0}', markdownCommandLink({ title: '`' + reason.id + '`', id: 'workbench.action.openSettings', arguments: [reason.id] }, false));
md = localize('chat.autoapprove.setting', 'Auto approved by {0}', createMarkdownCommandLink({ title: '`' + reason.id + '`', id: 'workbench.action.openSettings', arguments: [reason.id] }, false));
break;
case ToolConfirmKind.LmServicePerTool:
md = reason.scope === 'session'
......@@ -166,7 +166,7 @@ export class ChatInputOutputMarkdownProgressPart extends BaseChatToolInvocationS
: reason.scope === 'workspace'
? localize('chat.autoapprove.lmServicePerTool.workspace', 'Auto approved for this workspace')
: localize('chat.autoapprove.lmServicePerTool.profile', 'Auto approved for this profile');
md += ' (' + markdownCommandLink({ title: localize('edit', 'Edit'), id: 'workbench.action.chat.editToolApproval', arguments: [reason.scope] }) + ')';
md += ' (' + createMarkdownCommandLink({ title: localize('edit', 'Edit'), id: 'workbench.action.chat.editToolApproval', arguments: [reason.scope] }) + ')';
break;
case ToolConfirmKind.UserAction:
case ToolConfirmKind.Denied:
......
......@@ -14,7 +14,7 @@ import { VSBuffer } from '../../../../base/common/buffer.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { groupBy } from '../../../../base/common/collections.js';
import { Event } from '../../../../base/common/event.js';
import { markdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { createMarkdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
import { autorun, derived, derivedObservableWithCache, observableValue } from '../../../../base/common/observable.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
......@@ -548,7 +548,7 @@ export class MCPServerActionRendering extends Disposable implements IWorkbenchCo
}
protected override getHoverContents({ state, servers } = displayedStateCurrent.get()): string | undefined | IManagedHoverTooltipHTMLElement {
const link = (s: IMcpServer) => markdownCommandLink({
const link = (s: IMcpServer) => createMarkdownCommandLink({
title: s.definition.label,
id: McpCommandIds.ServerOptions,
arguments: [s.definition.id],
......
......@@ -5,7 +5,7 @@
import { computeLevenshteinDistance } from '../../../../base/common/diff/diff.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { markdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { createMarkdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { findNodeAtLocation, Node, parseTree } from '../../../../base/common/json.js';
import { Disposable, DisposableStore, dispose, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
import { IObservable } from '../../../../base/common/observable.js';
......@@ -388,9 +388,9 @@ export class McpLanguageFeatures extends Disposable implements IWorkbenchContrib
function pushAnnotation(savedId: string, offset: number, saved: IResolvedValue): InlayHint {
const tooltip = new MarkdownString([
markdownCommandLink({ id: McpCommandIds.EditStoredInput, title: localize('edit', 'Edit'), arguments: [savedId, model.uri, mcpConfigurationSection, inConfig!.target] }),
markdownCommandLink({ id: McpCommandIds.RemoveStoredInput, title: localize('clear', 'Clear'), arguments: [inConfig!.scope, savedId] }),
markdownCommandLink({ id: McpCommandIds.RemoveStoredInput, title: localize('clearAll', 'Clear All'), arguments: [inConfig!.scope] }),
createMarkdownCommandLink({ id: McpCommandIds.EditStoredInput, title: localize('edit', 'Edit'), arguments: [savedId, model.uri, mcpConfigurationSection, inConfig!.target] }),
createMarkdownCommandLink({ id: McpCommandIds.RemoveStoredInput, title: localize('clear', 'Clear'), arguments: [inConfig!.scope, savedId] }),
createMarkdownCommandLink({ id: McpCommandIds.RemoveStoredInput, title: localize('clearAll', 'Clear All'), arguments: [inConfig!.scope] }),
].join(' | '), { isTrusted: true });
const hint: InlayHint = {
......
......@@ -8,7 +8,7 @@ import * as dom from '../../../../base/browser/dom.js';
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { markdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { createMarkdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js';
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js';
import { DelayedPagedModel, IPagedModel, PagedModel, IterativePagedModel } from '../../../../base/common/paging.js';
import { localize, localize2 } from '../../../../nls.js';
......@@ -256,7 +256,7 @@ export class McpServersListView extends AbstractExtensionsListView<IWorkbenchMcp
const title = dom.append(welcomeContent, dom.$('.mcp-welcome-title'));
title.textContent = localize('mcp.welcome.title', "MCP Servers");
const settingsCommandLink = markdownCommandLink({ id: 'workbench.action.openSettings', arguments: [`@id:${mcpGalleryServiceEnablementConfig}`], title: mcpGalleryServiceEnablementConfig, tooltip: localize('mcp.welcome.settings.tooltip', "Open Settings") }).toString();
const settingsCommandLink = createMarkdownCommandLink({ id: 'workbench.action.openSettings', arguments: [`@id:${mcpGalleryServiceEnablementConfig}`], title: mcpGalleryServiceEnablementConfig, tooltip: localize('mcp.welcome.settings.tooltip', "Open Settings") }).toString();
const description = dom.append(welcomeContent, dom.$('.mcp-welcome-description'));
const markdownResult = this._register(this.markdownRendererService.render(
new MarkdownString(
......
......@@ -10,9 +10,11 @@ import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'
import { SimpleIconLabel } from '../../../../base/browser/ui/iconLabel/simpleIconLabel.js';
import { RunOnceScheduler } from '../../../../base/common/async.js';
import { Emitter } from '../../../../base/common/event.js';
import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';
import { IMarkdownString, MarkdownString, createMarkdownLink } from '../../../../base/common/htmlContent.js';
import { KeyCode } from '../../../../base/common/keyCodes.js';
import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import { URI } from '../../../../base/common/uri.js';
import { ILanguageService } from '../../../../editor/common/languages/language.js';
import { localize } from '../../../../nls.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
......@@ -455,7 +457,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
contentMarkdownString = prefaceText;
for (const scope of element.overriddenScopeList) {
const scopeDisplayText = this.getInlineScopeDisplayText(scope);
contentMarkdownString += `\n- [${scopeDisplayText}](${encodeURIComponent(scope)} "${getAccessibleScopeDisplayText(scope, this.languageService)}")`;
contentMarkdownString += '\n- ' + createMarkdownLink(scopeDisplayText, SettingScopeLink.create(scope).toString(), getAccessibleScopeDisplayText(scope, this.languageService));
}
}
if (element.overriddenDefaultsLanguageList.length) {
......@@ -466,7 +468,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
contentMarkdownString += prefaceText;
for (const language of element.overriddenDefaultsLanguageList) {
const scopeDisplayText = this.languageService.getLanguageName(language);
contentMarkdownString += `\n- [${scopeDisplayText}](${encodeURIComponent(`default:${language}`)} "${scopeDisplayText}")`;
contentMarkdownString += '\n- ' + createMarkdownLink(scopeDisplayText ?? language, SettingScopeLink.create(`default:${language}`).toString());
}
}
const content: IMarkdownString = {
......@@ -478,7 +480,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
...this.defaultHoverOptions,
content,
linkHandler: (url: string) => {
const [scope, language] = decodeURIComponent(url).split(':');
const [scope, language] = SettingScopeLink.parse(url).split(':');
onDidClickOverrideElement.fire({
settingKey: element.setting.key,
scope: scope as ScopeString,
......@@ -635,3 +637,21 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement,
const ariaLabel = ariaLabelSections.join('. ');
return ariaLabel;
}
/**
* Internal links used to open a specific scope in the settings editor
*/
namespace SettingScopeLink {
export function create(scope: string): URI {
return URI.from({
scheme: Schemas.internal,
path: '/',
query: encodeURIComponent(scope)
});
}
export function parse(link: string): string {
const uri = URI.parse(link);
return decodeURIComponent(uri.query);
}
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать