From 205c4a82b97b817ad86a9b51ecfba23cab83597e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:29:26 -0700 Subject: [PATCH 01/16] Position terminal tab hover to left Fixes #221531 --- .../contrib/terminal/browser/terminalTabsList.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index b81f20a1e7d..26a0d111935 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -52,6 +52,7 @@ import { getColorForSeverity } from 'vs/workbench/contrib/terminal/browser/termi import { TerminalContextActionRunner } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import type { IHoverAction } from 'vs/base/browser/ui/hover/hover'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; const $ = DOM.$; @@ -274,8 +275,15 @@ class TerminalTabsRenderer implements IListRenderer Date: Mon, 15 Jul 2024 08:34:14 -0700 Subject: [PATCH 02/16] Make overview ruler border consistently colored Fixes #221724 --- .../viewParts/overviewRuler/decorationsOverviewRuler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 0ca31065a45..8e3eb5666fa 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -512,9 +512,7 @@ export class DecorationsOverviewRuler extends ViewPart { canvasCtx.strokeStyle = this._settings.borderColor; canvasCtx.moveTo(0, 0); canvasCtx.lineTo(0, canvasHeight); - canvasCtx.stroke(); - - canvasCtx.moveTo(0, 0); + canvasCtx.moveTo(1, 0); canvasCtx.lineTo(canvasWidth, 0); canvasCtx.stroke(); } From 9cdf14ab18078c3e42c8bfc9684600cc7ab4607e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:07:34 -0700 Subject: [PATCH 03/16] Use opposite position when tab location is left --- .../workbench/contrib/terminal/browser/terminalTabsList.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 26a0d111935..86824099a3b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -10,7 +10,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; -import { ITerminalGroupService, ITerminalInstance, ITerminalService, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalConfigurationService, ITerminalGroupService, ITerminalInstance, ITerminalService, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -251,6 +251,7 @@ class TerminalTabsRenderer implements IListRenderer ITerminalInstance[], @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService, @ITerminalService private readonly _terminalService: ITerminalService, @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, @IHoverService private readonly _hoverService: IHoverService, @@ -283,7 +284,7 @@ class TerminalTabsRenderer implements IListRenderer Date: Mon, 15 Jul 2024 10:58:31 -0700 Subject: [PATCH 04/16] Use global fetch and crypto (#221736) Now that we're on Node 20, we can just use the global fetch and crypto which work the same in node and in the browser. --- .../extension-browser.webpack.config.js | 4 +-- .../microsoft-authentication/package.json | 1 - .../microsoft-authentication/src/AADHelper.ts | 3 +-- .../src/browser/crypto.ts | 6 ----- .../src/browser/fetch.ts | 6 ----- .../src/cryptoUtils.ts | 1 - .../src/node/crypto.ts | 7 ------ .../src/node/fetch.ts | 7 ------ extensions/microsoft-authentication/yarn.lock | 25 ------------------- 9 files changed, 2 insertions(+), 58 deletions(-) delete mode 100644 extensions/microsoft-authentication/src/browser/crypto.ts delete mode 100644 extensions/microsoft-authentication/src/browser/fetch.ts delete mode 100644 extensions/microsoft-authentication/src/node/crypto.ts delete mode 100644 extensions/microsoft-authentication/src/node/fetch.ts diff --git a/extensions/microsoft-authentication/extension-browser.webpack.config.js b/extensions/microsoft-authentication/extension-browser.webpack.config.js index 2513c7d0f9c..c0f4b92069f 100644 --- a/extensions/microsoft-authentication/extension-browser.webpack.config.js +++ b/extensions/microsoft-authentication/extension-browser.webpack.config.js @@ -22,10 +22,8 @@ module.exports = withBrowserDefaults({ }, resolve: { alias: { - './node/crypto': path.resolve(__dirname, 'src/browser/crypto'), './node/authServer': path.resolve(__dirname, 'src/browser/authServer'), - './node/buffer': path.resolve(__dirname, 'src/browser/buffer'), - './node/fetch': path.resolve(__dirname, 'src/browser/fetch'), + './node/buffer': path.resolve(__dirname, 'src/browser/buffer') } } }); diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index fd3ba077028..af46f732d92 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -117,7 +117,6 @@ "@types/uuid": "8.0.0" }, "dependencies": { - "node-fetch": "2.6.7", "@azure/ms-rest-azure-env": "^2.0.0", "@vscode/extension-telemetry": "^0.9.0" }, diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index bc4d71e56d6..713f5f12e9a 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -11,7 +11,6 @@ import { generateCodeChallenge, generateCodeVerifier, randomUUID } from './crypt import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecretStorage'; import { LoopbackAuthServer } from './node/authServer'; import { base64Decode } from './node/buffer'; -import { fetching } from './node/fetch'; import { UriEventHandler } from './UriEventHandler'; import TelemetryReporter from '@vscode/extension-telemetry'; import { Environment } from '@azure/ms-rest-azure-env'; @@ -806,7 +805,7 @@ export class AzureActiveDirectoryService { let result; let errorMessage: string | undefined; try { - result = await fetching(endpoint, { + result = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', diff --git a/extensions/microsoft-authentication/src/browser/crypto.ts b/extensions/microsoft-authentication/src/browser/crypto.ts deleted file mode 100644 index 37a1b43361f..00000000000 --- a/extensions/microsoft-authentication/src/browser/crypto.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const crypto = globalThis.crypto; diff --git a/extensions/microsoft-authentication/src/browser/fetch.ts b/extensions/microsoft-authentication/src/browser/fetch.ts deleted file mode 100644 index f7f69f1a1a1..00000000000 --- a/extensions/microsoft-authentication/src/browser/fetch.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const fetching = fetch; diff --git a/extensions/microsoft-authentication/src/cryptoUtils.ts b/extensions/microsoft-authentication/src/cryptoUtils.ts index 582dae74c3c..e608a81fc99 100644 --- a/extensions/microsoft-authentication/src/cryptoUtils.ts +++ b/extensions/microsoft-authentication/src/cryptoUtils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { base64Encode } from './node/buffer'; -import { crypto } from './node/crypto'; export function randomUUID() { return crypto.randomUUID(); diff --git a/extensions/microsoft-authentication/src/node/crypto.ts b/extensions/microsoft-authentication/src/node/crypto.ts deleted file mode 100644 index 1b45ad993c3..00000000000 --- a/extensions/microsoft-authentication/src/node/crypto.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as nodeCrypto from 'crypto'; - -export const crypto: Crypto = nodeCrypto.webcrypto as any; diff --git a/extensions/microsoft-authentication/src/node/fetch.ts b/extensions/microsoft-authentication/src/node/fetch.ts deleted file mode 100644 index 58718078e69..00000000000 --- a/extensions/microsoft-authentication/src/node/fetch.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import fetch from 'node-fetch'; - -export const fetching = fetch; diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 6f277110a56..703d54dd626 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -186,32 +186,7 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" From 4caa46afc48da8e63776ba557fb067fc04655831 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Jul 2024 11:39:33 -0700 Subject: [PATCH 05/16] Fix font family fallback in parameter hints (#221737) Fixes #211714 --- build/lib/stylelint/vscode-known-variables.json | 2 ++ .../parameterHints/browser/parameterHints.css | 4 ++++ .../parameterHints/browser/parameterHintsWidget.ts | 14 +++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 148aa2786dd..afe159177f8 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -835,6 +835,8 @@ "--vscode-editorStickyScroll-scrollableWidth", "--vscode-editorStickyScroll-foldingOpacityTransition", "--window-border-color", + "--vscode-parameterHintsWidget-editorFontFamily", + "--vscode-parameterHintsWidget-editorFontFamilyDefault", "--workspace-trust-check-color", "--workspace-trust-selected-color", "--workspace-trust-unselected-color", diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHints.css b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css index 93758171dac..3efac6c122c 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHints.css @@ -69,6 +69,10 @@ border-bottom: 1px solid var(--vscode-editorHoverWidget-border); } +.monaco-editor .parameter-hints-widget .code { + font-family: var(--vscode-parameterHintsWidget-editorFontFamily), var(--vscode-parameterHintsWidget-editorFontFamilyDefault); +} + .monaco-editor .parameter-hints-widget .docs { padding: 0 10px 0 5px; white-space: pre-wrap; diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 3036277c448..32546cff237 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -14,7 +14,7 @@ import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import 'vs/css!./parameterHints'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as languages from 'vs/editor/common/languages'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; @@ -126,9 +126,13 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { if (!this.domNodes) { return; } + const fontInfo = this.editor.getOption(EditorOption.fontInfo); - this.domNodes.element.style.fontSize = `${fontInfo.fontSize}px`; - this.domNodes.element.style.lineHeight = `${fontInfo.lineHeight / fontInfo.fontSize}`; + const element = this.domNodes.element; + element.style.fontSize = `${fontInfo.fontSize}px`; + element.style.lineHeight = `${fontInfo.lineHeight / fontInfo.fontSize}`; + element.style.setProperty('--vscode-parameterHintsWidget-editorFontFamily', fontInfo.fontFamily); + element.style.setProperty('--vscode-parameterHintsWidget-editorFontFamilyDefault', EDITOR_FONT_DEFAULTS.fontFamily); }; updateFont(); @@ -203,10 +207,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } const code = dom.append(this.domNodes.signature, $('.code')); - const fontInfo = this.editor.getOption(EditorOption.fontInfo); - code.style.fontSize = `${fontInfo.fontSize}px`; - code.style.fontFamily = fontInfo.fontFamily; - const hasParameters = signature.parameters.length > 0; const activeParameterIndex = signature.activeParameter ?? hints.activeParameter; From 1f6f8fb585ab5178c6237adc11360755d982cdfd Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 15 Jul 2024 20:25:03 +0200 Subject: [PATCH 06/16] Implements dryRun for workbench.extensions.action.debugExtensionHost --- .../debugExtensionHostAction.ts | 19 +++++++++++-------- .../extensions.contribution.ts | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts index 4ba4300bff2..e47895d7016 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts @@ -28,8 +28,7 @@ export class DebugExtensionHostAction extends Action { super(DebugExtensionHostAction.ID, DebugExtensionHostAction.LABEL, DebugExtensionHostAction.CSS_CLASS); } - override async run(): Promise { - + override async run(args: unknown): Promise { const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false); if (inspectPorts.length === 0) { const res = await this._dialogService.confirm({ @@ -49,11 +48,15 @@ export class DebugExtensionHostAction extends Action { console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`); } - return this._debugService.startDebugging(undefined, { - type: 'node', - name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), - request: 'attach', - port: inspectPorts[0].port, - }); + if (args !== undefined && (args as any)[0].dryRun) { + return { inspectPorts }; + } else { + return this._debugService.startDebugging(undefined, { + type: 'node', + name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), + request: 'attach', + port: inspectPorts[0].port, + }); + } } } diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts index ff915c04865..ded53b6b99b 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts @@ -77,9 +77,9 @@ workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, Lifecycl workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); // Register Commands -CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor) => { +CommandsRegistry.registerCommand(DebugExtensionHostAction.ID, (accessor: ServicesAccessor, ...args) => { const instantiationService = accessor.get(IInstantiationService); - instantiationService.createInstance(DebugExtensionHostAction).run(); + return instantiationService.createInstance(DebugExtensionHostAction).run(args); }); CommandsRegistry.registerCommand(StartExtensionHostProfileAction.ID, (accessor: ServicesAccessor) => { From 5491dc90d791cc2191f7a35012b4bc9835d58cb7 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:48:19 -0700 Subject: [PATCH 07/16] notebook code actions on save (#221510) --- .../browser/codeActions.contribution.ts | 5 +- .../browser/codeActionsContribution.ts | 53 ++++++++++++++++++- .../notebook/browser/notebook.contribution.ts | 12 ----- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts b/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts index afcf9915643..b8158d25544 100644 --- a/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts +++ b/src/vs/workbench/contrib/codeActions/browser/codeActions.contribution.ts @@ -11,7 +11,7 @@ import { CodeActionsExtensionPoint, codeActionsExtensionPointDescriptor } from ' import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/documentationExtensionPoint'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { CodeActionsContribution, editorConfiguration } from './codeActionsContribution'; +import { CodeActionsContribution, editorConfiguration, notebookEditorConfiguration } from './codeActionsContribution'; import { CodeActionDocumentationContribution } from './documentationContribution'; const codeActionsExtensionPoint = ExtensionsRegistry.registerExtensionPoint(codeActionsExtensionPointDescriptor); @@ -20,6 +20,9 @@ const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint(Extensions.Configuration) .registerConfiguration(editorConfiguration); +Registry.as(Extensions.Configuration) + .registerConfiguration(notebookEditorConfiguration); + class WorkbenchConfigurationContribution { constructor( @IInstantiationService instantiationService: IInstantiationService, diff --git a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts index 17e6217a6a9..3f075cb5eeb 100644 --- a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts +++ b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts @@ -35,6 +35,21 @@ const createCodeActionsAutoSave = (description: string): IJSONSchema => { }; }; +const createNotebookCodeActionsAutoSave = (description: string): IJSONSchema => { + return { + type: ['string', 'boolean'], + enum: ['explicit', 'never', true, false], + enumDescriptions: [ + nls.localize('explicit', 'Triggers Code Actions only when explicitly saved.'), + nls.localize('never', 'Never triggers Code Actions on save.'), + nls.localize('explicitBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "explicit".'), + nls.localize('neverBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "never".') + ], + default: 'explicit', + description: description + }; +}; + const codeActionsOnSaveSchema: IConfigurationPropertySchema = { oneOf: [ @@ -66,6 +81,37 @@ export const editorConfiguration = Object.freeze({ } }); +const notebookCodeActionsOnSaveSchema: IConfigurationPropertySchema = { + oneOf: [ + { + type: 'object', + additionalProperties: { + type: 'string' + }, + }, + { + type: 'array', + items: { type: 'string' } + } + ], + markdownDescription: nls.localize('notebook.codeActionsOnSave', 'Run a series of Code Actions for a notebook on save. Code Actions must be specified, the file must not be saved after delay, and the editor must not be shutting down. Example: `"notebook.source.organizeImports": "explicit"`'), + type: 'object', + additionalProperties: { + type: ['string', 'boolean'], + enum: ['explicit', 'never', true, false], + // enum: ['explicit', 'always', 'never'], -- autosave support needs to be built first + // nls.localize('always', 'Always triggers Code Actions on save, including autosave, focus, and window change events.'), + }, + default: {} +}; + +export const notebookEditorConfiguration = Object.freeze({ + ...editorConfigurationBaseNode, + properties: { + 'notebook.codeActionsOnSave': notebookCodeActionsOnSaveSchema + } +}); + export class CodeActionsContribution extends Disposable implements IWorkbenchContribution { private _contributedCodeActions: CodeActionsExtensionPoint[] = []; @@ -81,7 +127,6 @@ export class CodeActionsContribution extends Disposable implements IWorkbenchCon super(); // TODO: @justschen caching of code actions based on extensions loaded: https://github.com/microsoft/vscode/issues/216019 - languageFeatures.codeActionProvider.onDidChange(() => { this.updateSettingsFromCodeActionProviders(); this.updateConfigurationSchemaFromContribs(); @@ -114,23 +159,29 @@ export class CodeActionsContribution extends Disposable implements IWorkbenchCon private updateConfigurationSchema(codeActionContributions: readonly CodeActionsExtensionPoint[]) { const newProperties: IJSONSchemaMap = {}; + const newNotebookProperties: IJSONSchemaMap = {}; for (const [sourceAction, props] of this.getSourceActions(codeActionContributions)) { this.settings.add(sourceAction); newProperties[sourceAction] = createCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", props.title)); + newNotebookProperties[sourceAction] = createNotebookCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", props.title)); } codeActionsOnSaveSchema.properties = newProperties; + notebookCodeActionsOnSaveSchema.properties = newNotebookProperties; Registry.as(Extensions.Configuration) .notifyConfigurationSchemaUpdated(editorConfiguration); } private updateConfigurationSchemaFromContribs() { const properties: IJSONSchemaMap = { ...codeActionsOnSaveSchema.properties }; + const notebookProperties: IJSONSchemaMap = { ...notebookCodeActionsOnSaveSchema.properties }; for (const codeActionKind of this.settings) { if (!properties[codeActionKind]) { properties[codeActionKind] = createCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", codeActionKind)); + notebookProperties[codeActionKind] = createNotebookCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", codeActionKind)); } } codeActionsOnSaveSchema.properties = properties; + notebookCodeActionsOnSaveSchema.properties = notebookProperties; Registry.as(Extensions.Configuration) .notifyConfigurationSchemaUpdated(editorConfiguration); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 4e174318c11..e1206b813bd 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1004,18 +1004,6 @@ configurationRegistry.registerConfiguration({ tags: ['notebookLayout'], default: false }, - [NotebookSetting.codeActionsOnSave]: { - markdownDescription: nls.localize('notebook.codeActionsOnSave', 'Run a series of Code Actions for a notebook on save. Code Actions must be specified, the file must not be saved after delay, and the editor must not be shutting down. Example: `"notebook.source.organizeImports": "explicit"`'), - type: 'object', - additionalProperties: { - type: ['string', 'boolean'], - enum: ['explicit', 'never', true, false], - // enum: ['explicit', 'always', 'never'], -- autosave support needs to be built first - // nls.localize('always', 'Always triggers Code Actions on save, including autosave, focus, and window change events.'), - enumDescriptions: [nls.localize('explicit', 'Triggers Code Actions only when explicitly saved.'), nls.localize('never', 'Never triggers Code Actions on save.'), nls.localize('explicitBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "explicit".'), nls.localize('neverBoolean', 'Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of "never".')], - }, - default: {} - }, [NotebookSetting.formatOnCellExecution]: { markdownDescription: nls.localize('notebook.formatOnCellExecution', "Format a notebook cell upon execution. A formatter must be available."), type: 'boolean', From fbcb0742aa35b6fdab1175b163d969c9d133cee9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 15 Jul 2024 20:48:47 +0200 Subject: [PATCH 08/16] SCM - fix badge regression (#221738) --- src/vs/workbench/contrib/scm/browser/activity.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index f97f83bdcc2..332ac067fda 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -72,11 +72,11 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe switch (this._countBadgeConfig.read(reader)) { case 'all': { const repositories = this._repositories.read(reader); - return [...Iterable.map(repositories, r => ({ ...r.provider, resourceCount: this._getRepositoryResourceCount(r) }))]; + return [...Iterable.map(repositories, r => ({ provider: r.provider, resourceCount: this._getRepositoryResourceCount(r) }))]; } case 'focused': { const repository = this._activeRepository.read(reader); - return repository ? [{ ...repository.provider, resourceCount: this._getRepositoryResourceCount(repository) }] : []; + return repository ? [{ provider: repository.provider, resourceCount: this._getRepositoryResourceCount(repository) }] : []; } case 'off': return []; @@ -89,7 +89,7 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe let total = 0; for (const repository of this._countBadgeRepositories.read(reader)) { - const count = repository.count?.read(reader); + const count = repository.provider.count?.read(reader); const resourceCount = repository.resourceCount.read(reader); total = total + (count ?? resourceCount); From 0cba3e61b84760c4c1431ed1262bb8e47a7ef780 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Jul 2024 12:17:10 -0700 Subject: [PATCH 09/16] Align rendered header id generation with markdown language service (#221742) Fixes #220064 --- .../src/markdownEngine.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index fb906f63453..5f6e746a82d 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -313,7 +313,7 @@ export class MarkdownItEngine implements IMdParser { private _addNamedHeaders(md: MarkdownIt): void { const original = md.renderer.rules.heading_open; md.renderer.rules.heading_open = (tokens: Token[], idx: number, options, env, self) => { - const title = tokens[idx + 1].children!.reduce((acc, t) => acc + t.content, ''); + const title = this._tokenToPlainText(tokens[idx + 1]); let slug = this.slugifier.fromHeading(title); if (this._slugCount.has(slug.value)) { @@ -334,6 +334,21 @@ export class MarkdownItEngine implements IMdParser { }; } + private _tokenToPlainText(token: Token): string { + if (token.children) { + return token.children.map(x => this._tokenToPlainText(x)).join(''); + } + + switch (token.type) { + case 'text': + case 'emoji': + case 'code_inline': + return token.content; + default: + return ''; + } + } + private _addLinkRenderer(md: MarkdownIt): void { const original = md.renderer.rules.link_open; @@ -441,4 +456,4 @@ function normalizeHighlightLang(lang: string | undefined) { default: return lang; } -} \ No newline at end of file +} From 47e6c3aa7a1068847379000ad66fdfadc6aa748a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 15 Jul 2024 12:18:04 -0700 Subject: [PATCH 10/16] use better message in help dialog for unassigned kb in help dialog, provide configure assigned kb command, clean up code (#221728) --- .../accessibility/browser/accessibleView.ts | 7 +- .../accessibility/browser/accessibleView.ts | 136 +++++++++++------- .../browser/accessibleViewActions.ts | 21 ++- .../accessibleViewKeybindingResolver.ts | 14 +- .../common/accessibilityCommands.ts | 1 + 5 files changed, 121 insertions(+), 58 deletions(-) diff --git a/src/vs/platform/accessibility/browser/accessibleView.ts b/src/vs/platform/accessibility/browser/accessibleView.ts index 7a62c5dd358..a3e77f7f270 100644 --- a/src/vs/platform/accessibility/browser/accessibleView.ts +++ b/src/vs/platform/accessibility/browser/accessibleView.ts @@ -69,6 +69,11 @@ export interface IAccessibleViewOptions { * Keybinding items to configure */ configureKeybindingItems?: IQuickPickItem[]; + + /** + * Keybinding items that are already configured + */ + configuredKeybindingItems?: IQuickPickItem[]; } @@ -123,7 +128,7 @@ export interface IAccessibleViewService { */ getOpenAriaHint(verbositySettingKey: string): string | null; getCodeBlockContext(): ICodeBlockActionContext | undefined; - configureKeybindings(): void; + configureKeybindings(unassigned: boolean): void; openHelpLink(): void; } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index fbab89ea12e..2eea474603b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -334,7 +334,10 @@ export class AccessibleView extends Disposable { this._instantiationService.createInstance(AccessibleViewSymbolQuickPick, this).show(this._currentProvider); } - calculateCodeBlocks(markdown: string): void { + calculateCodeBlocks(markdown?: string): void { + if (!markdown) { + return; + } if (this._currentProvider?.id !== AccessibleViewProviderId.Chat) { return; } @@ -391,10 +394,10 @@ export class AccessibleView extends Disposable { this._openerService.open(URI.parse(this._currentProvider.options.readMoreUrl)); } - configureKeybindings(): void { + configureKeybindings(unassigned: boolean): void { this._inQuickPick = true; const provider = this._updateLastProvider(); - const items = provider?.options?.configureKeybindingItems; + const items = unassigned ? provider?.options?.configureKeybindingItems : provider?.options?.configuredKeybindingItems; if (!items) { return; } @@ -496,45 +499,39 @@ export class AccessibleView extends Disposable { this._accessibleViewGoToSymbolSupported.set(this._goToSymbolsSupported() ? this.getSymbols()?.length! > 0 : false); } + private _updateContent(provider: AccesibleViewContentProvider, updatedContent?: string): void { + let content = updatedContent ?? provider.provideContent(); + if (provider.options.type === AccessibleViewType.View) { + this._currentContent = content; + return; + } + const readMoreLinkHint = this._readMoreHint(provider); + const disableHelpHint = this._disableVerbosityHint(provider); + const screenReaderModeHint = this._screenReaderModeHint(provider); + const exitThisDialogHint = this._exitDialogHint(provider); + let configureKbHint = ''; + let configureAssignedKbHint = ''; + const resolvedContent = resolveContentAndKeybindingItems(this._keybindingService, screenReaderModeHint + content + readMoreLinkHint + disableHelpHint + exitThisDialogHint); + if (resolvedContent) { + content = resolvedContent.content.value; + if (resolvedContent.configureKeybindingItems) { + provider.options.configureKeybindingItems = resolvedContent.configureKeybindingItems; + configureKbHint = this._configureUnassignedKbHint(); + } + if (resolvedContent.configuredKeybindingItems) { + provider.options.configuredKeybindingItems = resolvedContent.configuredKeybindingItems; + configureAssignedKbHint = this._configureAssignedKbHint(); + } + } + this._currentContent = content + configureKbHint + configureAssignedKbHint; + } + private _render(provider: AccesibleViewContentProvider, container: HTMLElement, showAccessibleViewHelp?: boolean, updatedContent?: string): IDisposable { this._currentProvider = provider; this._accessibleViewCurrentProviderId.set(provider.id); const verbose = this._verbosityEnabled(); - const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\n\nOpen a browser window with more information related to accessibility.", AccessibilityCommandId.AccessibilityHelpOpenHelpLink) : ''; - let disableHelpHint = ''; - if (provider instanceof AccessibleContentProvider && provider.options.type === AccessibleViewType.Help && verbose) { - disableHelpHint = this._getDisableVerbosityHint(); - } - const accessibilitySupport = this._accessibilityService.isScreenReaderOptimized(); - let message = ''; - if (provider.options.type === AccessibleViewType.Help) { - const turnOnMessage = ( - isMacintosh - ? AccessibilityHelpNLS.changeConfigToOnMac - : AccessibilityHelpNLS.changeConfigToOnWinLinux - ); - if (accessibilitySupport && provider instanceof AccessibleContentProvider && provider.verbositySettingKey === AccessibilityVerbositySettingId.Editor) { - message = AccessibilityHelpNLS.auto_on; - message += '\n'; - } else if (!accessibilitySupport) { - message = AccessibilityHelpNLS.auto_off + '\n' + turnOnMessage; - message += '\n'; - } - } - const exitThisDialogHint = verbose && !provider.options.position ? localize('exit', '\n\nExit this dialog (Escape).') : ''; - let content = updatedContent ?? provider.provideContent(); - if (provider.options.type === AccessibleViewType.Help) { - const resolvedContent = resolveContentAndKeybindingItems(this._keybindingService, content + readMoreLink + disableHelpHint + exitThisDialogHint); - if (resolvedContent) { - content = resolvedContent.content.value; - if (resolvedContent.configureKeybindingItems) { - provider.options.configureKeybindingItems = resolvedContent.configureKeybindingItems; - } - } - } - const newContent = message + content; - this.calculateCodeBlocks(newContent); - this._currentContent = newContent; + this._updateContent(provider, updatedContent); + this.calculateCodeBlocks(this._currentContent); this._updateContextKeys(provider, true); const widgetIsFocused = this._editorWidget.hasTextFocus() || this._editorWidget.hasWidgetFocus(); this._getTextModel(URI.from({ path: `accessible-view-${provider.id}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { @@ -708,7 +705,7 @@ export class AccessibleView extends Disposable { accessibleViewHelpProvider = new AccessibleContentProvider( lastProvider.id as AccessibleViewProviderId, { type: AccessibleViewType.Help }, - () => lastProvider.options.customHelp ? lastProvider?.options.customHelp() : this._getAccessibleViewHelpDialogContent(this._goToSymbolsSupported()), + () => lastProvider.options.customHelp ? lastProvider?.options.customHelp() : this._accessibleViewHelpDialogContent(this._goToSymbolsSupported()), () => { this._contextViewService.hideContextView(); // HACK: Delay to allow the context view to hide #207638 @@ -720,7 +717,7 @@ export class AccessibleView extends Disposable { accessibleViewHelpProvider = new ExtensionContentProvider( lastProvider.id as AccessibleViewProviderId, { type: AccessibleViewType.Help }, - () => lastProvider.options.customHelp ? lastProvider?.options.customHelp() : this._getAccessibleViewHelpDialogContent(this._goToSymbolsSupported()), + () => lastProvider.options.customHelp ? lastProvider?.options.customHelp() : this._accessibleViewHelpDialogContent(this._goToSymbolsSupported()), () => { this._contextViewService.hideContextView(); // HACK: Delay to allow the context view to hide #207638 @@ -735,9 +732,9 @@ export class AccessibleView extends Disposable { } } - private _getAccessibleViewHelpDialogContent(providerHasSymbols?: boolean): string { - const navigationHint = this._getNavigationHint(); - const goToSymbolHint = this._getGoToSymbolHint(providerHasSymbols); + private _accessibleViewHelpDialogContent(providerHasSymbols?: boolean): string { + const navigationHint = this._navigationHint(); + const goToSymbolHint = this._goToSymbolHint(providerHasSymbols); const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab)."); const chatHints = this._getChatHints(); @@ -766,20 +763,61 @@ export class AccessibleView extends Disposable { localize('runInTerminal', " - Run the code block in the terminal.\n")].join('\n'); } - private _getNavigationHint(): string { + private _navigationHint(): string { return localize('accessibleViewNextPreviousHint', "Show the next item or previous item.", AccessibilityCommandId.ShowNext, AccessibilityCommandId.ShowPrevious); } - private _getDisableVerbosityHint(): string { - return localize('acessibleViewDisableHint', "\n\nDisable accessibility verbosity for this feature.", AccessibilityCommandId.DisableVerbosityHint); + private _disableVerbosityHint(provider: AccesibleViewContentProvider): string { + if (provider.options.type === AccessibleViewType.Help && this._verbosityEnabled()) { + return localize('acessibleViewDisableHint', "\n\nDisable accessibility verbosity for this feature.", AccessibilityCommandId.DisableVerbosityHint); + } + return ''; } - private _getGoToSymbolHint(providerHasSymbols?: boolean): string | undefined { + private _goToSymbolHint(providerHasSymbols?: boolean): string | undefined { if (!providerHasSymbols) { return; } return localize('goToSymbolHint', 'Go to a symbol.', AccessibilityCommandId.GoToSymbol); } + + private _configureUnassignedKbHint(): string { + const configureKb = this._keybindingService.lookupKeybinding(AccessibilityCommandId.AccessibilityHelpConfigureKeybindings)?.getAriaLabel(); + const keybindingToConfigureQuickPick = configureKb ? '(' + configureKb + ')' : 'by assigning a keybinding to the command Accessibility Help Configure Keybindings.'; + return localize('configureKb', '\n\nConfigure keybindings for commands that lack them {0}.', keybindingToConfigureQuickPick); + } + + private _configureAssignedKbHint(): string { + const configureKb = this._keybindingService.lookupKeybinding(AccessibilityCommandId.AccessibilityHelpConfigureAssignedKeybindings)?.getAriaLabel(); + const keybindingToConfigureQuickPick = configureKb ? '(' + configureKb + ')' : 'by assigning a keybinding to the command Accessibility Help Configure Assigned Keybindings.'; + return localize('configureKbAssigned', '\n\nConfigure keybindings for commands that already have assignments {0}.', keybindingToConfigureQuickPick); + } + + private _screenReaderModeHint(provider: AccesibleViewContentProvider): string { + const accessibilitySupport = this._accessibilityService.isScreenReaderOptimized(); + let screenReaderModeHint = ''; + const turnOnMessage = ( + isMacintosh + ? AccessibilityHelpNLS.changeConfigToOnMac + : AccessibilityHelpNLS.changeConfigToOnWinLinux + ); + if (accessibilitySupport && provider.id === AccessibleViewProviderId.Editor) { + screenReaderModeHint = AccessibilityHelpNLS.auto_on; + screenReaderModeHint += '\n'; + } else if (!accessibilitySupport) { + screenReaderModeHint = AccessibilityHelpNLS.auto_off + '\n' + turnOnMessage; + screenReaderModeHint += '\n'; + } + return screenReaderModeHint; + } + + private _exitDialogHint(provider: AccesibleViewContentProvider): string { + return this._verbosityEnabled() && !provider.options.position ? localize('exit', '\n\nExit this dialog (Escape).') : ''; + } + + private _readMoreHint(provider: AccesibleViewContentProvider): string { + return provider.options.readMoreUrl ? localize("openDoc", "\n\nOpen a browser window with more information related to accessibility.", AccessibilityCommandId.AccessibilityHelpOpenHelpLink) : ''; + } } export class AccessibleViewService extends Disposable implements IAccessibleViewService { @@ -800,8 +838,8 @@ export class AccessibleViewService extends Disposable implements IAccessibleView } this._accessibleView.show(provider, undefined, undefined, position); } - configureKeybindings(): void { - this._accessibleView?.configureKeybindings(); + configureKeybindings(unassigned: boolean): void { + this._accessibleView?.configureKeybindings(unassigned); } openHelpLink(): void { this._accessibleView?.openHelpLink(); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 7aba4fa5baf..2efee1b2df4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -62,7 +62,6 @@ class AccessibleViewNextCodeBlockAction extends Action2 { mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, }, weight: KeybindingWeight.WorkbenchContrib, }, - // to icon: Codicon.arrowRight, menu: { @@ -242,11 +241,29 @@ class AccessibilityHelpConfigureKeybindingsAction extends Action2 { }); } async run(accessor: ServicesAccessor): Promise { - await accessor.get(IAccessibleViewService).configureKeybindings(); + await accessor.get(IAccessibleViewService).configureKeybindings(true); } } registerAction2(AccessibilityHelpConfigureKeybindingsAction); +class AccessibilityHelpConfigureAssignedKeybindingsAction extends Action2 { + constructor() { + super({ + id: AccessibilityCommandId.AccessibilityHelpConfigureAssignedKeybindings, + precondition: ContextKeyExpr.and(accessibilityHelpIsShown), + keybinding: { + primary: KeyMod.Alt | KeyCode.KeyA, + weight: KeybindingWeight.WorkbenchContrib + }, + title: localize('editor.action.accessibilityHelpConfigureAssignedKeybindings', "Accessibility Help Configure Assigned Keybindings") + }); + } + async run(accessor: ServicesAccessor): Promise { + await accessor.get(IAccessibleViewService).configureKeybindings(false); + } +} +registerAction2(AccessibilityHelpConfigureAssignedKeybindingsAction); + class AccessibilityHelpOpenHelpLinkAction extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewKeybindingResolver.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewKeybindingResolver.ts index 88bfbd309d4..c9a2f4051eb 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewKeybindingResolver.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewKeybindingResolver.ts @@ -6,13 +6,13 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; -import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -export function resolveContentAndKeybindingItems(keybindingService: IKeybindingService, value?: string): { content: MarkdownString; configureKeybindingItems: IPickerQuickAccessItem[] | undefined } | undefined { +export function resolveContentAndKeybindingItems(keybindingService: IKeybindingService, value?: string): { content: MarkdownString; configureKeybindingItems: IPickerQuickAccessItem[] | undefined; configuredKeybindingItems: IPickerQuickAccessItem[] | undefined } | undefined { if (!value) { return; } const configureKeybindingItems: IPickerQuickAccessItem[] = []; + const configuredKeybindingItems: IPickerQuickAccessItem[] = []; const matches = value.matchAll(/\.*)\>/gm); for (const match of [...matches]) { const commandId = match?.groups?.commandId; @@ -20,21 +20,23 @@ export function resolveContentAndKeybindingItems(keybindingService: IKeybindingS if (match?.length && commandId) { const keybinding = keybindingService.lookupKeybinding(commandId)?.getAriaLabel(); if (!keybinding) { - const configureKb = keybindingService.lookupKeybinding(AccessibilityCommandId.AccessibilityHelpConfigureKeybindings)?.getAriaLabel(); - const keybindingToConfigureQuickPick = configureKb ? '(' + configureKb + ')' : 'by assigning a keybinding to the command Accessibility Help Configure Keybindings.'; - kbLabel = `, configure a keybinding ` + keybindingToConfigureQuickPick; + kbLabel = ` (unassigned keybinding)`; configureKeybindingItems.push({ label: commandId, id: commandId }); } else { kbLabel = ' (' + keybinding + ')'; + configuredKeybindingItems.push({ + label: commandId, + id: commandId + }); } value = value.replace(match[0], kbLabel); } } const content = new MarkdownString(value); content.isTrusted = true; - return { content, configureKeybindingItems: configureKeybindingItems.length ? configureKeybindingItems : undefined }; + return { content, configureKeybindingItems: configureKeybindingItems.length ? configureKeybindingItems : undefined, configuredKeybindingItems: configuredKeybindingItems.length ? configuredKeybindingItems : undefined }; } diff --git a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts index d689c02503e..7e84a29bc75 100644 --- a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts +++ b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts @@ -14,5 +14,6 @@ export const enum AccessibilityCommandId { NextCodeBlock = 'editor.action.accessibleViewNextCodeBlock', PreviousCodeBlock = 'editor.action.accessibleViewPreviousCodeBlock', AccessibilityHelpConfigureKeybindings = 'editor.action.accessibilityHelpConfigureKeybindings', + AccessibilityHelpConfigureAssignedKeybindings = 'editor.action.accessibilityHelpConfigureAssignedKeybindings', AccessibilityHelpOpenHelpLink = 'editor.action.accessibilityHelpOpenHelpLink', } From 286795f0c5798c24aa52bf679874ed3572cc5406 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 15 Jul 2024 13:06:34 -0700 Subject: [PATCH 11/16] Pick up latest TS for building VS Code (#221743) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7dd025650de..1217f5ebc26 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.6.0-dev.20240711", + "typescript": "^5.6.0-dev.20240715", "util": "^0.12.4", "vscode-nls-dev": "^3.3.1", "webpack": "^5.91.0", diff --git a/yarn.lock b/yarn.lock index beb0f96be56..4b971ed46a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10316,10 +10316,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.6.0-dev.20240711: - version "5.6.0-dev.20240711" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.0-dev.20240711.tgz#2190141012fd1f3a535c13c7d4ba258c13ab4d74" - integrity sha512-7P96lBbF9T4qBB2IseUQBz/IakXa9a0zLOywLmWTZQB6EFbRnRMlwa7103Q44mwI8tfvdXRmed3ICuxHxSNUwA== +typescript@^5.6.0-dev.20240715: + version "5.6.0-dev.20240715" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.0-dev.20240715.tgz#18b9e994f99916b90917943d5de8e78f781d1d70" + integrity sha512-CLF8WFoqLgHgxQqjklkEOw3gT99Y2YNU4+TfkJurX5bfejAUYpb2jBjiYOn5Rq9HCew6ceZlRaG7Q++6/fBvVA== typical@^4.0.0: version "4.0.0" From 02e78484555b39323e0322abe76be9d2e6be7a36 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 15 Jul 2024 15:52:06 -0700 Subject: [PATCH 12/16] Tweak lazy var setting description (#221752) See #221513 --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index ad182943034..5a5fa7d5cc6 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -628,7 +628,7 @@ configurationRegistry.registerConfiguration({ nls.localize('debug.autoExpandLazyVariables.on', "Always automatically expand lazy variables."), nls.localize('debug.autoExpandLazyVariables.off', "Never automatically expand lazy variables.") ], - description: nls.localize('debug.autoExpandLazyVariables', "Controls if variables, such as getters, are automatically resolved and expanded by the debugger.") + description: nls.localize('debug.autoExpandLazyVariables', "Controls whether variables that are lazily resolved, such as getters, are automatically resolved and expanded by the debugger.") }, 'debug.enableStatusBarColor': { type: 'boolean', From 6d8cfe1becb5aaca667e00e1370ddb31ae500241 Mon Sep 17 00:00:00 2001 From: Yutian Li Date: Mon, 15 Jul 2024 19:06:13 -0400 Subject: [PATCH 13/16] Use the builtin local command in shell integration scripts --- .../browser/media/shellIntegration-bash.sh | 18 +++++++++--------- .../browser/media/shellIntegration-rc.zsh | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index 06036fcbaae..917cc1e46d0 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -112,15 +112,15 @@ __vsc_escape_value() { fi # Process text byte by byte, not by codepoint. - local -r LC_ALL=C - local -r str="${1}" - local -ir len="${#str}" + builtin local -r LC_ALL=C + builtin local -r str="${1}" + builtin local -ir len="${#str}" - local -i i - local -i val - local byte - local token - local out='' + builtin local -i i + builtin local -i val + builtin local byte + builtin local token + builtin local out='' for (( i=0; i < "${#str}"; ++i )); do # Escape backslashes, semi-colons specially, then special ASCII chars below space (0x20). @@ -326,7 +326,7 @@ __vsc_prompt_cmd_original() { __vsc_restore_exit_code "${__vsc_status}" # Evaluate the original PROMPT_COMMAND similarly to how bash would normally # See https://unix.stackexchange.com/a/672843 for technique - local cmd + builtin local cmd for cmd in "${__vsc_original_prompt_command[@]}"; do eval "${cmd:-}" done diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh index c555d1ac157..54a13d575f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh @@ -159,7 +159,7 @@ __vsc_update_prompt() { } __vsc_precmd() { - local __vsc_status="$?" + builtin local __vsc_status="$?" if [ -z "${__vsc_in_command_execution-}" ]; then # not in command execution __vsc_command_output_start From 716fbebf83c21b931f4378677859f6a022600861 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 15 Jul 2024 16:32:44 -0700 Subject: [PATCH 14/16] fix: honor `isSticky` for autodetected chat participants (#221747) --- src/vs/workbench/contrib/chat/browser/chat.ts | 1 + src/vs/workbench/contrib/chat/browser/chatWidget.ts | 8 ++++++++ .../chat/browser/contrib/chatInputEditorContrib.ts | 10 ++++++++++ src/vs/workbench/contrib/chat/common/chatModel.ts | 10 +++++++++- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 083e4766c29..6d15c37c9d3 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -139,6 +139,7 @@ export interface IChatWidget { readonly onDidAcceptInput: Event; readonly onDidHide: Event; readonly onDidSubmitAgent: Event<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>; + readonly onDidChangeAgent: Event<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>; readonly onDidChangeParsedInput: Event; readonly onDidChangeContext: Event<{ removed?: IChatRequestVariableEntry[]; added?: IChatRequestVariableEntry[] }>; readonly location: ChatAgentLocation; diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 1347c91a598..b48cec90f5f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -88,6 +88,9 @@ export class ChatWidget extends Disposable implements IChatWidget { private readonly _onDidSubmitAgent = this._register(new Emitter<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>()); public readonly onDidSubmitAgent = this._onDidSubmitAgent.event; + private _onDidChangeAgent = this._register(new Emitter<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>()); + readonly onDidChangeAgent = this._onDidChangeAgent.event; + private _onDidFocus = this._register(new Emitter()); readonly onDidFocus = this._onDidFocus.event; @@ -688,6 +691,11 @@ export class ChatWidget extends Disposable implements IChatWidget { c.setInputState(viewState.inputState?.[c.id]); } }); + this.viewModelDisposables.add(model.onDidChange((e) => { + if (e.kind === 'setAgent') { + this._onDidChangeAgent.fire({ agent: e.agent, slashCommand: e.command }); + } + })); if (this.tree) { this.onDidChangeItems(); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 24aafd6c101..b72abe680df 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -242,12 +242,22 @@ class InputEditorSlashCommandMode extends Disposable { private readonly widget: IChatWidget ) { super(); + this._register(this.widget.onDidChangeAgent(e => { + if (e.slashCommand && e.slashCommand.isSticky || !e.slashCommand && e.agent.metadata.isSticky) { + this.repopulateAgentCommand(e.agent, e.slashCommand); + } + })); this._register(this.widget.onDidSubmitAgent(e => { this.repopulateAgentCommand(e.agent, e.slashCommand); })); } private async repopulateAgentCommand(agent: IChatAgentData, slashCommand: IChatAgentCommand | undefined) { + // Make sure we don't repopulate if the user already has something in the input + if (this.widget.inputEditor.getValue().trim()) { + return; + } + let value: string | undefined; if (slashCommand && slashCommand.isSticky) { value = `${chatAgentLeader}${agent.name} ${chatSubcommandLeader}${slashCommand.name} `; diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index f4119a6ea55..113f94f6c8a 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -545,7 +545,8 @@ export function isSerializableSessionData(obj: unknown): obj is ISerializableCha export type IChatChangeEvent = | IChatInitEvent | IChatAddRequestEvent | IChatChangedRequestEvent | IChatRemoveRequestEvent - | IChatAddResponseEvent; + | IChatAddResponseEvent + | IChatSetAgentEvent; export interface IChatAddRequestEvent { kind: 'addRequest'; @@ -586,6 +587,12 @@ export interface IChatRemoveRequestEvent { reason: ChatRequestRemovalReason; } +export interface IChatSetAgentEvent { + kind: 'setAgent'; + agent: IChatAgentData; + command?: IChatAgentCommand; +} + export interface IChatInitEvent { kind: 'initialize'; } @@ -892,6 +899,7 @@ export class ChatModel extends Disposable implements IChatModel { const agent = this.chatAgentService.getAgent(progress.agentId); if (agent) { request.response.setAgent(agent, progress.command); + this._onDidChange.fire({ kind: 'setAgent', agent, command: progress.command }); } } else { this.logService.error(`Couldn't handle progress: ${JSON.stringify(progress)}`); From 06f8ef165cc37104bc78b7f36209d95333474348 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 16 Jul 2024 02:42:25 +0200 Subject: [PATCH 15/16] Removing the introductory text in the accessible editor hover (#221628) removing the introductory text in the editor hover --- src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts index 8404497154c..382054b8262 100644 --- a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts +++ b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts @@ -23,8 +23,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { labelForHoverVerbosityAction } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant'; namespace HoverAccessibilityHelpNLS { - export const introHoverPart = localize('introHoverPart', 'The focused hover part content is the following:'); - export const introHoverFull = localize('introHoverFull', 'The full focused hover content is the following:'); export const increaseVerbosity = localize('increaseVerbosity', '- The focused hover part verbosity level can be increased with the Increase Hover Verbosity command.', INCREASE_HOVER_VERBOSITY_ACTION_ID); export const decreaseVerbosity = localize('decreaseVerbosity', '- The focused hover part verbosity level can be decreased with the Decrease Hover Verbosity command.', DECREASE_HOVER_VERBOSITY_ACTION_ID); } @@ -123,7 +121,6 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc if (includeVerbosityActions) { contents.push(...this._descriptionsOfVerbosityActionsForIndex(focusedHoverIndex)); } - contents.push(HoverAccessibilityHelpNLS.introHoverPart); contents.push(accessibleContent); return contents.join('\n\n'); } else { @@ -132,7 +129,6 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc return ''; } const contents: string[] = []; - contents.push(HoverAccessibilityHelpNLS.introHoverFull); contents.push(accessibleContent); return contents.join('\n\n'); } From 8d40a807262b5c08f745dfaf12eb3848203a8faf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 16 Jul 2024 00:56:23 -0700 Subject: [PATCH 16/16] Add drop/paste resource for css (#221612) * New drop and paste providers that create a url function snippet * added url pasting feature * added url pasting feature * added url pasting feature * Target just dropping/pasting resources for now * Move files * Remove unused strings * Removing more unused logic for now * Remove tsconfig change * Remove doc file * Capitalize * Remove old proposal names --------- Co-authored-by: Meghan Kulkarni Co-authored-by: Martin Aeschlimann --- .../client/src/browser/cssClientMain.ts | 2 + .../src/dropOrPaste/dropOrPasteResource.ts | 153 ++++++++++++++++++ .../client/src/dropOrPaste/shared.ts | 42 +++++ .../client/src/dropOrPaste/uriList.ts | 38 +++++ .../client/src/node/cssClientMain.ts | 13 +- .../client/tsconfig.json | 3 +- extensions/css-language-features/package.json | 3 + 7 files changed, 248 insertions(+), 6 deletions(-) create mode 100644 extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts create mode 100644 extensions/css-language-features/client/src/dropOrPaste/shared.ts create mode 100644 extensions/css-language-features/client/src/dropOrPaste/uriList.ts diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts index c89997ffaa0..1d4153d9836 100644 --- a/extensions/css-language-features/client/src/browser/cssClientMain.ts +++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts @@ -7,6 +7,7 @@ import { ExtensionContext, Uri, l10n } from 'vscode'; import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../cssClient'; import { LanguageClient } from 'vscode-languageclient/browser'; +import { registerDropOrPasteResourceSupport } from '../dropOrPaste/dropOrPasteResource'; let client: BaseLanguageClient | undefined; @@ -23,6 +24,7 @@ export async function activate(context: ExtensionContext) { client = await startClient(context, newLanguageClient, { TextDecoder }); + context.subscriptions.push(registerDropOrPasteResourceSupport({ language: 'css', scheme: '*' })); } catch (e) { console.log(e); } diff --git a/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts b/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts new file mode 100644 index 00000000000..046fe6a2e88 --- /dev/null +++ b/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as vscode from 'vscode'; +import { getDocumentDir, Mimes, Schemes } from './shared'; +import { UriList } from './uriList'; + +class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider { + readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'url'); + + async provideDocumentDropEdits( + document: vscode.TextDocument, + position: vscode.Position, + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken, + ): Promise { + const uriList = await this.getUriList(dataTransfer); + if (!uriList.entries.length || token.isCancellationRequested) { + return; + } + + const snippet = await this.createUriListSnippet(uriList); + if (!snippet || token.isCancellationRequested) { + return; + } + + return { + kind: this.kind, + title: snippet.label, + insertText: snippet.snippet.value, + yieldTo: this.pasteAsCssUrlByDefault(document, position) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')] + }; + } + + async provideDocumentPasteEdits( + document: vscode.TextDocument, + ranges: readonly vscode.Range[], + dataTransfer: vscode.DataTransfer, + _context: vscode.DocumentPasteEditContext, + token: vscode.CancellationToken + ): Promise { + const uriList = await this.getUriList(dataTransfer); + if (!uriList.entries.length || token.isCancellationRequested) { + return; + } + + const snippet = await this.createUriListSnippet(uriList); + if (!snippet || token.isCancellationRequested) { + return; + } + + return [{ + kind: this.kind, + title: snippet.label, + insertText: snippet.snippet.value, + yieldTo: this.pasteAsCssUrlByDefault(document, ranges[0].start) ? [] : [vscode.DocumentDropOrPasteEditKind.Empty.append('uri')] + }]; + } + + private async getUriList(dataTransfer: vscode.DataTransfer): Promise { + const urlList = await dataTransfer.get(Mimes.uriList)?.asString(); + if (urlList) { + return UriList.from(urlList); + } + + // Find file entries + const uris: vscode.Uri[] = []; + for (const [_, entry] of dataTransfer) { + const file = entry.asFile(); + if (file?.uri) { + uris.push(file.uri); + } + } + + return new UriList(uris.map(uri => ({ uri, str: uri.toString(true) }))); + } + + private async createUriListSnippet(uriList: UriList): Promise<{ readonly snippet: vscode.SnippetString; readonly label: string } | undefined> { + if (!uriList.entries.length) { + return; + } + + const snippet = new vscode.SnippetString(); + for (let i = 0; i < uriList.entries.length; i++) { + const uri = uriList.entries[i]; + const relativePath = getRelativePath(uri.uri); + const urlText = relativePath ?? uri.str; + + snippet.appendText(`url(${urlText})`); + if (i !== uriList.entries.length - 1) { + snippet.appendText(' '); + } + } + + return { + snippet, + label: uriList.entries.length > 1 + ? vscode.l10n.t('Insert url() Functions') + : vscode.l10n.t('Insert url() Function') + }; + } + + private pasteAsCssUrlByDefault(document: vscode.TextDocument, position: vscode.Position): boolean { + const regex = /url\(.+?\)/gi; + for (const match of Array.from(document.lineAt(position.line).text.matchAll(regex))) { + if (position.character > match.index && position.character < match.index + match[0].length) { + return false; + } + } + return true; + } +} + +function getRelativePath(file: vscode.Uri): string | undefined { + const dir = getDocumentDir(file); + if (dir && dir.scheme === file.scheme && dir.authority === file.authority) { + if (file.scheme === Schemes.file) { + // On windows, we must use the native `path.relative` to generate the relative path + // so that drive-letters are resolved cast insensitively. However we then want to + // convert back to a posix path to insert in to the document + const relativePath = path.relative(dir.fsPath, file.fsPath); + return path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep)); + } + + return path.posix.relative(dir.path, file.path); + } + + return undefined; +} + +export function registerDropOrPasteResourceSupport(selector: vscode.DocumentSelector): vscode.Disposable { + const provider = new DropOrPasteResourceProvider(); + + return vscode.Disposable.from( + vscode.languages.registerDocumentDropEditProvider(selector, provider, { + providedDropEditKinds: [provider.kind], + dropMimeTypes: [ + Mimes.uriList, + 'files' + ] + }), + vscode.languages.registerDocumentPasteEditProvider(selector, provider, { + providedPasteEditKinds: [provider.kind], + pasteMimeTypes: [ + Mimes.uriList, + 'files' + ] + }) + ); +} diff --git a/extensions/css-language-features/client/src/dropOrPaste/shared.ts b/extensions/css-language-features/client/src/dropOrPaste/shared.ts new file mode 100644 index 00000000000..548bccfec69 --- /dev/null +++ b/extensions/css-language-features/client/src/dropOrPaste/shared.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Utils } from 'vscode-uri'; + +export const Schemes = Object.freeze({ + file: 'file', + notebookCell: 'vscode-notebook-cell', + untitled: 'untitled', +}); + +export const Mimes = Object.freeze({ + plain: 'text/plain', + uriList: 'text/uri-list', +}); + + +export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined { + const docUri = getParentDocumentUri(uri); + if (docUri.scheme === Schemes.untitled) { + return vscode.workspace.workspaceFolders?.[0]?.uri; + } + return Utils.dirname(docUri); +} + +function getParentDocumentUri(uri: vscode.Uri): vscode.Uri { + if (uri.scheme === Schemes.notebookCell) { + // is notebook documents necessary? + for (const notebook of vscode.workspace.notebookDocuments) { + for (const cell of notebook.getCells()) { + if (cell.document.uri.toString() === uri.toString()) { + return notebook.uri; + } + } + } + } + + return uri; +} diff --git a/extensions/css-language-features/client/src/dropOrPaste/uriList.ts b/extensions/css-language-features/client/src/dropOrPaste/uriList.ts new file mode 100644 index 00000000000..ed20b1ee797 --- /dev/null +++ b/extensions/css-language-features/client/src/dropOrPaste/uriList.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +function splitUriList(str: string): string[] { + return str.split('\r\n'); +} + +function parseUriList(str: string): string[] { + return splitUriList(str) + .filter(value => !value.startsWith('#')) // Remove comments + .map(value => value.trim()); +} + +export class UriList { + + static from(str: string): UriList { + return new UriList(coalesce(parseUriList(str).map(line => { + try { + return { uri: vscode.Uri.parse(line), str: line }; + } catch { + // Uri parse failure + return undefined; + } + }))); + } + + constructor( + public readonly entries: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str: string }> + ) { } +} + +function coalesce(array: ReadonlyArray): T[] { + return array.filter(e => !!e); +} diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts index 802b58ab286..96926979b2a 100644 --- a/extensions/css-language-features/client/src/node/cssClientMain.ts +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getNodeFSRequestService } from './nodeFs'; -import { ExtensionContext, extensions, l10n } from 'vscode'; -import { startClient, LanguageClientConstructor } from '../cssClient'; -import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; import { TextDecoder } from 'util'; - +import { ExtensionContext, extensions, l10n } from 'vscode'; +import { BaseLanguageClient, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; +import { LanguageClientConstructor, startClient } from '../cssClient'; +import { getNodeFSRequestService } from './nodeFs'; +import { registerDropOrPasteResourceSupport } from '../dropOrPaste/dropOrPasteResource'; let client: BaseLanguageClient | undefined; @@ -37,6 +37,8 @@ export async function activate(context: ExtensionContext) { process.env['VSCODE_L10N_BUNDLE_LOCATION'] = l10n.uri?.toString() ?? ''; client = await startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder }); + + context.subscriptions.push(registerDropOrPasteResourceSupport({ language: 'css', scheme: '*' })); } export async function deactivate(): Promise { @@ -45,3 +47,4 @@ export async function deactivate(): Promise { client = undefined; } } + diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index 44b77895c10..17bf7e962a8 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -8,6 +8,7 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts" + "../../../src/vscode-dts/vscode.d.ts", + "../../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" ] } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 4ddfe8fce0d..57cbba323a4 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -23,6 +23,9 @@ "supported": true } }, + "enabledApiProposals": [ + "documentPaste" + ], "scripts": { "compile": "npx gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", "watch": "npx gulp watch-extension:css-language-features-client watch-extension:css-language-features-server",