diff --git a/.eslintplugin/code-limited-top-functions.ts b/.eslintplugin/code-limited-top-functions.ts new file mode 100644 index 0000000000000..97eef9a6e9d78 --- /dev/null +++ b/.eslintplugin/code-limited-top-functions.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { dirname, relative } from 'path'; +import minimatch from 'minimatch'; + +export = new class implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + layerbreaker: 'You are only allowed to define limited top level functions.' + }, + schema: { + type: "array", + items: { + type: "object", + additionalProperties: { + type: "array", + items: { + type: "string" + } + } + } + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + let fileRelativePath = relative(dirname(__dirname), context.getFilename()); + if (!fileRelativePath.endsWith('/')) { + fileRelativePath += '/'; + } + const ruleArgs = >context.options[0]; + + const matchingKey = Object.keys(ruleArgs).find(key => fileRelativePath.startsWith(key) || minimatch(fileRelativePath, key)); + if (!matchingKey) { + // nothing + return {}; + } + + const restrictedFunctions = ruleArgs[matchingKey]; + + return { + FunctionDeclaration: (node: any) => { + const isTopLevel = node.parent.type === 'Program'; + const functionName = node.id.name; + if (isTopLevel && !restrictedFunctions.includes(node.id.name)) { + context.report({ + node, + message: `Top-level function '${functionName}' is restricted in this file. Allowed functions are: ${restrictedFunctions.join(', ')}.` + }); + } + }, + ExportNamedDeclaration(node: any) { + if (node.declaration && node.declaration.type === 'FunctionDeclaration') { + const functionName = node.declaration.id.name; + const isTopLevel = node.parent.type === 'Program'; + if (isTopLevel && !restrictedFunctions.includes(node.declaration.id.name)) { + context.report({ + node, + message: `Top-level function '${functionName}' is restricted in this file. Allowed functions are: ${restrictedFunctions.join(', ')}.` + }); + } + } + } + } + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index e5294d9351cd3..27a94df6dcce2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1203,6 +1203,15 @@ "**/*" ] } + ], + "local/code-limited-top-functions": [ + "error", + { + "src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts": [ + "webviewPreloads", + "preloadsScriptStr" + ] + } ] } } diff --git a/extensions/postinstall.mjs b/extensions/postinstall.mjs index 04e54dd6e2cab..f073aa720c91f 100644 --- a/extensions/postinstall.mjs +++ b/extensions/postinstall.mjs @@ -26,7 +26,10 @@ function processRoot() { function processLib() { const toDelete = new Set([ 'tsc.js', + '_tsc.js', + 'typescriptServices.js', + '_typescriptServices.js', ]); const libRoot = path.join(root, 'lib'); diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index d9c4ddb2a65b8..21506b11ede96 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -236,7 +236,7 @@ "configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors on web even when project wide IntelliSense is enabled. This is always on when project wide IntelliSense is not enabled or available. See `#typescript.tsserver.web.projectWideIntellisense.enabled#`", "configuration.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web. This enables IntelliSense for imported packages. Requires `#typescript.tsserver.web.projectWideIntellisense.enabled#`. Currently not supported for Safari.", "configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.", - "configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.6+.", + "configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.7+.", "configuration.expandableHover": "(Experimental) Enable/disable expanding on hover.", "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", diff --git a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts index 3e2e61a8a52c3..debf060d0ea58 100644 --- a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts @@ -50,7 +50,24 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { private readonly _client: ITypeScriptServiceClient, ) { } - prepareDocumentPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken) { + async prepareDocumentPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken) { + if (!this.isEnabled(document)) { + return; + } + + const file = this._client.toOpenTsFilePath(document); + if (!file) { + return; + } + + const response = await this._client.execute('preparePasteEdits', { + file, + copiedTextSpan: ranges.map(typeConverters.Range.toTextSpan), + }, token); + if (token.isCancellationRequested || response.type !== 'response' || !response.body) { + return; + } + dataTransfer.set(DocumentPasteProvider.metadataMimeType, new vscode.DataTransferItem(new CopyMetadata(document.uri, ranges).toJSON())); } @@ -62,8 +79,7 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { _context: vscode.DocumentPasteEditContext, token: vscode.CancellationToken, ): Promise { - const config = vscode.workspace.getConfiguration(this._modeId, document.uri); - if (!config.get(settingId, false)) { + if (!this.isEnabled(document)) { return; } @@ -127,12 +143,17 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { return metadata ? CopyMetadata.fromJSON(metadata) : undefined; } + + private isEnabled(document: vscode.TextDocument) { + const config = vscode.workspace.getConfiguration(this._modeId, document.uri); + return config.get(settingId, false); + } } export function register(selector: DocumentSelector, language: LanguageDescription, client: ITypeScriptServiceClient) { return conditionalRegistration([ requireSomeCapability(client, ClientCapability.Semantic), - requireMinVersion(client, API.v560), + requireMinVersion(client, API.v570), requireGlobalConfiguration(language.id, settingId), ], () => { return vscode.languages.registerDocumentPasteEditProvider(selector.semantic, new DocumentPasteProvider(language.id, client), { diff --git a/extensions/typescript-language-features/src/tsServer/api.ts b/extensions/typescript-language-features/src/tsServer/api.ts index 7b6d160952ee2..4ddc29944f0a4 100644 --- a/extensions/typescript-language-features/src/tsServer/api.ts +++ b/extensions/typescript-language-features/src/tsServer/api.ts @@ -28,7 +28,6 @@ export class API { public static readonly v520 = API.fromSimpleString('5.2.0'); public static readonly v544 = API.fromSimpleString('5.4.4'); public static readonly v540 = API.fromSimpleString('5.4.0'); - public static readonly v550 = API.fromSimpleString('5.5.0'); public static readonly v560 = API.fromSimpleString('5.6.0'); public static readonly v570 = API.fromSimpleString('5.7.0'); diff --git a/extensions/typescript-language-features/src/tsServer/protocol/protocol.d.ts b/extensions/typescript-language-features/src/tsServer/protocol/protocol.d.ts index 747e7c22e3724..cd70b6b7d41c3 100644 --- a/extensions/typescript-language-features/src/tsServer/protocol/protocol.d.ts +++ b/extensions/typescript-language-features/src/tsServer/protocol/protocol.d.ts @@ -19,5 +19,18 @@ declare module '../../../../node_modules/typescript/lib/typescript' { interface Response { readonly _serverType?: ServerType; } + + //#region PreparePasteEdits + interface PreparePasteEditsRequest extends FileRequest { + command: 'preparePasteEdits'; + arguments: PreparePasteEditsRequestArgs; + } + interface PreparePasteEditsRequestArgs extends FileRequestArgs { + copiedTextSpan: TextSpan[]; + } + interface PreparePasteEditsResponse extends Response { + body: boolean; + } + //#endregion } } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 931b287df03e1..90528ee47dc8c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -78,6 +78,7 @@ interface StandardTsServerRequests { 'linkedEditingRange': [Proto.FileLocationRequestArgs, Proto.LinkedEditingRangeResponse]; 'mapCode': [Proto.MapCodeRequestArgs, Proto.MapCodeResponse]; 'getPasteEdits': [Proto.GetPasteEditsRequestArgs, Proto.GetPasteEditsResponse]; + 'preparePasteEdits': [Proto.PreparePasteEditsRequestArgs, Proto.PreparePasteEditsResponse]; } interface NoResponseTsServerRequests { diff --git a/package-lock.json b/package-lock.json index d1ca8b05e78f0..114cfbd760420 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,14 +26,14 @@ "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/headless": "^5.6.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/headless": "^5.6.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", @@ -42,7 +42,7 @@ "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta21", + "node-pty": "^1.1.0-beta22", "open": "^8.4.2", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", @@ -3505,65 +3505,65 @@ } }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.47", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.47.tgz", - "integrity": "sha512-mJHV1770gwVckj4mgjdxegt4zLL5WGCoJblTqaLbgEF7xOwsJUzvGk38qsZ4ZpdTa2ti76H0pkb1CpZGrXL9qg==", + "version": "0.2.0-beta.48", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.48.tgz", + "integrity": "sha512-oTrGoGMOQaW7PINAShhNR3duV8l2No/DbJ9rtfSBjfoQ9clWe0K+4fUHdJIuzW65AZsiLo9oh5IbHLWxlFP3Jg==", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.64.tgz", - "integrity": "sha512-1Aqqytx/z/Khecf5xrO++f/p5EQX4uG87TbbAmE0OQys/JgKMnNggDzqon5bpHboimLfQiFPw716NbTqMphjfw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.65.tgz", + "integrity": "sha512-Ud5FI5cGtpbz8yYCHC4QINlbC1K04Mex1JD5oAWPFwjlOz/Si/DhqbY1QBDl6QrUmSUUTZc5DKT3v3nN5WbZxQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.64.tgz", - "integrity": "sha512-vUsvTkEOFJwv8nVtiiGr/vTMeQhu62VkUejY2h1+yZagnVEusGVPU1JYoqBTLu5HM4cFiTGsJYXFI/0apASXYA==", + "version": "0.16.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.65.tgz", + "integrity": "sha512-eHfbbgFCS3AcD8YSvGFIkW9NW4pfGHrtRDWIojM/zqmyi3JaCmHUgZmlxbaANwBlgLiNpJLDq7cmoz//86NgGw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.64.tgz", - "integrity": "sha512-0AqVveTV3Pr9Lp+beJYOP5WuRvIrPSDGAK24da7BRGcLIKVq8pok1FdlM9jPeUbkX0DKo6Ortm77qeb2Z+n8+A==", + "version": "0.14.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.65.tgz", + "integrity": "sha512-NlvXVtFAHpLKEDzd+6/68yPFQ4Oe02jvQH6+a8p+gcxbSEyYj1l0sqqeABQ3gC5QDCmQepk6BQVB8Rpz/e4HgQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.64.tgz", - "integrity": "sha512-3+PKu+DXFq32xlsYfnI058/T8dTY2tQhRpJLP2CNR/jVWk5uBkVO0dBV0Srqj5QOAlAhzzZuS6TbsVRXORptqw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.65.tgz", + "integrity": "sha512-sH9mu2GEIVuc6cs6HxsmCXn6KKzCa5D+Zyl1Pt9d2S4FKqSB17Fc4mVukCOMW7wUR9UChKTbxmByGJK+4xwdCA==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.64.tgz", - "integrity": "sha512-cxTkMPAXPQAZnWqahNu7ff9mbJvM1sQqKnZWiFZAHTsZXym4oa3KCB4pjH4didSyYkhkPY/17E8I0UOUpvv0Yw==", + "version": "0.19.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.65.tgz", + "integrity": "sha512-gPwP8ozqrLFWOlo9eh2I+acxq0Z6obG7GpL8HpJ+B+q6+vjfxoXHQ6vV04W90aoAvbLpScHcT/JtXA58GcH2vw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/headless": { - "version": "5.6.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.64.tgz", - "integrity": "sha512-my08kgH2K3CBEtBg/o8lamtD6BYNBv03Akq747Lajv0QzOiwan7B4r2xdbA6i9sovB+MdjFs0XP1ksc3EpKakg==" + "version": "5.6.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.65.tgz", + "integrity": "sha512-gQICZsBkFHQEb/K+dc+KN4u5aBwrDPgai0S7W8/KT7fqTPWz8Cgjq119WQLshnzfLeMMFPYGttNHFGUbpp/wmw==" }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.64.tgz", - "integrity": "sha512-iSKS6tQTFpoN5y487eptlxkUT0jqK5hrvtFQMLugMcQdn7gCUCiFGa4Z1RdqKYSGvAypka+b9gMKPrKWZ7Q3yQ==" + "version": "5.6.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.65.tgz", + "integrity": "sha512-o6IIg4hqs994gtSQMbzQM6OUeLIXPG6RUxjoref0KJX4PIa/BqQjwQLPuSxnjOFeRQIvhZ2mSAPfGWJQ6jEGAg==" }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", @@ -13520,9 +13520,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta21", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta21.tgz", - "integrity": "sha512-FYpnY9g8qMQLTpqyeY9NVli6YfCWwvG6v6gmaDBbPjlc1VMp/+Zivq0SStDrRr1aciGnFCZzpL0BzdMnmbDnAw==", + "version": "1.1.0-beta22", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta22.tgz", + "integrity": "sha512-CpT334H2oAIULlENvd9U+VBW4ZL+G3clOnpXYzUIurlPCLnl/9xen/KDHBLRcwhZuWcHxIrsCxR1TDCdVDtr0w==", "hasInstallScript": true, "dependencies": { "node-addon-api": "^7.1.0" diff --git a/package.json b/package.json index 7fbc5d9c91d75..5c441319f438f 100644 --- a/package.json +++ b/package.json @@ -83,14 +83,14 @@ "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/headless": "^5.6.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/headless": "^5.6.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", @@ -99,7 +99,7 @@ "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta21", + "node-pty": "^1.1.0-beta22", "open": "^8.4.2", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", diff --git a/remote/package-lock.json b/remote/package-lock.json index 58725751af9ee..5f8891ee061a1 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -20,14 +20,14 @@ "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/headless": "^5.6.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/headless": "^5.6.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "cookie": "^0.4.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", @@ -35,7 +35,7 @@ "kerberos": "2.1.1", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta21", + "node-pty": "^1.1.0-beta22", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", @@ -233,65 +233,65 @@ "hasInstallScript": true }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.47", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.47.tgz", - "integrity": "sha512-mJHV1770gwVckj4mgjdxegt4zLL5WGCoJblTqaLbgEF7xOwsJUzvGk38qsZ4ZpdTa2ti76H0pkb1CpZGrXL9qg==", + "version": "0.2.0-beta.48", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.48.tgz", + "integrity": "sha512-oTrGoGMOQaW7PINAShhNR3duV8l2No/DbJ9rtfSBjfoQ9clWe0K+4fUHdJIuzW65AZsiLo9oh5IbHLWxlFP3Jg==", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.64.tgz", - "integrity": "sha512-1Aqqytx/z/Khecf5xrO++f/p5EQX4uG87TbbAmE0OQys/JgKMnNggDzqon5bpHboimLfQiFPw716NbTqMphjfw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.65.tgz", + "integrity": "sha512-Ud5FI5cGtpbz8yYCHC4QINlbC1K04Mex1JD5oAWPFwjlOz/Si/DhqbY1QBDl6QrUmSUUTZc5DKT3v3nN5WbZxQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.64.tgz", - "integrity": "sha512-vUsvTkEOFJwv8nVtiiGr/vTMeQhu62VkUejY2h1+yZagnVEusGVPU1JYoqBTLu5HM4cFiTGsJYXFI/0apASXYA==", + "version": "0.16.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.65.tgz", + "integrity": "sha512-eHfbbgFCS3AcD8YSvGFIkW9NW4pfGHrtRDWIojM/zqmyi3JaCmHUgZmlxbaANwBlgLiNpJLDq7cmoz//86NgGw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.64.tgz", - "integrity": "sha512-0AqVveTV3Pr9Lp+beJYOP5WuRvIrPSDGAK24da7BRGcLIKVq8pok1FdlM9jPeUbkX0DKo6Ortm77qeb2Z+n8+A==", + "version": "0.14.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.65.tgz", + "integrity": "sha512-NlvXVtFAHpLKEDzd+6/68yPFQ4Oe02jvQH6+a8p+gcxbSEyYj1l0sqqeABQ3gC5QDCmQepk6BQVB8Rpz/e4HgQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.64.tgz", - "integrity": "sha512-3+PKu+DXFq32xlsYfnI058/T8dTY2tQhRpJLP2CNR/jVWk5uBkVO0dBV0Srqj5QOAlAhzzZuS6TbsVRXORptqw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.65.tgz", + "integrity": "sha512-sH9mu2GEIVuc6cs6HxsmCXn6KKzCa5D+Zyl1Pt9d2S4FKqSB17Fc4mVukCOMW7wUR9UChKTbxmByGJK+4xwdCA==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.64.tgz", - "integrity": "sha512-cxTkMPAXPQAZnWqahNu7ff9mbJvM1sQqKnZWiFZAHTsZXym4oa3KCB4pjH4didSyYkhkPY/17E8I0UOUpvv0Yw==", + "version": "0.19.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.65.tgz", + "integrity": "sha512-gPwP8ozqrLFWOlo9eh2I+acxq0Z6obG7GpL8HpJ+B+q6+vjfxoXHQ6vV04W90aoAvbLpScHcT/JtXA58GcH2vw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/headless": { - "version": "5.6.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.64.tgz", - "integrity": "sha512-my08kgH2K3CBEtBg/o8lamtD6BYNBv03Akq747Lajv0QzOiwan7B4r2xdbA6i9sovB+MdjFs0XP1ksc3EpKakg==" + "version": "5.6.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.65.tgz", + "integrity": "sha512-gQICZsBkFHQEb/K+dc+KN4u5aBwrDPgai0S7W8/KT7fqTPWz8Cgjq119WQLshnzfLeMMFPYGttNHFGUbpp/wmw==" }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.64.tgz", - "integrity": "sha512-iSKS6tQTFpoN5y487eptlxkUT0jqK5hrvtFQMLugMcQdn7gCUCiFGa4Z1RdqKYSGvAypka+b9gMKPrKWZ7Q3yQ==" + "version": "5.6.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.65.tgz", + "integrity": "sha512-o6IIg4hqs994gtSQMbzQM6OUeLIXPG6RUxjoref0KJX4PIa/BqQjwQLPuSxnjOFeRQIvhZ2mSAPfGWJQ6jEGAg==" }, "node_modules/agent-base": { "version": "7.1.1", @@ -748,9 +748,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta21", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta21.tgz", - "integrity": "sha512-FYpnY9g8qMQLTpqyeY9NVli6YfCWwvG6v6gmaDBbPjlc1VMp/+Zivq0SStDrRr1aciGnFCZzpL0BzdMnmbDnAw==", + "version": "1.1.0-beta22", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta22.tgz", + "integrity": "sha512-CpT334H2oAIULlENvd9U+VBW4ZL+G3clOnpXYzUIurlPCLnl/9xen/KDHBLRcwhZuWcHxIrsCxR1TDCdVDtr0w==", "hasInstallScript": true, "dependencies": { "node-addon-api": "^7.1.0" diff --git a/remote/package.json b/remote/package.json index c107b048e45c0..0d0a37e4b9046 100644 --- a/remote/package.json +++ b/remote/package.json @@ -15,14 +15,14 @@ "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/headless": "^5.6.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/headless": "^5.6.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "cookie": "^0.4.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", @@ -30,7 +30,7 @@ "kerberos": "2.1.1", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta21", + "node-pty": "^1.1.0-beta22", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", diff --git a/remote/web/package-lock.json b/remote/web/package-lock.json index b971ca265f03e..04199f4e652db 100644 --- a/remote/web/package-lock.json +++ b/remote/web/package-lock.json @@ -13,13 +13,13 @@ "@vscode/iconv-lite-umd": "0.7.0", "@vscode/tree-sitter-wasm": "^0.0.4", "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "jschardet": "3.1.3", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", @@ -87,60 +87,60 @@ } }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.47", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.47.tgz", - "integrity": "sha512-mJHV1770gwVckj4mgjdxegt4zLL5WGCoJblTqaLbgEF7xOwsJUzvGk38qsZ4ZpdTa2ti76H0pkb1CpZGrXL9qg==", + "version": "0.2.0-beta.48", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.48.tgz", + "integrity": "sha512-oTrGoGMOQaW7PINAShhNR3duV8l2No/DbJ9rtfSBjfoQ9clWe0K+4fUHdJIuzW65AZsiLo9oh5IbHLWxlFP3Jg==", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.64.tgz", - "integrity": "sha512-1Aqqytx/z/Khecf5xrO++f/p5EQX4uG87TbbAmE0OQys/JgKMnNggDzqon5bpHboimLfQiFPw716NbTqMphjfw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.65.tgz", + "integrity": "sha512-Ud5FI5cGtpbz8yYCHC4QINlbC1K04Mex1JD5oAWPFwjlOz/Si/DhqbY1QBDl6QrUmSUUTZc5DKT3v3nN5WbZxQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.64.tgz", - "integrity": "sha512-vUsvTkEOFJwv8nVtiiGr/vTMeQhu62VkUejY2h1+yZagnVEusGVPU1JYoqBTLu5HM4cFiTGsJYXFI/0apASXYA==", + "version": "0.16.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.65.tgz", + "integrity": "sha512-eHfbbgFCS3AcD8YSvGFIkW9NW4pfGHrtRDWIojM/zqmyi3JaCmHUgZmlxbaANwBlgLiNpJLDq7cmoz//86NgGw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.64.tgz", - "integrity": "sha512-0AqVveTV3Pr9Lp+beJYOP5WuRvIrPSDGAK24da7BRGcLIKVq8pok1FdlM9jPeUbkX0DKo6Ortm77qeb2Z+n8+A==", + "version": "0.14.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.65.tgz", + "integrity": "sha512-NlvXVtFAHpLKEDzd+6/68yPFQ4Oe02jvQH6+a8p+gcxbSEyYj1l0sqqeABQ3gC5QDCmQepk6BQVB8Rpz/e4HgQ==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.64.tgz", - "integrity": "sha512-3+PKu+DXFq32xlsYfnI058/T8dTY2tQhRpJLP2CNR/jVWk5uBkVO0dBV0Srqj5QOAlAhzzZuS6TbsVRXORptqw==", + "version": "0.9.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.65.tgz", + "integrity": "sha512-sH9mu2GEIVuc6cs6HxsmCXn6KKzCa5D+Zyl1Pt9d2S4FKqSB17Fc4mVukCOMW7wUR9UChKTbxmByGJK+4xwdCA==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.64.tgz", - "integrity": "sha512-cxTkMPAXPQAZnWqahNu7ff9mbJvM1sQqKnZWiFZAHTsZXym4oa3KCB4pjH4didSyYkhkPY/17E8I0UOUpvv0Yw==", + "version": "0.19.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.65.tgz", + "integrity": "sha512-gPwP8ozqrLFWOlo9eh2I+acxq0Z6obG7GpL8HpJ+B+q6+vjfxoXHQ6vV04W90aoAvbLpScHcT/JtXA58GcH2vw==", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.64" + "@xterm/xterm": "^5.6.0-beta.65" } }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.64", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.64.tgz", - "integrity": "sha512-iSKS6tQTFpoN5y487eptlxkUT0jqK5hrvtFQMLugMcQdn7gCUCiFGa4Z1RdqKYSGvAypka+b9gMKPrKWZ7Q3yQ==" + "version": "5.6.0-beta.65", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.65.tgz", + "integrity": "sha512-o6IIg4hqs994gtSQMbzQM6OUeLIXPG6RUxjoref0KJX4PIa/BqQjwQLPuSxnjOFeRQIvhZ2mSAPfGWJQ6jEGAg==" }, "node_modules/js-base64": { "version": "3.7.7", diff --git a/remote/web/package.json b/remote/web/package.json index 27be6a503bdc7..a4a18b9369cb0 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -8,13 +8,13 @@ "@vscode/iconv-lite-umd": "0.7.0", "@vscode/tree-sitter-wasm": "^0.0.4", "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-clipboard": "^0.2.0-beta.47", - "@xterm/addon-image": "^0.9.0-beta.64", - "@xterm/addon-search": "^0.16.0-beta.64", - "@xterm/addon-serialize": "^0.14.0-beta.64", - "@xterm/addon-unicode11": "^0.9.0-beta.64", - "@xterm/addon-webgl": "^0.19.0-beta.64", - "@xterm/xterm": "^5.6.0-beta.64", + "@xterm/addon-clipboard": "^0.2.0-beta.48", + "@xterm/addon-image": "^0.9.0-beta.65", + "@xterm/addon-search": "^0.16.0-beta.65", + "@xterm/addon-serialize": "^0.14.0-beta.65", + "@xterm/addon-unicode11": "^0.9.0-beta.65", + "@xterm/addon-webgl": "^0.19.0-beta.65", + "@xterm/xterm": "^5.6.0-beta.65", "jschardet": "3.1.3", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 0c4cf7598cc44..b49b11c86872f 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -230,6 +230,7 @@ export class MenuId { static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext'); static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext'); static readonly ChatCommandCenter = new MenuId('ChatCommandCenter'); + static readonly ChatAttachmentsContext = new MenuId('ChatAttachmentsContext'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar'); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 2e335cbaa5da6..8c04da5bb7c44 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1454,7 +1454,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, registerChatParticipantDetectionProvider(provider: vscode.ChatParticipantDetectionProvider) { checkProposedApiEnabled(extension, 'chatParticipantAdditions'); - return extHostChatAgents2.registerChatParticipantDetectionProvider(provider); + return extHostChatAgents2.registerChatParticipantDetectionProvider(extension, provider); }, }; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index cc1ecb1291290..c31f91957d7fb 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -285,7 +285,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private readonly _proxy: MainThreadChatAgentsShape2; private static _participantDetectionProviderIdPool = 0; - private readonly _participantDetectionProviders = new Map(); + private readonly _participantDetectionProviders = new Map(); private readonly _sessionDisposables: DisposableMap = this._register(new DisposableMap()); private readonly _completionDisposables: DisposableMap = this._register(new DisposableMap()); @@ -323,9 +323,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS return agent.apiAgent; } - registerChatParticipantDetectionProvider(provider: vscode.ChatParticipantDetectionProvider): vscode.Disposable { + registerChatParticipantDetectionProvider(extension: IExtensionDescription, provider: vscode.ChatParticipantDetectionProvider): vscode.Disposable { const handle = ExtHostChatAgents2._participantDetectionProviderIdPool++; - this._participantDetectionProviders.set(handle, provider); + this._participantDetectionProviders.set(handle, new ExtHostParticipantDetector(extension, provider)); this._proxy.$registerChatParticipantDetectionProvider(handle); return toDisposable(() => { this._participantDetectionProviders.delete(handle); @@ -336,13 +336,18 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS async $detectChatParticipant(handle: number, requestDto: Dto, context: { history: IChatAgentHistoryEntryDto[] }, options: { location: ChatAgentLocation; participants?: vscode.ChatParticipantMetadata[] }, token: CancellationToken): Promise { const { request, location, history } = await this._createRequest(requestDto, context); - const provider = this._participantDetectionProviders.get(handle); - if (!provider) { + const detector = this._participantDetectionProviders.get(handle); + if (!detector) { return undefined; } - return provider.provideParticipantDetection( - typeConvert.ChatAgentRequest.to(request, location), + const extRequest = typeConvert.ChatAgentRequest.to(request, location); + if (request.userSelectedModelId && isProposedApiEnabled(detector.extension, 'chatParticipantAdditions')) { + extRequest.userSelectedModel = await this._languageModels.getLanguageModelByIdentifier(detector.extension, request.userSelectedModelId); + } + + return detector.provider.provideParticipantDetection( + extRequest, { history }, { participants: options.participants, location: typeConvert.ChatLocation.to(options.location) }, token @@ -588,6 +593,13 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS } } +class ExtHostParticipantDetector { + constructor( + public readonly extension: IExtensionDescription, + public readonly provider: vscode.ChatParticipantDetectionProvider, + ) { } +} + class ExtHostChatAgent { private _followupProvider: vscode.ChatFollowupProvider | undefined; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index cb5c64f760b2e..c9c0311cc8d95 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -19,6 +19,7 @@ import { ChatEditorInput } from '../chatEditorInput.js'; import { ChatViewPane } from '../chatViewPane.js'; import { CONTEXT_IN_CHAT_SESSION, CONTEXT_CHAT_ENABLED, CONTEXT_CHAT_EDITING_PARTICIPANT_REGISTERED } from '../../common/chatContextKeys.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; +import { ChatAgentLocation } from '../../common/chatAgents.js'; export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`; @@ -139,7 +140,7 @@ export function registerNewChatActions() { const viewsService = accessor.get(IViewsService); let widget = widgetService.lastFocusedWidget; - if (!widget) { + if (!widget || widget.location !== ChatAgentLocation.EditingSession) { const chatView = await viewsService.openView(EDITS_VIEW_ID) as ChatViewPane; widget = chatView.widget; } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index fdff4f2c19797..94d12546a42c3 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -167,7 +167,7 @@ export class CancelAction extends Action2 { menu: { id: MenuId.ChatExecute, when: CONTEXT_CHAT_REQUEST_IN_PROGRESS, - order: 2, + order: 4, group: 'navigation', }, keybinding: { diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index bec60f8fd124a..dda6330c614ac 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { timeout } from '../../../../base/common/async.js'; import { MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../base/common/network.js'; @@ -263,6 +264,11 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { progress.report({ content: new MarkdownString(defaultAgent.metadata.helpTextPostfix), kind: 'markdownContent' }); } } + + // Without this, the response will be done before it renders and so it will not stream. This ensures that if the response starts + // rendering during the next 200ms, then it will be streamed. Once it starts streaming, the whole response streams even after + // it has received all response data has been received. + await timeout(200); })); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts index 552f7522e7676..1d7731a17b704 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts @@ -26,6 +26,8 @@ import { IChatProgressRenderableResponseContent } from '../../common/chatModel.j import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; import { CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js'; import { URI } from '../../../../../base/common/uri.js'; +import { IEditorService } from '../../../../services/editor/common/editorService.js'; +import { InlineAnchorWidget } from '../chatInlineAnchorWidget.js'; const $ = dom.$; @@ -52,7 +54,8 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP rendererOptions: IChatListItemRendererOptions, @IContextKeyService contextKeyService: IContextKeyService, @ITextModelService private readonly textModelService: ITextModelService, - @IInstantiationService instantiationService: IInstantiationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly editorService: IEditorService, ) { super(); @@ -132,6 +135,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP this.codeBlockModelCollection.update(data.element.sessionId, data.element, data.codeBlockIndex, { text, languageId: data.languageId }).then((e) => { // Update the existing object's codemapperUri this.codeblocks[data.codeBlockIndex].codemapperUri = e.codemapperUri; + this._onDidChangeHeight.fire(); }); } @@ -145,7 +149,20 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP } layout(width: number): void { - this.allRefs.forEach(ref => ref.object.layout(width)); + this.allRefs.forEach((ref, index) => { + const codeblockModel = this.codeblocks[index]; + if (codeblockModel.codemapperUri) { + const fileWidgetAnchor = $('.chat-codeblock'); + this._register(this.instantiationService.createInstance(InlineAnchorWidget, fileWidgetAnchor, { uri: codeblockModel.codemapperUri }, { handleClick: (uri) => this.editorService.openEditor({ resource: uri }) })); + const existingCodeblock = ref.object.element.parentElement?.querySelector('.chat-codeblock'); + if (!existingCodeblock) { + ref.object.element.parentElement?.appendChild(fileWidgetAnchor); + ref.object.element.style.display = 'none'; + } + } else { + ref.object.layout(width); + } + }); } addDisposable(disposable: IDisposable): void { diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts index 2ca9d305f544d..0c31726d04843 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts @@ -6,6 +6,7 @@ import * as dom from '../../../../../base/browser/dom.js'; import { Button } from '../../../../../base/browser/ui/button/button.js'; import { IListRenderer, IListVirtualDelegate } from '../../../../../base/browser/ui/list/list.js'; +import { IAction } from '../../../../../base/common/actions.js'; import { coalesce } from '../../../../../base/common/arrays.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; @@ -15,13 +16,13 @@ import { basename } from '../../../../../base/common/path.js'; import { basenameOrAuthority, isEqualAuthority } from '../../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; -import { localize } from '../../../../../nls.js'; +import { localize, localize2 } from '../../../../../nls.js'; +import { createAndFillInContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MenuWorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; -import { MenuId } from '../../../../../platform/actions/common/actions.js'; -import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; +import { Action2, IMenuService, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { FileKind } from '../../../../../platform/files/common/files.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { WorkbenchList } from '../../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; @@ -30,12 +31,14 @@ import { IThemeService } from '../../../../../platform/theme/common/themeService import { fillEditorsDragData } from '../../../../browser/dnd.js'; import { IResourceLabel, ResourceLabels } from '../../../../browser/labels.js'; import { ColorScheme } from '../../../../browser/web.api.js'; +import { ResourceContextKey } from '../../../../common/contextkeys.js'; import { SETTINGS_AUTHORITY } from '../../../../services/preferences/common/preferences.js'; import { createFileIconThemableTreeContainerScope } from '../../../files/browser/views/explorerView.js'; +import { ExplorerFolderContext } from '../../../files/common/files.js'; import { ChatResponseReferencePartStatusKind, IChatContentReference, IChatWarningMessage } from '../../common/chatService.js'; import { IChatVariablesService } from '../../common/chatVariables.js'; import { IChatRendererContent, IChatResponseViewModel } from '../../common/chatViewModel.js'; -import { ChatTreeItem } from '../chat.js'; +import { ChatTreeItem, IChatWidgetService } from '../chat.js'; import { IDisposableReference, ResourcePool } from './chatCollections.js'; import { IChatContentPart } from './chatContentParts.js'; @@ -59,8 +62,9 @@ export class ChatCollapsibleListContentPart extends Disposable implements IChatC element: IChatResponseViewModel, contentReferencesListPool: CollapsibleListPool, @IOpenerService openerService: IOpenerService, + @IMenuService menuService: IMenuService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IClipboardService private readonly clipboardService: IClipboardService, ) { super(); @@ -120,34 +124,32 @@ export class ChatCollapsibleListContentPart extends Disposable implements IChatC } } })); - this._register(list.onContextMenu((e) => { - e.browserEvent.preventDefault(); - e.browserEvent.stopPropagation(); - if (e.element && 'reference' in e.element && typeof e.element.reference === 'object') { - const uriOrLocation = 'variableName' in e.element.reference ? e.element.reference.value : e.element.reference; - const uri = URI.isUri(uriOrLocation) ? uriOrLocation : - uriOrLocation?.uri; - if (uri) { - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => { - return [{ - id: 'workbench.action.chat.copyReference', - title: localize('copyReference', "Copy"), - label: localize('copyReference', "Copy"), - tooltip: localize('copyReference', "Copy"), - enabled: e.element?.kind === 'reference', - class: undefined, - run: () => { - void this.clipboardService.writeResources([uri]); - } - }]; - } - }); - } + this._register(list.onContextMenu(e => { + dom.EventHelper.stop(e.browserEvent, true); + + const uri = e.element && getResourceForElement(e.element); + if (!uri) { + return; } + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => { + const menu = menuService.getMenuActions(MenuId.ChatAttachmentsContext, list.contextKeyService, { shouldForwardArgs: true, arg: uri }); + const primary: IAction[] = []; + createAndFillInContextMenuActions(menu, primary); + return primary; + } + }); + })); + + const resourceContextKey = this._register(this.instantiationService.createInstance(ResourceContextKey)); + this._register(list.onDidChangeFocus(e => { + resourceContextKey.reset(); + const element = e.elements.length ? e.elements[0] : undefined; + const uri = element && getResourceForElement(element); + resourceContextKey.set(uri ?? null); })); const maxItemsShown = 6; @@ -197,20 +199,6 @@ export class CollapsibleListPool extends Disposable { const container = $('.chat-used-context-list'); this._register(createFileIconThemableTreeContainerScope(container, this.themeService)); - const getDragURI = (element: IChatCollapsibleListItem): URI | null => { - if (element.kind === 'warning') { - return null; - } - const { reference } = element; - if (typeof reference === 'string' || 'variableName' in reference) { - return null; - } else if (URI.isUri(reference)) { - return reference; - } else { - return reference.uri; - } - }; - const list = this.instantiationService.createInstance( WorkbenchList, 'ChatListRenderer', @@ -239,9 +227,9 @@ export class CollapsibleListPool extends Disposable { getWidgetAriaLabel: () => localize('chatCollapsibleList', "Collapsible Chat List") }, dnd: { - getDragURI: (element: IChatCollapsibleListItem) => getDragURI(element)?.toString() ?? null, + getDragURI: (element: IChatCollapsibleListItem) => getResourceForElement(element)?.toString() ?? null, getDragLabel: (elements, originalEvent) => { - const uris: URI[] = coalesce(elements.map(getDragURI)); + const uris: URI[] = coalesce(elements.map(getResourceForElement)); if (!uris.length) { return undefined; } else if (uris.length === 1) { @@ -256,7 +244,7 @@ export class CollapsibleListPool extends Disposable { onDragStart: (data, originalEvent) => { try { const elements = data.getData() as IChatCollapsibleListItem[]; - const uris: URI[] = coalesce(elements.map(getDragURI)); + const uris: URI[] = coalesce(elements.map(getResourceForElement)); this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, uris, originalEvent)); } catch { // noop @@ -294,8 +282,8 @@ class CollapsibleListDelegate implements IListVirtualDelegate { @@ -414,3 +402,58 @@ class CollapsibleListRenderer implements IListRenderer { + const chatWidgetService = accessor.get(IChatWidgetService); + const variablesService = accessor.get(IChatVariablesService); + + if (!resource) { + return; + } + + const widget = chatWidgetService.lastFocusedWidget; + if (!widget) { + return; + } + + variablesService.attachContext('file', resource, widget.location); + } +}); + +//#endregion diff --git a/src/vs/workbench/contrib/chat/browser/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditingService.ts index 3321419dba3a0..14874cf40adf9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditingService.ts @@ -574,6 +574,7 @@ class ChatEditingSession extends Disposable implements IChatEditingSession { @ITextModelService private readonly _textModelService: ITextModelService, @IBulkEditService public readonly _bulkEditService: IBulkEditService, @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, + @IEditorService private readonly editorService: IEditorService, ) { super(); } @@ -708,7 +709,8 @@ class ChatEditingSession extends Disposable implements IChatEditingSession { private async _acceptTextEdits(resource: URI, textEdits: TextEdit[]): Promise { const entry = await this._getOrCreateModifiedFileEntry(resource); - entry.appyEdits(textEdits); + entry.applyEdits(textEdits); + await this.editorService.openEditor({ original: { resource: entry.originalURI }, modified: { resource: entry.modifiedURI }, options: { inactive: true } }); } private async _resolve(): Promise { @@ -796,7 +798,7 @@ class ModifiedFileEntry extends Disposable implements IModifiedFileEntry { this._register(resourceRef); } - appyEdits(textEdits: TextEdit[]): void { + applyEdits(textEdits: TextEdit[]): void { this.doc.applyEdits(textEdits); this._stateObs.set(ModifiedFileEntryState.Undecided, undefined); } diff --git a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts index bb24ba0946c53..41a39c9aa88ae 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts @@ -40,9 +40,12 @@ import { IChatWidgetService } from './chat.js'; export class InlineAnchorWidget extends Disposable { + public static readonly className = 'chat-inline-anchor-widget'; + constructor( - element: HTMLAnchorElement, + element: HTMLAnchorElement | HTMLElement, data: ContentRefData, + options: { handleClick?: (uri: URI) => void } = {}, @IContextKeyService originalContextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IFileService fileService: IFileService, @@ -60,7 +63,10 @@ export class InlineAnchorWidget extends Disposable { const contextKeyService = this._register(originalContextKeyService.createScoped(element)); const anchorId = new Lazy(generateUuid); - element.classList.add('chat-inline-anchor-widget', 'show-file-icons'); + element.classList.add(InlineAnchorWidget.className, 'show-file-icons'); + if (options.handleClick) { + element.classList.add('clickable'); + } let iconText: string; let iconClasses: string[]; @@ -128,6 +134,8 @@ export class InlineAnchorWidget extends Disposable { .catch(() => { }); this._register(dom.addDisposableListener(element, 'click', () => { + options.handleClick?.(location.uri); + telemetryService.publicLog2<{ anchorId: string; }, { diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 79555a8099fe2..43ed2cc5a5bf6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -246,7 +246,7 @@ export class ChatMarkdownDecorationsRenderer extends Disposable { return; } - store.add(this.instantiationService.createInstance(InlineAnchorWidget, a, data)); + store.add(this.instantiationService.createInstance(InlineAnchorWidget, a, data, undefined)); } private renderResourceWidget(name: string, args: IDecorationWidgetArgs | undefined, store: DisposableStore): HTMLElement { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 13849afedf0ad..3de9f7c3bf16e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -984,7 +984,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.tree.getHTMLElement().style.height = `${listHeight}px`; // Push the welcome message down so it doesn't change position when followups appear - const followupsOffset = 100 - this.inputPart.followupsHeight; + const followupsOffset = Math.max(100 - this.inputPart.followupsHeight, 0); this.welcomeMessageContainer.style.height = `${listHeight - followupsOffset}px`; this.welcomeMessageContainer.style.paddingBottom = `${followupsOffset}px`; this.renderer.layout(width); diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css index 751bbdf6350f0..fc672aae75fbc 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css @@ -9,6 +9,15 @@ margin: 0 1px; padding: 1px 3px; text-wrap: nowrap; + width: fit-content; +} + +.chat-inline-anchor-widget .icon-label { + padding-right: 3px; +} + +.chat-inline-anchor-widget .clickable { + cursor: pointer; } .interactive-item-container .value .rendered-markdown .chat-inline-anchor-widget { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7e33db78057e0..613fed3ce95ba 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -210,6 +210,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.refreshReplElements(true); if (this.styleChangedWhenInvisible) { this.styleChangedWhenInvisible = false; + this.tree?.updateChildren(undefined, true, false); this.onDidStyleChange(); } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 81904a45cec17..ec2a3a69d0aaa 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -767,6 +767,36 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { }); +// Chat used attachment anchor context menu + +MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { + group: 'navigation', + order: 10, + command: openToSideCommand, + when: ContextKeyExpr.and(ResourceContextKey.HasResource, ExplorerFolderContext.toNegated()) +}); + +MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { + group: 'navigation', + order: 20, + command: revealInsideBarCommand, + when: ResourceContextKey.IsFileSystemResource +}); + +MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { + group: '1_cutcopypaste', + order: 10, + command: copyPathCommand, + when: ResourceContextKey.IsFileSystemResource +}); + +MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { + group: '1_cutcopypaste', + order: 20, + command: copyRelativePathCommand, + when: ResourceContextKey.IsFileSystemResource +}); + // Chat resource anchor context menu MenuRegistry.appendMenuItem(MenuId.ChatInlineResourceAnchorContext, { diff --git a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts index 1d75d5e5e44ca..5581931a9e7d1 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileActions.contribution.ts @@ -97,6 +97,15 @@ appendToCommandPalette({ category: category }, REVEAL_IN_OS_WHEN_CONTEXT); +// Menu registration - chat attachments context + +MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { + group: 'navigation', + order: 20, + command: revealInOsCommand, + when: REVEAL_IN_OS_WHEN_CONTEXT +}); + // Menu registration - chat inline anchor MenuRegistry.appendMenuItem(MenuId.ChatInlineResourceAnchorContext, { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 154a29b9173ee..dd7de68ed52be 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -33,6 +33,7 @@ import { IAccessibilityService } from '../../../../../platform/accessibility/com import { localize } from '../../../../../nls.js'; import { IEditorConstructionOptions } from '../../../../../editor/browser/config/editorConfiguration.js'; import { IDiffEditorConstructionOptions } from '../../../../../editor/browser/editorBrowser.js'; +import { EditorExtensionsRegistry } from '../../../../../editor/browser/editorExtensions.js'; export class NotebookCellTextDiffListDelegate implements IListVirtualDelegate { private readonly lineHeight: number; @@ -607,7 +608,9 @@ function buildDiffEditorWidget(instantiationService: IInstantiationService, note function buildSourceEditor(instantiationService: IInstantiationService, notebookEditor: INotebookTextDiffEditor, sourceContainer: HTMLElement, options: IEditorConstructionOptions = {}) { const editorContainer = DOM.append(sourceContainer, DOM.$('.editor-container')); - + const skipContributions = [ + 'editor.contrib.emptyTextEditorHint' + ]; const editor = instantiationService.createInstance(CodeEditorWidget, editorContainer, { ...fixedEditorOptions, glyphMargin: false, @@ -618,7 +621,9 @@ function buildSourceEditor(instantiationService: IInstantiationService, notebook automaticLayout: false, overflowWidgetsDomNode: notebookEditor.getOverflowContainerDomNode(), readOnly: true, - }, {}); + }, { + contributions: EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1) + }); return { editor, editorContainer }; } diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 5592e4db6c93e..37559fe0aa3ee 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -30,6 +30,7 @@ import { ITextResourceConfigurationService } from '../../../../editor/common/ser import { Registry } from '../../../../platform/registry/common/platform.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, getWorkbenchContribution } from '../../../common/contributions.js'; import { ICustomEditorLabelService } from '../../../services/editor/common/customEditorLabelService.js'; +import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; export class PerfviewContrib { @@ -111,6 +112,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { @ITimerService private readonly _timerService: ITimerService, @IExtensionService private readonly _extensionService: IExtensionService, @IProductService private readonly _productService: IProductService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @ITerminalService private readonly _terminalService: ITerminalService ) { } @@ -138,7 +140,8 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._timerService.whenReady(), this._lifecycleService.when(LifecyclePhase.Eventually), this._extensionService.whenInstalledExtensionsRegistered(), - this._terminalService.whenConnected + // The terminal service never connects to the pty host on the web + isWeb && !this._remoteAgentService.getConnection()?.remoteAuthority ? Promise.resolve() : this._terminalService.whenConnected ]).then(() => { if (this._model && !this._model.isDisposed()) { diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 0c72ca522f9ae..4ba122fa37019 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -168,6 +168,12 @@ line-height: 18px; } +.scm-view .monaco-list-row .history-item > .label-container > .label > .count { + font-size: 12px; + margin-left: 0; + padding-left: 4px; +} + .scm-view .monaco-list-row .history-item > .label-container > .label > .codicon { font-size: 14px; color: inherit !important; diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 4de2e1841eb22..79ae478779259 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -365,17 +365,27 @@ class HistoryItemRenderer implements ITreeRenderer { const labelConfig = this._badgesConfig.read(reader); - const firstColoredRef = historyItem.references?.find(ref => ref.color); templateData.labelContainer.textContent = ''; + const references = historyItem.references ? + historyItem.references.slice(0) : []; + + // If the first reference is colored, we render it + // separately since we have to show the description + // for the first colored reference. + if (references.length > 0 && references[0].color) { + this._renderBadge([references[0]], true, templateData); + + // Remove the rendered reference from the collection + references.splice(0, 1); + } + // Group history item references by color - const historyItemRefsByColor = groupBy2( - (historyItem.references ?? []), - ref => ref.color ? ref.color : ''); + const historyItemRefsByColor = groupBy2(references, ref => ref.color ? ref.color : ''); for (const [key, historyItemRefs] of Object.entries(historyItemRefsByColor)) { - // Skip badges with no color + // If needed skip badges without a color if (key === '' && labelConfig !== 'all') { continue; } @@ -383,35 +393,44 @@ class HistoryItemRenderer implements ITreeRenderer ThemeIcon.isThemeIcon(ref.icon) ? ref.icon.id : ''); for (const [key, historyItemRefs] of Object.entries(historyItemRefByIconId)) { - if (key === '' || historyItemRefs.length === 0) { + // Skip badges without an icon + if (key === '') { continue; } - this._renderBadge(historyItemRefs[0], historyItemRefs[0] === firstColoredRef, templateData); + this._renderBadge(historyItemRefs, false, templateData); } } })); } - private _renderBadge(historyItemRef: ISCMHistoryItemRef, showDescription: boolean, templateData: HistoryItemTemplate): void { - if (!ThemeIcon.isThemeIcon(historyItemRef.icon)) { + private _renderBadge(historyItemRefs: ISCMHistoryItemRef[], showDescription: boolean, templateData: HistoryItemTemplate): void { + if (historyItemRefs.length === 0 || !ThemeIcon.isThemeIcon(historyItemRefs[0].icon)) { return; } const elements = h('div.label', { style: { - color: historyItemRef.color ? asCssVariable(historyItemHoverLabelForeground) : asCssVariable(foreground), - backgroundColor: historyItemRef.color ? asCssVariable(historyItemRef.color) : asCssVariable(historyItemHoverDefaultLabelBackground) + color: historyItemRefs[0].color ? asCssVariable(historyItemHoverLabelForeground) : asCssVariable(foreground), + backgroundColor: historyItemRefs[0].color ? asCssVariable(historyItemRefs[0].color) : asCssVariable(historyItemHoverDefaultLabelBackground) } }, [ + h('div.count@count', { + style: { + display: historyItemRefs.length > 1 ? '' : 'none' + } + }), h('div.icon@icon'), - h('div.description@description') + h('div.description@description', { + style: { + display: showDescription ? '' : 'none' + } + }) ]); - elements.icon.classList.add(...ThemeIcon.asClassNameArray(historyItemRef.icon)); - - elements.description.textContent = historyItemRef.name; - elements.description.style.display = showDescription ? '' : 'none'; + elements.count.textContent = historyItemRefs.length > 1 ? historyItemRefs.length.toString() : ''; + elements.icon.classList.add(...ThemeIcon.asClassNameArray(historyItemRefs[0].icon)); + elements.description.textContent = showDescription ? historyItemRefs[0].name : ''; append(templateData.labelContainer, elements.root); } diff --git a/src/vs/workbench/contrib/terminalContrib/commandGuide/browser/terminal.commandGuide.contribution.ts b/src/vs/workbench/contrib/terminalContrib/commandGuide/browser/terminal.commandGuide.contribution.ts index ec3adb8093704..ef18811c74866 100644 --- a/src/vs/workbench/contrib/terminalContrib/commandGuide/browser/terminal.commandGuide.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/commandGuide/browser/terminal.commandGuide.contribution.ts @@ -80,7 +80,7 @@ class TerminalCommandGuideContribution extends Disposable implements ITerminalCo if (!rect) { return; } - const mouseCursorY = Math.floor(e.offsetY / (rect.height / xterm.raw.rows)); + const mouseCursorY = Math.floor((e.clientY - rect.top) / (rect.height / xterm.raw.rows)); const command = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.getCommandForLine(xterm.raw.buffer.active.viewportY + mouseCursorY); if (command && 'getOutput' in command) { xterm.markTracker.showCommandGuide(command); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts index d9bd159bfc9a3..228d047f4b254 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type { IViewportRange, IBufferRange, ILink, ILinkDecorations, Terminal } from '@xterm/xterm'; -import { DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import * as dom from '../../../../../base/browser/dom.js'; import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { convertBufferRangeToViewport } from './terminalLinkHelpers.js'; @@ -16,12 +16,11 @@ import type { URI } from '../../../../../base/common/uri.js'; import type { IParsedLink } from './terminalLinkParsing.js'; import type { IHoverAction } from '../../../../../base/browser/ui/hover/hover.js'; -export class TerminalLink extends DisposableStore implements ILink { +export class TerminalLink extends Disposable implements ILink { decorations: ILinkDecorations; - asyncActivate: Promise | undefined; - private _tooltipScheduler: RunOnceScheduler | undefined; - private _hoverListeners: DisposableStore | undefined; + private readonly _tooltipScheduler: MutableDisposable = this._register(new MutableDisposable()); + private readonly _hoverListeners = this._register(new MutableDisposable()); private readonly _onInvalidated = new Emitter(); get onInvalidated(): Event { return this._onInvalidated.event; } @@ -50,38 +49,28 @@ export class TerminalLink extends DisposableStore implements ILink { }; } - override dispose(): void { - super.dispose(); - this._hoverListeners?.dispose(); - this._hoverListeners = undefined; - this._tooltipScheduler?.dispose(); - this._tooltipScheduler = undefined; - } - activate(event: MouseEvent | undefined, text: string): void { - // Trigger the xterm.js callback synchronously but track the promise resolution so we can - // use it in tests - this.asyncActivate = this._activateCallback(event, text); + this._activateCallback(event, text); } hover(event: MouseEvent, text: string): void { const w = dom.getWindow(event); const d = w.document; // Listen for modifier before handing it off to the hover to handle so it gets disposed correctly - this._hoverListeners = new DisposableStore(); - this._hoverListeners.add(dom.addDisposableListener(d, 'keydown', e => { + const hoverListeners = this._hoverListeners.value = new DisposableStore(); + hoverListeners.add(dom.addDisposableListener(d, 'keydown', e => { if (!e.repeat && this._isModifierDown(e)) { this._enableDecorations(); } })); - this._hoverListeners.add(dom.addDisposableListener(d, 'keyup', e => { + hoverListeners.add(dom.addDisposableListener(d, 'keyup', e => { if (!e.repeat && !this._isModifierDown(e)) { this._disableDecorations(); } })); // Listen for when the terminal renders on the same line as the link - this._hoverListeners.add(this._xterm.onRender(e => { + hoverListeners.add(this._xterm.onRender(e => { const viewportRangeY = this.range.start.y - this._viewportY; if (viewportRangeY >= e.start && viewportRangeY <= e.end) { this._onInvalidated.fire(); @@ -91,7 +80,7 @@ export class TerminalLink extends DisposableStore implements ILink { // Only show the tooltip and highlight for high confidence links (not word/search workspace // links). Feedback was that this makes using the terminal overly noisy. if (this._isHighConfidenceLink) { - this._tooltipScheduler = new RunOnceScheduler(() => { + this._tooltipScheduler.value = new RunOnceScheduler(() => { this._tooltipCallback( this, convertBufferRangeToViewport(this.range, this._viewportY), @@ -99,15 +88,13 @@ export class TerminalLink extends DisposableStore implements ILink { this._isHighConfidenceLink ? () => this._disableDecorations() : undefined ); // Clear out scheduler until next hover event - this._tooltipScheduler?.dispose(); - this._tooltipScheduler = undefined; + this._tooltipScheduler.clear(); }, this._configurationService.getValue('workbench.hover.delay')); - this.add(this._tooltipScheduler); - this._tooltipScheduler.schedule(); + this._tooltipScheduler.value.schedule(); } const origin = { x: event.pageX, y: event.pageY }; - this._hoverListeners.add(dom.addDisposableListener(d, dom.EventType.MOUSE_MOVE, e => { + hoverListeners.add(dom.addDisposableListener(d, dom.EventType.MOUSE_MOVE, e => { // Update decorations if (this._isModifierDown(e)) { this._enableDecorations(); @@ -119,16 +106,14 @@ export class TerminalLink extends DisposableStore implements ILink { if (Math.abs(e.pageX - origin.x) > w.devicePixelRatio * 2 || Math.abs(e.pageY - origin.y) > w.devicePixelRatio * 2) { origin.x = e.pageX; origin.y = e.pageY; - this._tooltipScheduler?.schedule(); + this._tooltipScheduler.value?.schedule(); } })); } leave(): void { - this._hoverListeners?.dispose(); - this._hoverListeners = undefined; - this._tooltipScheduler?.dispose(); - this._tooltipScheduler = undefined; + this._hoverListeners.clear(); + this._tooltipScheduler.clear(); } private _enableDecorations(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts index 7f148d7fecf15..4ff47d600d311 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts @@ -109,14 +109,19 @@ function generateLinkSuffixRegex(eolOnly: boolean) { // "foo", lines 339-341 [#171880] // "foo", lines 339-341, characters 12-789 [#178287] `['"]?(?:,? |: ?| on )lines? ${r()}(?:-${re()})?(?:,? (?:col(?:umn)?|characters?) ${c()}(?:-${ce()})?)?` + eolSuffix, + // () and [] are interchangeable // foo(339) // foo(339,12) // foo(339, 12) // foo (339) - // ... + // foo (339,12) + // foo (339, 12) // foo: (339) - // ... - `:? ?[\\[\\(]${r()}(?:, ?${c()})?[\\]\\)]` + eolSuffix, + // foo: (339,12) + // foo: (339, 12) + // foo(339:12) [#229842] + // foo (339:12) [#229842] + `:? ?[\\[\\(]${r()}(?:(?:, ?|:)${c()})?[\\]\\)]` + eolSuffix, ]; const suffixClause = lineAndColumnRegexClauses diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts index 3f61393ab7110..099e639ee820e 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts @@ -125,6 +125,8 @@ const testLinks: ITestLink[] = [ { link: 'foo: (339)', prefix: undefined, suffix: ': (339)', hasRow: true, hasCol: false }, { link: 'foo: (339,12)', prefix: undefined, suffix: ': (339,12)', hasRow: true, hasCol: true }, { link: 'foo: (339, 12)', prefix: undefined, suffix: ': (339, 12)', hasRow: true, hasCol: true }, + { link: 'foo(339:12)', prefix: undefined, suffix: '(339:12)', hasRow: true, hasCol: true }, + { link: 'foo (339:12)', prefix: undefined, suffix: ' (339:12)', hasRow: true, hasCol: true }, // Square brackets { link: 'foo[339]', prefix: undefined, suffix: '[339]', hasRow: true, hasCol: false }, @@ -136,6 +138,8 @@ const testLinks: ITestLink[] = [ { link: 'foo: [339]', prefix: undefined, suffix: ': [339]', hasRow: true, hasCol: false }, { link: 'foo: [339,12]', prefix: undefined, suffix: ': [339,12]', hasRow: true, hasCol: true }, { link: 'foo: [339, 12]', prefix: undefined, suffix: ': [339, 12]', hasRow: true, hasCol: true }, + { link: 'foo[339:12]', prefix: undefined, suffix: '[339:12]', hasRow: true, hasCol: true }, + { link: 'foo [339:12]', prefix: undefined, suffix: ' [339:12]', hasRow: true, hasCol: true }, // OCaml-style { link: '"foo", line 339, character 12', prefix: '"', suffix: '", line 339, character 12', hasRow: true, hasCol: true }, diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts index 70d503f1526ef..df3d642769a3e 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts @@ -110,6 +110,8 @@ const supportedLinkFormats: LinkFormatInfo[] = [ { urlFormat: '{0}({1}, {2})', line: '5', column: '3' }, { urlFormat: '{0} ({1}, {2})', line: '5', column: '3' }, { urlFormat: '{0}: ({1}, {2})', line: '5', column: '3' }, + { urlFormat: '{0}({1}:{2})', line: '5', column: '3' }, + { urlFormat: '{0} ({1}:{2})', line: '5', column: '3' }, { urlFormat: '{0}:{1}', line: '5' }, { urlFormat: '{0}:{1}:{2}', line: '5', column: '3' }, { urlFormat: '{0} {1}:{2}', line: '5', column: '3' }, @@ -121,6 +123,8 @@ const supportedLinkFormats: LinkFormatInfo[] = [ { urlFormat: '{0}[{1}, {2}]', line: '5', column: '3' }, { urlFormat: '{0} [{1}, {2}]', line: '5', column: '3' }, { urlFormat: '{0}: [{1}, {2}]', line: '5', column: '3' }, + { urlFormat: '{0}[{1}:{2}]', line: '5', column: '3' }, + { urlFormat: '{0} [{1}:{2}]', line: '5', column: '3' }, { urlFormat: '{0}",{1}', line: '5' }, { urlFormat: '{0}\',{1}', line: '5' }, { urlFormat: '{0}#{1}', line: '5' }, diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts index 3a07a2806feda..11350dd2893dc 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -353,9 +353,8 @@ export class TerminalMessagePeek extends Disposable implements IPeekOutputRender if (prev) { prev.xterm.clearBuffer(); prev.xterm.clearSearchDecorations(); - // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. - // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. - prev.xterm.write(`\x1b[2J\x1b[0;0H`); + // clearBuffer tries to retain the prompt. Reset prompt, scrolling state, etc. + prev.xterm.write(`\x1bc`); return prev; } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index f6bae665b130d..72527bf9ba539 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -251,7 +251,7 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '../../platform/window/electron-s ], 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in none full screen mode with `never` when {0} is set to `native`.", '`#window.titleBarStyle#`'), + 'markdownDescription': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in non full screen mode with `never` when {0} is set to `native`.", '`#window.titleBarStyle#`'), }, 'window.dialogStyle': { 'type': 'string', diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 8bc828292b109..e97874375fae9 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -422,7 +422,8 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, if (!model.isDisposed()) { visit(model.getValue(), visitor); } - return settings.length > 0 ? [{ + return settings.length > 0 ? [{ + id: model.isDisposed() ? '' : model.id, sections: [ { settings @@ -431,7 +432,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, title: '', titleRange: nullRange, range - }] : []; + } satisfies ISettingsGroup] : []; } export class WorkspaceConfigurationEditorModel extends SettingsEditorModel { @@ -844,7 +845,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements const metadata = this.collectMetadata(resultGroups); return resultGroups.length ? - { + { allGroups: this.settingsGroups, filteredGroups, matches, @@ -894,9 +895,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements filterMatches = filterMatches .map(filteredMatch => { // Fix match ranges to offset from setting start line - return { + return { setting: filteredMatch.setting, score: filteredMatch.score, + matchType: filteredMatch.matchType, matches: filteredMatch.matches && filteredMatch.matches.map(match => { return new Range( match.startLineNumber - filteredMatch.setting.range.startLineNumber, @@ -965,7 +967,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup { - return { + return { id: resultGroup.id, range: nullRange, title: resultGroup.label, diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index bb3c6a2ac975a..f7f38038aeb67 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -395,12 +395,14 @@ export class FileIconThemeLoader { const fonts = iconThemeDocument.fonts; const fontSizes = new Map(); if (Array.isArray(fonts)) { - const defaultFontSize = fonts[0].size || '150%'; + const defaultFontSize = this.tryNormalizeFontSize(fonts[0].size) || '150%'; fonts.forEach(font => { const src = font.src.map(l => `${asCSSUrl(resolvePath(l.path))} format('${l.format}')`).join(', '); cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weight: ${font.weight}; font-style: ${font.style}; font-display: block; }`); - if (font.size !== undefined && font.size !== defaultFontSize) { - fontSizes.set(font.id, font.size); + + const fontSize = this.tryNormalizeFontSize(font.size); + if (fontSize !== undefined && fontSize !== defaultFontSize) { + fontSizes.set(font.id, fontSize); } }); cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${defaultFontSize}; }`); @@ -455,6 +457,27 @@ export class FileIconThemeLoader { return result; } + /** + * Try converting absolute font sizes to relative values. + * + * This allows them to be scaled nicely depending on where they are used. + */ + private tryNormalizeFontSize(size: string | undefined): string | undefined { + if (!size) { + return undefined; + } + + const defaultFontSizeInPx = 13; + + if (size.endsWith('px')) { + const value = parseInt(size, 10); + if (!isNaN(value)) { + return Math.round((value / defaultFontSizeInPx) * 100) + '%'; + } + } + + return size; + } } function handleParentFolder(key: string, selectors: string[]): string { diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index a8f07fb74a4f1..4d18093f41e14 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -185,7 +185,7 @@ const schema: IJSONSchema = { }, size: { type: 'string', - description: nls.localize('schema.font-size', 'The default size of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-size for valid values.'), + description: nls.localize('schema.font-size', 'The default size of the font. We strongly recommend using a percentage value, for example: 125%.'), pattern: fontSizeRegex } },