diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index ceef664b85..a192a31e57 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -94,6 +94,10 @@ "name": "vs/workbench/contrib/issue", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/keybindings", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/markers", "project": "vscode-workbench" diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 477d530a0e..3afe9fa433 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -54,7 +54,7 @@ "url": "vscode://schemas/keybindings" }, { - "fileMatch": "vscode://defaultsettings/*/*.json", + "fileMatch": "vscode://defaultsettings/*.json", "url": "vscode://schemas/settings/default" }, { diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index 17d27d437c..9e08022d9a 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -123,7 +123,10 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) try { quickpick.busy = true; - const children = (await vscode.workspace.fs.readDirectory(folder)).map(([name]) => name); + const children = (await vscode.workspace.fs.readDirectory(folder)) + .map(([name]) => name) + .filter(name => name !== '.git'); + quickpick.items = children.map(name => ({ label: name })); quickpick.selectedItems = quickpick.items; quickpick.busy = false; diff --git a/resources/web/code-web.js b/resources/web/code-web.js index b6d19a65f1..1ab1b7cde5 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -36,7 +36,8 @@ const args = minimist(process.argv, { 'help', 'verbose', 'wrap-iframe', - 'enable-sync' + 'enable-sync', + 'trusted-types' ], string: [ 'scheme', @@ -53,6 +54,7 @@ if (args.help) { 'yarn web [options]\n' + ' --no-launch Do not open VSCode web in the browser\n' + ' --wrap-iframe Wrap the Web Worker Extension Host in an iframe\n' + + ' --trusted-types Enable trusted types (report only)\n' + ' --enable-sync Enable sync by default\n' + ' --scheme Protocol (https or http)\n' + ' --host Remote host\n' + @@ -396,7 +398,13 @@ async function handleRoot(req, res) { .replace('{{WEBVIEW_ENDPOINT}}', '') .replace('{{REMOTE_USER_DATA_URI}}', ''); - res.writeHead(200, { 'Content-Type': 'text/html' }); + + const headers = { 'Content-Type': 'text/html' }; + if (args['trusted-types']) { + headers['Content-Security-Policy-Report-Only'] = 'require-trusted-types-for \'script\';'; + } + + res.writeHead(200, headers); return res.end(data); } diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 0cbcab9921..861728d0b5 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -533,6 +533,53 @@ class Queue { } } +class LoadEstimator { + + private static _HISTORY_LENGTH = 10; + private static _INSTANCE: LoadEstimator | null = null; + public static getInstance(): LoadEstimator { + if (!LoadEstimator._INSTANCE) { + LoadEstimator._INSTANCE = new LoadEstimator(); + } + return LoadEstimator._INSTANCE; + } + + private lastRuns: number[]; + + constructor() { + this.lastRuns = []; + const now = Date.now(); + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + this.lastRuns[i] = now - 1000 * i; + } + setInterval(() => { + for (let i = LoadEstimator._HISTORY_LENGTH; i >= 1; i--) { + this.lastRuns[i] = this.lastRuns[i - 1]; + } + this.lastRuns[0] = Date.now(); + }, 1000); + } + + /** + * returns an estimative number, from 0 (low load) to 1 (high load) + */ + public load(): number { + const now = Date.now(); + const historyLimit = (1 + LoadEstimator._HISTORY_LENGTH) * 1000; + let score = 0; + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + if (now - this.lastRuns[i] <= historyLimit) { + score++; + } + } + return 1 - score / LoadEstimator._HISTORY_LENGTH; + } + + public hasHighLoad(): boolean { + return this.load() >= 0.5; + } +} + /** * Same as Protocol, but will actually track messages and acks. * Moreover, it will ensure no messages are lost if there are no event listeners. @@ -559,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _socketReader: ProtocolReader; private _socketDisposables: IDisposable[]; + private readonly _loadEstimator = LoadEstimator.getInstance(); + private readonly _onControlMessage = new BufferedEmitter(); readonly onControlMessage: Event = this._onControlMessage.event; @@ -670,15 +719,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const timeSinceLastIncomingMsg = Date.now() - this._socketReader.lastReadTime; if (timeSinceLastIncomingMsg >= ProtocolConstants.KeepAliveTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since we received a server message + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._incomingKeepAliveTimeout = setTimeout(() => { this._incomingKeepAliveTimeout = null; this._recvKeepAliveCheck(); - }, ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg + 5); + }, Math.max(ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg, 0) + 5); } public getSocket(): ISocket { @@ -821,15 +874,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const oldestUnacknowledgedMsg = this._outgoingUnackMsg.peek()!; const timeSinceOldestUnacknowledgedMsg = Date.now() - oldestUnacknowledgedMsg.writtenTime; if (timeSinceOldestUnacknowledgedMsg >= ProtocolConstants.AcknowledgeTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since our sent message was acknowledged + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._outgoingAckTimeout = setTimeout(() => { this._outgoingAckTimeout = null; this._recvAckCheck(); - }, ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg + 5); + }, Math.max(ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg, 0) + 5); } private _sendAck(): void { diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 4b97402366..d7cbb94985 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -440,7 +440,13 @@ class WindowIndicator implements IWindowIndicator { // Find credentials from DOM const credentialsElement = document.getElementById('vscode-workbench-credentials'); const credentialsElementAttribute = credentialsElement ? credentialsElement.getAttribute('data-settings') : undefined; - const credentialsProvider = new LocalStorageCredentialsProvider(credentialsElementAttribute ? JSON.parse(credentialsElementAttribute) : []); + let credentials = undefined; + if (credentialsElementAttribute) { + try { + credentials = JSON.parse(credentialsElementAttribute); + } catch (error) { /* Invalid credentials are passed. Ignore. */ } + } + const credentialsProvider = new LocalStorageCredentialsProvider(credentials || []); // Finally create workbench create(document.body, { diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index 7c88cd8384..e81b971389 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -358,7 +358,7 @@ registerThemingParticipant((theme, collector) => { if (!caretBackground) { caretBackground = caret.opposite(); } - collector.addRule(`.monaco-editor .cursor { background-color: ${caret}; border-color: ${caret}; color: ${caretBackground}; }`); + collector.addRule(`.monaco-editor .cursors-layer .cursor { background-color: ${caret}; border-color: ${caret}; color: ${caretBackground}; }`); if (theme.type === 'hc') { collector.addRule(`.monaco-editor .cursors-layer.has-selection .cursor { border-left: 1px solid ${caretBackground}; border-right: 1px solid ${caretBackground}; }`); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index b495d77449..ac6d80a89b 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -553,28 +553,51 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise { - const dependents = this.getDependents(extension, installed); - if (dependents.length) { - const remainingDependents = dependents.filter(dependent => extension !== dependent && otherExtensionsToUninstall.indexOf(dependent) === -1); - if (remainingDependents.length) { - return Promise.reject(new Error(this.getDependentsErrorMessage(extension, remainingDependents))); - } + private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise { + const extensionsToUninstall = [extension, ...otherExtensionsToUninstall]; + for (const e of extensionsToUninstall) { + this.checkForDependents(e, extensionsToUninstall, installed, extension); } - return Promise.all([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]).then(() => undefined); + await Promise.all([this.uninstallExtension(extension), ...otherExtensionsToUninstall.map(d => this.doUninstall(d))]); } - private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { + private checkForDependents(extension: ILocalExtension, extensionsToUninstall: ILocalExtension[], installed: ILocalExtension[], extensionToUninstall: ILocalExtension): void { + const dependents = this.getDependents(extension, installed); + if (dependents.length) { + const remainingDependents = dependents.filter(dependent => extensionsToUninstall.indexOf(dependent) === -1); + if (remainingDependents.length) { + throw new Error(this.getDependentsErrorMessage(extension, remainingDependents, extensionToUninstall)); + } + } + } + + private getDependentsErrorMessage(dependingExtension: ILocalExtension, dependents: ILocalExtension[], extensionToUninstall: ILocalExtension): string { + if (extensionToUninstall === dependingExtension) { + if (dependents.length === 1) { + return nls.localize('singleDependentError', "Cannot uninstall '{0}' extension. '{1}' extension depends on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name); + } + if (dependents.length === 2) { + return nls.localize('twoDependentsError', "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + } + return nls.localize('multipleDependentsError', "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + } if (dependents.length === 1) { - return nls.localize('singleDependentError', "Cannot uninstall extension '{0}'. Extension '{1}' depends on this.", - extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name); + return nls.localize('singleIndirectDependentError', "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName + || dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name); } if (dependents.length === 2) { - return nls.localize('twoDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}' and '{2}' depend on this.", - extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + return nls.localize('twoIndirectDependentsError', "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName + || dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); } - return nls.localize('multipleDependentsError', "Cannot uninstall extension '{0}'. Extensions '{1}', '{2}' and others depend on this.", - extension.manifest.displayName || extension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + return nls.localize('multipleIndirectDependentsError', "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this.", + extensionToUninstall.manifest.displayName || extensionToUninstall.manifest.name, dependingExtension.manifest.displayName + || dependingExtension.manifest.name, dependents[0].manifest.displayName || dependents[0].manifest.name, dependents[1].manifest.displayName || dependents[1].manifest.name); + } private getAllPackExtensionsToUninstall(extension: ILocalExtension, installed: ILocalExtension[], checked: ILocalExtension[] = []): ILocalExtension[] { diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 059e275bf5..797c15e3bc 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -81,8 +81,6 @@ export abstract class AbstractKeybindingService extends Disposable implements IK protected _log(str: string): void { if (this._logging) { this._logService.info(`[KeybindingService]: ${str}`); - } else { - this._logService.trace(`[KeybindingService]: ${str}`); } } diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index d7d50d2c18..649a928441 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -20,7 +20,7 @@ import { fromNow } from 'vs/base/common/date'; import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Platform, platform } from 'vs/base/common/platform'; -const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser']; +const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser', 'ms-vscode.github-browser']; interface IAccountUsage { extensionId: string; diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 83e7a95e42..a792578e6f 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -32,7 +32,7 @@ class WorkerRequireInterceptor extends RequireInterceptor { } export class ExtHostExtensionService extends AbstractExtHostExtensionService { - readonly extensionRuntime = ExtensionRuntime.Node; + readonly extensionRuntime = ExtensionRuntime.Webworker; private _fakeModules?: WorkerRequireInterceptor; diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 7f08978583..7a4aad9027 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -324,8 +324,6 @@ configurationRegistry.registerConfiguration({ type: 'string', format: 'color-hex', default: '#FF0000', - minLength: 4, - maxLength: 9, description: nls.localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.") }, 'screencastMode.mouseIndicatorSize': { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index bdefcbd5c0..158991a4d2 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -106,6 +106,11 @@ export abstract class CompositePart extends Part { return this.activeComposite; } + // We cannot open the composite if we have not been created yet + if (!this.element) { + return undefined; // {{SQL CARBON EDIT}} strict-null-checks + } + // Open return this.doOpenComposite(id, focus); } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 5f994a915f..8dcae53d43 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -187,11 +187,11 @@ function registerCommandsAndActions(): void { registerDebugViewMenuItem(MenuId.DebugCallStackContext, RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED)); registerDebugViewMenuItem(MenuId.DebugCallStackContext, COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame')); - registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.localize('setValue', "Set Value"), 10, CONTEXT_SET_VARIABLE_SUPPORTED); - registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 20); - registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 30, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT); - registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 10, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '3_watch'); - registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break When Value Changes"), 20, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, '5_breakpoint'); + registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.localize('setValue', "Set Value"), 10, CONTEXT_SET_VARIABLE_SUPPORTED, undefined, '3_modification'); + registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste'); + registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste'); + registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands'); + registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break When Value Changes"), 200, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands'); // Touch Bar if (isMacintosh) { diff --git a/src/vs/workbench/contrib/debug/browser/replFilter.ts b/src/vs/workbench/contrib/debug/browser/replFilter.ts index 8e5313b233..e9f6500a0f 100644 --- a/src/vs/workbench/contrib/debug/browser/replFilter.ts +++ b/src/vs/workbench/contrib/debug/browser/replFilter.ts @@ -22,6 +22,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ReplEvaluationResult, ReplEvaluationInput } from 'vs/workbench/contrib/debug/common/replModel'; type ParsedQuery = { @@ -51,6 +52,11 @@ export class ReplFilter implements ITreeFilter { } filter(element: IReplElement, parentVisibility: TreeVisibility): TreeFilterResult { + if (element instanceof ReplEvaluationInput || element instanceof ReplEvaluationResult) { + // Only filter the output events, everything else is visible https://github.com/microsoft/vscode/issues/105863 + return TreeVisibility.Visible; + } + let includeQueryPresent = false; let includeQueryMatched = false; @@ -107,7 +113,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem { @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService) { super(null, action); - this.delayedFilterUpdate = new Delayer(200); + this.delayedFilterUpdate = new Delayer(400); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); } diff --git a/src/vs/workbench/contrib/debug/common/replModel.ts b/src/vs/workbench/contrib/debug/common/replModel.ts index eba05910d1..01ba94178a 100644 --- a/src/vs/workbench/contrib/debug/common/replModel.ts +++ b/src/vs/workbench/contrib/debug/common/replModel.ts @@ -27,7 +27,7 @@ export class SimpleReplElement implements IReplElement { ) { } toString(): string { - const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}:${this.sourceData.lineNumber}` : ''; + const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}` : ''; return this.value + sourceStr; } @@ -145,7 +145,7 @@ export class ReplGroup implements IReplElement { } toString(): string { - const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}:${this.sourceData.lineNumber}` : ''; + const sourceStr = this.sourceData ? ` ${this.sourceData.source.name}` : ''; return this.name + sourceStr; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 750d0cb5d8..36deb574e5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -117,9 +117,9 @@ export class ExtensionRecommendationsService extends Disposable implements IExte this.staticRecommendations.activate(), // {{SQL CARBON EDIT}} add ours this.scenarioRecommendations.activate(), // {{SQL CARBON EDIT}} add ours this.lifecycleService.when(LifecyclePhase.Eventually) - .then(() => { + .then(async () => { if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - this.activateProactiveRecommendations(); + await this.activateProactiveRecommendations(); } }) ]); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 60db2f72ad..72f4fc8b0e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -686,7 +686,7 @@ export class ExtensionsListView extends ViewPane { const recommendations = await this.extensionRecommendationsService.getWorkspaceRecommendations(); const { important } = await this.extensionRecommendationsService.getConfigBasedRecommendations(); for (const configBasedRecommendation of important) { - if (recommendations.some(r => r.extensionId !== configBasedRecommendation.extensionId)) { + if (!recommendations.find(r => r.extensionId === configBasedRecommendation.extensionId)) { recommendations.push(configBasedRecommendation); } } diff --git a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts new file mode 100644 index 0000000000..9f3f969506 --- /dev/null +++ b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { rendererLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; +import { IOutputService } from 'vs/workbench/contrib/output/common/output'; + +const developerCategory = { value: nls.localize({ key: 'developer', comment: ['A developer on Code itself or someone diagnosing issues in Code'] }, "Developer"), original: 'Developer' }; + +class ToggleKeybindingsLogAction extends Action2 { + + constructor() { + super({ + id: 'workbench.action.toggleKeybindingsLog', + title: { value: nls.localize('toggleKeybindingsLog', "Toggle Keyboard Shortcuts Troubleshooting"), original: 'Toggle Keyboard Shortcuts Troubleshooting' }, + category: developerCategory, + f1: true + }); + } + + run(accessor: ServicesAccessor): void { + const logging = accessor.get(IKeybindingService).toggleLogging(); + if (logging) { + const outputService = accessor.get(IOutputService); + outputService.showChannel(rendererLogChannelId); + } + } +} + +registerAction2(ToggleKeybindingsLogAction); diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index fff3755745..61529e31ce 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -279,7 +279,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { ) { super(null, action); this.focusContextKey = Constants.MarkerViewFilterFocusContextKey.bindTo(contextKeyService); - this.delayedFilterUpdate = new Delayer(200); + this.delayedFilterUpdate = new Delayer(400); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); this._register(filterController.onDidFocusFilter(() => this.focus())); this._register(filterController.onDidClearFilterText(() => this.clearFilterText())); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 964ab17c56..47f8211040 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -209,7 +209,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } async updateLayout() { - console.log('update layout'); if (!this._model) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index b72d06aaca..84a884bdba 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -387,7 +387,7 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri const existingEditors = group.editors.filter(editor => editor.resource && isEqual(editor.resource, notebookUri) && !(editor instanceof NotebookEditorInput)); if (existingEditors.length) { - return { override: this.editorService.openEditor(existingEditors[0]) }; + return undefined; } const userAssociatedEditors = this.getUserAssociatedEditors(notebookUri); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 8b423fea14..a67cb51ce6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -44,9 +44,8 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor } dispose(): void { - + super.dispose(); } - } export class NotebookDiffEditorInput extends EditorInput { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 274f203204..eb7668ce89 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -193,7 +193,6 @@ export type ToWebviewMessage = | IHideOutputMessage | IShowOutputMessage | IUpdatePreloadResourceMessage - | IFocusOutputMessage | IUpdateDecorationsMessage | ICustomRendererMessage; diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 187f24163d..66a80e3e51 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -561,7 +561,7 @@ export class OutlinePane extends ViewPane { return; } - this._revealTreeSelection(newModel, e.element, !!e.editorOptions.preserveFocus || !e.editorOptions.pinned, e.sideBySide); + this._revealTreeSelection(newModel, e.element, !!e.editorOptions.preserveFocus, !!e.editorOptions.pinned, e.sideBySide); })); // feature: reveal editor selection in outline @@ -615,12 +615,13 @@ export class OutlinePane extends ViewPane { })); } - private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, preserveFocus: boolean, aside: boolean): Promise { + private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, preserveFocus: boolean, pinned: boolean, aside: boolean): Promise { await this._editorService.openCodeEditor( { resource: model.uri, options: { preserveFocus, + pinned, selection: Range.collapseToStart(element.symbol.selectionRange), selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport, } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 6caf9ac07a..e806bb90f1 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/scm'; import { Event, Emitter } from 'vs/base/common/event'; import { basename, dirname, isEqual } from 'vs/base/common/resources'; -import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, addClass, toggleClass, removeClass, Dimension } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; @@ -74,6 +74,7 @@ import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; import { Codicon } from 'vs/base/common/codicons'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; +import { IPosition } from 'vs/editor/common/core/position'; type TreeElement = ISCMRepository | ISCMInput | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -131,6 +132,7 @@ class InputRenderer implements ICompressibleTreeRenderer(); private contentHeights = new WeakMap(); + private editorPositions = new WeakMap(); constructor( private outerLayout: ISCMLayout, @@ -161,6 +163,21 @@ class InputRenderer implements ICompressibleTreeRenderer this.inputWidgets.delete(input) }); + // Widget position + const position = this.editorPositions.get(input); + + if (position) { + templateData.inputWidget.position = position; + } + + disposables.add(toDisposable(() => { + const position = templateData.inputWidget.position; + + if (position) { + this.editorPositions.set(input, position); + } + })); + // Rerender the element whenever the editor content height changes const onDidChangeContentHeight = () => { const contentHeight = templateData.inputWidget.getContentHeight(); @@ -1343,6 +1360,16 @@ class SCMInputWidget extends Disposable { this.model = { input, textModel }; } + get position(): IPosition | null { + return this.inputEditor.getPosition(); + } + + set position(position: IPosition | null) { + if (position) { + this.inputEditor.setPosition(position); + } + } + constructor( container: HTMLElement, @IContextKeyService contextKeyService: IContextKeyService, diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 1006603a05..fd4af3b47d 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -473,7 +473,7 @@ configurationRegistry.registerConfiguration({ default: 30, minimum: 0, maximum: 30 }, 'task.quickOpen.detail': { - markdownDescription: nls.localize('task.quickOpen.detail', "Controls whether to show the task detail for task that have a detail in the Run Task quick pick."), + markdownDescription: nls.localize('task.quickOpen.detail', "Controls whether to show the task detail for tasks that have a detail in task quick picks, such as Run Task."), type: 'boolean', default: true }, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 554752b802..6f605e3ba2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -39,6 +39,7 @@ import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/co import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; async function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { @@ -1350,6 +1351,28 @@ export function registerTerminalActions() { accessor.get(ITerminalService).findPrevious(); } }); + registerAction2(class extends Action2 { + constructor() { + super({ + id: TERMINAL_COMMAND_ID.SEARCH_WORKSPACE, + title: { value: localize('workbench.action.terminal.searchWorkspace', "Search Workspace"), original: 'Search Workspace' }, + f1: true, + category, + keybinding: [ + { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F, + when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED), + weight: KeybindingWeight.WorkbenchContrib + } + ], + precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED + }); + } + run(accessor: ServicesAccessor) { + const query = accessor.get(ITerminalService).getActiveInstance()?.selection; + FindInFilesCommand(accessor, { query } as IFindInFilesArgs); + } + }); registerAction2(class extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c049940fbd..c9d820b20b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -838,9 +838,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { setTimeout(() => this.layout(this._timeoutDimension!), 0); } } - if (!visible) { - this._widgetManager.hideHovers(); - } } public scrollDownLine(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts index ff772754eb..f8090a35cf 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts @@ -5,15 +5,11 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; -import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class TerminalWidgetManager implements IDisposable { private _container: HTMLElement | undefined; private _attached: Map = new Map(); - constructor(@IHoverService private readonly _hoverService: IHoverService) { - } - attachToElement(terminalWrapper: HTMLElement) { if (!this._container) { this._container = document.createElement('div'); @@ -23,17 +19,12 @@ export class TerminalWidgetManager implements IDisposable { } dispose(): void { - this.hideHovers(); if (this._container && this._container.parentElement) { this._container.parentElement.removeChild(this._container); this._container = undefined; } } - hideHovers(): void { - this._hoverService.hideHover(); - } - attachWidget(widget: ITerminalWidget): IDisposable | undefined { if (!this._container) { return undefined; // {{SQL CARBON EDIT}} strict-null-check diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1ddf483e9a..9a1fdfd43b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -500,7 +500,8 @@ export const enum TERMINAL_COMMAND_ID { NAVIGATION_MODE_EXIT = 'workbench.action.terminal.navigationModeExit', NAVIGATION_MODE_FOCUS_NEXT = 'workbench.action.terminal.navigationModeFocusNext', NAVIGATION_MODE_FOCUS_PREVIOUS = 'workbench.action.terminal.navigationModeFocusPrevious', - SHOW_ENVIRONMENT_INFORMATION = 'workbench.action.terminal.showEnvironmentInformation' + SHOW_ENVIRONMENT_INFORMATION = 'workbench.action.terminal.showEnvironmentInformation', + SEARCH_WORKSPACE = 'workbench.action.terminal.searchWorkspace' } export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index 82241fc9a9..3128ed1500 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -41,6 +41,7 @@ import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme import { IHoverService, IHoverOptions, IHoverTarget } from 'vs/workbench/services/hover/browser/hover'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { isMacintosh } from 'vs/base/common/platform'; class Root implements ITreeItem { label = { label: 'root' }; @@ -692,7 +693,6 @@ class TreeRenderer extends Disposable implements ITreeRenderer('editor.hover.delay'); } get templateId(): string { @@ -757,35 +756,33 @@ class TreeRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); - templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { + const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); + templateData.resourceLabel.setResource({ name: label, description, resource: labelResource }, { fileKind: this.getFileKind(node), - title: undefined, + title: '', hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough, + strikethrough: treeItemLabel?.strikethrough }); + fallbackHover = this.labelService.getUriLabel(labelResource); } else { templateData.resourceLabel.setResource({ name: label, description }, { - title: undefined, + title: '', hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData), - strikethrough: treeItemLabel?.strikethrough, + strikethrough: treeItemLabel?.strikethrough }); } - templateData.icon.title = title ? title : ''; - if (iconUrl) { templateData.icon.className = 'custom-view-tree-node-item-icon'; templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl); @@ -808,19 +805,27 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.setAlignment(templateData.container, node))); - this.setupHovers(node, templateData.container, disposableStore, label); + this.setupHovers(node, templateData.resourceLabel.element.firstElementChild!, disposableStore, fallbackHover); + this.setupHovers(node, templateData.icon, disposableStore, fallbackHover); } private setupHovers(node: ITreeItem, htmlElement: HTMLElement, disposableStore: DisposableStore, label: string | undefined): void { const hoverService = this.hoverService; - const hoverDelay = this.hoverDelay; + // Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely. + // On Mac, the delay is 1500. + const hoverDelay = isMacintosh ? 1500 : 500; let hoverOptions: IHoverOptions | undefined; + let mouseX: number | undefined; function mouseOver(this: HTMLElement, e: MouseEvent): any { let isHovering = true; + function mouseMove(this: HTMLElement, e: MouseEvent): any { + mouseX = e.x; + } function mouseLeave(this: HTMLElement, e: MouseEvent): any { isHovering = false; } this.addEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave, { passive: true }); + this.addEventListener(DOM.EventType.MOUSE_MOVE, mouseMove, { passive: true }); setTimeout(async () => { if (node instanceof ResolvableTreeItem) { await node.resolve(); @@ -834,9 +839,12 @@ class TreeRenderer extends Disposable implements ITreeRendererhoverOptions.target).x = e.x; + if (mouseX !== undefined) { + (hoverOptions.target).x = mouseX; + } hoverService.showHover(hoverOptions); } + this.removeEventListener(DOM.EventType.MOUSE_MOVE, mouseMove); this.removeEventListener(DOM.EventType.MOUSE_LEAVE, mouseLeave); }, hoverDelay); } diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index db9ff0a3b3..b33019a825 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -45,6 +45,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { isUUID } from 'vs/base/common/uuid'; +import { join } from 'vs/base/common/path'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -154,17 +155,24 @@ export class LocalProcessExtensionHost implements IExtensionHost { ]).then(data => { const pipeName = data[0]; const portNumber = data[1]; + const env = objects.mixin(objects.deepClone(process.env), { + AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true, + VSCODE_IPC_HOOK_EXTHOST: pipeName, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose), + VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : this._environmentService.log + }); + + if (platform.isMacintosh) { + // Unset `DYLD_LIBRARY_PATH`, as it leads to extension host crashes + // See https://github.com/microsoft/vscode/issues/104525 + delete env['DYLD_LIBRARY_PATH']; + } const opts = { - env: objects.mixin(objects.deepClone(process.env), { - AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', - PIPE_LOGGING: 'true', - VERBOSE_LOGGING: true, - VSCODE_IPC_HOOK_EXTHOST: pipeName, - VSCODE_HANDLES_UNCAUGHT_ERRORS: true, - VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose), - VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : this._environmentService.log - }), + env: env, // We only detach the extension host on windows. Linux and Mac orphan by default // and detach under Linux and Mac create another process group. // We detach because we have noticed that when the renderer exits, its child processes @@ -199,6 +207,11 @@ export class LocalProcessExtensionHost implements IExtensionHost { crashReporterStartOptions.submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId); crashReporterStartOptions.uploadToServer = true; } + // In the upload to server case, there is a bug in electron that creates client_id file in the current + // working directory. Setting the env BREAKPAD_DUMP_LOCATION will force electron to create the file in that location, + // For https://github.com/microsoft/vscode/issues/105743 + const extHostCrashDirectory = this._environmentService.crashReporterDirectory || this._environmentService.userDataPath; + opts.env.BREAKPAD_DUMP_LOCATION = join(extHostCrashDirectory, `${ExtensionHostLogFileName} Crash Reports`); opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterStartOptions); } diff --git a/src/vs/workbench/services/hover/browser/hover.ts b/src/vs/workbench/services/hover/browser/hover.ts index 322c89470a..3bc7b6adcd 100644 --- a/src/vs/workbench/services/hover/browser/hover.ts +++ b/src/vs/workbench/services/hover/browser/hover.ts @@ -30,11 +30,6 @@ export interface IHoverService { * ``` */ showHover(options: IHoverOptions, focus?: boolean): void; - - /** - * Hides the hover if it was visible. - */ - hideHover(): void; } export interface IHoverOptions { diff --git a/src/vs/workbench/services/hover/browser/hoverService.ts b/src/vs/workbench/services/hover/browser/hoverService.ts index fad5c3ad49..3852bb0ba1 100644 --- a/src/vs/workbench/services/hover/browser/hoverService.ts +++ b/src/vs/workbench/services/hover/browser/hoverService.ts @@ -12,6 +12,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { HoverWidget } from 'vs/workbench/services/hover/browser/hoverWidget'; import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview'; +import { IDisposable } from 'vs/base/common/lifecycle'; export class HoverService implements IHoverService { declare readonly _serviceBrand: undefined; @@ -35,14 +36,20 @@ export class HoverService implements IHoverService { const provider = this._contextViewService as IContextViewProvider; provider.showContextView(new HoverContextViewDelegate(hover, focus)); hover.onRequestLayout(() => provider.layout()); + + if ('IntersectionObserver' in window) { + const observer = new IntersectionObserver(e => this._intersectionChange(e, hover), { threshold: 0 }); + const firstTargetElement = 'targetElements' in options.target ? options.target.targetElements[0] : options.target; + observer.observe(firstTargetElement); + hover.onDispose(() => observer.disconnect()); + } } - hideHover(): void { - if (!this._currentHoverOptions) { - return; + private _intersectionChange(entries: IntersectionObserverEntry[], hover: IDisposable): void { + const entry = entries[entries.length - 1]; + if (!entry.isIntersecting) { + hover.dispose(); } - this._currentHoverOptions = undefined; - this._contextViewService.hideContextView(); } } diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index fe4a710bac..f1e444cebf 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -31,7 +31,7 @@ import { IUserKeybindingItem, KeybindingIO, OutputBuilder } from 'vs/workbench/s import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { Action2, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -48,7 +48,6 @@ import { ScanCode, ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/com import { flatten } from 'vs/base/common/arrays'; import { BrowserFeatures, KeyboardSupport } from 'vs/base/browser/canIUse'; import { ILogService } from 'vs/platform/log/common/log'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; interface ContributedKeyBinding { @@ -745,26 +744,6 @@ let schema: IJSONSchema = { } }; -const preferencesCategory = nls.localize('preferences', "Preferences"); - -class ToggleKeybindingsLogAction extends Action2 { - - constructor() { - super({ - id: 'workbench.action.toggleKeybindingsLog', - title: { value: nls.localize('toggleKeybindingsLog', "Toggle Keyboard Shortcuts Troubleshooting"), original: 'Toggle Keyboard Shortcuts Troubleshooting' }, - category: preferencesCategory, - f1: true - }); - } - - run(accessor: ServicesAccessor): void { - accessor.get(IKeybindingService).toggleLogging(); - } -} - -registerAction2(ToggleKeybindingsLogAction); - let schemaRegistry = Registry.as(Extensions.JSONContribution); schemaRegistry.registerSchema(schemaId, schema); diff --git a/src/vs/workbench/services/preferences/common/preferencesValidation.ts b/src/vs/workbench/services/preferences/common/preferencesValidation.ts index 6f44d79b54..10a1f36318 100644 --- a/src/vs/workbench/services/preferences/common/preferencesValidation.ts +++ b/src/vs/workbench/services/preferences/common/preferencesValidation.ts @@ -3,9 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { JSONSchemaType } from 'vs/base/common/jsonSchema'; -import { isArray } from 'vs/base/common/types'; import * as nls from 'vs/nls'; +import { JSONSchemaType } from 'vs/base/common/jsonSchema'; +import { Color } from 'vs/base/common/color'; +import { isArray } from 'vs/base/common/types'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; type Validator = { enabled: boolean, isValid: (value: T) => boolean; message: string }; @@ -111,6 +112,11 @@ function getStringValidators(prop: IConfigurationPropertySchema) { isValid: ((value: string) => patternRegex!.test(value)), message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern) }, + { + enabled: prop.format === 'color-hex', + isValid: ((value: string) => Color.Format.CSS.parseHex(value)), + message: nls.localize('validations.colorFormat', "Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.") + } ].filter(validation => validation.enabled); } diff --git a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts index 3dd26081c3..fae06b3cc6 100644 --- a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts @@ -19,7 +19,7 @@ export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadStreamCapability { - readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities; + get capabilities() { return this.fileSystemProvider.capabilities; } readonly onDidChangeCapabilities: Event = this.fileSystemProvider.onDidChangeCapabilities; private readonly _onDidChangeFile = this._register(new Emitter()); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 2302f04fc0..dfa2f3be0b 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -342,6 +342,9 @@ import 'vs/workbench/contrib/remote/browser/remote'; // CodeEditor Contributions import 'vs/workbench/contrib/codeEditor/browser/codeEditor.contribution'; +// Keybindings Contributions +import 'vs/workbench/contrib/keybindings/browser/keybindings.contribution'; + // Execution import 'vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 551a60897a..e6e9f54219 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -148,10 +148,14 @@ interface IWindowIndicator { } interface IInitialColorTheme { + + /** + * Initial color theme type. + */ themeType: 'light' | 'dark' | 'hc'; /** - * a list of workbench colors + * A list of workbench colors to apply initially. */ colors?: { [colorId: string]: string }; } @@ -241,6 +245,8 @@ interface IWorkbenchConstructionOptions { /** * Session id of the current authenticated user + * + * @deprecated Instead pass current authenticated user info through [credentialsProvider](#credentialsProvider) */ readonly authenticationSessionId?: string; @@ -293,7 +299,9 @@ interface IWorkbenchConstructionOptions { userDataProvider?: IFileSystemProvider; /** - * Enables user data sync by default and syncs into the current authenticated user account using the provided [authenticationSessionId}(#authenticationSessionId). + * Enables Settings Sync by default. + * + * Syncs with the current authenticated user account (provided in [credentialsProvider](#credentialsProvider)) by default. */ readonly enableSyncByDefault?: boolean; @@ -554,6 +562,7 @@ export { IHomeIndicator, IProductConfiguration, IWindowIndicator, + IInitialColorTheme, // Default layout IDefaultView,