From 698d823d2b9ac36084a572b968e7d0d3d4942ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janey=20Mu=C3=B1oz?= <38973325+janeymunoz@users.noreply.github.com> Date: Thu, 15 Oct 2020 12:07:15 -0700 Subject: [PATCH 001/121] Update vscode.d.ts - Fixed erroneous reference to deprecated function - Fixed typo ("ExtensionContent" to "ExtensionContext") --- src/vs/vscode.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 1168fb0a790..d013982d6b9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5659,7 +5659,7 @@ declare module 'vscode' { * Get the absolute path of a resource contained in the extension. * * *Note* that an absolute uri can be constructed via [`Uri.joinPath`](#Uri.joinPath) and - * [`extensionUri`](#ExtensionContent.extensionUri), e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` + * [`extensionUri`](#ExtensionContext.extensionUri), e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` * * @param relativePath A relative path to a resource contained in the extension. * @return The absolute path of the resource. @@ -5688,7 +5688,7 @@ declare module 'vscode' { * Use [`workspaceState`](#ExtensionContext.workspaceState) or * [`globalState`](#ExtensionContext.globalState) to store key value data. * - * @deprecated Use [storagePath](#ExtensionContent.storageUri) instead. + * @deprecated Use [storagePath](#ExtensionContext.storageUri) instead. */ readonly storagePath: string | undefined; @@ -5711,7 +5711,7 @@ declare module 'vscode' { * * Use [`globalState`](#ExtensionContext.globalState) to store key value data. * - * @deprecated Use [globalStoragePath](#ExtensionContent.globalStorageUri) instead. + * @deprecated Use [globalStorageUri](#ExtensionContext.globalStorageUri) instead. */ readonly globalStoragePath: string; From f3e58fbdd06169cd1d8de3ba81c763867e9808d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janey=20Mu=C3=B1oz?= <38973325+janeymunoz@users.noreply.github.com> Date: Thu, 15 Oct 2020 12:20:34 -0700 Subject: [PATCH 002/121] Fix nearby link typo --- src/vs/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d013982d6b9..4a1a51b934c 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5688,7 +5688,7 @@ declare module 'vscode' { * Use [`workspaceState`](#ExtensionContext.workspaceState) or * [`globalState`](#ExtensionContext.globalState) to store key value data. * - * @deprecated Use [storagePath](#ExtensionContext.storageUri) instead. + * @deprecated Use [storageUri](#ExtensionContext.storageUri) instead. */ readonly storagePath: string | undefined; From c54735c3420728f36d5959711cd48adf1826ebdc Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 15 Oct 2020 15:59:46 -0700 Subject: [PATCH 003/121] wip --- .../terminal/browser/terminalConfigHelper.ts | 4 + .../terminal/browser/terminalInstance.ts | 3 + .../browser/terminalTypeAheadAddon.ts | 396 ++++++++++++++++++ .../terminal/browser/xterm-private.d.ts | 2 + .../contrib/terminal/common/terminal.ts | 2 + .../terminal/common/terminalConfiguration.ts | 30 ++ 6 files changed, 437 insertions(+) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 35b90cbeaa0..6dc11347198 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -40,6 +40,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { private readonly _onWorkspacePermissionsChanged = new Emitter(); public get onWorkspacePermissionsChanged(): Event { return this._onWorkspacePermissionsChanged.event; } + private readonly _onConfigChanged = new Emitter(); + public get onConfigChanged(): Event { return this._onConfigChanged.event; } + public constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, @@ -71,6 +74,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { configValues.fontWeightBold = this._normalizeFontWeight(configValues.fontWeightBold, DEFAULT_BOLD_FONT_WEIGHT); this.config = configValues; + this._onConfigChanged.fire(); } public configFontIsMonospace(): boolean { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c781273cd58..707572a0506 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -45,6 +45,7 @@ import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/bro import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { LatencyTelemetryAddon } from 'vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon'; +import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -452,6 +453,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const latencyAddon = this._register(this._instantiationService.createInstance(LatencyTelemetryAddon, this._processManager)); this._xterm.loadAddon(latencyAddon); + this._xterm.loadAddon(new TypeAheadAddon(this._processManager, this._configHelper)); + return xterm; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts new file mode 100644 index 00000000000..148c618cdb3 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -0,0 +1,396 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Terminal, ITerminalAddon, IDisposable, IBuffer, IBufferCell } from 'xterm'; +import { ITerminalProcessManager, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { Color } from 'vs/base/common/color'; + +const CSI = '\x1b['; +const SHOW_CURSOR = `${CSI}?25h`; +const HIDE_CURSOR = `${CSI}?25l`; + +const setCursorPos = (x: number, y: number) => `${CSI}${y + 1};${x + 1}H`; +const setCursorCoordinate = (buffer: IBuffer, c: ICoordinate) => setCursorPos(c.x, c.y + (c.baseY - buffer.baseY)); + +const enum ReapplyBehavior { + /** Applying the prediction to the line is a no-op */ + NoOp, + /** The prediction can be applied to the given line */ + Apply, + /** The prediction would overwrite other, unexpected data on the line */ + Mismatch, +} + +interface ICoordinate { + x: number; + y: number; + baseY: number; +} + +const getCellAtCoordinate = (b: IBuffer, c: ICoordinate) => b.getLine(c.y + c.baseY)?.getCell(c.x); + +interface IPrediction { + /** + * Returns a sequence to apply the prediction. + * @param buffer to write to + * @param cursor position to write the data. Should advance the cursor. + */ + apply(buffer: IBuffer, cursor: ICoordinate): string; + + /** + * Returns a sequence to roll back a previous `apply()` call. + */ + rollback(buffer: IBuffer): string; + + /** + * Returns whether the given input is one expected by this prediction. + */ + matches(input: string): boolean; + + /** + * Returns the reapply behavior for the given line. + */ + getReapplyBehavior(buffer: IBuffer, allPredictions: ReadonlyArray): ReapplyBehavior; +} + +/** + * Boundary added to the prediction timeline to indicate that predictions + * should be held and not applied until all previous predictions are validated. + */ +interface IPredictionBoundary { + /** + * Checks whether a change was made that satisfies the expectation for the + * prediction boundary. For instance is pressing enter, it checks that that + * results in a new line. + */ + test(input: string): boolean; +} + +const csiRe = /\x1b\[*?[a-zA-Z]/g; + +/** + * Boundary which never tests true. Will always discard predictions. + */ +class HardBoundary implements IPredictionBoundary { + public test() { + return false; + } +} + +class CharacterPrediction implements IPrediction { + private appliedAt?: { + x: number; + y: number; + baseY: number; + oldAttributes: string; + oldChar: string; + }; + + constructor(private readonly style: string, private readonly char: string) { } + + public apply(buffer: IBuffer, cursor: ICoordinate) { + const cell = getCellAtCoordinate(buffer, cursor); + this.appliedAt = cell + ? { ...cursor, oldAttributes: getBufferCellAttributes(cell), oldChar: cell.getChars() } + : { ...cursor, oldAttributes: '', oldChar: '' }; + + cursor.x++; + return this.style + this.char + `${CSI}0m`; + } + + public rollback(buffer: IBuffer) { + if (!this.appliedAt) { + return ''; // not applied + } + + const a = this.appliedAt; + this.appliedAt = undefined; + return setCursorCoordinate(buffer, a) + (a.oldChar ? `${a.oldAttributes}${a.oldChar}${setCursorCoordinate(buffer, a)}` : `${CSI}X`); + } + + public matches(input: string) { + csiRe.lastIndex = 0; + return input.replace(csiRe, '') === this.char; + } + + public getReapplyBehavior(buffer: IBuffer) { + if (!this.appliedAt) { + return ReapplyBehavior.Apply; + } + + const cellText = getCellAtCoordinate(buffer, this.appliedAt)?.getChars() ?? ''; + if (cellText === this.char) { + return ReapplyBehavior.NoOp; + } else if (cellText === this.appliedAt.oldChar) { + return ReapplyBehavior.Apply; + } else { + return ReapplyBehavior.Mismatch; + } + } +} + +interface IPredictionGroup { + boundary?: IPredictionBoundary; + predictions: IPrediction[]; +} + +const invalidatedRestoreInterval = 200; + +class PredictionTimeline { + /** + * Expected queue of events, separated whenever a PredictionBoundary is + * emitted. Prediction groups _always_ apply to the active row. + */ + private expected: IPredictionGroup[] = []; + + /** + * Last-invalidated set of predictions. These are restored if the predictions + * become valid again in a short period of time. Some terminal programs + * rewrite lines or the entire display on backspace, for example, which will + * invalidate the predictions even if they become valid again a moment later. + */ + private invalidated: { at: number; p: IPredictionGroup[] } | undefined; + + /** + * Cursor position -- kept outside the buffer since it can be ahead if + * typing swiftly. + */ + private cursor: ICoordinate | undefined; + + constructor(public readonly terminal: Terminal) { } + + /** + * Should be called when input is incoming to the temrinal. + */ + public beforeServerInput(input: string): string { + if (!this.expected.length) { + this.cursor = undefined; + return input; + } + + const buffer = this.getActiveBuffer(); + if (!buffer) { + this.cursor = undefined; + return input; + } + + let output = ''; + let brokeBoundary = false; + for (let i = 0; i < input.length; i++) { + const test = this.expected[0]?.predictions[0]; + const char = input[i]; + + // if we reached the end of our tests, try to break through the boundary + // and start applying its items. + if (!test) { + this.expected.shift(); + if (!this.expected.length) { + return output + input.slice(i); + } + brokeBoundary = true; + if (this.expected[0].boundary?.test(char) === false) { + this.expected = []; + return output + input.slice(i); // boundary assumption invalid, throw out predictions + } + } + // if the input character matches what the next prediction expected, undo + // the prediction and write the real character out. + else if (test.matches(char)) { + output += test.rollback(buffer) + char; + this.expected[0].predictions.shift(); + } + // otherwise, roll back all pending predictions and move the current stack + // of predictions into the "invalidated" key (for possible resurrection). + else { + this.invalidated = { at: Date.now(), p: this.expected }; + this.expected = []; + this.cursor = undefined; + if (!brokeBoundary) { // on a new boundary, we did not apply predictions yet + output += this.invalidated.p[0].predictions.map(p => p.rollback(buffer)).reverse().join(''); + } + + output += input.slice(i); + break; + } + } + + if (this.cursor) { + output += setCursorCoordinate(buffer, this.cursor); + } + + // prevent cursor flickering while typing, since output will *always* + // contains cursor moves if we did anything with predictions: + output = HIDE_CURSOR + output + SHOW_CURSOR; + + return output; + } + + /** + * Should be called after data is applied to the terminal. + */ + public afterServerInput() { + const buffer = this.getActiveBuffer(); + if (buffer && this.invalidated && Date.now() - this.invalidated.at < invalidatedRestoreInterval) { + this.tryReapplyInvalidated(buffer, this.invalidated.p); + } + } + + /** + * Appends a typeahead prediction. + */ + public addPrediction(buffer: IBuffer, prediction: IPrediction) { + const l = this.expected.length - 1; + if (l === -1) { + this.expected = [{ predictions: [prediction] }]; + } else { + this.expected[l].predictions.push(prediction); + } + + if (l <= 1) { + const text = prediction.apply(buffer, this.getCursor(buffer)); + console.log('prediction:', text); + this.terminal.write(text); + } + } + + /** + * Appends a boundary to the preduction. + */ + public addBoundary(boundary: IPredictionBoundary) { + this.expected.push({ boundary, predictions: [] }); + } + + private getCursor(buffer: IBuffer) { + if (!this.cursor) { + this.cursor = { baseY: buffer.baseY, y: buffer.cursorY, x: buffer.cursorX }; + } + + return this.cursor; + } + + private getActiveBuffer() { + const buffer = this.terminal.buffer.active; + return buffer.type === 'normal' ? buffer : undefined; + } + + private tryReapplyInvalidated(buffer: IBuffer, invalidated: IPredictionGroup[]) { + if (!invalidated.length) { + return; + } + + const predictions = invalidated[0].predictions; + let lastNoop = -1; + let hasApply = false; + for (let i = 0; i < predictions.length; i++) { + switch (predictions[i].getReapplyBehavior(buffer, predictions)) { + case ReapplyBehavior.NoOp: + if (!hasApply) { lastNoop = i; } + break; + case ReapplyBehavior.Mismatch: + return; // do not reapply any prediction in this set if there's a mismatch + case ReapplyBehavior.Apply: + hasApply = true; + break; + } + } + + if (!hasApply) { + return; + } + + for (let i = lastNoop + 1; i < predictions.length; i++) { + this.terminal.write(predictions[i].apply(buffer, this.getCursor(buffer))); + } + + this.expected = invalidated.slice(1); + + if (lastNoop < predictions.length - 1) { + this.expected.unshift({ predictions: predictions.slice(lastNoop) }); + } + } +} +/** + * Gets the escape sequence to restore state/appearence in the cell. + */ +const getBufferCellAttributes = (cell: IBufferCell) => cell.isAttributeDefault() + ? `${CSI}0m` + : [ + cell.isBold() && `${CSI}1m`, + cell.isDim() && `${CSI}2m`, + cell.isItalic() && `${CSI}3m`, + cell.isUnderline() && `${CSI}4m`, + cell.isBlink() && `${CSI}5m`, + cell.isInverse() && `${CSI}7m`, + cell.isInvisible() && `${CSI}8m`, + + cell.isFgRGB() && `${CSI}38;2;${cell.getFgColor() >>> 24};${(cell.getFgColor() >>> 16) & 0xFF};${cell.getFgColor() & 0xFF}m`, + cell.isFgPalette() && `${CSI}38;5;${cell.getFgColor()}m`, + cell.isFgDefault() && `${CSI}39m`, + + cell.isBgRGB() && `${CSI}48;2;${cell.getBgColor() >>> 24};${(cell.getBgColor() >>> 16) & 0xFF};${cell.getBgColor() & 0xFF}m`, + cell.isBgPalette() && `${CSI}48;5;${cell.getBgColor()}m`, + cell.isBgDefault() && `${CSI}49m`, + ].filter(seq => !!seq).join(''); + +const parseTypeheadStyle = (style: string | number) => { + if (typeof style === 'number') { + return `${CSI}${style}m`; + } + + const { r, g, b } = Color.fromHex(style).rgba; + return `${CSI}32;${r};${g};${b}m`; +}; + +export class TypeAheadAddon implements ITerminalAddon { + private disposables: IDisposable[] = []; + private typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle); + private timeline?: PredictionTimeline; + + constructor(private readonly _processManager: ITerminalProcessManager, private readonly config: TerminalConfigHelper) { + } + + public activate(terminal: Terminal): void { + this.timeline = new PredictionTimeline(terminal); + this.disposables.push(terminal.onData(e => this.onUserData(e))); + this.disposables.push(this.config.onConfigChanged(() => this.typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle))); + this.disposables.push(this._processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); + } + + public dispose(): void { + // this.disposables.forEach(d => d.dispose()); + } + + private onUserData(data: string): void { + if (this.timeline?.terminal.buffer.active.type !== 'normal') { + return; + } + + console.log('user data:', data); + if (data.length !== 1) { + this.timeline.addBoundary(new HardBoundary()); + return; + } + + const terminal = this.timeline.terminal; + const code = data.charCodeAt(0); + if (code >= 32 && code < 126) { + if (terminal.buffer.active.cursorX === terminal.cols - 1) { + } else { + this.timeline.addPrediction(terminal.buffer.active, new CharacterPrediction(this.typeheadStyle, data)); + } + } + } + + private onBeforeProcessData(event: IBeforeProcessDataEvent): void { + if (!this.timeline) { + return; + } + + console.log('incoming data:', event.data); + event.data = this.timeline.beforeServerInput(event.data); + console.log('emitted data:', event.data); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts index c559ff7b8db..a46aa4477ff 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts @@ -12,6 +12,8 @@ export interface XTermCore { height: number; }; + writeSync(input: Buffer | string): void; + _coreService: { triggerDataEvent(data: string, wasUserInput?: boolean): void; }; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1af8a10e218..c8e63273704 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -135,6 +135,8 @@ export interface ITerminalConfiguration { enableFileLinks: boolean; unicodeVersion: '6' | '11'; experimentalLinkProvider: boolean; + typeaheadThreshold: number; + typeaheadStyle: number | string; } export interface ITerminalConfigHelper { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 985fde8246b..2cce39b1a95 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -351,6 +351,36 @@ export const terminalConfiguration: IConfigurationNode = { description: localize('terminal.integrated.experimentalLinkProvider', "An experimental setting that aims to improve link detection in the terminal by improving when links are detected and by enabling shared link detection with the editor. Currently this only supports web links."), type: 'boolean', default: true + }, + 'terminal.integrated.typeaheadThreshold': { + description: localize('terminal.integrated.typeaheadThreshold', "Experimental: length of time, in milliseconds, where typeahead will active. If '0', typeahead will always be on, and if '-1' it will be disabled."), + type: 'integer', + minimum: -1, + default: -1, + }, + 'terminal.integrated.typeaheadStyle': { + description: localize('terminal.integrated.typeaheadStyle', "Experimental: terminal style of typeahead text, either a font style or an RGB color."), + default: 2, + oneOf: [ + { + type: 'integer', + default: 2, + enum: [0, 1, 2, 3, 4, 7], + enumDescriptions: [ + localize('terminal.integrated.typeaheadStyle.0', 'Normal'), + localize('terminal.integrated.typeaheadStyle.1', 'Bold'), + localize('terminal.integrated.typeaheadStyle.2', 'Dim'), + localize('terminal.integrated.typeaheadStyle.3', 'Italic'), + localize('terminal.integrated.typeaheadStyle.4', 'Underlined'), + localize('terminal.integrated.typeaheadStyle.7', 'Inverted'), + ] + }, + { + type: 'string', + format: 'color-hex', + default: '#ff0000', + } + ] } } }; From d81f0e499283838ab70eb681ec913df198c038e2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 16 Oct 2020 11:38:30 +0200 Subject: [PATCH 004/121] Protect from throwing `IWorkbenchExtensionEnablementService.isEnabled` --- .../extensions/common/abstractExtensionService.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 7a6406a499a..f9fbf7e59be 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -141,7 +141,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx let toAdd: IExtension[] = []; let toRemove: string[] = []; for (const extension of extensions) { - if (this._extensionEnablementService.isEnabled(extension)) { + if (this._safeInvokeIsEnabled(extension)) { // an extension has been enabled toAdd.push(extension); } else { @@ -154,7 +154,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._register(this._extensionManagementService.onDidInstallExtension((event) => { if (event.local) { - if (this._extensionEnablementService.isEnabled(event.local)) { + if (this._safeInvokeIsEnabled(event.local)) { // an extension has been installed this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); } @@ -618,7 +618,15 @@ export abstract class AbstractExtensionService extends Disposable implements IEx return false; } - return this._extensionEnablementService.isEnabled(toExtension(extension)); + return this._safeInvokeIsEnabled(toExtension(extension)); + } + + protected _safeInvokeIsEnabled(extension: IExtension): boolean { + try { + return this._extensionEnablementService.isEnabled(extension); + } catch (err) { + return false; + } } protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void { From 3bfa6735c338bb397ba932eec8350bc2e906ff53 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 16 Oct 2020 12:30:39 +0200 Subject: [PATCH 005/121] Add explorer context menu to install an extension from a VSIX (#108787) --- .../browser/extensions.contribution.ts | 55 ++++++++++++++++++- .../extensions/browser/extensionsActions.ts | 48 ++++------------ .../extensions/browser/extensionsViewlet.ts | 6 +- .../contrib/extensions/common/extensions.ts | 1 + 4 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index dcc07b01e51..205ca37d121 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -14,7 +14,7 @@ import { IExtensionRecommendationsService } from 'vs/workbench/services/extensio import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction, @@ -57,6 +57,10 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; +import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ResourceContextKey } from 'vs/workbench/common/resources'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -74,6 +78,16 @@ Registry.as(Extensions.Quickaccess).registerQuickAccessPro helpEntries: [{ description: localize('manageExtensionsHelp', "Manage Extensions"), needsEditor: false }] }); +// Explorer +MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { + group: '4_extensions', + command: { + id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, + title: localize('installVSIX', "Install VSIX"), + }, + when: ResourceContextKey.Extension.isEqualTo('.vsix') +}); + // Editor Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( @@ -206,6 +220,45 @@ CommandsRegistry.registerCommand({ } }); +CommandsRegistry.registerCommand({ + id: INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, + handler: async (accessor: ServicesAccessor, resources: URI[] | URI) => { + const extensionService = accessor.get(IExtensionService); + const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const instantiationService = accessor.get(IInstantiationService); + const hostService = accessor.get(IHostService); + const notificationService = accessor.get(INotificationService); + + const viewletService = accessor.get(IViewletService); + const viewlet = await viewletService.openViewlet(VIEWLET_ID, true); + + if (!viewlet) { + return; + } + + const extensions = Array.isArray(resources) ? resources : [resources]; + await Promise.all(extensions.map(async (vsix) => await extensionsWorkbenchService.install(vsix))) + .then(async (extensions) => { + for (const extension of extensions) { + const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); + const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name) + : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name); + const actions = requireReload ? [{ + label: localize('InstallVSIXAction.reloadNow', "Reload Now"), + run: () => hostService.reload() + }] : []; + notificationService.prompt( + Severity.Info, + message, + actions, + { sticky: true } + ); + } + await instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run(true); + }); + } +}); + CommandsRegistry.registerCommand({ id: 'workbench.extensions.uninstallExtension', description: { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 0f54df948fc..8ef92310fcb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -12,7 +12,7 @@ import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { dispose, Disposable } from 'vs/base/common/lifecycle'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -3007,50 +3007,26 @@ export class InstallVSIXAction extends Action { constructor( id = InstallVSIXAction.ID, label = InstallVSIXAction.LABEL, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @INotificationService private readonly notificationService: INotificationService, - @IHostService private readonly hostService: IHostService, @IFileDialogService private readonly fileDialogService: IFileDialogService, - @IExtensionService private readonly extensionService: IExtensionService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @ICommandService private readonly commandService: ICommandService ) { super(id, label, 'extension-action install-vsix', true); } - async run(vsixPaths?: URI[]): Promise { - if (!vsixPaths) { - vsixPaths = await this.fileDialogService.showOpenDialog({ - title: localize('installFromVSIX', "Install from VSIX"), - filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], - canSelectFiles: true, - openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) - }); + async run(): Promise { + const vsixPaths = await this.fileDialogService.showOpenDialog({ + title: localize('installFromVSIX', "Install from VSIX"), + filters: [{ name: 'VSIX Extensions', extensions: ['vsix'] }], + canSelectFiles: true, + openLabel: mnemonicButtonLabel(localize({ key: 'installButton', comment: ['&& denotes a mnemonic'] }, "&&Install")) + }); - if (!vsixPaths) { - return; - } + if (!vsixPaths) { + return; } // Install extension(s), display notification(s), display @installed extensions - await Promise.all(vsixPaths.map(async (vsix) => await this.extensionsWorkbenchService.install(vsix))) - .then(async (extensions) => { - for (const extension of extensions) { - const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); - const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.displayName || extension.name) - : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.displayName || extension.name); - const actions = requireReload ? [{ - label: localize('InstallVSIXAction.reloadNow', "Reload Now"), - run: () => this.hostService.reload() - }] : []; - this.notificationService.prompt( - Severity.Info, - message, - actions, - { sticky: true } - ); - } - await this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run(true); - }); + await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 13d923aefba..68731f3aed6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -17,7 +17,7 @@ import { append, $, Dimension, hide, show } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID } from '../common/extensions'; import { ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, @@ -60,6 +60,7 @@ import { URI } from 'vs/base/common/uri'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; +import { ICommandService } from 'vs/platform/commands/common/commands'; const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -364,6 +365,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IExtensionService extensionService: IExtensionService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IPreferencesService private readonly preferencesService: IPreferencesService, + @ICommandService private readonly commandService: ICommandService ) { super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); @@ -475,7 +477,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE try { // Attempt to install the extension(s) - await this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL).run(vsixPaths); + await this.commandService.executeCommand(INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, vsixPaths); } catch (err) { this.notificationService.error(err); diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 83067be82bc..110c26969b5 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -145,3 +145,4 @@ export class ExtensionContainers extends Disposable { } export const TOGGLE_IGNORE_EXTENSION_ACTION_ID = 'workbench.extensions.action.toggleIgnoreExtension'; +export const INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID = 'workbench.extensions.command.installFromVSIX'; From 0ea68382da8946c59f57ed1990639d1d0f57f2e5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 12:36:28 +0200 Subject: [PATCH 006/121] web - do not enable confirmBeforeQuit per default if running in kiosk mode --- src/vs/workbench/browser/workbench.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index d946123195e..ac34be15cd5 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -8,6 +8,7 @@ import * as nls from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { isStandalone } from 'vs/base/browser/browser'; // Configuration (function registerConfiguration(): void { @@ -404,7 +405,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio }, 'window.confirmBeforeQuit': { 'type': 'boolean', - 'default': isWeb, + 'default': isWeb && !isStandalone, // on by default in web, unless PWA 'description': nls.localize('confirmBeforeQuitWeb', "Controls whether to ask for confirmation before closing the browser tab or window."), 'scope': ConfigurationScope.APPLICATION, 'included': isWeb From 627fa8d42529364741784dbb71db8a07b71e5bc9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 13:29:14 +0200 Subject: [PATCH 007/121] Web: do not disable backups when auto save is enabled (fix #108789) --- .../contrib/backup/browser/backupTracker.ts | 39 ++++++++++++++-- .../contrib/backup/common/backupTracker.ts | 44 +++++++------------ .../backup/electron-sandbox/backupTracker.ts | 33 +++++++++++++- .../electron-browser/backupTracker.test.ts | 5 ++- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/backup/browser/backupTracker.ts b/src/vs/workbench/contrib/backup/browser/backupTracker.ts index 743585520e0..f4be13d34b9 100644 --- a/src/vs/workbench/contrib/backup/browser/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/browser/backupTracker.ts @@ -5,22 +5,53 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkingCopy, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; export class BrowserBackupTracker extends BackupTracker implements IWorkbenchContribution { + // Delay creation of backups when content changes to avoid too much + // load on the backup service when the user is typing into the editor + // Since we always schedule a backup, even when auto save is on (web + // only), we have different scheduling delays based on auto save. This + // helps to avoid a race between saving (after 1s per default) and making + // a backup of the working copy. + private static readonly BACKUP_SCHEDULE_DELAYS = { + [AutoSaveMode.OFF]: 1000, + [AutoSaveMode.ON_FOCUS_CHANGE]: 1000, + [AutoSaveMode.ON_WINDOW_CHANGE]: 1000, + [AutoSaveMode.AFTER_SHORT_DELAY]: 2000, // explicitly higher to prevent races + [AutoSaveMode.AFTER_LONG_DELAY]: 1000 + }; + constructor( @IBackupFileService backupFileService: IBackupFileService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IWorkingCopyService workingCopyService: IWorkingCopyService, @ILifecycleService lifecycleService: ILifecycleService, @ILogService logService: ILogService ) { - super(backupFileService, filesConfigurationService, workingCopyService, logService, lifecycleService); + super(backupFileService, workingCopyService, logService, lifecycleService); + } + + protected shouldScheduleBackup(workingCopy: IWorkingCopy): boolean { + // Web: we always want to schedule a backup, even if auto save + // is enabled because in web there is no handler on shutdown + // to trigger saving so there is a higher chance of dataloss. + // See https://github.com/microsoft/vscode/issues/108789 + return true; + } + + protected getBackupScheduleDelay(workingCopy: IWorkingCopy): number { + let autoSaveMode = this.filesConfigurationService.getAutoSaveMode(); + if (workingCopy.capabilities & WorkingCopyCapabilities.Untitled) { + autoSaveMode = AutoSaveMode.OFF; // auto-save is never on for untitled working copies + } + + return BrowserBackupTracker.BACKUP_SCHEDULE_DELAYS[autoSaveMode]; } protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts index a854320c20b..9658fda1ad0 100644 --- a/src/vs/workbench/contrib/backup/common/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -5,47 +5,28 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { IFilesConfigurationService, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; export abstract class BackupTracker extends Disposable { - // Disable backup for when a short auto-save delay is configured with - // the rationale that the auto save will trigger a save periodically - // anway and thus creating frequent backups is not useful - // - // This will only apply to working copies that are not untitled where - // auto save is actually saving. - private static DISABLE_BACKUP_AUTO_SAVE_THRESHOLD = 1500; - - // Delay creation of backups when content changes to avoid too much - // load on the backup service when the user is typing into the editor - protected static BACKUP_FROM_CONTENT_CHANGE_DELAY = 1000; - // A map from working copy to a version ID we compute on each content // change. This version ID allows to e.g. ask if a backup for a specific // content has been made before closing. private readonly mapWorkingCopyToContentVersion = new Map(); - private backupsDisabledForAutoSaveables = false; - // A map of scheduled pending backups for working copies private readonly pendingBackups = new Map(); constructor( protected readonly backupFileService: IBackupFileService, - protected readonly filesConfigurationService: IFilesConfigurationService, protected readonly workingCopyService: IWorkingCopyService, protected readonly logService: ILogService, protected readonly lifecycleService: ILifecycleService ) { super(); - // Figure out initial auto save config - this.onAutoSaveConfigurationChange(filesConfigurationService.getAutoSaveConfiguration()); - // Fill in initial dirty working copies this.workingCopyService.dirtyWorkingCopies.forEach(workingCopy => this.onDidRegister(workingCopy)); @@ -60,9 +41,6 @@ export abstract class BackupTracker extends Disposable { this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.onDidChangeDirty(workingCopy))); this._register(this.workingCopyService.onDidChangeContent(workingCopy => this.onDidChangeContent(workingCopy))); - // Listen to auto save config changes - this._register(this.filesConfigurationService.onAutoSaveConfigurationChange(c => this.onAutoSaveConfigurationChange(c))); - // Lifecycle (handled in subclasses) this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); } @@ -105,13 +83,21 @@ export abstract class BackupTracker extends Disposable { } } - private onAutoSaveConfigurationChange(configuration: IAutoSaveConfiguration): void { - this.backupsDisabledForAutoSaveables = typeof configuration.autoSaveDelay === 'number' && configuration.autoSaveDelay < BackupTracker.DISABLE_BACKUP_AUTO_SAVE_THRESHOLD; - } + /** + * Allows subclasses to conditionally opt-out of doing a backup, e.g. if + * auto save is enabled. + */ + protected abstract shouldScheduleBackup(workingCopy: IWorkingCopy): boolean; + + /** + * Allows subclasses to control the delay before performing a backup from + * working copy content changes. + */ + protected abstract getBackupScheduleDelay(workingCopy: IWorkingCopy): number; private scheduleBackup(workingCopy: IWorkingCopy): void { - if (this.backupsDisabledForAutoSaveables && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled)) { - return; // skip if auto save is enabled with a short delay + if (!this.shouldScheduleBackup(workingCopy)) { + return; // subclass prevented backup for working copy } // Clear any running backup operation @@ -137,7 +123,7 @@ export abstract class BackupTracker extends Disposable { this.logService.error(error); } } - }, BackupTracker.BACKUP_FROM_CONTENT_CHANGE_DELAY); + }, this.getBackupScheduleDelay(workingCopy)); // Keep in map for disposal as needed this.pendingBackups.set(workingCopy, toDisposable(() => { diff --git a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts index 403322f11e2..f47ccd5545b 100644 --- a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts @@ -23,9 +23,21 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' export class NativeBackupTracker extends BackupTracker implements IWorkbenchContribution { + // Delay creation of backups when working copy changes to avoid too much + // load on the backup service when the user is typing into the editor + private static readonly BACKUP_SCHEDULE_DELAY = 1000; + + // Disable backup for when a short auto-save delay is configured with + // the rationale that the auto save will trigger a save periodically + // anway and thus creating frequent backups is not useful + // + // This will only apply to working copies that are not untitled where + // auto save is actually saving. + private static readonly DISABLE_BACKUP_AUTO_SAVE_THRESHOLD = 1500; + constructor( @IBackupFileService backupFileService: IBackupFileService, - @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IWorkingCopyService workingCopyService: IWorkingCopyService, @ILifecycleService lifecycleService: ILifecycleService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @@ -36,7 +48,24 @@ export class NativeBackupTracker extends BackupTracker implements IWorkbenchCont @IEditorService private readonly editorService: IEditorService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { - super(backupFileService, filesConfigurationService, workingCopyService, logService, lifecycleService); + super(backupFileService, workingCopyService, logService, lifecycleService); + } + + protected shouldScheduleBackup(workingCopy: IWorkingCopy): boolean { + if (workingCopy.capabilities & WorkingCopyCapabilities.Untitled) { + return true; // always backup untitled + } + + const autoSaveConfiguration = this.filesConfigurationService.getAutoSaveConfiguration(); + if (typeof autoSaveConfiguration.autoSaveDelay === 'number' && autoSaveConfiguration.autoSaveDelay < NativeBackupTracker.DISABLE_BACKUP_AUTO_SAVE_THRESHOLD) { + return false; // skip backup when auto save is already enabled with a low delay + } + + return true; + } + + protected getBackupScheduleDelay(): number { + return NativeBackupTracker.BACKUP_SCHEDULE_DELAY; } protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index 5e77da98cba..beb7ff089b0 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -69,9 +69,10 @@ class TestBackupTracker extends NativeBackupTracker { @IEnvironmentService environmentService: IEnvironmentService ) { super(backupFileService, filesConfigurationService, workingCopyService, lifecycleService, fileDialogService, dialogService, contextService, nativeHostService, logService, editorService, environmentService); + } - // Reduce timeout for tests - BackupTracker.BACKUP_FROM_CONTENT_CHANGE_DELAY = 10; + protected getBackupScheduleDelay(): number { + return 10; // Reduce timeout for tests } } From b71ede64c13ed59414c9bb4ef8627daa00a3bf7e Mon Sep 17 00:00:00 2001 From: Andrey Sinitsyn Date: Fri, 16 Oct 2020 16:26:15 +0300 Subject: [PATCH 008/121] fix(git): fatal when adding, reverting files or cleaning repo on win32 (#108691) Fixes issue #108690 --- extensions/git/src/git.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 48e5c457c22..d3c574c74b1 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1233,7 +1233,7 @@ export class Repository { } if (paths && paths.length) { - for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { await this.run([...args, '--', ...chunk]); } } else { @@ -1455,7 +1455,7 @@ export class Repository { const args = ['clean', '-f', '-q']; for (const paths of groups) { - for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { promises.push(limiter.queue(() => this.run([...args, '--', ...chunk]))); } } @@ -1495,7 +1495,7 @@ export class Repository { try { if (paths && paths.length > 0) { - for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { await this.run([...args, '--', ...chunk]); } } else { From 29d459b68b815113e74268af156dee48b3c2d353 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 15:40:39 +0200 Subject: [PATCH 009/121] backup - add cancellation token for more clever cancelling of scheduled backups --- .../contrib/backup/common/backupTracker.ts | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts index 9658fda1ad0..db280087d25 100644 --- a/src/vs/workbench/contrib/backup/common/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -8,6 +8,7 @@ import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/l import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export abstract class BackupTracker extends Disposable { @@ -96,39 +97,58 @@ export abstract class BackupTracker extends Disposable { protected abstract getBackupScheduleDelay(workingCopy: IWorkingCopy): number; private scheduleBackup(workingCopy: IWorkingCopy): void { - if (!this.shouldScheduleBackup(workingCopy)) { - return; // subclass prevented backup for working copy - } // Clear any running backup operation - dispose(this.pendingBackups.get(workingCopy)); - this.pendingBackups.delete(workingCopy); + this.cancelBackup(workingCopy); + + // subclass prevented backup for working copy + if (!this.shouldScheduleBackup(workingCopy)) { + return; + } this.logService.trace(`[backup tracker] scheduling backup`, workingCopy.resource.toString()); // Schedule new backup + const cts = new CancellationTokenSource(); const handle = setTimeout(async () => { - - // Clear disposable - this.pendingBackups.delete(workingCopy); + if (cts.token.isCancellationRequested) { + return; + } // Backup if dirty if (workingCopy.isDirty()) { - this.logService.trace(`[backup tracker] running backup`, workingCopy.resource.toString()); + this.logService.trace(`[backup tracker] creating backup`, workingCopy.resource.toString()); try { const backup = await workingCopy.backup(); - await this.backupFileService.backup(workingCopy.resource, backup.content, this.getContentVersion(workingCopy), backup.meta); + if (cts.token.isCancellationRequested) { + return; + } + + if (workingCopy.isDirty()) { + this.logService.trace(`[backup tracker] storing backup`, workingCopy.resource.toString()); + + await this.backupFileService.backup(workingCopy.resource, backup.content, this.getContentVersion(workingCopy), backup.meta); + } } catch (error) { this.logService.error(error); } } + + if (cts.token.isCancellationRequested) { + return; + } + + // Clear disposable + this.pendingBackups.delete(workingCopy); + }, this.getBackupScheduleDelay(workingCopy)); // Keep in map for disposal as needed this.pendingBackups.set(workingCopy, toDisposable(() => { this.logService.trace(`[backup tracker] clearing pending backup`, workingCopy.resource.toString()); + cts.dispose(true); clearTimeout(handle); })); } @@ -141,12 +161,16 @@ export abstract class BackupTracker extends Disposable { this.logService.trace(`[backup tracker] discarding backup`, workingCopy.resource.toString()); // Clear any running backup operation - dispose(this.pendingBackups.get(workingCopy)); - this.pendingBackups.delete(workingCopy); + this.cancelBackup(workingCopy); // Forward to backup file service this.backupFileService.discardBackup(workingCopy.resource); } + private cancelBackup(workingCopy: IWorkingCopy): void { + dispose(this.pendingBackups.get(workingCopy)); + this.pendingBackups.delete(workingCopy); + } + protected abstract onBeforeShutdown(reason: ShutdownReason): boolean | Promise; } From 72950f8be75fdb38a49b1af9fb9bf06307352b56 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 15:57:42 +0200 Subject: [PATCH 010/121] backup - wire in cancellation token to implementation --- .../contrib/backup/common/backupTracker.ts | 2 +- .../workbench/services/backup/common/backup.ts | 4 +++- .../services/backup/common/backupFileService.ts | 16 ++++++++++++---- .../electron-browser/backupFileService.test.ts | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts index db280087d25..0da57922996 100644 --- a/src/vs/workbench/contrib/backup/common/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -128,7 +128,7 @@ export abstract class BackupTracker extends Disposable { if (workingCopy.isDirty()) { this.logService.trace(`[backup tracker] storing backup`, workingCopy.resource.toString()); - await this.backupFileService.backup(workingCopy.resource, backup.content, this.getContentVersion(workingCopy), backup.meta); + await this.backupFileService.backup(workingCopy.resource, backup.content, this.getContentVersion(workingCopy), backup.meta, cts.token); } } catch (error) { this.logService.error(error); diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 3637e0d4467..dfd54a9ee75 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -6,6 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const IBackupFileService = createDecorator('backupFileService'); @@ -59,8 +60,9 @@ export interface IBackupFileService { * @param versionId The optionsl version id of the resource to backup. * @param meta The optional meta data of the resource to backup. This information * can be restored later when loading the backup again. + * @param token The optional cancellation token if the operation can be cancelled. */ - backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise; + backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T, token?: CancellationToken): Promise; /** * Discards the backup associated with a resource if it exists. diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts index 175d5e9ecca..5049d8f6dc3 100644 --- a/src/vs/workbench/services/backup/common/backupFileService.ts +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -21,6 +21,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IBackupFilesModel { resolve(backupRoot: URI): Promise; @@ -157,8 +158,8 @@ export class BackupFileService implements IBackupFileService { return this.impl.hasBackupSync(resource, versionId); } - backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise { - return this.impl.backup(resource, content, versionId, meta); + backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T, token?: CancellationToken): Promise { + return this.impl.backup(resource, content, versionId, meta, token); } discardBackup(resource: URI): Promise { @@ -232,8 +233,11 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { return this.model.has(backupResource, versionId); } - async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise { + async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T, token?: CancellationToken): Promise { const model = await this.ready; + if (token?.isCancellationRequested) { + return; + } const backupResource = this.toBackupResource(resource); if (model.has(backupResource, versionId, meta)) { @@ -241,6 +245,10 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService { } return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + if (token?.isCancellationRequested) { + return; + } + let preamble: string | undefined = undefined; // With Metadata: URI + META-START + Meta + END @@ -419,7 +427,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return this.backups.has(backupResource.toString()); } - async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise { + async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T, token?: CancellationToken): Promise { const backupResource = this.toBackupResource(resource); this.backups.set(backupResource.toString(), content || stringToSnapshot('')); } diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 80d03fe3ae6..0e00c5a4982 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -28,6 +28,7 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { TestProductService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const backupHome = path.join(userdataDir, 'Backups'); @@ -80,8 +81,8 @@ export class NodeTestBackupFileService extends BackupFileService { return new Promise(resolve => this.backupResourceJoiners.push(resolve)); } - async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: any): Promise { - await super.backup(resource, content, versionId, meta); + async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: any, token?: CancellationToken): Promise { + await super.backup(resource, content, versionId, meta, token); while (this.backupResourceJoiners.length) { this.backupResourceJoiners.pop()!(); @@ -262,6 +263,16 @@ suite('BackupFileService', () => { model.dispose(); }); + + test('cancellation', async () => { + const cts = new CancellationTokenSource(); + const promise = service.backup(fooFile, undefined, undefined, undefined, cts.token); + cts.cancel(); + await promise; + + assert.equal(fs.existsSync(fooBackupPath), false); + assert.ok(!service.hasBackupSync(fooFile)); + }); }); suite('discardBackup', () => { From fbcd41079ddc1bb1f34e528a924cf83a227bcf81 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 16:12:30 +0200 Subject: [PATCH 011/121] add retries for test that depends on focus (#108727) --- .../workbench/contrib/files/test/browser/editorAutoSave.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 6d7d1cdf745..644cdf7437f 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -95,6 +95,8 @@ suite('EditorAutoSave', () => { }); test('editor auto saves on focus change if configured', async function () { + this.retries(3); // https://github.com/microsoft/vscode/issues/108727 + const [accessor, part, editorAutoSave] = await createEditorAutoSave({ autoSave: 'onFocusChange' }); const resource = toResource.call(this, '/path/index.txt'); From 7a935539d41f0e913fb0d999edb043fc792dab5b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 16 Oct 2020 16:19:59 +0200 Subject: [PATCH 012/121] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5d9e60b7ec5..09301c1b568 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.51.0", - "distro": "cb22bfd6ccdabc4a7765c63d247de8580923bd4a", + "distro": "5d8936bbb004bf40d1780f2c9845517a25d71f88", "author": { "name": "Microsoft Corporation" }, From 009156fc7642d3356561d419fb0a50fdc878f649 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 13 Oct 2020 12:58:57 -0700 Subject: [PATCH 013/121] Increase the timeout of tryActivateProvider --- .../services/authentication/browser/authenticationService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 59f88d7cebf..0392acfd564 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -457,7 +457,7 @@ export class AuthenticationService extends Disposable implements IAuthentication const didTimeout: Promise = new Promise((_, reject) => { setTimeout(() => { reject(); - }, 2000); + }, 5000); }); return Promise.race([didRegister, didTimeout]); From b9115b8b9c692fb1983b84791fb6db5aa989786e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 16 Oct 2020 16:58:20 +0200 Subject: [PATCH 014/121] fixes #108714 --- extensions/git/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/git/package.json b/extensions/git/package.json index f2fa3baf958..dbb65d6c7c7 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1637,6 +1637,7 @@ "null" ], "default": null, + "scope": "machine", "description": "%config.defaultCloneDirectory%" }, "git.enableSmartCommit": { From 0fdbff1e346e1ac4f6e4193ef09a0a7cc7472a38 Mon Sep 17 00:00:00 2001 From: Chuck Lantz Date: Fri, 16 Oct 2020 08:05:58 -0700 Subject: [PATCH 015/121] Remove unneeded extensions --- .devcontainer/devcontainer.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b32dd7f64e2..cd632e134ef 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,14 +15,12 @@ "resmon.show.cpufreq": false }, - // noVNC, VNC ports, debug + // noVNC, VNC, debug ports "forwardPorts": [6080, 5901, 9222], "extensions": [ "dbaeumer.vscode-eslint", - "EditorConfig.EditorConfig", - "mutantdino.resourcemonitor", - "GitHub.vscode-pull-request-github" + "mutantdino.resourcemonitor" ], // Optionally loads a cached yarn install for the repo From 6d36470eb8c12724a4de7f6cae07b9bd5007ef91 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 16 Oct 2020 15:22:01 +0000 Subject: [PATCH 016/121] avoid publishing test results for non x64 linux --- build/azure-pipelines/linux/product-build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 270beb898a0..031a491648a 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -157,7 +157,7 @@ steps: inputs: testResultsFiles: '*-results.xml' searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' - condition: succeededOrFailed() + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e From b4e81df81aa422d1ff9e5a6e2e55ce3a3618e42a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Oct 2020 17:56:53 +0200 Subject: [PATCH 017/121] web - rename to window.confirmBeforeQuit and show in file menu for awareness --- .../browser/actions/developerActions.ts | 3 +- .../browser/actions/navigationActions.ts | 2 + .../browser/actions/windowActions.ts | 60 ++++++++++++------- .../browser/workbench.contribution.ts | 4 +- .../host/browser/browserHostService.ts | 4 +- 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 97c3bdb4838..9eb0d3ea714 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -284,8 +284,9 @@ registerAction2(ToggleScreencastModeAction); registerAction2(LogStorageAction); registerAction2(LogWorkingCopiesAction); +// --- Configuration -// Screencast Mode +// Screen Cast Mode const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ id: 'screencastMode', diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index a53bd1e9a88..b9198a1e02f 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -298,6 +298,8 @@ class GoHomeContributor implements IWorkbenchContribution { } } +// --- Actions Registration + const actionsRegistry = Registry.as(Extensions.WorkbenchActions); actionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(NavigateUpAction, undefined), 'View: Navigate to the View Above', CATEGORIES.View.value); diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index a91096687ae..1115d77f027 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -32,6 +32,8 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ResourceMap } from 'vs/base/common/map'; import { Codicon } from 'vs/base/common/codicons'; import { isHTMLElement } from 'vs/base/browser/dom'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; @@ -333,6 +335,24 @@ export class NewWindowAction extends Action { } } +class BlurAction extends Action2 { + + constructor() { + super({ + id: 'workbench.action.blur', + title: nls.localize('blur', "Remove keyboard focus from focused element") + }); + } + + run(): void { + const el = document.activeElement; + + if (isHTMLElement(el)) { + el.blur(); + } + } +} + const registry = Registry.as(Extensions.WorkbenchActions); // --- Actions Registration @@ -348,6 +368,8 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(ReloadWindowAction), registry.registerWorkbenchAction(SyncActionDescriptor.from(ShowAboutDialogAction), `Help: About`, CATEGORIES.Help.value); +registerAction2(BlurAction); + // --- Commands/Keybindings Registration const recentFilesPickerContext = ContextKeyExpr.and(inQuickPickContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); @@ -372,26 +394,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } }); -class BlurAction extends Action2 { - - constructor() { - super({ - id: 'workbench.action.blur', - title: nls.localize('blur', "Remove keyboard focus from focused element") - }); - } - - run(): void { - const el = document.activeElement; - - if (isHTMLElement(el)) { - el.blur(); - } - } -} - -registerAction2(BlurAction); - KeybindingsRegistry.registerKeybindingRule({ id: ReloadWindowAction.ID, weight: KeybindingWeight.WorkbenchContrib + 50, @@ -399,8 +401,26 @@ KeybindingsRegistry.registerKeybindingRule({ primary: KeyMod.CtrlCmd | KeyCode.KEY_R }); +CommandsRegistry.registerCommand('workbench.action.toggleConfirmBeforeClose', accessor => { + const configurationService = accessor.get(IConfigurationService); + const setting = configurationService.inspect('window.confirmBeforeClose').userValue; + + return configurationService.updateValue('window.confirmBeforeClose', setting === false ? true : false, ConfigurationTarget.USER); +}); + // --- Menu Registration +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: 'z_ConfirmClose', + command: { + id: 'workbench.action.toggleConfirmBeforeClose', + title: nls.localize('miConfirmClose', "Confirm Before Close"), + toggled: ContextKeyExpr.equals('config.window.confirmBeforeClose', true) + }, + order: 1, + when: IsWebContext +}); + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '1_new', command: { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index ac34be15cd5..c28dada37fb 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -403,10 +403,10 @@ import { isStandalone } from 'vs/base/browser/browser'; 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, - 'window.confirmBeforeQuit': { + 'window.confirmBeforeClose': { 'type': 'boolean', 'default': isWeb && !isStandalone, // on by default in web, unless PWA - 'description': nls.localize('confirmBeforeQuitWeb', "Controls whether to ask for confirmation before closing the browser tab or window."), + 'description': nls.localize('confirmBeforeCloseWeb', "Controls whether to ask for confirmation before closing the browser tab or window."), 'scope': ConfigurationScope.APPLICATION, 'included': isWeb } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index c3e626fe751..dea4121181d 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -98,8 +98,8 @@ export class BrowserHostService extends Disposable implements IHostService { // Veto is setting is configured as such and we are not // expecting a navigation that was triggered by the user - if (!this.signalExpectedShutdown && this.configurationService.getValue('window.confirmBeforeQuit')) { - console.warn('Unload prevented: window.confirmBeforeQuit=true'); + if (!this.signalExpectedShutdown && this.configurationService.getValue('window.confirmBeforeClose')) { + console.warn('Unload prevented: window.confirmBeforeClose=true'); e.veto(true); } From 7b0088998afc9654f1326c35419139173239db3d Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 16 Oct 2020 10:11:30 -0700 Subject: [PATCH 018/121] fix #107890 --- .../workbench/contrib/debug/browser/media/debugViewlet.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index b6c1ee1f468..78195e5fd2c 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -55,6 +55,11 @@ padding-bottom: 0; } + +.monaco-workbench.safari .monaco-action-bar .start-debug-action-item .configuration .monaco-select-box { + margin-bottom: 0px; +} + .monaco-workbench .monaco-action-bar .start-debug-action-item .configuration.disabled .monaco-select-box { opacity: 0.7; font-style: italic; From b3204b9260357b44047d8b7fd5f57d8a28fb541c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 16 Oct 2020 10:18:25 -0700 Subject: [PATCH 019/121] fixes #108307 --- src/vs/base/browser/ui/menu/menubar.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index cb03f5f2df7..be71339754f 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -949,16 +949,18 @@ export class MenuBar extends Disposable { customMenu.buttonElement.classList.add('open'); + const buttonBoundingRect = customMenu.buttonElement.getBoundingClientRect(); + if (this.options.compactMode === Direction.Right) { - menuHolder.style.top = `0px`; - menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left + this.container.clientWidth}px`; + menuHolder.style.top = `${buttonBoundingRect.top}px`; + menuHolder.style.left = `${buttonBoundingRect.left + this.container.clientWidth}px`; } else if (this.options.compactMode === Direction.Left) { - menuHolder.style.top = `0px`; + menuHolder.style.top = `${buttonBoundingRect.top}px`; menuHolder.style.right = `${this.container.clientWidth}px`; menuHolder.style.left = 'auto'; } else { menuHolder.style.top = `${this.container.clientHeight}px`; - menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left}px`; + menuHolder.style.left = `${buttonBoundingRect.left}px`; } customMenu.buttonElement.appendChild(menuHolder); From fbc4c87523a2a968c0b3829b4810bc902e38cbef Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Fri, 16 Oct 2020 10:42:36 -0700 Subject: [PATCH 020/121] Bump vscode emmet helper to 2.0.5 --- extensions/emmet/yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 9f616275b11..f2ab2e5275f 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -41,9 +41,9 @@ integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.11.7": - version "12.12.67" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.67.tgz#4f86badb292e822e3b13730a1f9713ed2377f789" - integrity sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg== + version "12.12.68" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.68.tgz#dd5acf4a52a458ff1d9ef4fd66406fba0afbbb33" + integrity sha512-3RW2s24ewB7F9dAHvgb9FRvNHn6nO9IK6Eaknbz7HTOe2a5GVne5XbUh5+YA+kcCn67glyHhClUUdFP73LWrgQ== ajv@^6.12.3: version "6.12.6" @@ -2383,9 +2383,9 @@ vinyl@~2.0.1: replace-ext "^1.0.0" vscode-emmet-helper@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.4.tgz#d088bd4b2d52917d69e43df7a1989ceb06958eb2" - integrity sha512-g5yf6RnhGsCymg2YOK2HoK5hyBphB6b5bCEqF/3YwLTMO+9epZnSa6qSzAzz3U68y7IOlmW7ssFP5kOjybcA9g== + version "2.0.5" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.0.5.tgz#72058cdb62b6d86e77c8f42de6c05491a700a88f" + integrity sha512-lDP+soFnJgEkUrdAWqdUYRFfXRFnmXhjzyzca+fy9vCUorr3lp32IKIys8mYwnlAUencmyXmF5JwN0VikUXj/Q== dependencies: "@emmetio/extract-abbreviation" "^0.2.0" jsonc-parser "^2.3.0" From f10b984bc77897efb182c3326aaf491d62673949 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 16 Oct 2020 20:27:36 +0200 Subject: [PATCH 021/121] Themes: there is an file icon change event after startup. Fixes #99249 --- .../themes/browser/workbenchThemeService.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 36b7fd663c3..eb96d29d5cd 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -152,12 +152,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const fileIconData = FileIconThemeData.fromStorageData(this.storageService); if (fileIconData) { - this.applyAndSetFileIconTheme(fileIconData); + this.applyAndSetFileIconTheme(fileIconData, true); } const productIconData = ProductIconThemeData.fromStorageData(this.storageService); if (productIconData) { - this.applyAndSetProductIconTheme(productIconData); + this.applyAndSetProductIconTheme(productIconData, true); } this.initialize().then(undefined, errors.onUnexpectedError).then(_ => { @@ -569,7 +569,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return false; } - private applyAndSetFileIconTheme(iconThemeData: FileIconThemeData): void { + private applyAndSetFileIconTheme(iconThemeData: FileIconThemeData, silent = false): void { this.currentFileIconTheme = iconThemeData; _applyRules(iconThemeData.styleSheetContent!, fileIconThemeRulesClassName); @@ -585,8 +585,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (iconThemeData.id) { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon'); } - this.onFileIconThemeChange.fire(this.currentFileIconTheme); + if (!silent) { + this.onFileIconThemeChange.fire(this.currentFileIconTheme); + } } public getProductIconThemes(): Promise { @@ -639,7 +641,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return false; } - private applyAndSetProductIconTheme(iconThemeData: ProductIconThemeData): void { + private applyAndSetProductIconTheme(iconThemeData: ProductIconThemeData, silent = false): void { this.currentProductIconTheme = iconThemeData; @@ -650,8 +652,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (iconThemeData.id) { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'productIcon'); } - this.onProductIconThemeChange.fire(this.currentProductIconTheme); - + if (!silent) { + this.onProductIconThemeChange.fire(this.currentProductIconTheme); + } } } From 59df6b40f135bdf3d5831258735e1a9bc53c887b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 16 Oct 2020 22:41:45 +0200 Subject: [PATCH 022/121] code.sh: Prevent stealing of stdin (for #99815) --- resources/win32/bin/code.sh | 2 +- scripts/code.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 39b904ecd3f..23fbbc9bf20 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -43,7 +43,7 @@ if [ $IN_WSL = true ]; then # use the Remote WSL extension if installed WSL_EXT_ID="ms-vscode-remote.remote-wsl" - ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null + ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null Date: Fri, 16 Oct 2020 22:53:52 +0200 Subject: [PATCH 023/121] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09301c1b568..bda7354c003 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.51.0", - "distro": "5d8936bbb004bf40d1780f2c9845517a25d71f88", + "distro": "2efd579dedb12e7e9ed9ebd112120fa8b3b0922e", "author": { "name": "Microsoft Corporation" }, From 2f28a7f8b86244f9d3df00e6a79d763753b0222c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 16 Oct 2020 16:10:28 -0700 Subject: [PATCH 024/121] get it in a better state, support for more codes --- .../browser/terminalTypeAheadAddon.ts | 389 ++++++++++-------- 1 file changed, 228 insertions(+), 161 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 148c618cdb3..ed234e30dee 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -3,27 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { Terminal, ITerminalAddon, IDisposable, IBuffer, IBufferCell } from 'xterm'; -import { ITerminalProcessManager, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { Color } from 'vs/base/common/color'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; const CSI = '\x1b['; const SHOW_CURSOR = `${CSI}?25h`; const HIDE_CURSOR = `${CSI}?25l`; +const DELETE_CHAR = `${CSI}X`; +const CSI_STYLE_RE = /^\x1b\[[0-9;]+m/; +const CSI_MOVE_RE = /^\x1b\[([0-9]*)([DC])/; + +const enum CursorMoveDirection { + Back = 'D', + Forwards = 'C', +} const setCursorPos = (x: number, y: number) => `${CSI}${y + 1};${x + 1}H`; const setCursorCoordinate = (buffer: IBuffer, c: ICoordinate) => setCursorPos(c.x, c.y + (c.baseY - buffer.baseY)); -const enum ReapplyBehavior { - /** Applying the prediction to the line is a no-op */ - NoOp, - /** The prediction can be applied to the given line */ - Apply, - /** The prediction would overwrite other, unexpected data on the line */ - Mismatch, -} - interface ICoordinate { x: number; y: number; @@ -41,47 +40,86 @@ interface IPrediction { apply(buffer: IBuffer, cursor: ICoordinate): string; /** - * Returns a sequence to roll back a previous `apply()` call. + * Returns a sequence to roll back a previous `apply()` call. If + * `rollForwards` is not given, then this is also called if a prediction + * is correct before show the user's data. */ rollback(buffer: IBuffer): string; + /** + * If available, this will be called when the prediction is correct. + */ + rollForwards?(buffer: IBuffer, withInput: string): string; + /** * Returns whether the given input is one expected by this prediction. */ - matches(input: string): boolean; - - /** - * Returns the reapply behavior for the given line. - */ - getReapplyBehavior(buffer: IBuffer, allPredictions: ReadonlyArray): ReapplyBehavior; + matches(input: StringReader): boolean; } -/** - * Boundary added to the prediction timeline to indicate that predictions - * should be held and not applied until all previous predictions are validated. - */ -interface IPredictionBoundary { - /** - * Checks whether a change was made that satisfies the expectation for the - * prediction boundary. For instance is pressing enter, it checks that that - * results in a new line. - */ - test(input: string): boolean; -} -const csiRe = /\x1b\[*?[a-zA-Z]/g; +class StringReader { + public index = 0; + + public get remaining() { + return this.input.length - this.index; + } + + constructor(private readonly input: string) { } + + public eatStr(substr: string) { + if (this.input.slice(this.index, this.index + substr.length) !== substr) { + return; + } + + this.index += substr.length; + return substr; + } + + public eatRe(re: RegExp) { + const match = re.exec(this.input.slice(this.index)); + if (!match) { + return; + } + + this.index += match[0].length; + return match; + } + + public eatCharCode(min = 0, max = Infinity) { + const code = this.input.charCodeAt(this.index); + if (code < min || code >= max) { + return undefined; + } + + this.index++; + return code; + } + + public rest() { + return this.input.slice(this.index); + } +} /** * Boundary which never tests true. Will always discard predictions. */ -class HardBoundary implements IPredictionBoundary { - public test() { +class HardBoundary implements IPrediction { + public apply() { + return ''; + } + + public rollback() { + return ''; + } + + public matches() { return false; } } class CharacterPrediction implements IPrediction { - private appliedAt?: { + protected appliedAt?: { x: number; y: number; baseY: number; @@ -108,51 +146,112 @@ class CharacterPrediction implements IPrediction { const a = this.appliedAt; this.appliedAt = undefined; - return setCursorCoordinate(buffer, a) + (a.oldChar ? `${a.oldAttributes}${a.oldChar}${setCursorCoordinate(buffer, a)}` : `${CSI}X`); + return setCursorCoordinate(buffer, a) + (a.oldChar ? `${a.oldAttributes}${a.oldChar}${setCursorCoordinate(buffer, a)}` : DELETE_CHAR); } - public matches(input: string) { - csiRe.lastIndex = 0; - return input.replace(csiRe, '') === this.char; - } + public matches(input: StringReader) { + let startIndex = input.index; - public getReapplyBehavior(buffer: IBuffer) { - if (!this.appliedAt) { - return ReapplyBehavior.Apply; + // remove any styling CSI before checking the char + while (input.eatRe(CSI_STYLE_RE)) { } + if (input.eatStr(this.char)) { + return true; } - const cellText = getCellAtCoordinate(buffer, this.appliedAt)?.getChars() ?? ''; - if (cellText === this.char) { - return ReapplyBehavior.NoOp; - } else if (cellText === this.appliedAt.oldChar) { - return ReapplyBehavior.Apply; - } else { - return ReapplyBehavior.Mismatch; - } + input.index = startIndex; + return false; } } -interface IPredictionGroup { - boundary?: IPredictionBoundary; - predictions: IPrediction[]; +class BackspacePrediction extends CharacterPrediction { + public apply(buffer: IBuffer, cursor: ICoordinate) { + const cell = getCellAtCoordinate(buffer, cursor); + this.appliedAt = cell + ? { ...cursor, oldAttributes: getBufferCellAttributes(cell), oldChar: cell.getChars() } + : { ...cursor, oldAttributes: '', oldChar: '' }; + + cursor.x--; + return setCursorCoordinate(buffer, cursor) + DELETE_CHAR; + } + + public matches(input: StringReader) { + return !!input.eatStr('\b'); + } +} + +class NewlinePrediction implements IPrediction { + protected prevPosition?: ICoordinate; + + public apply(_: IBuffer, cursor: ICoordinate) { + this.prevPosition = { ...cursor }; + cursor.x = 0; + cursor.y++; + return '\r\n'; + } + + public rollback(buffer: IBuffer) { + if (!this.prevPosition) { + return ''; // not applied + } + + const p = this.prevPosition; + this.prevPosition = undefined; + return setCursorCoordinate(buffer, p) + DELETE_CHAR; + } + + public rollForwards() { + return ''; // does not need to rewrite + } + + public matches(input: StringReader) { + return !!input.eatStr('\r\n'); + } +} + +class CursorMovePrediction implements IPrediction { + constructor(private readonly direction: CursorMoveDirection, private readonly amount: number) { } + + public apply(_: IBuffer, cursor: ICoordinate) { + const { amount, direction } = this; + cursor.x += (direction === CursorMoveDirection.Back ? -1 : 1) * amount; + return `${CSI}${amount}${direction}`; + } + + public rollback() { + const fn = this.direction === CursorMoveDirection.Back ? CursorMoveDirection.Forwards : CursorMoveDirection.Back; + return `${CSI}${this.amount}${fn}`; + } + + public rollForwards() { + return ''; // does not need to rewrite + } + + public matches(input: StringReader) { + const { amount, direction } = this; + if (amount === 1 && input.eatStr(`${CSI}${direction}`)) { + return true; + } + + if (amount === 1 && this.direction === CursorMoveDirection.Back && input.eatStr('\b')) { + return true; + } + + return !!input.eatStr(`${CSI}${amount}${direction}`); + } } -const invalidatedRestoreInterval = 200; class PredictionTimeline { /** - * Expected queue of events, separated whenever a PredictionBoundary is - * emitted. Prediction groups _always_ apply to the active row. + * Expected queue of events. Only predictions for the lowest are + * written into the terminal. */ - private expected: IPredictionGroup[] = []; + private expected: ({ gen: number; p: IPrediction })[] = []; /** - * Last-invalidated set of predictions. These are restored if the predictions - * become valid again in a short period of time. Some terminal programs - * rewrite lines or the entire display on backspace, for example, which will - * invalidate the predictions even if they become valid again a moment later. + * Current prediction generation. */ - private invalidated: { at: number; p: IPredictionGroup[] } | undefined; + private currentGen = 0; /** * Cursor position -- kept outside the buffer since it can be ahead if @@ -178,42 +277,43 @@ class PredictionTimeline { } let output = ''; - let brokeBoundary = false; - for (let i = 0; i < input.length; i++) { - const test = this.expected[0]?.predictions[0]; - const char = input[i]; - // if we reached the end of our tests, try to break through the boundary - // and start applying its items. - if (!test) { - this.expected.shift(); - if (!this.expected.length) { - return output + input.slice(i); - } - brokeBoundary = true; - if (this.expected[0].boundary?.test(char) === false) { - this.expected = []; - return output + input.slice(i); // boundary assumption invalid, throw out predictions - } - } + const reader = new StringReader(input); + const startingGen = this.expected[0].gen; + while (this.expected.length && reader.remaining > 0) { + const prediction = this.expected[0].p; + let beforeTestReaderIndex = reader.index; + // if the input character matches what the next prediction expected, undo // the prediction and write the real character out. - else if (test.matches(char)) { - output += test.rollback(buffer) + char; - this.expected[0].predictions.shift(); + if (prediction.matches(reader)) { + const eaten = input.slice(beforeTestReaderIndex, reader.index); + output += prediction.rollForwards?.(buffer, eaten) + ?? (prediction.rollback(buffer) + input.slice(beforeTestReaderIndex, reader.index)); + this.expected.shift(); } - // otherwise, roll back all pending predictions and move the current stack - // of predictions into the "invalidated" key (for possible resurrection). + // otherwise, roll back all pending predictions else { - this.invalidated = { at: Date.now(), p: this.expected }; + output += this.expected.filter(p => p.gen === startingGen) + .map(({ p }) => p.rollback(buffer)) + .reverse() + .join(''); this.expected = []; this.cursor = undefined; - if (!brokeBoundary) { // on a new boundary, we did not apply predictions yet - output += this.invalidated.p[0].predictions.map(p => p.rollback(buffer)).reverse().join(''); + break; + } + } + + output += reader.rest(); + + // If we passed a generation boundary, apply the current generation's predictions + if (this.expected.length && startingGen !== this.expected[0].gen) { + for (const { p, gen } of this.expected) { + if (gen !== this.expected[0].gen) { + break; } - output += input.slice(i); - break; + output += p.apply(buffer, this.getCursor(buffer)); } } @@ -228,42 +328,26 @@ class PredictionTimeline { return output; } - /** - * Should be called after data is applied to the terminal. - */ - public afterServerInput() { - const buffer = this.getActiveBuffer(); - if (buffer && this.invalidated && Date.now() - this.invalidated.at < invalidatedRestoreInterval) { - this.tryReapplyInvalidated(buffer, this.invalidated.p); - } - } - /** * Appends a typeahead prediction. */ public addPrediction(buffer: IBuffer, prediction: IPrediction) { - const l = this.expected.length - 1; - if (l === -1) { - this.expected = [{ predictions: [prediction] }]; - } else { - this.expected[l].predictions.push(prediction); - } - - if (l <= 1) { + this.expected.push({ gen: this.currentGen, p: prediction }); + if (this.currentGen === this.expected[0].gen) { const text = prediction.apply(buffer, this.getCursor(buffer)); - console.log('prediction:', text); + console.log('prediction:', JSON.stringify(text)); this.terminal.write(text); } } /** - * Appends a boundary to the preduction. + * Appends a boundary to the prediction. */ - public addBoundary(boundary: IPredictionBoundary) { - this.expected.push({ boundary, predictions: [] }); + public addBoundary() { + this.currentGen++; } - private getCursor(buffer: IBuffer) { + public getCursor(buffer: IBuffer) { if (!this.cursor) { this.cursor = { baseY: buffer.baseY, y: buffer.cursorY, x: buffer.cursorX }; } @@ -275,42 +359,6 @@ class PredictionTimeline { const buffer = this.terminal.buffer.active; return buffer.type === 'normal' ? buffer : undefined; } - - private tryReapplyInvalidated(buffer: IBuffer, invalidated: IPredictionGroup[]) { - if (!invalidated.length) { - return; - } - - const predictions = invalidated[0].predictions; - let lastNoop = -1; - let hasApply = false; - for (let i = 0; i < predictions.length; i++) { - switch (predictions[i].getReapplyBehavior(buffer, predictions)) { - case ReapplyBehavior.NoOp: - if (!hasApply) { lastNoop = i; } - break; - case ReapplyBehavior.Mismatch: - return; // do not reapply any prediction in this set if there's a mismatch - case ReapplyBehavior.Apply: - hasApply = true; - break; - } - } - - if (!hasApply) { - return; - } - - for (let i = lastNoop + 1; i < predictions.length; i++) { - this.terminal.write(predictions[i].apply(buffer, this.getCursor(buffer))); - } - - this.expected = invalidated.slice(1); - - if (lastNoop < predictions.length - 1) { - this.expected.unshift({ predictions: predictions.slice(lastNoop) }); - } - } } /** * Gets the escape sequence to restore state/appearence in the cell. @@ -368,19 +416,38 @@ export class TypeAheadAddon implements ITerminalAddon { return; } - console.log('user data:', data); - if (data.length !== 1) { - this.timeline.addBoundary(new HardBoundary()); - return; - } + console.log('user data:', JSON.stringify(data)); const terminal = this.timeline.terminal; - const code = data.charCodeAt(0); - if (code >= 32 && code < 126) { - if (terminal.buffer.active.cursorX === terminal.cols - 1) { - } else { - this.timeline.addPrediction(terminal.buffer.active, new CharacterPrediction(this.typeheadStyle, data)); + const buffer = terminal.buffer.active; + const reader = new StringReader(data); + while (reader.remaining > 0) { + if (reader.eatStr('\b')) { // backspace + this.timeline.addPrediction(buffer, new BackspacePrediction(this.typeheadStyle, '\b')); + continue; } + + if (reader.eatCharCode(32, 126)) { // alphanum + const char = data[reader.index - 1]; + this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeheadStyle, char)); + if (this.timeline.getCursor(buffer).x === terminal.cols) { + this.timeline.addPrediction(buffer, new NewlinePrediction()); + this.timeline.addBoundary(); + } + continue; + } + + const cursorMv = reader.eatRe(CSI_MOVE_RE); + if (cursorMv) { + this.timeline.addPrediction(buffer, new CursorMovePrediction( + cursorMv[2] as CursorMoveDirection, Number(cursorMv[1]) || 1)); + continue; + } + + // something else + this.timeline.addPrediction(buffer, new HardBoundary()); + this.timeline.addBoundary(); + break; } } @@ -389,8 +456,8 @@ export class TypeAheadAddon implements ITerminalAddon { return; } - console.log('incoming data:', event.data); + console.log('incoming data:', JSON.stringify(event.data)); event.data = this.timeline.beforeServerInput(event.data); - console.log('emitted data:', event.data); + console.log('emitted data:', JSON.stringify(event.data)); } } From 979f9f73b3d5cd55356c0117be169fb1edbfd92b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 16 Oct 2020 16:16:50 -0700 Subject: [PATCH 025/121] fixes #991 --- src/vs/editor/browser/editorExtensions.ts | 4 ++-- src/vs/editor/contrib/find/findController.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index dcbb98b473f..aaf9feaf3ed 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -129,8 +129,8 @@ export abstract class Command { command: { id: this.id, title: item.title, - icon: item.icon - // precondition: this.precondition + icon: item.icon, + precondition: this.precondition }, when: item.when, order: item.order diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 09e4fa0e159..b65d6029cfd 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -473,7 +473,7 @@ export class StartFindAction extends MultiEditorAction { id: FIND_IDS.StartFindAction, label: nls.localize('startFindAction', "Find"), alias: 'Find', - precondition: undefined, + precondition: ContextKeyExpr.has('editorIsOpen'), kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_F, @@ -722,7 +722,7 @@ export class StartFindReplaceAction extends MultiEditorAction { id: FIND_IDS.StartFindReplaceAction, label: nls.localize('startReplace', "Replace"), alias: 'Replace', - precondition: undefined, + precondition: ContextKeyExpr.has('editorIsOpen'), kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_H, From ae5ecf9e17a4fc18219c8f8a3ede09112c3a3627 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 16 Oct 2020 16:23:16 -0700 Subject: [PATCH 026/121] Remove deprecated API usage from search #103454 --- .../search/browser/patternInputWidget.ts | 2 +- .../search/browser/searchResultsView.ts | 6 +++--- .../contrib/search/browser/searchView.ts | 2 +- .../contrib/search/browser/searchWidget.ts | 18 +++++++++--------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 056378f5b6b..63bf2771a46 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -138,7 +138,7 @@ export class PatternInputWidget extends Widget implements IThemable { private render(options: IOptions): void { this.domNode = document.createElement('div'); this.domNode.style.width = this.width + 'px'; - dom.addClass(this.domNode, 'monaco-findInput'); + this.domNode.classList.add('monaco-findInput'); this.inputBox = new ContextScopedHistoryInputBox(this.domNode, this.contextViewProvider, { placeholder: this.placeholder || '', diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index ba7bd8e4b98..c1a29a74874 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -225,7 +225,7 @@ export class MatchRenderer extends Disposable implements ITreeRenderer('search').showLineNumbers; const lineNumberStr = showLineNumbers ? `:${match.range().startLineNumber}` : ''; - DOM.toggleClass(templateData.lineNumber, 'show', (numLines > 0) || showLineNumbers); + templateData.lineNumber.classList.toggle('show', (numLines > 0) || showLineNumbers); templateData.lineNumber.textContent = lineNumberStr + extraLinesStr; templateData.lineNumber.setAttribute('title', this.getMatchTitle(match, showLineNumbers)); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index e123c8bb3af..9afc71db8f5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -988,7 +988,7 @@ export class SearchView extends ViewPane { } const actionsPosition = this.searchConfig.actionsPosition; - dom.toggleClass(this.getContainer(), SearchView.ACTIONS_RIGHT_CLASS_NAME, actionsPosition === 'right'); + this.getContainer().classList.toggle(SearchView.ACTIONS_RIGHT_CLASS_NAME, actionsPosition === 'right'); this.searchWidget.setWidth(this.size.width - 28 /* container margin */); diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index d149e29f822..c566e5d387f 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -293,7 +293,7 @@ export class SearchWidget extends Widget { }; this.toggleReplaceButton = this._register(new Button(parent, opts)); this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); - dom.addClasses(this.toggleReplaceButton.element, searchHideReplaceIcon.classNames); + this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); this.toggleReplaceButton.icon = 'toggle-replace-button'; // TODO@joh need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); @@ -356,7 +356,7 @@ export class SearchWidget extends Widget { if (options.showContextToggle) { this.contextLinesInput = new InputBox(searchInputContainer, this.contextViewService, { type: 'number' }); - dom.addClass(this.contextLinesInput.element, 'context-lines-input'); + this.contextLinesInput.element.classList.add('context-lines-input'); this.contextLinesInput.value = '' + (this.configurationService.getValue('search').searchEditor.defaultNumberOfContextLines ?? 1); this._register(this.contextLinesInput.onDidChange(() => this.onContextLinesChanged())); this._register(attachInputBoxStyler(this.contextLinesInput, this.themeService)); @@ -365,7 +365,7 @@ export class SearchWidget extends Widget { } private onContextLinesChanged() { - dom.toggleClass(this.domNode, 'show-context', this.showContextCheckbox.checked); + this.domNode.classList.toggle('show-context', this.showContextCheckbox.checked); this._onDidToggleContext.fire(); if (this.contextLinesInput.value.includes('-')) { @@ -383,7 +383,7 @@ export class SearchWidget extends Widget { this.showContextCheckbox.checked = true; this.contextLinesInput.value = '' + lines; } - dom.toggleClass(this.domNode, 'show-context', this.showContextCheckbox.checked); + this.domNode.classList.toggle('show-context', this.showContextCheckbox.checked); } private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void { @@ -429,13 +429,13 @@ export class SearchWidget extends Widget { } private onToggleReplaceButton(): void { - dom.toggleClass(this.replaceContainer, 'disabled'); + this.replaceContainer.classList.toggle('disabled'); if (this.isReplaceShown()) { - dom.removeClasses(this.toggleReplaceButton.element, searchHideReplaceIcon.classNames); - dom.addClasses(this.toggleReplaceButton.element, searchShowReplaceIcon.classNames); + this.toggleReplaceButton.element.classList.remove(...searchHideReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.add(...searchShowReplaceIcon.classNamesArray); } else { - dom.removeClasses(this.toggleReplaceButton.element, searchShowReplaceIcon.classNames); - dom.addClasses(this.toggleReplaceButton.element, searchHideReplaceIcon.classNames); + this.toggleReplaceButton.element.classList.remove(...searchShowReplaceIcon.classNamesArray); + this.toggleReplaceButton.element.classList.add(...searchHideReplaceIcon.classNamesArray); } this.toggleReplaceButton.element.setAttribute('aria-expanded', this.isReplaceShown() ? 'true' : 'false'); this.updateReplaceActiveState(); From 189d79df76344d3610773419c1c0267ed32f1e92 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Oct 2020 16:34:25 -0700 Subject: [PATCH 027/121] Migrate off of require.toUrl (#107620) Fixes #107439 --- .../electron-main/webviewProtocolProvider.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts index 77b451c9005..a019d267e69 100644 --- a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts @@ -7,7 +7,7 @@ import { protocol, session } from 'electron'; import { Readable } from 'stream'; import { bufferToStream, VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -130,13 +130,12 @@ export class WebviewProtocolProvider extends Disposable { const uri = URI.parse(request.url); const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path); if (typeof entry === 'string') { - let url: string; - if (uri.path.startsWith('/electron-browser')) { - url = require.toUrl(`vs/workbench/contrib/webview/electron-browser/pre/${entry}`); - } else { - url = require.toUrl(`vs/workbench/contrib/webview/browser/pre/${entry}`); - } - return callback(decodeURIComponent(url.replace(`${Schemas.file}://`, ''))); + const relativeResourcePath = uri.path.startsWith('/electron-browser') + ? `vs/workbench/contrib/webview/electron-browser/pre/${entry}` + : `vs/workbench/contrib/webview/browser/pre/${entry}`; + + const url = FileAccess.asFileUri(relativeResourcePath, require); + return callback(decodeURIComponent(url.fsPath)); } } catch { // noop From dd124a5cae3d82f159cf4adf3fea98408e12f6be Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Oct 2020 15:58:06 -0700 Subject: [PATCH 028/121] Cleaning up code - Use more explicit names - extract - Use `??` --- .../src/features/foldingProvider.ts | 55 ++++++++++--------- .../src/tableOfContentsProvider.ts | 14 ++--- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 590a7a1b246..553c1a3257b 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -11,12 +11,6 @@ import { flatten } from '../util/arrays'; const rangeLimit = 5000; -const isStartRegion = (t: string) => /^\s*/.test(t); -const isEndRegion = (t: string) => /^\s*/.test(t); - -const isRegionMarker = (token: Token) => - token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); - export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvider { constructor( @@ -69,24 +63,6 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi } private async getBlockFoldingRanges(document: vscode.TextDocument): Promise { - - const isFoldableToken = (token: Token): boolean => { - switch (token.type) { - case 'fence': - case 'list_item_open': - return token.map[1] > token.map[0]; - - case 'html_block': - if (isRegionMarker(token)) { - return false; - } - return token.map[1] > token.map[0] + 1; - - default: - return false; - } - }; - const tokens = await this.engine.parse(document); const multiLineListItems = tokens.filter(isFoldableToken); return multiLineListItems.map(listItem => { @@ -95,7 +71,36 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) { end = end - 1; } - return new vscode.FoldingRange(start, end, listItem.type === 'html_block' && listItem.content.startsWith('/.test(t); +const isEndRegion = (t: string) => /^\s*/.test(t); + +const isRegionMarker = (token: Token) => + token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); + +const isFoldableToken = (token: Token): boolean => { + switch (token.type) { + case 'fence': + case 'list_item_open': + return token.map[1] > token.map[0]; + + case 'html_block': + if (isRegionMarker(token)) { + return false; + } + return token.map[1] > token.map[0] + 1; + + default: + return false; + } +}; diff --git a/extensions/markdown-language-features/src/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index 6a3a5b62867..3e1de6f6779 100644 --- a/extensions/markdown-language-features/src/tableOfContentsProvider.ts +++ b/extensions/markdown-language-features/src/tableOfContentsProvider.ts @@ -57,19 +57,19 @@ export class TableOfContentsProvider { const toc: TocEntry[] = []; const tokens = await this.engine.parse(document); - const slugCount = new Map(); + const existingSlugEntries = new Map(); for (const heading of tokens.filter(token => token.type === 'heading_open')) { const lineNumber = heading.map[0]; const line = document.lineAt(lineNumber); let slug = githubSlugifier.fromHeading(line.text); - if (slugCount.has(slug.value)) { - const count = slugCount.get(slug.value)!; - slugCount.set(slug.value, count + 1); - slug = githubSlugifier.fromHeading(slug.value + '-' + (count + 1)); + const existingSlugEntry = existingSlugEntries.get(slug.value); + if (existingSlugEntry) { + ++existingSlugEntry.count; + slug = githubSlugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); } else { - slugCount.set(slug.value, 0); + existingSlugEntries.set(slug.value, { count: 0 }); } toc.push({ @@ -91,7 +91,7 @@ export class TableOfContentsProvider { break; } } - const endLine = end !== undefined ? end : document.lineCount - 1; + const endLine = end ?? document.lineCount - 1; return { ...entry, location: new vscode.Location(document.uri, From c13678f258248b38570ef08311047d29d40d9c45 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Oct 2020 17:04:52 -0700 Subject: [PATCH 029/121] Set extension on webview views Fixes #108689 This fixes loading resources in webview views on web --- .../workbench/api/browser/mainThreadWebviewViews.ts | 12 ++++++++++-- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostWebviewView.ts | 4 ++-- .../contrib/webview/browser/baseWebviewElement.ts | 2 +- .../webview/browser/dynamicWebviewEditorOverlay.ts | 10 +++++++++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index d43016bcd07..346ca9957b6 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -6,7 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebviews'; +import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; @@ -43,11 +43,17 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc webviewView.show(preserveFocus); } - public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void { + public $registerWebviewViewProvider( + extensionData: extHostProtocol.WebviewExtensionDescription, + viewType: string, + options?: { retainContextWhenHidden?: boolean } + ): void { if (this._webviewViewProviders.has(viewType)) { throw new Error(`View provider for ${viewType} already registered`); } + const extension = reviveWebviewExtension(extensionData); + this._webviewViewService.register(viewType, { resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { const handle = webviewView.webview.id; @@ -64,6 +70,8 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc } } + webviewView.webview.extension = extension; + if (options) { webviewView.webview.options = options; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b7ad89e366b..ddca6becf25 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -644,7 +644,7 @@ export interface MainThreadCustomEditorsShape extends IDisposable { } export interface MainThreadWebviewViewsShape extends IDisposable { - $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void; + $registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options?: { retainContextWhenHidden?: boolean }): void; $unregisterWebviewViewProvider(viewType: string): void; $setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void; diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index a9f3db59641..205ad24c09e 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostWebview, ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; @@ -146,7 +146,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS } this._viewProviders.set(viewType, { provider, extension }); - this._proxy.$registerWebviewViewProvider(viewType, webviewOptions); + this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, webviewOptions); return new extHostTypes.Disposable(() => { this._viewProviders.delete(viewType); diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 947f7740e95..6f19e4548e7 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -83,7 +83,7 @@ export abstract class BaseWebview extends Disposable { public readonly id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, - public readonly extension: WebviewExtensionDescription | undefined, + public extension: WebviewExtensionDescription | undefined, private readonly webviewThemeDataProvider: WebviewThemeDataProvider, @INotificationService notificationService: INotificationService, @ILogService private readonly _logService: ILogService, diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index f5a6f27629e..3168612411f 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -30,6 +30,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv private _initialScrollProgress: number = 0; private _state: string | undefined = undefined; + private _extension: WebviewExtensionDescription | undefined; private _contentOptions: WebviewContentOptions; private _options: WebviewOptions; @@ -42,13 +43,14 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv public readonly id: string, initialOptions: WebviewOptions, initialContentOptions: WebviewContentOptions, - public readonly extension: WebviewExtensionDescription | undefined, + extension: WebviewExtensionDescription | undefined, @ILayoutService private readonly _layoutService: ILayoutService, @IWebviewService private readonly _webviewService: IWebviewService, @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { super(); + this._extension = extension; this._options = initialOptions; this._contentOptions = initialContentOptions; @@ -175,6 +177,12 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv this.withWebview(webview => webview.state = value); } + public get extension(): WebviewExtensionDescription | undefined { return this._extension; } + public set extension(value: WebviewExtensionDescription | undefined) { + this._extension = value; + this.withWebview(webview => webview.extension = value); + } + public get options(): WebviewOptions { return this._options; } public set options(value: WebviewOptions) { this._options = { customClasses: this._options.customClasses, ...value }; } From 24876cda75a2f15f5d0609d40eed0d28bad91fbc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 17 Oct 2020 09:20:57 +0200 Subject: [PATCH 030/121] Cannot close tab opened by piping to `code -` (fix #108752) --- .../browser/parts/editor/editorGroupView.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 5cac87be5a8..e6d4dd3491f 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1361,7 +1361,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.handleDirtyClosing(editors); } - private async doHandleDirtyClosing(editor: EditorInput): Promise { + private async doHandleDirtyClosing(editor: EditorInput, options?: { skipAutoSave: boolean }): Promise { if (!editor.isDirty() || editor.isSaving()) { return false; // editor must be dirty and not saving } @@ -1396,9 +1396,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Auto-save on focus change: assume to Save unless the editor is untitled // because bringing up a dialog would save in this case anyway. + // However, make sure to respect `skipAutoSave` option in case the automated + // save fails which would result in the editor never closing + // (see https://github.com/microsoft/vscode/issues/108752) let confirmation: ConfirmResult; let saveReason = SaveReason.EXPLICIT; - if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.ON_FOCUS_CHANGE && !editor.isUntitled()) { + let autoSave = false; + if (this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.ON_FOCUS_CHANGE && !editor.isUntitled() && !options?.skipAutoSave) { + autoSave = true; confirmation = ConfirmResult.SAVE; saveReason = SaveReason.FOCUS_CHANGE; } @@ -1430,7 +1435,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Otherwise, handle accordingly switch (confirmation) { case ConfirmResult.SAVE: - await editor.save(this.id, { reason: saveReason }); + const result = await editor.save(this.id, { reason: saveReason }); + if (!result && autoSave) { + // Save failed and we need to signal this back to the user, so + // we handle the dirty editor again but this time ensuring to + // show the confirm dialog + // (see https://github.com/microsoft/vscode/issues/108752) + return this.doHandleDirtyClosing(editor, { skipAutoSave: true }); + } return editor.isDirty(); // veto if still dirty case ConfirmResult.DONT_SAVE: From 41e73c836f5a23c711bab89b5f35a7703e7da20f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 17 Oct 2020 09:25:26 +0200 Subject: [PATCH 031/121] backup - wire cancellation into backup() method --- .../api/browser/mainThreadCustomEditors.ts | 2 +- .../contrib/backup/common/backupTracker.ts | 2 +- .../backup/electron-sandbox/backupTracker.ts | 3 +- .../electron-browser/backupTracker.test.ts | 54 ++++++++++++++- .../notebook/common/notebookEditorModel.ts | 11 ++-- .../searchEditor/browser/searchEditorInput.ts | 5 +- .../textfile/common/textFileEditorModel.ts | 4 +- .../common/untitledTextEditorModel.ts | 3 +- .../workingCopy/common/workingCopyService.ts | 5 +- .../browser/workingCopyFileService.test.ts | 2 +- .../test/common/workingCopyService.test.ts | 66 +------------------ .../test/common/workbenchTestServices.ts | 61 ++++++++++++++++- 12 files changed, 138 insertions(+), 80 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 26d98c78511..410b6b061dc 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -567,7 +567,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod } } - public async backup(): Promise { + public async backup(token: CancellationToken): Promise { const editors = this._getEditors(); if (!editors.length) { throw new Error('No editors found for resource, cannot back up'); diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts index 0da57922996..b79002d2009 100644 --- a/src/vs/workbench/contrib/backup/common/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -120,7 +120,7 @@ export abstract class BackupTracker extends Disposable { this.logService.trace(`[backup tracker] creating backup`, workingCopy.resource.toString()); try { - const backup = await workingCopy.backup(); + const backup = await workingCopy.backup(cts.token); if (cts.token.isCancellationRequested) { return; } diff --git a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts index f47ccd5545b..51cc964f498 100644 --- a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts @@ -20,6 +20,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { SaveReason } from 'vs/workbench/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class NativeBackupTracker extends BackupTracker implements IWorkbenchContribution { @@ -195,7 +196,7 @@ export class NativeBackupTracker extends BackupTracker implements IWorkbenchCont // Backup does not exist else { - const backup = await workingCopy.backup(); + const backup = await workingCopy.backup(CancellationToken.None); await this.backupFileService.backup(workingCopy.resource, backup.content, contentVersion, backup.meta); backups.push(workingCopy); diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index beb7ff089b0..ae5b2e4a3f6 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -28,7 +28,7 @@ import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/ele import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { toResource } from 'vs/base/test/common/utils'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyBackup, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; @@ -45,6 +45,9 @@ import { TestFilesConfigurationService } from 'vs/workbench/test/browser/workben import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { timeout } from 'vs/base/common/async'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer'); const backupHome = path.join(userdataDir, 'Backups'); @@ -216,6 +219,55 @@ suite('BackupTracker', () => { tracker.dispose(); }); + test('Track backups (custom)', async function () { + const [accessor, part, tracker] = await createTracker(); + + class TestBackupWorkingCopy extends TestWorkingCopy { + + backupDelay = 0; + + constructor(resource: URI) { + super(resource); + + accessor.workingCopyService.registerWorkingCopy(this); + } + + async backup(token: CancellationToken): Promise { + await timeout(this.backupDelay); + + return {}; + } + } + + const resource = toResource.call(this, '/path/custom.txt'); + const customWorkingCopy = new TestBackupWorkingCopy(resource); + + // Normal + customWorkingCopy.setDirty(true); + await accessor.backupFileService.joinBackupResource(); + assert.equal(accessor.backupFileService.hasBackupSync(resource), true); + + customWorkingCopy.setDirty(false); + customWorkingCopy.setDirty(true); + await accessor.backupFileService.joinBackupResource(); + assert.equal(accessor.backupFileService.hasBackupSync(resource), true); + + customWorkingCopy.setDirty(false); + await accessor.backupFileService.joinDiscardBackup(); + assert.equal(accessor.backupFileService.hasBackupSync(resource), false); + + // Cancellation + customWorkingCopy.setDirty(true); + await timeout(0); + customWorkingCopy.setDirty(false); + await accessor.backupFileService.joinDiscardBackup(); + assert.equal(accessor.backupFileService.hasBackupSync(resource), false); + + customWorkingCopy.dispose(); + part.dispose(); + tracker.dispose(); + }); + test('onWillShutdown - no veto if no dirty files', async function () { const [accessor, part, tracker] = await createTracker(); diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 5311ecb8aac..fd79380d7c9 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -11,7 +11,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; import { IWorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Schemas } from 'vs/base/common/network'; import { IFileStatWithMetadata, IFileService } from 'vs/platform/files/common/files'; @@ -66,7 +66,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM readonly onDidChangeDirty = that.onDidChangeDirty; readonly onDidChangeContent = that.onDidChangeContent; isDirty(): boolean { return that.isDirty(); } - backup(): Promise { return that.backup(); } + backup(token: CancellationToken): Promise { return that.backup(token); } save(): Promise { return that.save(); } revert(options?: IRevertOptions): Promise { return that.revert(options); } }; @@ -89,10 +89,13 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } } - async backup(): Promise> { + async backup(token: CancellationToken): Promise> { if (this._notebook.supportBackup) { - const tokenSource = new CancellationTokenSource(); + const tokenSource = new CancellationTokenSource(token); const backupId = await this._notebookService.backup(this.viewType, this.resource, tokenSource.token); + if (token.isCancellationRequested) { + return {}; + } const stats = await this._resolveStats(this.resource); return { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index fd9de5391f2..7a265a1f7a1 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -28,6 +28,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { ITextFileSaveOptions, ITextFileService, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export type SearchConfiguration = { query: string, @@ -113,7 +114,7 @@ export class SearchEditorInput extends EditorInput { readonly onDidChangeDirty = input.onDidChangeDirty; readonly onDidChangeContent = input.onDidChangeContent; isDirty(): boolean { return input.isDirty(); } - backup(): Promise { return input.backup(); } + backup(token: CancellationToken): Promise { return input.backup(token); } save(options?: ISaveOptions): Promise { return input.save(0, options).then(editor => !!editor); } revert(options?: IRevertOptions): Promise { return input.revert(0, options); } }; @@ -265,7 +266,7 @@ export class SearchEditorInput extends EditorInput { return false; } - private async backup(): Promise { + private async backup(token: CancellationToken): Promise { const content = stringToSnapshot((await this.model).getValue()); return { content }; } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 0f1d5bb3263..ffcc91f57fa 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -22,7 +22,7 @@ import { basename } from 'vs/base/common/path'; import { IWorkingCopyService, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { UTF8 } from 'vs/workbench/services/textfile/common/encoding'; interface IBackupMetaData { @@ -187,7 +187,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil //#region Backup - async backup(): Promise { + async backup(token: CancellationToken): Promise { // Fill in metadata if we are resolved let meta: IBackupMetaData | undefined = undefined; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index c06d942139a..6d27bf234b3 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -21,6 +21,7 @@ import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { ILabelService } from 'vs/platform/label/common/label'; import { ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport, IEncodingSupport, IWorkingCopy { @@ -265,7 +266,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt this.dispose(); } - async backup(): Promise { + async backup(token: CancellationToken): Promise { return { content: withNullAsUndefined(this.createSnapshot()) }; } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index e5f96509de8..a390c9be0dd 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -11,6 +11,7 @@ import { Disposable, IDisposable, toDisposable, DisposableStore, dispose } from import { ResourceMap } from 'vs/base/common/map'; import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; import { ITextSnapshot } from 'vs/editor/common/model'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const enum WorkingCopyCapabilities { @@ -98,8 +99,10 @@ export interface IWorkingCopy { * * Providers of working copies should use `IBackupFileService.resolve(workingCopy.resource)` * to retrieve the backup metadata associated when loading the working copy. + * + * @param token support for cancellation */ - backup(): Promise; + backup(token: CancellationToken): Promise; /** * Asks the working copy to save. If the working copy was dirty, it is diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index a9178e8c36b..a1c06d18642 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -11,7 +11,7 @@ import { toResource } from 'vs/base/test/common/utils'; import { workbenchInstantiationService, TestServiceAccessor, TestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { FileOperation } from 'vs/platform/files/common/files'; -import { TestWorkingCopy } from 'vs/workbench/services/workingCopy/test/common/workingCopyService.test'; +import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { VSBuffer } from 'vs/base/common/buffer'; suite('WorkingCopyFileService', () => { diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 97840f97145..df5f7b2e048 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -4,74 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { URI } from 'vs/base/common/uri'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { TestWorkingCopyService } from 'vs/workbench/test/common/workbenchTestServices'; -import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; -import { basename } from 'vs/base/common/resources'; - -export class TestWorkingCopy extends Disposable implements IWorkingCopy { - - private readonly _onDidChangeDirty = this._register(new Emitter()); - readonly onDidChangeDirty = this._onDidChangeDirty.event; - - private readonly _onDidChangeContent = this._register(new Emitter()); - readonly onDidChangeContent = this._onDidChangeContent.event; - - private readonly _onDispose = this._register(new Emitter()); - readonly onDispose = this._onDispose.event; - - readonly capabilities = WorkingCopyCapabilities.None; - - readonly name = basename(this.resource); - - private dirty = false; - - constructor(public readonly resource: URI, isDirty = false) { - super(); - - this.dirty = isDirty; - } - - setDirty(dirty: boolean): void { - if (this.dirty !== dirty) { - this.dirty = dirty; - this._onDidChangeDirty.fire(); - } - } - - setContent(content: string): void { - this._onDidChangeContent.fire(); - } - - isDirty(): boolean { - return this.dirty; - } - - async save(options?: ISaveOptions): Promise { - return true; - } - - async revert(options?: IRevertOptions): Promise { - this.setDirty(false); - } - - async backup(): Promise { - return {}; - } - - dispose(): void { - this._onDispose.fire(); - - super.dispose(); - } -} +import { TestWorkingCopy, TestWorkingCopyService } from 'vs/workbench/test/common/workbenchTestServices'; suite('WorkingCopyService', () => { - test('registry - basics', () => { const service = new TestWorkingCopyService(); diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 866a0fda122..b0bddb28d99 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -14,12 +14,14 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderW import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { InMemoryStorageService, IWillSaveStateEvent } from 'vs/platform/storage/common/storage'; -import { WorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { WorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class TestTextResourcePropertiesService implements ITextResourcePropertiesService { @@ -125,6 +127,63 @@ export class TestStorageService extends InMemoryStorageService { export class TestWorkingCopyService extends WorkingCopyService { } +export class TestWorkingCopy extends Disposable implements IWorkingCopy { + + private readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; + + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; + + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; + + readonly capabilities = WorkingCopyCapabilities.None; + + readonly name = resources.basename(this.resource); + + private dirty = false; + + constructor(public readonly resource: URI, isDirty = false) { + super(); + + this.dirty = isDirty; + } + + setDirty(dirty: boolean): void { + if (this.dirty !== dirty) { + this.dirty = dirty; + this._onDidChangeDirty.fire(); + } + } + + setContent(content: string): void { + this._onDidChangeContent.fire(); + } + + isDirty(): boolean { + return this.dirty; + } + + async save(options?: ISaveOptions): Promise { + return true; + } + + async revert(options?: IRevertOptions): Promise { + this.setDirty(false); + } + + async backup(token: CancellationToken): Promise { + return {}; + } + + dispose(): void { + this._onDispose.fire(); + + super.dispose(); + } +} + export class TestWorkingCopyFileService implements IWorkingCopyFileService { declare readonly _serviceBrand: undefined; From 3f9a7a2e05e4a48c56f9877916d2848f68074a43 Mon Sep 17 00:00:00 2001 From: a5hk <5412540+a5hk@users.noreply.github.com> Date: Fri, 16 Oct 2020 10:24:08 +0330 Subject: [PATCH 032/121] closes #97890 --- .../inspectEditorTokens/inspectEditorTokens.css | 12 ++++++++++++ .../inspectEditorTokens/inspectEditorTokens.ts | 12 +++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css index 63601ef1c79..35526df5a0b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css @@ -34,6 +34,18 @@ text-align: right; word-break: break-word; } + +.tiw-metadata-values { + list-style: none; + max-height: 300px; + overflow-y: auto; + margin-right: -10px; +} + +.tiw-metadata-values > .tiw-metadata-value { + margin-right: 10px; +} + .tiw-metadata-key { vertical-align: top; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 75138d4a33c..97a0010dfbd 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -594,11 +594,17 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { theme.resolveScopes(definition, scopesDefinition); const matchingRule = scopesDefinition[property]; if (matchingRule && scopesDefinition.scope) { - const strScopes = Array.isArray(matchingRule.scope) ? matchingRule.scope.join(', ') : String(matchingRule.scope); + const scopes = $('ul.tiw-metadata-values'); + const strScopes = Array.isArray(matchingRule.scope) ? matchingRule.scope : [String(matchingRule.scope)]; + + for (let strScope of strScopes) { + scopes.appendChild($('li.tiw-metadata-value.tiw-metadata-scopes', undefined, strScope)); + } + elements.push( scopesDefinition.scope.join(' '), - $('br'), - $('code.tiw-theme-selector', undefined, strScopes, $('br'), JSON.stringify(matchingRule.settings, null, '\t'))); + scopes, + $('code.tiw-theme-selector', undefined, JSON.stringify(matchingRule.settings, null, '\t'))); return elements; } return elements; From 177eba74b695d1cfe02d2bc7aea89dd974f40d42 Mon Sep 17 00:00:00 2001 From: a5hk <5412540+a5hk@users.noreply.github.com> Date: Sat, 17 Oct 2020 17:20:15 +0330 Subject: [PATCH 033/121] left aligned values --- .../browser/inspectEditorTokens/inspectEditorTokens.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css index 35526df5a0b..5f7ec45d7f7 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.css @@ -31,7 +31,6 @@ .tiw-metadata-value { font-family: var(--monaco-monospace-font); - text-align: right; word-break: break-word; } @@ -40,6 +39,7 @@ max-height: 300px; overflow-y: auto; margin-right: -10px; + padding-left: 0; } .tiw-metadata-values > .tiw-metadata-value { @@ -47,6 +47,10 @@ } .tiw-metadata-key { + width: 1px; + min-width: 150px; + padding-right: 10px; + white-space: nowrap; vertical-align: top; } From 5e1d9eb316e63eaf129ab02b670d510f4a418d9e Mon Sep 17 00:00:00 2001 From: Kenny Smith Date: Sat, 17 Oct 2020 08:31:00 -0700 Subject: [PATCH 034/121] Trash/delete shortcut for forward delete on MacOS --- .../contrib/files/browser/fileActions.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 086fa8c123a..6b097374647 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -80,7 +80,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash), primary: KeyCode.Delete, mac: { - primary: KeyMod.CtrlCmd | KeyCode.Backspace + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + secondary: [KeyCode.Delete] }, handler: moveFileToTrashHandler }); From 74ef0a92fe8be7034d7d5f513e31262ab77d3bf1 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Sat, 17 Oct 2020 09:45:19 -0700 Subject: [PATCH 035/121] Update callback url for ms authentication when not using local server --- extensions/microsoft-authentication/src/AADHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index d0872e17e1f..a30d58be4a5 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -359,7 +359,7 @@ export class AzureActiveDirectoryService { case 'online.dev.core.vsengsaas.visualstudio.com': return 'vsodev,'; default: - return ''; + return `${callbackUri.scheme},`; } } From 8d7ad831e57e9038d8c982d861c647d2a487f40e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Oct 2020 08:04:47 +0200 Subject: [PATCH 036/121] Improve window.withProgress in status bar to use a static spin icon (fix #108657) --- src/vs/base/browser/codicons.ts | 6 +- src/vs/base/browser/dom.ts | 9 +- .../browser/parts/statusbar/statusbarPart.ts | 91 +++++++++++++++---- .../extensionProfileService.ts | 5 +- .../contrib/remote/browser/remoteIndicator.ts | 5 +- .../progress/browser/progressService.ts | 3 +- .../services/statusbar/common/statusbar.ts | 5 + 7 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/vs/base/browser/codicons.ts b/src/vs/base/browser/codicons.ts index ab29f13ce8b..cf55c1a9b92 100644 --- a/src/vs/base/browser/codicons.ts +++ b/src/vs/base/browser/codicons.ts @@ -18,7 +18,7 @@ export function renderCodicons(text: string): Array { textStart = (match.index || 0) + match[0].length; const [, escaped, codicon, name, animation] = match; - elements.push(escaped ? `$(${codicon})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`)); + elements.push(escaped ? `$(${codicon})` : renderCodicon(name, animation)); } if (textStart < text.length) { @@ -26,3 +26,7 @@ export function renderCodicons(text: string): Array { } return elements; } + +export function renderCodicon(name: string, animation: string): HTMLSpanElement { + return dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`); +} diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 0c19deeb873..8a3e808a871 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -998,8 +998,15 @@ export function prepend(parent: HTMLElement, child: T): T { /** * Removes all children from `parent` and appends `children` */ -export function reset(parent: HTMLElement, ...children: Array) { +export function reset(parent: HTMLElement, ...children: Array): void { parent.innerText = ''; + appendChildren(parent, ...children); +} + +/** + * Appends `children` to `parent` + */ +export function appendChildren(parent: HTMLElement, ...children: Array): void { for (const child of children) { if (child instanceof Node) { parent.appendChild(child); diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index ce83ddf403b..ced7b6480a6 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -21,7 +21,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; -import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor } from 'vs/base/browser/dom'; +import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -39,6 +39,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { renderCodicon, renderCodicons } from 'vs/base/browser/codicons'; interface IPendingStatusbarEntry { id: string; @@ -64,17 +65,18 @@ class StatusbarViewModel extends Disposable { static readonly HIDDEN_ENTRIES_KEY = 'workbench.statusbar.hidden'; + private readonly _onDidChangeEntryVisibility = this._register(new Emitter<{ id: string, visible: boolean }>()); + readonly onDidChangeEntryVisibility = this._onDidChangeEntryVisibility.event; + private readonly _entries: IStatusbarViewModelEntry[] = []; get entries(): IStatusbarViewModelEntry[] { return this._entries; } - private hidden!: Set; + private _lastFocusedEntry: IStatusbarViewModelEntry | undefined; get lastFocusedEntry(): IStatusbarViewModelEntry | undefined { return this._lastFocusedEntry && !this.isHidden(this._lastFocusedEntry.id) ? this._lastFocusedEntry : undefined; } - private _lastFocusedEntry: IStatusbarViewModelEntry | undefined; - private readonly _onDidChangeEntryVisibility = this._register(new Emitter<{ id: string, visible: boolean }>()); - readonly onDidChangeEntryVisibility = this._onDidChangeEntryVisibility.event; + private hidden!: Set; constructor(private readonly storageService: IStorageService) { super(); @@ -706,12 +708,67 @@ export class StatusbarPart extends Part implements IStatusbarService { } } +class StatusBarCodiconLabel extends CodiconLabel { + + private readonly progressCodicon = renderCodicon('sync', 'spin'); + + private currentText = ''; + private currentShowProgress = false; + + constructor( + private readonly container: HTMLElement + ) { + super(container); + } + + set showProgress(showProgress: boolean) { + if (this.currentShowProgress !== showProgress) { + this.currentShowProgress = showProgress; + this.text = this.currentText; + } + } + + set text(text: string) { + + // Progress: insert progress codicon as first element as needed + // but keep it stable so that the animation does not reset + if (this.currentShowProgress) { + + // Append as needed + if (this.container.firstChild !== this.progressCodicon) { + this.container.appendChild(this.progressCodicon); + } + + // Remove others + for (const node of Array.from(this.container.childNodes)) { + if (node !== this.progressCodicon) { + node.remove(); + } + } + + // If we have text to show, add a space to separate from progress + let textContent = text ?? ''; + if (textContent) { + textContent = ` ${textContent}`; + } + + // Append new elements + appendChildren(this.container, ...renderCodicons(textContent)); + } + + // No Progress: no special handling + else { + super.text = text; + } + } +} + class StatusbarEntryItem extends Disposable { - private entry!: IStatusbarEntry; + readonly labelContainer: HTMLElement; + private readonly label: StatusBarCodiconLabel; - labelContainer!: HTMLElement; - private label!: CodiconLabel; + private entry: IStatusbarEntry | undefined = undefined; private readonly foregroundListener = this._register(new MutableDisposable()); private readonly backgroundListener = this._register(new MutableDisposable()); @@ -729,26 +786,25 @@ class StatusbarEntryItem extends Disposable { ) { super(); - this.create(); - this.update(entry); - } - - private create(): void { - // Label Container this.labelContainer = document.createElement('a'); this.labelContainer.tabIndex = -1; // allows screen readers to read title, but still prevents tab focus. this.labelContainer.setAttribute('role', 'button'); - // Label - this.label = new CodiconLabel(this.labelContainer); + // Label (with support for progress) + this.label = new StatusBarCodiconLabel(this.labelContainer); // Add to parent this.container.appendChild(this.labelContainer); + + this.update(entry); } update(entry: IStatusbarEntry): void { + // Update: Progress + this.label.showProgress = !!entry.showProgress; + // Update: Text if (!this.entry || entry.text !== this.entry.text) { this.label.text = entry.text; @@ -760,8 +816,9 @@ class StatusbarEntryItem extends Disposable { } } + // Set the aria label on both elements so screen readers would read + // the correct thing without duplication #96210 if (!this.entry || entry.ariaLabel !== this.entry.ariaLabel) { - // Set the aria label on both elements so screen readers would read the correct thing without duplication #96210 this.container.setAttribute('aria-label', entry.ariaLabel); this.labelContainer.setAttribute('aria-label', entry.ariaLabel); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index d491483b338..34cdcea7efa 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -82,7 +82,8 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio if (visible) { const indicator: IStatusbarEntry = { - text: '$(sync~spin) ' + nls.localize('profilingExtensionHost', "Profiling Extension Host"), + text: nls.localize('profilingExtensionHost', "Profiling Extension Host"), + showProgress: true, ariaLabel: nls.localize('profilingExtensionHost', "Profiling Extension Host"), tooltip: nls.localize('selectAndStartDebug', "Click to stop profiling."), command: 'workbench.action.extensionHostProfilder.stop' @@ -91,7 +92,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio const timeStarted = Date.now(); const handle = setInterval(() => { if (this.profilingStatusBarIndicator) { - this.profilingStatusBarIndicator.update({ ...indicator, text: '$(sync~spin) ' + nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); + this.profilingStatusBarIndicator.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); } }, 1000); this.profilingStatusBarIndicatorLabelUpdater.value = toDisposable(() => clearInterval(handle)); diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 8a1a986c824..83e5499b936 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -197,7 +197,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr const hostLabel = this.labelService.getHostLabel(Schemas.vscodeRemote, this.remoteAuthority) || this.remoteAuthority; switch (this.connectionState) { case 'initializing': - this.renderRemoteStatusIndicator(`$(sync~spin) ${nls.localize('host.open', "Opening Remote...")}`, nls.localize('host.open', "Opening Remote...")); + this.renderRemoteStatusIndicator(nls.localize('host.open', "Opening Remote..."), nls.localize('host.open', "Opening Remote..."), undefined, true /* progress */); break; case 'disconnected': this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", hostLabel)}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); @@ -219,7 +219,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr } } - private renderRemoteStatusIndicator(text: string, tooltip?: string, command?: string): void { + private renderRemoteStatusIndicator(text: string, tooltip?: string, command?: string, showProgress?: boolean): void { const name = nls.localize('remoteHost', "Remote Host"); if (typeof command !== 'string' && this.remoteMenu.getActions().length > 0) { command = RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID; @@ -230,6 +230,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr color: themeColorFromId(STATUS_BAR_HOST_NAME_FOREGROUND), ariaLabel: name, text, + showProgress, tooltip, command }; diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 597aaf8c80c..481205112be 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -151,7 +151,8 @@ export class ProgressService extends Disposable implements IProgressService { } const statusEntryProperties: IStatusbarEntry = { - text: `$(sync~spin) ${text}`, + text, + showProgress: true, ariaLabel: text, tooltip: title, command: progressCommand diff --git a/src/vs/workbench/services/statusbar/common/statusbar.ts b/src/vs/workbench/services/statusbar/common/statusbar.ts index 14a52e61d68..3d65b4fbaa1 100644 --- a/src/vs/workbench/services/statusbar/common/statusbar.ts +++ b/src/vs/workbench/services/statusbar/common/statusbar.ts @@ -63,6 +63,11 @@ export interface IStatusbarEntry { * Whether to show a beak above the status bar entry. */ readonly showBeak?: boolean; + + /** + * Will enable a spinning icon in front of the text to indicate progress. + */ + readonly showProgress?: boolean; } export interface IStatusbarService { From 845d0144295d4ed519c25fde044a344c3304af8d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 19 Oct 2020 09:55:52 +0200 Subject: [PATCH 037/121] [scss] Normalise SCSS attributes with CSS/LESS/SASS. Fixes #108840 --- .../scss/test/colorize-results/test_scss.json | 20 +++++++++---------- extensions/theme-defaults/themes/dark_vs.json | 1 - .../themes/hc_black_defaults.json | 3 --- .../theme-defaults/themes/light_vs.json | 1 - 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index 5841c3b8e3f..66f3e1f376d 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -16206,11 +16206,11 @@ "c": "rel", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { - "dark_plus": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_plus": "entity.other.attribute-name.attribute.scss: #800000", - "dark_vs": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_vs": "entity.other.attribute-name.attribute.scss: #800000", - "hc_black": "entity.other.attribute-name.attribute.scss: #D7BA7D" + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" } }, { @@ -20606,11 +20606,11 @@ "c": "data-icon", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.attribute-selector.scss entity.other.attribute-name.attribute.scss", "r": { - "dark_plus": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_plus": "entity.other.attribute-name.attribute.scss: #800000", - "dark_vs": "entity.other.attribute-name.attribute.scss: #D7BA7D", - "light_vs": "entity.other.attribute-name.attribute.scss: #800000", - "hc_black": "entity.other.attribute-name.attribute.scss: #D7BA7D" + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" } }, { diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 1b4cf8b967e..3a5008aef7a 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -86,7 +86,6 @@ "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 495a15238dc..d0382cec294 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -105,10 +105,7 @@ "entity.other.attribute-name.parent-selector.css", "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 3410551898b..23881ae8dc7 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -87,7 +87,6 @@ "entity.other.attribute-name.pseudo-class.css", "entity.other.attribute-name.pseudo-element.css", "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.attribute.scss", "entity.other.attribute-name.scss" ], "settings": { From c84c1fd4ad2b310c7c3edf17ee1a484b6cf7bb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 12 Oct 2020 16:11:11 +0200 Subject: [PATCH 038/121] :lipstick: --- src/vs/workbench/api/browser/mainThreadSCM.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 2f0b06a143d..9594c9137d6 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -133,8 +133,7 @@ class MainThreadSCMProvider implements ISCMProvider { private readonly _handle: number, private readonly _contextValue: string, private readonly _label: string, - private readonly _rootUri: URI | undefined, - @ISCMService scmService: ISCMService + private readonly _rootUri: URI | undefined ) { } $updateSourceControl(features: SCMProviderFeatures): void { @@ -290,7 +289,7 @@ export class MainThreadSCM implements MainThreadSCMShape { } $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): void { - const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, this.scmService); + const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined); const repository = this.scmService.registerSCMProvider(provider); this._repositories.set(handle, repository); From cd2a92ae134d4eb20f20f8f6250a4a89c86e7abb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Oct 2020 10:48:34 +0200 Subject: [PATCH 039/121] web - allow to download folders (fix #83579) --- src/vs/base/browser/dom.ts | 45 ++++ src/vs/platform/files/common/files.ts | 8 +- src/vs/workbench/browser/contextkeys.ts | 8 +- .../files/browser/fileActions.contribution.ts | 13 +- .../contrib/files/browser/fileActions.ts | 238 +++++++++++++++--- .../files/browser/views/explorerViewer.ts | 21 +- 6 files changed, 284 insertions(+), 49 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 8a3e808a871..2d753c91e78 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1390,3 +1390,48 @@ function toBinary(str: string): string { export function multibyteAwareBtoa(str: string): string { return btoa(toBinary(str)); } + +/** + * Typings for the https://wicg.github.io/file-system-access + * + * Use `supported(window)` to find out if the browser supports this kind of API. + */ +export namespace WebFileSystemAccess { + + // https://wicg.github.io/file-system-access/#dom-window-showdirectorypicker + export interface FileSystemAccess { + showDirectoryPicker: () => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemdirectoryhandle + export interface FileSystemDirectoryHandle { + readonly kind: 'directory', + readonly name: string, + + getFileHandle: (name: string, options?: { create?: boolean }) => Promise; + getDirectoryHandle: (name: string, options?: { create?: boolean }) => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemfilehandle + export interface FileSystemFileHandle { + readonly kind: 'file', + readonly name: string, + + createWritable: (options?: { keepExistingData?: boolean }) => Promise; + } + + // https://wicg.github.io/file-system-access/#api-filesystemwritablefilestream + export interface FileSystemWritableFileStream { + write: (buffer: Uint8Array) => Promise; + close: () => Promise; + } + + export function supported(obj: any & Window): obj is FileSystemAccess { + const candidate = obj as FileSystemAccess; + if (typeof candidate?.showDirectoryPicker === 'function') { + return true; + } + + return false; + } +} diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 52e9ecdf293..2d2cdcfeef4 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Event } from 'vs/base/common/event'; import { startsWithIgnoreCase } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { isNumber, isUndefinedOrNull } from 'vs/base/common/types'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; import { ReadableStreamEvents } from 'vs/base/common/stream'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -978,8 +978,12 @@ export class BinarySize { static readonly TB = BinarySize.GB * BinarySize.KB; static formatSize(size: number): string { + if (!isNumber(size)) { + size = 0; + } + if (size < BinarySize.KB) { - return localize('sizeB', "{0}B", size); + return localize('sizeB', "{0}B", size.toFixed(0)); } if (size < BinarySize.MB) { diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 1e9fb3756bb..10e7b53ef34 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor'; -import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { trackFocus, addDisposableListener, EventType, WebFileSystemAccess } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -32,6 +32,9 @@ export const RemoteNameContext = new RawContextKey('remoteName', ''); export const IsFullscreenContext = new RawContextKey('isFullscreen', false); +// Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access) +export const HasWebFileSystemAccess = new RawContextKey('hasWebFileSystemAccess', false); + export class WorkbenchContextKeysHandler extends Disposable { private inputFocusedContext: IContextKey; @@ -85,6 +88,9 @@ export class WorkbenchContextKeysHandler extends Disposable { RemoteNameContext.bindTo(this.contextKeyService).set(getRemoteName(this.environmentService.remoteAuthority) || ''); + // Capabilities + HasWebFileSystemAccess.bindTo(this.contextKeyService).set(WebFileSystemAccess.supported(window)); + // Development IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 086fa8c123a..29b83a97cc2 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -22,7 +22,7 @@ import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfi import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { Schemas } from 'vs/base/common/network'; -import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; +import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, HasWebFileSystemAccess, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; @@ -222,7 +222,7 @@ appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('com appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category); appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); -appendToCommandPalette(DOWNLOAD_COMMAND_ID, { value: DOWNLOAD_LABEL, original: 'Download' }, category, ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file))); +appendToCommandPalette(DOWNLOAD_COMMAND_ID, { value: DOWNLOAD_LABEL, original: 'Download...' }, category, ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file))); appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, category); // Menu registration - open editors @@ -489,7 +489,14 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, ({ id: DOWNLOAD_COMMAND_ID, title: DOWNLOAD_LABEL, }, - when: ContextKeyExpr.or(ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file), IsWebContext.toNegated()), ContextKeyExpr.and(ResourceContextKey.Scheme.notEqualsTo(Schemas.file), ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated())) + when: ContextKeyExpr.or( + // native: for any remote resource + ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)), + // web: for any files + ContextKeyExpr.and(IsWebContext, ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated()), + // web: for any folders if file system API support is provided + ContextKeyExpr.and(IsWebContext, HasWebFileSystemAccess) + ) })); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index b5437e7859a..f91217259bd 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -11,10 +11,10 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Action } from 'vs/base/common/actions'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { VIEWLET_ID, IExplorerService, IFilesConfiguration, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; +import { BinarySize, IFileService, IFileStatWithMetadata, IFileStreamContent } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; @@ -39,7 +39,7 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { coalesce } from 'vs/base/common/arrays'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { getErrorMessage } from 'vs/base/common/errors'; -import { triggerDownload } from 'vs/base/browser/dom'; +import { WebFileSystemAccess, triggerDownload } from 'vs/base/browser/dom'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -49,6 +49,9 @@ import { once } from 'vs/base/common/functional'; import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; import { trim, rtrim } from 'vs/base/common/strings'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ILogService } from 'vs/platform/log/common/log'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -66,7 +69,7 @@ export const PASTE_FILE_LABEL = nls.localize('pasteFile', "Paste"); export const FileCopiedContext = new RawContextKey('fileCopied', false); -export const DOWNLOAD_LABEL = nls.localize('download', "Download"); +export const DOWNLOAD_LABEL = nls.localize('download', "Download..."); const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; @@ -997,49 +1000,212 @@ export const cutFileHandler = async (accessor: ServicesAccessor) => { export const DOWNLOAD_COMMAND_ID = 'explorer.download'; const downloadFileHandler = (accessor: ServicesAccessor) => { + const logService = accessor.get(ILogService); const fileService = accessor.get(IFileService); const workingCopyFileService = accessor.get(IWorkingCopyFileService); const fileDialogService = accessor.get(IFileDialogService); const explorerService = accessor.get(IExplorerService); - const stats = explorerService.getContext(true); + const progressService = accessor.get(IProgressService); - let canceled = false; - sequence(stats.map(s => async () => { - if (canceled) { - return; - } + const context = explorerService.getContext(true); + const explorerItems = context.length ? context : explorerService.roots; - if (isWeb) { - if (!s.isDirectory) { - let bufferOrUri: Uint8Array | URI; - try { - bufferOrUri = (await fileService.readFile(s.resource, { limits: { size: 1024 * 1024 /* set a limit to reduce memory pressure */ } })).value.buffer; - } catch (error) { - bufferOrUri = FileAccess.asBrowserUri(s.resource); + const cts = new CancellationTokenSource(); + + const downloadPromise = progressService.withProgress({ + location: ProgressLocation.Window, + delay: 800, + cancellable: isWeb, + title: nls.localize('downloadingFiles', "Downloading") + }, async progress => { + return sequence(explorerItems.map(explorerItem => async () => { + if (cts.token.isCancellationRequested) { + return; + } + + // Web: use DOM APIs to download files with optional support + // for folders and large files + if (isWeb) { + const stat = await fileService.resolve(explorerItem.resource, { resolveMetadata: true }); + + if (cts.token.isCancellationRequested) { + return; } - triggerDownload(bufferOrUri, s.name); - } - } else { - let defaultUri = s.isDirectory ? fileDialogService.defaultFolderPath(Schemas.file) : fileDialogService.defaultFilePath(Schemas.file); - if (defaultUri) { - defaultUri = resources.joinPath(defaultUri, s.name); + const maxBlobDownloadSize = 32 * BinarySize.MB; // avoid to download via blob-trick >32MB to avoid memory pressure + const preferFileSystemAccessWebApis = stat.isDirectory || stat.size > maxBlobDownloadSize; + + // Folder: use FS APIs to download files and folders if available and preferred + if (preferFileSystemAccessWebApis && WebFileSystemAccess.supported(window)) { + + interface IDownloadOperation { + startTime: number, + + filesTotal: number; + filesDownloaded: number; + + totalBytesDownloaded: 0 + fileBytesDownloaded: 0 + } + + async function pipeContents(name: string, source: IFileStreamContent, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { + return new Promise((resolve, reject) => { + const sourceStream = source.value; + + const disposables = new DisposableStore(); + disposables.add(toDisposable(() => target.close())); + + let disposed = false; + disposables.add(toDisposable(() => disposed = true)); + + disposables.add(once(cts.token.onCancellationRequested)(() => { + disposables.dispose(); + reject(); + })); + + sourceStream.on('data', data => { + if (!disposed) { + target.write(data.buffer); + reportProgress(name, source.size, data.byteLength, operation); + } + }); + + sourceStream.on('error', error => { + disposables.dispose(); + reject(error); + }); + + sourceStream.on('end', () => { + disposables.dispose(); + resolve(); + }); + }); + } + + async function downloadFile(targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, name: string, resource: URI, operation: IDownloadOperation): Promise { + + // Report progress + operation.filesDownloaded++; + operation.fileBytesDownloaded = 0; // reset for this file + reportProgress(name, 0, 0, operation); + + // Start to download + const targetFile = await targetFolder.getFileHandle(name, { create: true }); + const targetFileWriter = await targetFile.createWritable(); + + return pipeContents(name, await fileService.readFileStream(resource), targetFileWriter, operation); + } + + async function downloadFolder(folder: IFileStatWithMetadata, targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle, operation: IDownloadOperation): Promise { + if (folder.children) { + operation.filesTotal += (folder.children.map(child => child.isFile)).length; + + for (const child of folder.children) { + if (cts.token.isCancellationRequested) { + return; + } + + if (child.isFile) { + await downloadFile(targetFolder, child.name, child.resource, operation); + } else { + const childFolder = await targetFolder.getDirectoryHandle(child.name, { create: true }); + const resolvedChildFolder = await fileService.resolve(child.resource, { resolveMetadata: true }); + + await downloadFolder(resolvedChildFolder, childFolder, operation); + } + } + } + } + + function reportProgress(name: string, fileSize: number, bytesDownloaded: number, operation: IDownloadOperation): void { + operation.fileBytesDownloaded += bytesDownloaded; + operation.totalBytesDownloaded += bytesDownloaded; + + const bytesDownloadedPerSecond = operation.totalBytesDownloaded / ((Date.now() - operation.startTime) / 1000); + + // Small file + let message: string; + if (fileSize < BinarySize.MB) { + if (operation.filesTotal === 1) { + message = name; + } else { + message = nls.localize('downloadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesDownloaded, operation.filesTotal, BinarySize.formatSize(bytesDownloadedPerSecond)); + } + } + + // Large file + else { + message = nls.localize('downloadProgressLarge', "{0} ({1} of {2}, {3}/s)", name, BinarySize.formatSize(operation.fileBytesDownloaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesDownloadedPerSecond)); + } + + progress.report({ message }); + } + + try { + const targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle = await window.showDirectoryPicker(); + const operation: IDownloadOperation = { + startTime: Date.now(), + + filesTotal: stat.isDirectory ? 0 : 1, // folders increment filesTotal within downloadFolder method + filesDownloaded: 0, + + totalBytesDownloaded: 0, + fileBytesDownloaded: 0 + }; + + if (stat.isDirectory) { + await downloadFolder(stat, targetFolder, operation); + } else { + await downloadFile(targetFolder, stat.name, stat.resource, operation); + } + } catch (error) { + logService.trace(error); + cts.cancel(); // `showDirectoryPicker` will throw an error when the user cancels + } + } + + // File: use traditional download to circumvent browser limitations + else if (stat.isFile) { + let bufferOrUri: Uint8Array | URI; + try { + bufferOrUri = (await fileService.readFile(stat.resource, { limits: { size: maxBlobDownloadSize } })).value.buffer; + } catch (error) { + bufferOrUri = FileAccess.asBrowserUri(stat.resource); + } + + if (!cts.token.isCancellationRequested) { + triggerDownload(bufferOrUri, stat.name); + } + } } - const destination = await fileDialogService.showSaveDialog({ - availableFileSystems: [Schemas.file], - saveLabel: mnemonicButtonLabel(nls.localize('download', "Download")), - title: s.isDirectory ? nls.localize('downloadFolder', "Download Folder") : nls.localize('downloadFile', "Download File"), - defaultUri - }); - if (destination) { - await workingCopyFileService.copy([{ source: s.resource, target: destination }], { overwrite: true }); - } else { - // User canceled a download. In case there were multiple files selected we should cancel the remainder of the prompts #86100 - canceled = true; + // Native: use working copy file service to get at the contents + else { + progress.report({ message: explorerItem.name }); + + let defaultUri = explorerItem.isDirectory ? fileDialogService.defaultFolderPath(Schemas.file) : fileDialogService.defaultFilePath(Schemas.file); + if (defaultUri) { + defaultUri = resources.joinPath(defaultUri, explorerItem.name); + } + + const destination = await fileDialogService.showSaveDialog({ + availableFileSystems: [Schemas.file], + saveLabel: mnemonicButtonLabel(nls.localize('downloadButton', "Download")), + title: explorerItem.isDirectory ? nls.localize('downloadFolder', "Download Folder") : nls.localize('downloadFile', "Download File"), + defaultUri + }); + + if (destination) { + await workingCopyFileService.copy([{ source: explorerItem.resource, target: destination }], { overwrite: true }); + } else { + cts.cancel(); // User canceled a download. In case there were multiple files selected we should cancel the remainder of the prompts #86100 + } } - } - })); + })); + }, () => cts.dispose(true)); + + // Also indicate progress in the files view + progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => downloadPromise); }; CommandsRegistry.registerCommand({ diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 4ed199508e9..01b3dbc05a5 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1037,6 +1037,9 @@ export class FileDragAndDrop implements ITreeDragAndDrop { title: localize('uploadingFiles', "Uploading") }, async progress => { for (let entry of entries) { + if (cts.token.isCancellationRequested) { + break; + } // Confirm overwrite as needed if (target && entry.name && target.getChild(entry.name)) { @@ -1081,15 +1084,19 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const bytesUploadedPerSecond = operation.bytesUploaded / ((Date.now() - operation.startTime) / 1000); + // Small file let message: string; - if (operation.filesTotal === 1 && entry.name) { - message = entry.name; - } else { - message = localize('uploadProgress', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, BinarySize.formatSize(bytesUploadedPerSecond)); + if (fileSize < BinarySize.MB) { + if (operation.filesTotal === 1) { + message = `${entry.name}`; + } else { + message = localize('uploadProgressSmallMany', "{0} of {1} files ({2}/s)", operation.filesUploaded, operation.filesTotal, BinarySize.formatSize(bytesUploadedPerSecond)); + } } - if (fileSize > BinarySize.MB) { - message = localize('uploadProgressDetail', "{0} ({1} of {2}, {3}/s)", message, BinarySize.formatSize(fileBytesUploaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesUploadedPerSecond)); + // Large file + else { + message = localize('uploadProgressLarge', "{0} ({1} of {2}, {3}/s)", entry.name, BinarySize.formatSize(fileBytesUploaded), BinarySize.formatSize(fileSize), BinarySize.formatSize(bytesUploadedPerSecond)); } progress.report({ message }); @@ -1140,7 +1147,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } else { done = true; // an empty array is a signal that all entries have been read } - } while (!done); + } while (!done && !token.isCancellationRequested); // Update operation total based on new counts operation.filesTotal += childEntries.length; From e1692b33b135cf9155b6602977965b77e18c972d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 19 Oct 2020 10:59:36 +0200 Subject: [PATCH 040/121] Abort rebase command polish (#108919) * Abort rebase command polish --- extensions/git/package.json | 4 ++++ extensions/git/src/commands.ts | 6 +++++- extensions/git/src/repository.ts | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index dbb65d6c7c7..0e08b6a84dc 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -623,6 +623,10 @@ "command": "git.commitAllAmend", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.rebaseAbort", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && gitRebaseInProgress" + }, { "command": "git.commitNoVerify", "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index fb66583c3d4..99068f285cc 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2498,7 +2498,11 @@ export class CommandCenter { @command('git.rebaseAbort', { repository: true }) async rebaseAbort(repository: Repository): Promise { - await repository.rebaseAbort(); + if (repository.rebaseCommit) { + await repository.rebaseAbort(); + } else { + await window.showInformationMessage(localize('no rebase', "No rebase in progress.")); + } } private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 5250ab4920a..c9ac43f8960 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode'; import * as nls from 'vscode-nls'; import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git'; import { AutoFetcher } from './autofetch'; @@ -642,6 +642,7 @@ export class Repository implements Disposable { } this._rebaseCommit = rebaseCommit; + commands.executeCommand('setContext', 'gitRebaseInProgress', !!this._rebaseCommit); } get rebaseCommit(): Commit | undefined { From 04799ce2f9641b66564e769f4539bb630efbc1ed Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 19 Oct 2020 12:51:34 +0200 Subject: [PATCH 041/121] fix typo; fixes #108339 --- src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index 8b5ab068ee3..a2a177f6e94 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -41,7 +41,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { } onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void { - if (this.eventCallback) { + if (this.messageCallback) { this._onError.fire(new Error(`attempt to set more than one 'Message' callback`)); } this.messageCallback = callback; From 0ea7e1ca9425b3814c9d0a3c0aa719daad92a53a Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 19 Oct 2020 13:54:56 +0200 Subject: [PATCH 042/121] debug issues assign to Andre --- .github/classifier.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index e483a011577..e4acc63c170 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -20,8 +20,8 @@ "context-keys": {"assign": []}, "css-less-scss": {"assign": ["aeschli"]}, "custom-editors": {"assign": ["mjbvz"]}, - "debug": {"assign": ["connor4312 "]}, - "debug-console": {"assign": ["connor4312 "]}, + "debug": {"assign": ["weinand"]}, + "debug-console": {"assign": ["weinand"]}, "dialogs": {"assign": ["sbatten"]}, "diff-editor": {"assign": []}, "dropdown": {"assign": []}, From 9a51f2450226dbcdc40aff47463cf42fc35081e2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Oct 2020 14:19:37 +0200 Subject: [PATCH 043/121] add message to assertion --- .../extensionRecommendationsService.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 94fc4af4425..40a1185bb6d 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -448,9 +448,9 @@ suite('ExtensionRecommendationsService Test', () => { await testObject.activationPromise; const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(recommendations['ms-python.python']); - assert.ok(!recommendations['mockpublisher2.mockextension2']); - assert.ok(!recommendations['ms-dotnettools.csharp']); + assert.ok(recommendations['ms-python.python'], 'ms-python.python extension shall exist'); + assert.ok(!recommendations['mockpublisher2.mockextension2'], 'mockpublisher2.mockextension2 extension shall not exist'); + assert.ok(!recommendations['ms-dotnettools.csharp'], 'ms-dotnettools.csharp extension shall not exist'); }); test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', async () => { From df696e3afc079cbee4aadbae18389c762530af80 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Oct 2020 14:46:34 +0200 Subject: [PATCH 044/121] move assertions out of promise --- .../electron-browser/configurationService.test.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 7692bb61260..57a0b059141 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -53,6 +53,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; +import { Event } from 'vs/base/common/event'; class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService { @@ -1218,15 +1219,12 @@ suite('WorkspaceConfigurationService - Folder', () => { const workspaceSettingsResource = URI.file(path.join(workspaceDir, '.vscode', 'settings.json')); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); - await new Promise(async (c) => { - const disposable = testObject.onDidChangeConfiguration(e => { - assert.ok(e.affectsConfiguration('configurationService.folder.testSetting')); - assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); - disposable.dispose(); - c(); - }); + const e = await new Promise(async (c) => { + Event.once(testObject.onDidChangeConfiguration)(c); await fileService.del(workspaceSettingsResource); }); + assert.ok(e.affectsConfiguration('configurationService.folder.testSetting')); + assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); }); }); From fff9302b367acf12ef7e183fe8b03708b5476e2e Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 19 Oct 2020 15:59:47 +0200 Subject: [PATCH 045/121] folding: provider event to signal that folding ranges have changed. Fixes #99914 --- src/vs/editor/common/modes.ts | 6 +++++ src/vs/editor/contrib/folding/folding.ts | 2 +- .../contrib/folding/syntaxRangeProvider.ts | 15 ++++++++++-- .../contrib/folding/test/syntaxFold.test.ts | 2 +- src/vs/monaco.d.ts | 4 ++++ src/vs/vscode.proposed.d.ts | 11 +++++++++ .../api/browser/mainThreadLanguageFeatures.ts | 24 +++++++++++++++---- .../workbench/api/common/extHost.protocol.ts | 3 ++- .../api/common/extHostLanguageFeatures.ts | 18 ++++++++++---- 9 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index faad9ab6da5..34f041ea626 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1293,6 +1293,12 @@ export interface FoldingContext { * A provider of folding ranges for editor models. */ export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChange?: Event; + /** * Provides the folding ranges for a specific model. */ diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 251f7aa1607..0bfa5f68166 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -264,7 +264,7 @@ export class FoldingController extends Disposable implements IEditorContribution }, 30000); return rangeProvider; // keep memento in case there are still no foldingProviders on the next request. } else if (foldingProviders.length > 0) { - this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders); + this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders, () => this.onModelContentChanged()); } } this.foldingStateMemento = null; diff --git a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts index 4e66801be36..898694bb2f8 100644 --- a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts @@ -9,6 +9,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { RangeProvider } from './folding'; import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const MAX_FOLDING_REGIONS = 5000; @@ -25,7 +26,17 @@ export class SyntaxRangeProvider implements RangeProvider { readonly id = ID_SYNTAX_PROVIDER; - constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) { + readonly disposables: DisposableStore | undefined; + + constructor(private readonly editorModel: ITextModel, private providers: FoldingRangeProvider[], handleFoldingRangesChange: () => void, private limit = MAX_FOLDING_REGIONS) { + for (const provider of providers) { + if (typeof provider.onDidChange === 'function') { + if (!this.disposables) { + this.disposables = new DisposableStore(); + } + this.disposables.add(provider.onDidChange(handleFoldingRangesChange)); + } + } } compute(cancellationToken: CancellationToken): Promise { @@ -39,8 +50,8 @@ export class SyntaxRangeProvider implements RangeProvider { } dispose() { + this.disposables?.dispose(); } - } function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextModel, cancellationToken: CancellationToken): Promise { diff --git a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts index 86a4280259e..915ba173661 100644 --- a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts +++ b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts @@ -74,7 +74,7 @@ suite('Syntax folding', () => { let providers = [new TestFoldingRangeProvider(model, ranges)]; async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) { - let indentRanges = await new SyntaxRangeProvider(model, providers, maxEntries).compute(CancellationToken.None); + let indentRanges = await new SyntaxRangeProvider(model, providers, () => { }, maxEntries).compute(CancellationToken.None); let actual: IndentRange[] = []; if (indentRanges) { for (let i = 0; i < indentRanges.length; i++) { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a44cb3bb307..1cd72bf8512 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6137,6 +6137,10 @@ declare namespace monaco.languages { * A provider of folding ranges for editor models. */ export interface FoldingRangeProvider { + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChange?: IEvent; /** * Provides the folding ranges for a specific model. */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5a3de044162..63712708d62 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2190,4 +2190,15 @@ declare module 'vscode' { canReply: boolean; } //#endregion + + //#region https://github.com/microsoft/vscode/issues/108929 FoldingRangeProvider.onDidChangeFoldingRanges @aeschli + export interface FoldingRangeProvider2 extends FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + + } + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 635730ba75b..9cbb21d567f 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -571,13 +571,27 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- folding - $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void { - const proxy = this._proxy; - this._registrations.set(handle, modes.FoldingRangeProviderRegistry.register(selector, { + $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { + const provider = { provideFoldingRanges: (model, context, token) => { - return proxy.$provideFoldingRanges(handle, model.uri, context, token); + return this._proxy.$provideFoldingRanges(handle, model.uri, context, token); } - })); + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations.set(eventHandle, emitter); + provider.onDidChange = emitter.event; + } + + this._registrations.set(handle, modes.FoldingRangeProviderRegistry.register(selector, provider)); + } + + $emitFoldingRangeEvent(eventHandle: number, event?: any): void { + const obj = this._registrations.get(eventHandle); + if (obj instanceof Emitter) { + obj.fire(event); + } } // -- smart select diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ddca6becf25..dd64b66a1ac 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -398,7 +398,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void; $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void; - $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; + $emitFoldingRangeEvent(eventHandle: number, event?: any): void; $registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; $setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index ef49bb42954..8e65422a946 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1810,10 +1810,20 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined); } - registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider), extension); - this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector)); - return this._createDisposable(handle); + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider2): vscode.Disposable { + const handle = this._nextHandle(); + const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined; + + this._adapter.set(handle, new AdapterData(new FoldingProviderAdapter(this._documents, provider), extension)); + this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector), eventHandle); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeFoldingRanges!(_ => this._proxy.$emitFoldingRangeEvent(eventHandle)); + result = Disposable.from(result, subscription); + } + + return result; } $provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Promise { From af993f9c0a63c9af595dce38c83cdb496e0a14f2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Oct 2020 16:33:28 +0200 Subject: [PATCH 046/121] #108548 do not apply padding for output --- src/vs/workbench/contrib/output/browser/outputView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 262c391623d..460b5e5a564 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -207,6 +207,7 @@ export class OutputEditor extends AbstractTextResourceEditor { options.renderLineHighlight = 'none'; options.minimap = { enabled: false }; options.renderValidationDecorations = 'editable'; + options.padding = undefined; const outputConfig = this.configurationService.getValue('[Log]'); if (outputConfig) { From b233cec28384ee40a20de8f5564d8e3c3db055e7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Oct 2020 17:28:22 +0200 Subject: [PATCH 047/121] fix bad quick access when tabs disabled (#107543) (#108736) --- .../browser/parts/editor/noTabsTitleControl.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 49944a9ae70..66a7caec173 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -9,7 +9,7 @@ import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; -import { addDisposableListener, EventType, EventHelper, Dimension } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, EventHelper, Dimension, isAncestor } from 'vs/base/browser/dom'; import { IAction } from 'vs/base/common/actions'; import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Color } from 'vs/base/common/color'; @@ -110,6 +110,16 @@ export class NoTabsTitleControl extends TitleControl { } private onTitleTap(e: GestureEvent): void { + + // We only want to open the quick access picker when + // the tap occured over the editor label, so we need + // to check on the target + // (https://github.com/microsoft/vscode/issues/107543) + const target = e.initialTarget; + if (!(target instanceof HTMLElement) || !this.editorLabel || !isAncestor(target, this.editorLabel.element)) { + return; + } + // TODO@rebornix gesture tap should open the quick access // editorGroupView will focus on the editor again when there are mouse/pointer/touch down events // we need to wait a bit as `GesureEvent.Tap` is generated from `touchstart` and then `touchend` evnets, which are not an atom event. From dbc90498b440183bb94394269e935930ed52989d Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 19 Oct 2020 17:49:54 +0200 Subject: [PATCH 048/121] Add hint (fixes microsoft/vscode-remote-release#486) --- .../configuration-editing/schemas/devContainer.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index b37e07fa65c..1d3f7640ef1 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -298,7 +298,7 @@ }, "workspaceFolder": { "type": "string", - "description": "The path of the workspace folder inside the container." + "description": "The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml." }, "shutdownAction": { "type": "string", From bfe35d4427220bb28022bd3c3b9d6f5e4fed99b9 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 19 Oct 2020 17:50:36 +0200 Subject: [PATCH 049/121] Settings copied only once (microsoft/vscode-remote-release#484) --- .../configuration-editing/schemas/attachContainer.schema.json | 2 +- .../configuration-editing/schemas/devContainer.schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index 2b7952446f0..b408c46f42e 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -21,7 +21,7 @@ }, "settings": { "$ref": "vscode://schemas/settings/machine", - "description": "Machine specific settings that should be copied into the container." + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time." }, "remoteEnv": { "type": "object", diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 1d3f7640ef1..4719d53e6db 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -23,7 +23,7 @@ }, "settings": { "$ref": "vscode://schemas/settings/machine", - "description": "Machine specific settings that should be copied into the container." + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." }, "forwardPorts": { "type": "array", From 2c53d687894305ce78bffd9080b6d27802c28d71 Mon Sep 17 00:00:00 2001 From: Kai Maetzel Date: Mon, 19 Oct 2020 17:22:06 +0000 Subject: [PATCH 050/121] fix GDPR annotation --- .../contrib/terminal/browser/terminalLatencyTelemetryAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts index ba307010967..563f3f55251 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts @@ -55,7 +55,7 @@ export class LatencyTelemetryAddon extends Disposable implements ITerminalAddon "min" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "max" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "median" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ const median = this._unprocessedLatencies.sort()[Math.floor(this._unprocessedLatencies.length / 2)]; From 0c50806e8ceaeae401b8bc7bc13fe7fb7f9401b0 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 19 Oct 2020 10:55:41 -0700 Subject: [PATCH 051/121] Refactor Emmet abbrevationAction files --- extensions/emmet/src/abbreviationActions.ts | 42 +++---- .../emmet/src/test/abbreviationAction.test.ts | 108 +++++++++--------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 6a2734cb9fa..9badf78f4f9 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -66,7 +66,7 @@ function doWrapping(individualLines: boolean, args: any) { const helper = getEmmetHelper(); // Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents - let rangesToReplace: PreviewRangesWithContent[] = editor.selections.sort((a: vscode.Selection, b: vscode.Selection) => { return a.start.compareTo(b.start); }).map(selection => { + const rangesToReplace: PreviewRangesWithContent[] = editor.selections.sort((a: vscode.Selection, b: vscode.Selection) => { return a.start.compareTo(b.start); }).map(selection => { let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection; if (!rangeToReplace.isSingleLine && rangeToReplace.end.character === 0) { const previousLine = rangeToReplace.end.line - 1; @@ -88,7 +88,7 @@ function doWrapping(individualLines: boolean, args: any) { rangeToReplace = new vscode.Range(rangeToReplace.start.line, rangeToReplace.start.character + extraWhitespaceSelected, rangeToReplace.end.line, rangeToReplace.end.character); let textToWrapInPreview: string[]; - let textToReplace = editor.document.getText(rangeToReplace); + const textToReplace = editor.document.getText(rangeToReplace); if (individualLines) { textToWrapInPreview = textToReplace.split('\n').map(x => x.trim()); } else { @@ -144,7 +144,7 @@ function doWrapping(individualLines: boolean, args: any) { const oldPreviewLines = oldPreviewRange.end.line - oldPreviewRange.start.line + 1; const newLinesInserted = expandedTextLines.length - oldPreviewLines; - let newPreviewLineStart = oldPreviewRange.start.line + totalLinesInserted; + const newPreviewLineStart = oldPreviewRange.start.line + totalLinesInserted; let newPreviewStart = oldPreviewRange.start.character; const newPreviewLineEnd = oldPreviewRange.end.line + totalLinesInserted + newLinesInserted; let newPreviewEnd = expandedTextLines[expandedTextLines.length - 1].length; @@ -177,19 +177,19 @@ function doWrapping(individualLines: boolean, args: any) { return inPreview ? revertPreview().then(() => { return false; }) : Promise.resolve(inPreview); } - let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation); + const extractedResults = helper.extractAbbreviationFromText(inputAbbreviation); if (!extractedResults) { return Promise.resolve(inPreview); } else if (extractedResults.abbreviation !== inputAbbreviation) { // Not clear what should we do in this case. Warn the user? How? } - let { abbreviation, filter } = extractedResults; + const { abbreviation, filter } = extractedResults; if (definitive) { const revertPromise = inPreview ? revertPreview() : Promise.resolve(); return revertPromise.then(() => { const expandAbbrList: ExpandAbbreviationInput[] = rangesToReplace.map(rangesAndContent => { - let rangeToReplace = rangesAndContent.originalRange; + const rangeToReplace = rangesAndContent.originalRange; let textToWrap: string[]; if (individualLines) { textToWrap = rangesAndContent.textToWrapInPreview; @@ -270,17 +270,17 @@ export function expandEmmetAbbreviation(args: any): Thenable { + const getAbbreviation = (document: vscode.TextDocument, selection: vscode.Selection, position: vscode.Position, syntax: string): [vscode.Range | null, string, string] => { position = document.validatePosition(position); let rangeToReplace: vscode.Range = selection; let abbr = document.getText(rangeToReplace); if (!rangeToReplace.isEmpty) { - let extractedResults = helper.extractAbbreviationFromText(abbr); + const extractedResults = helper.extractAbbreviationFromText(abbr); if (extractedResults) { return [rangeToReplace, extractedResults.abbreviation, extractedResults.filter]; } @@ -293,23 +293,23 @@ export function expandEmmetAbbreviation(args: any): Thenable explicitly // else we will end up with <
if (syntax === 'html') { - let matches = textTillPosition.match(/<(\w+)$/); + const matches = textTillPosition.match(/<(\w+)$/); if (matches) { abbr = matches[1]; rangeToReplace = new vscode.Range(position.translate(0, -(abbr.length + 1)), position); return [rangeToReplace, abbr, '']; } } - let extractedResults = helper.extractAbbreviation(toLSTextDocument(editor.document), position, false); + const extractedResults = helper.extractAbbreviation(toLSTextDocument(editor.document), position, false); if (!extractedResults) { return [null, '', '']; } - let { abbreviationRange, abbreviation, filter } = extractedResults; + const { abbreviationRange, abbreviation, filter } = extractedResults; return [new vscode.Range(abbreviationRange.start.line, abbreviationRange.start.character, abbreviationRange.end.line, abbreviationRange.end.character), abbreviation, filter]; }; - let selectionsInReverseOrder = editor.selections.slice(0); + const selectionsInReverseOrder = editor.selections.slice(0); selectionsInReverseOrder.sort((a, b) => { const posA = a.isReversed ? a.anchor : a.active; const posB = b.isReversed ? b.anchor : b.active; @@ -322,7 +322,7 @@ export function expandEmmetAbbreviation(args: any): Thenable 1000) { rootNode = parsePartialStylesheet(editor.document, editor.selection.isReversed ? editor.selection.anchor : editor.selection.active); } else { @@ -333,8 +333,8 @@ export function expandEmmetAbbreviation(args: any): Thenable { - let position = selection.isReversed ? selection.anchor : selection.active; - let [rangeToReplace, abbreviation, filter] = getAbbreviation(editor.document, selection, position, syntax); + const position = selection.isReversed ? selection.anchor : selection.active; + const [rangeToReplace, abbreviation, filter] = getAbbreviation(editor.document, selection, position, syntax); if (!rangeToReplace) { return; } @@ -578,7 +578,7 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex // Snippet to replace at multiple cursors are not the same // `editor.insertSnippet` will have to be called for each instance separately // We will not be able to maintain multiple cursors after snippet insertion - let insertPromises: Thenable[] = []; + const insertPromises: Thenable[] = []; if (!insertSameSnippet) { expandAbbrList.sort((a: ExpandAbbreviationInput, b: ExpandAbbreviationInput) => { return b.rangeToReplace.start.compareTo(a.rangeToReplace.start); }).forEach((expandAbbrInput: ExpandAbbreviationInput) => { let expandedText = expandAbbr(expandAbbrInput); @@ -596,8 +596,8 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex // We can pass all ranges to `editor.insertSnippet` in a single call so that // all cursors are maintained after snippet insertion const anyExpandAbbrInput = expandAbbrList[0]; - let expandedText = expandAbbr(anyExpandAbbrInput); - let allRanges = expandAbbrList.map(value => { + const expandedText = expandAbbr(anyExpandAbbrInput); + const allRanges = expandAbbrList.map(value => { return new vscode.Range(value.rangeToReplace.start.line, value.rangeToReplace.start.character, value.rangeToReplace.end.line, value.rangeToReplace.end.character); }); if (expandedText) { @@ -614,7 +614,7 @@ function walk(root: any, fn: ((node: any) => boolean)): boolean { let ctx = root; while (ctx) { - let next = ctx.next; + const next = ctx.next; if (fn(ctx) === false || walk(ctx.firstChild, fn) === false) { return false; } @@ -653,7 +653,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string | undefined { // Expand the abbreviation if (input.textToWrap) { - let parsedAbbr = helper.parseAbbreviation(input.abbreviation, expandOptions); + const parsedAbbr = helper.parseAbbreviation(input.abbreviation, expandOptions); if (input.rangeToReplace.isSingleLine && input.textToWrap.length === 1) { // Fetch rightmost element in the parsed abbreviation (i.e the element that will contain the wrapped text). diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index cedf3244903..7577cd6a718 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -61,7 +61,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor('img', 'html', async (editor, _doc) => { editor.selection = new Selection(0, 3, 0, 3); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), '\"\"'); + assert.strictEqual(editor.document.getText(), '\"\"'); return Promise.resolve(); }); }); @@ -72,14 +72,14 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { - assert.equal(!completionPromise, false, `Got unexpected undefined instead of a completion promise`); + assert.strictEqual(!completionPromise, false, `Got unexpected undefined instead of a completion promise`); return Promise.resolve(); } const completionList = await completionPromise; - assert.equal(completionList && completionList.items && completionList.items.length > 0, true); + assert.strictEqual(completionList && completionList.items && completionList.items.length > 0, true); if (completionList) { - assert.equal(completionList.items[0].label, 'img'); - assert.equal(((completionList.items[0].documentation) || '').replace(/\|/g, ''), '\"\"'); + assert.strictEqual(completionList.items[0].label, 'img'); + assert.strictEqual(((completionList.items[0].documentation) || '').replace(/\|/g, ''), '\"\"'); } return Promise.resolve(); }); @@ -161,7 +161,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(2, 4, 2, 4); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), htmlContents); + assert.strictEqual(editor.document.getText(), htmlContents); return Promise.resolve(); }); }); @@ -171,7 +171,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { editor.selection = new Selection(2, 4, 2, 4); const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); return Promise.resolve(); }); }); @@ -180,7 +180,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(9, 8, 9, 8); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), htmlContents); + assert.strictEqual(editor.document.getText(), htmlContents); return Promise.resolve(); }); }); @@ -190,7 +190,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { editor.selection = new Selection(9, 8, 9, 8); const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); return Promise.resolve(); }); }); @@ -200,7 +200,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor(fileContents, 'html', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), fileContents); + assert.strictEqual(editor.document.getText(), fileContents); return Promise.resolve(); }); }); @@ -211,7 +211,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { editor.selection = new Selection(0, 6, 0, 6); const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); return Promise.resolve(); }); }); @@ -219,12 +219,12 @@ suite('Tests for Expand Abbreviations (HTML)', () => { test('Expand css when inside style tag (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(13, 16, 13, 19); - let expandPromise = expandEmmetAbbreviation({ language: 'css' }); + const expandPromise = expandEmmetAbbreviation({ language: 'css' }); if (!expandPromise) { return Promise.resolve(); } await expandPromise; - assert.equal(editor.document.getText(), htmlContents.replace('m10', 'margin: 10px;')); + assert.strictEqual(editor.document.getText(), htmlContents.replace('m10', 'margin: 10px;')); return Promise.resolve(); }); }); @@ -238,19 +238,19 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { - assert.equal(1, 2, `Problem with expanding m10`); + assert.strictEqual(1, 2, `Problem with expanding m10`); return Promise.resolve(); } const completionList = await completionPromise; if (!completionList || !completionList.items || !completionList.items.length) { - assert.equal(1, 2, `Problem with expanding m10`); + assert.strictEqual(1, 2, `Problem with expanding m10`); return Promise.resolve(); } const emmetCompletionItem = completionList.items[0]; - assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); - assert.equal(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); - assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); + assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); return Promise.resolve(); }); }); @@ -259,7 +259,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(13, 14, 13, 14); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), htmlContents); + assert.strictEqual(editor.document.getText(), htmlContents); return Promise.resolve(); }); }); @@ -268,12 +268,12 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const styleAttributeContent = '
'; return withRandomFileEditor(styleAttributeContent, 'html', async (editor, _doc) => { editor.selection = new Selection(0, 15, 0, 15); - let expandPromise = expandEmmetAbbreviation(null); + const expandPromise = expandEmmetAbbreviation(null); if (!expandPromise) { return Promise.resolve(); } await expandPromise; - assert.equal(editor.document.getText(), styleAttributeContent.replace('m10', 'margin: 10px;')); + assert.strictEqual(editor.document.getText(), styleAttributeContent.replace('m10', 'margin: 10px;')); return Promise.resolve(); }); }); @@ -287,19 +287,19 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { - assert.equal(1, 2, `Problem with expanding m10`); + assert.strictEqual(1, 2, `Problem with expanding m10`); return Promise.resolve(); } const completionList = await completionPromise; if (!completionList || !completionList.items || !completionList.items.length) { - assert.equal(1, 2, `Problem with expanding m10`); + assert.strictEqual(1, 2, `Problem with expanding m10`); return Promise.resolve(); } const emmetCompletionItem = completionList.items[0]; - assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); - assert.equal(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); - assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); + assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); return Promise.resolve(); }); }); @@ -307,12 +307,12 @@ suite('Tests for Expand Abbreviations (HTML)', () => { test('Expand html when inside script tag with html type (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(21, 12, 21, 12); - let expandPromise = expandEmmetAbbreviation(null); + const expandPromise = expandEmmetAbbreviation(null); if (!expandPromise) { return Promise.resolve(); } await expandPromise; - assert.equal(editor.document.getText(), htmlContents.replace('span.hello', '')); + assert.strictEqual(editor.document.getText(), htmlContents.replace('span.hello', '')); return Promise.resolve(); }); }); @@ -326,18 +326,18 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { - assert.equal(1, 2, `Problem with expanding span.hello`); + assert.strictEqual(1, 2, `Problem with expanding span.hello`); return Promise.resolve(); } const completionList = await completionPromise; if (!completionList || !completionList.items || !completionList.items.length) { - assert.equal(1, 2, `Problem with expanding span.hello`); + assert.strictEqual(1, 2, `Problem with expanding span.hello`); return Promise.resolve(); } const emmetCompletionItem = completionList.items[0]; - assert.equal(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`); - assert.equal(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`); + assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); return Promise.resolve(); }); }); @@ -346,7 +346,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 12, 24, 12); await expandEmmetAbbreviation(null); - assert.equal(editor.document.getText(), htmlContents); + assert.strictEqual(editor.document.getText(), htmlContents); return Promise.resolve(); }); }); @@ -356,7 +356,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { editor.selection = new Selection(24, 12, 24, 12); const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); return Promise.resolve(); }); }); @@ -365,12 +365,12 @@ suite('Tests for Expand Abbreviations (HTML)', () => { await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global); await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 10, 24, 10); - let expandPromise = expandEmmetAbbreviation(null); + const expandPromise = expandEmmetAbbreviation(null); if (!expandPromise) { return Promise.resolve(); } await expandPromise; - assert.equal(editor.document.getText(), htmlContents.replace('span.bye', '')); + assert.strictEqual(editor.document.getText(), htmlContents.replace('span.bye', '')); }); return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForInlcudeLanguages || {}, ConfigurationTarget.Global); }); @@ -384,17 +384,17 @@ suite('Tests for Expand Abbreviations (HTML)', () => { const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { - assert.equal(1, 2, `Problem with expanding span.bye`); + assert.strictEqual(1, 2, `Problem with expanding span.bye`); return Promise.resolve(); } const completionList = await completionPromise; if (!completionList || !completionList.items || !completionList.items.length) { - assert.equal(1, 2, `Problem with expanding span.bye`); + assert.strictEqual(1, 2, `Problem with expanding span.bye`); return Promise.resolve(); } const emmetCompletionItem = completionList.items[0]; - assert.equal(emmetCompletionItem.label, abbreviation, `Label of completion item (${emmetCompletionItem.label}) doesnt match.`); - assert.equal(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item (${emmetCompletionItem.label}) doesnt match.`); + assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); return Promise.resolve(); }); return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForInlcudeLanguages || {}, ConfigurationTarget.Global); @@ -433,7 +433,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('ul.nav', 'javascriptreact', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'javascriptreact' }); - assert.equal(editor.document.getText(), '
    '); + assert.strictEqual(editor.document.getText(), '
      '); return Promise.resolve(); }); }); @@ -442,7 +442,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('img', 'javascriptreact', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'javascriptreact' }); - assert.equal(editor.document.getText(), ''); + assert.strictEqual(editor.document.getText(), ''); return Promise.resolve(); }); }); @@ -452,7 +452,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('img', 'javascriptreact', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'javascriptreact' }); - assert.equal(editor.document.getText(), '\'\'/'); + assert.strictEqual(editor.document.getText(), '\'\'/'); return workspace.getConfiguration('emmet').update('syntaxProfiles', oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global); }); }); @@ -461,7 +461,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('img', 'xml', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'xml' }); - assert.equal(editor.document.getText(), ''); + assert.strictEqual(editor.document.getText(), ''); return Promise.resolve(); }); }); @@ -470,7 +470,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('img', 'html', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'html' }); - assert.equal(editor.document.getText(), ''); + assert.strictEqual(editor.document.getText(), ''); return Promise.resolve(); }); }); @@ -479,7 +479,7 @@ suite('Tests for jsx, xml and xsl', () => { return withRandomFileEditor('if (foo < 10) { span.bar', 'javascriptreact', async (editor, _doc) => { editor.selection = new Selection(0, 27, 0, 27); await expandEmmetAbbreviation({ language: 'javascriptreact' }); - assert.equal(editor.document.getText(), 'if (foo < 10) { '); + assert.strictEqual(editor.document.getText(), 'if (foo < 10) { '); return Promise.resolve(); }); }); @@ -505,15 +505,15 @@ suite('Tests for jsx, xml and xsl', () => { function testExpandAbbreviation(syntax: string, selection: Selection, abbreviation: string, expandedText: string, shouldFail?: boolean): Thenable { return withRandomFileEditor(htmlContents, syntax, async (editor, _doc) => { editor.selection = selection; - let expandPromise = expandEmmetAbbreviation(null); + const expandPromise = expandEmmetAbbreviation(null); if (!expandPromise) { if (!shouldFail) { - assert.equal(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); + assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); } return Promise.resolve(); } await expandPromise; - assert.equal(editor.document.getText(), htmlContents.replace(abbreviation, expandedText)); + assert.strictEqual(editor.document.getText(), htmlContents.replace(abbreviation, expandedText)); return Promise.resolve(); }); } @@ -525,7 +525,7 @@ function testHtmlCompletionProvider(selection: Selection, abbreviation: string, const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); if (!completionPromise) { if (!shouldFail) { - assert.equal(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); + assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); } return Promise.resolve(); } @@ -533,13 +533,13 @@ function testHtmlCompletionProvider(selection: Selection, abbreviation: string, const completionList = await completionPromise; if (!completionList || !completionList.items || !completionList.items.length) { if (!shouldFail) { - assert.equal(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); + assert.strictEqual(1, 2, `Problem with expanding ${abbreviation} to ${expandedText}`); } return Promise.resolve(); } const emmetCompletionItem = completionList.items[0]; - assert.equal(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`); - assert.equal(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.strictEqual(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`); + assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); return Promise.resolve(); }); } @@ -549,7 +549,7 @@ function testNoCompletion(syntax: string, fileContents: string, selection: Selec editor.selection = selection; const cancelSrc = new CancellationTokenSource(); const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + assert.strictEqual(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); return Promise.resolve(); }); } From d10e2fc0e6c109a383c028edf98432877b6e3cea Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 19 Oct 2020 11:26:12 -0700 Subject: [PATCH 052/121] Use new helper extractAbbreviation function --- extensions/emmet/src/abbreviationActions.ts | 2 +- .../emmet/src/defaultCompletionProvider.ts | 5 ++- extensions/emmet/yarn.lock | 41 +++++++++++++++---- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 9badf78f4f9..16dc7b61d6d 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -300,7 +300,7 @@ export function expandEmmetAbbreviation(args: any): Thenable Date: Mon, 19 Oct 2020 11:47:07 -0700 Subject: [PATCH 053/121] Update deps to include libgbm (#107611) Fixes #106936 --- resources/linux/debian/control.template | 2 +- resources/linux/rpm/dependencies.json | 3 ++- resources/linux/snap/snapcraft.yaml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index 903640cd503..5a6d7be652b 100644 --- a/resources/linux/debian/control.template +++ b/resources/linux/debian/control.template @@ -1,7 +1,7 @@ Package: @@NAME@@ Version: @@VERSION@@ Section: devel -Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 +Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1, libgbm1 Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index 07e2b307fcd..7f95cd3e5db 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -62,7 +62,8 @@ "libc.so.6(GLIBC_2.9)(64bit)", "libxcb.so.1()(64bit)", "libxkbfile.so.1()(64bit)", - "libsecret-1.so.0()(64bit)" + "libsecret-1.so.0()(64bit)", + "libgbm.so.1()(64bit)" ], "aarch64": [ "libpthread.so.0()(aarch64)", diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index 7dbc1680ba2..046158e888e 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -31,6 +31,7 @@ parts: - libgconf-2-4 - libglib2.0-bin - libgnome-keyring0 + - libgbm1 - libgtk-3-0 - libnotify4 - libnspr4 From c4d7a5b362b63a9cc7698417c3e4e60301dd2fe9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 19 Oct 2020 12:23:40 -0700 Subject: [PATCH 054/121] polish --- .../browser/terminalTypeAheadAddon.ts | 329 +++++++++++++++--- 1 file changed, 272 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index ed234e30dee..62c39bf2830 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -12,8 +12,18 @@ const CSI = '\x1b['; const SHOW_CURSOR = `${CSI}?25h`; const HIDE_CURSOR = `${CSI}?25l`; const DELETE_CHAR = `${CSI}X`; -const CSI_STYLE_RE = /^\x1b\[[0-9;]+m/; -const CSI_MOVE_RE = /^\x1b\[([0-9]*)([DC])/; +const CSI_STYLE_RE = /^\x1b\[[0-9;]*m/; +const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;5)?([DC])/; +const PASSWORD_INPUT_RE = /(password|passphrase|passwd).*: /i; +const WHITESPACE_RE = /\s/; + +/** + * Codes that should be omitted from sending to the prediction engine and + * insted omitted directly: + * - cursor hide/show + * - mode set/reset + */ +const PREDICTION_OMIT_RE = /^(\x1b\[\??25[hl])+/; const enum CursorMoveDirection { Back = 'D', @@ -31,6 +41,46 @@ interface ICoordinate { const getCellAtCoordinate = (b: IBuffer, c: ICoordinate) => b.getLine(c.y + c.baseY)?.getCell(c.x); +const moveToWordBoundary = (b: IBuffer, cursor: ICoordinate, direction: -1 | 1) => { + let ateLeadingWhitespace = false; + if (direction < 0) { + cursor.x--; + } + + while (cursor.x >= 0) { + const cell = getCellAtCoordinate(b, cursor); + if (!cell?.getCode()) { + return; + } + + const chars = cell.getChars(); + if (WHITESPACE_RE.test(chars)) { + if (ateLeadingWhitespace) { + break; + } + } else { + ateLeadingWhitespace = true; + } + + cursor.x += direction; + } + + if (direction < 0) { + cursor.x++; // we want to place the cursor after the whitespace starting the word + } + + cursor.x = Math.max(0, cursor.x); +}; + +const enum MatchResult { + /** matched successfully */ + Success, + /** failed to match */ + Failure, + /** buffer data, it might match in the future one more data comes in */ + Buffer, +} + interface IPrediction { /** * Returns a sequence to apply the prediction. @@ -54,10 +104,9 @@ interface IPrediction { /** * Returns whether the given input is one expected by this prediction. */ - matches(input: StringReader): boolean; + matches(input: StringReader): MatchResult; } - class StringReader { public index = 0; @@ -65,10 +114,33 @@ class StringReader { return this.input.length - this.index; } + public get eof() { + return this.index === this.input.length; + } + + public get rest() { + return this.input.slice(this.index); + } + constructor(private readonly input: string) { } + /** + * Advances the reader and returns the character if it matches. + */ + public eatChar(char: string) { + if (this.input[this.index] !== char) { + return; + } + + this.index++; + return char; + } + + /** + * Advances the reader and returns the string if it matches. + */ public eatStr(substr: string) { - if (this.input.slice(this.index, this.index + substr.length) !== substr) { + if (this.input.slice(this.index, substr.length) !== substr) { return; } @@ -76,6 +148,30 @@ class StringReader { return substr; } + /** + * Matches and eats the substring character-by-character. If EOF is reached + * before the substring is consumed, it will buffer. Index is not moved + * if it's not a match. + */ + public eatGradually(substr: string): MatchResult { + let prevIndex = this.index; + for (const char of substr) { + if (!this.eatChar(char)) { + this.index = prevIndex; + return MatchResult.Failure; + } + + if (this.eof) { + return MatchResult.Buffer; + } + } + + return MatchResult.Success; + } + + /** + * Advances the reader and returns the regex if it matches. + */ public eatRe(re: RegExp) { const match = re.exec(this.input.slice(this.index)); if (!match) { @@ -86,7 +182,10 @@ class StringReader { return match; } - public eatCharCode(min = 0, max = Infinity) { + /** + * Advances the reader and returns the character if the code matches. + */ + public eatCharCode(min = 0, max = min + 1) { const code = this.input.charCodeAt(this.index); if (code < min || code >= max) { return undefined; @@ -95,14 +194,11 @@ class StringReader { this.index++; return code; } - - public rest() { - return this.input.slice(this.index); - } } /** - * Boundary which never tests true. Will always discard predictions. + * Preidction which never tests true. Will always discard predictions made + * after it. */ class HardBoundary implements IPrediction { public apply() { @@ -114,15 +210,15 @@ class HardBoundary implements IPrediction { } public matches() { - return false; + return MatchResult.Failure; } } +/** + * Prediction for a single alphanumeric character. + */ class CharacterPrediction implements IPrediction { - protected appliedAt?: { - x: number; - y: number; - baseY: number; + protected appliedAt?: ICoordinate & { oldAttributes: string; oldChar: string; }; @@ -154,16 +250,25 @@ class CharacterPrediction implements IPrediction { // remove any styling CSI before checking the char while (input.eatRe(CSI_STYLE_RE)) { } - if (input.eatStr(this.char)) { - return true; + + if (input.eof) { + return MatchResult.Buffer; + } + + if (input.eatChar(this.char)) { + return MatchResult.Success; } input.index = startIndex; - return false; + return MatchResult.Failure; } } class BackspacePrediction extends CharacterPrediction { + constructor() { + super('', '\b'); + } + public apply(buffer: IBuffer, cursor: ICoordinate) { const cell = getCellAtCoordinate(buffer, cursor); this.appliedAt = cell @@ -175,7 +280,15 @@ class BackspacePrediction extends CharacterPrediction { } public matches(input: StringReader) { - return !!input.eatStr('\b'); + // if at end of line, allow backspace + clear line. Zsh does this. + if (this.appliedAt?.oldChar === '') { + const r = input.eatGradually(`\b${CSI}K`); + if (r !== MatchResult.Failure) { + return r; + } + } + + return input.eatChar('\b') ? MatchResult.Success : MatchResult.Failure; } } @@ -204,22 +317,48 @@ class NewlinePrediction implements IPrediction { } public matches(input: StringReader) { - return !!input.eatStr('\r\n'); + return input.eatGradually('\r\n'); } } class CursorMovePrediction implements IPrediction { - constructor(private readonly direction: CursorMoveDirection, private readonly amount: number) { } + private applied?: { + rollForward: string; + rollBack: string; + amount: number; + }; - public apply(_: IBuffer, cursor: ICoordinate) { - const { amount, direction } = this; - cursor.x += (direction === CursorMoveDirection.Back ? -1 : 1) * amount; - return `${CSI}${amount}${direction}`; + constructor( + private readonly direction: CursorMoveDirection, + private readonly moveByWords: boolean, + private readonly amount: number, + ) { } + + public apply(buffer: IBuffer, cursor: ICoordinate) { + let rollBack = setCursorCoordinate(buffer, cursor); + const currentCell = getCellAtCoordinate(buffer, cursor); + if (currentCell) { + rollBack += getBufferCellAttributes(currentCell); + } + + const { amount, direction, moveByWords } = this; + const delta = direction === CursorMoveDirection.Back ? -1 : 1; + const startX = cursor.x; + if (moveByWords) { + for (let i = 0; i < amount; i++) { + moveToWordBoundary(buffer, cursor, delta); + } + } else { + cursor.x += delta * amount; + } + + const rollForward = setCursorCoordinate(buffer, cursor); + this.applied = { amount: Math.abs(cursor.x - startX), rollBack, rollForward }; + return this.applied.rollForward; } public rollback() { - const fn = this.direction === CursorMoveDirection.Back ? CursorMoveDirection.Forwards : CursorMoveDirection.Back; - return `${CSI}${this.amount}${fn}`; + return this.applied?.rollBack ?? ''; } public rollForwards() { @@ -227,16 +366,36 @@ class CursorMovePrediction implements IPrediction { } public matches(input: StringReader) { - const { amount, direction } = this; - if (amount === 1 && input.eatStr(`${CSI}${direction}`)) { - return true; + if (!this.applied) { + return MatchResult.Failure; } - if (amount === 1 && this.direction === CursorMoveDirection.Back && input.eatStr('\b')) { - return true; + const direction = this.direction; + const { amount, rollForward } = this.applied; + + if (amount === 1) { + // arg can be omitted to move one character + const r = input.eatGradually(`${CSI}${direction}`); + if (r !== MatchResult.Failure) { + return r; + } + + // \b is the equivalent to moving one character back + if (direction === CursorMoveDirection.Back && input.eatChar('\b')) { + return MatchResult.Success; + } } - return !!input.eatStr(`${CSI}${amount}${direction}`); + // check if the cursor position is set absolutely + if (rollForward) { + const r = input.eatGradually(rollForward); + if (r !== MatchResult.Failure) { + return r; + } + } + + // check for a relative move in the direction + return input.eatGradually(`${CSI}${amount}${direction}`); } } @@ -259,12 +418,23 @@ class PredictionTimeline { */ private cursor: ICoordinate | undefined; + /** + * Previously sent data that was buffered and should be prepended to the + * next input. + */ + private inputBuffer?: string; + constructor(public readonly terminal: Terminal) { } /** * Should be called when input is incoming to the temrinal. */ public beforeServerInput(input: string): string { + if (this.inputBuffer) { + input = this.inputBuffer + input; + this.inputBuffer = undefined; + } + if (!this.expected.length) { this.cursor = undefined; return input; @@ -280,31 +450,55 @@ class PredictionTimeline { const reader = new StringReader(input); const startingGen = this.expected[0].gen; - while (this.expected.length && reader.remaining > 0) { + const emitPredictionOmitted = () => { + const omit = reader.eatRe(PREDICTION_OMIT_RE); + if (omit) { + output += omit[0]; + } + }; + + ReadLoop: while (this.expected.length && reader.remaining > 0) { + emitPredictionOmitted(); + const prediction = this.expected[0].p; let beforeTestReaderIndex = reader.index; - - // if the input character matches what the next prediction expected, undo - // the prediction and write the real character out. - if (prediction.matches(reader)) { - const eaten = input.slice(beforeTestReaderIndex, reader.index); - output += prediction.rollForwards?.(buffer, eaten) - ?? (prediction.rollback(buffer) + input.slice(beforeTestReaderIndex, reader.index)); - this.expected.shift(); - } - // otherwise, roll back all pending predictions - else { - output += this.expected.filter(p => p.gen === startingGen) - .map(({ p }) => p.rollback(buffer)) - .reverse() - .join(''); - this.expected = []; - this.cursor = undefined; - break; + switch (prediction.matches(reader)) { + case MatchResult.Success: + // if the input character matches what the next prediction expected, undo + // the prediction and write the real character out. + const eaten = input.slice(beforeTestReaderIndex, reader.index); + output += prediction.rollForwards?.(buffer, eaten) + ?? (prediction.rollback(buffer) + input.slice(beforeTestReaderIndex, reader.index)); + this.expected.shift(); + break; + case MatchResult.Buffer: + // on a buffer, store the remaining data and completely read data + // to be output as normal. + this.inputBuffer = input.slice(beforeTestReaderIndex); + reader.index = input.length; + break ReadLoop; + case MatchResult.Failure: + // on a failure, roll back all remaining items in this generation + // and clear predictions, since they are no longer valid + output += this.expected.filter(p => p.gen === startingGen) + .map(({ p }) => p.rollback(buffer)) + .reverse() + .join(''); + this.expected = []; + this.cursor = undefined; + break ReadLoop; } } - output += reader.rest(); + emitPredictionOmitted(); + + // Extra data (like the result of running a command) should cause us to + // reset the cursor + if (!reader.eof) { + output += reader.rest; + this.expected = []; + this.cursor = undefined; + } // If we passed a generation boundary, apply the current generation's predictions if (this.expected.length && startingGen !== this.expected[0].gen) { @@ -317,6 +511,10 @@ class PredictionTimeline { } } + if (output.length === 0) { + return ''; + } + if (this.cursor) { output += setCursorCoordinate(buffer, this.cursor); } @@ -422,8 +620,8 @@ export class TypeAheadAddon implements ITerminalAddon { const buffer = terminal.buffer.active; const reader = new StringReader(data); while (reader.remaining > 0) { - if (reader.eatStr('\b')) { // backspace - this.timeline.addPrediction(buffer, new BackspacePrediction(this.typeheadStyle, '\b')); + if (reader.eatCharCode(127)) { // backspace + this.timeline.addPrediction(buffer, new BackspacePrediction()); continue; } @@ -440,7 +638,15 @@ export class TypeAheadAddon implements ITerminalAddon { const cursorMv = reader.eatRe(CSI_MOVE_RE); if (cursorMv) { this.timeline.addPrediction(buffer, new CursorMovePrediction( - cursorMv[2] as CursorMoveDirection, Number(cursorMv[1]) || 1)); + cursorMv[3] as CursorMoveDirection, + !!cursorMv[2], + Number(cursorMv[1]) || 1) + ); + continue; + } + + if (reader.eatChar('\r') && buffer.cursorY < terminal.rows - 1) { + this.timeline.addPrediction(buffer, new NewlinePrediction()); continue; } @@ -459,5 +665,14 @@ export class TypeAheadAddon implements ITerminalAddon { console.log('incoming data:', JSON.stringify(event.data)); event.data = this.timeline.beforeServerInput(event.data); console.log('emitted data:', JSON.stringify(event.data)); + + // If there's something that looks like a password prompt, omit giving + // input. This is approximate since there's no TTY "password here" code, + // but should be enough to cover common cases like sudo + if (PASSWORD_INPUT_RE.test(event.data)) { + const terminal = this.timeline.terminal; + this.timeline.addPrediction(terminal.buffer.active, new HardBoundary()); + this.timeline.addBoundary(); + } } } From 82df497acd08f78c2c550739d8164cb9ce46c60d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 19 Oct 2020 13:00:50 -0700 Subject: [PATCH 055/121] cursor moves for nix --- .../terminal/browser/terminalTypeAheadAddon.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 62c39bf2830..f55b970a811 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -8,7 +8,8 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; -const CSI = '\x1b['; +const ESC = '\x1b'; +const CSI = `${ESC}[`; const SHOW_CURSOR = `${CSI}?25h`; const HIDE_CURSOR = `${CSI}?25l`; const DELETE_CHAR = `${CSI}X`; @@ -645,6 +646,16 @@ export class TypeAheadAddon implements ITerminalAddon { continue; } + if (reader.eatStr(`${ESC}f`)) { + this.timeline.addPrediction(buffer, new CursorMovePrediction(CursorMoveDirection.Forwards, true, 1)); + continue; + } + + if (reader.eatStr(`${ESC}b`)) { + this.timeline.addPrediction(buffer, new CursorMovePrediction(CursorMoveDirection.Back, true, 1)); + continue; + } + if (reader.eatChar('\r') && buffer.cursorY < terminal.rows - 1) { this.timeline.addPrediction(buffer, new NewlinePrediction()); continue; From 4c520a11a682530b68b25c80b5509ddd1e93ca73 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 16 Oct 2020 09:31:47 -0700 Subject: [PATCH 056/121] update --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 8a5eff502f6..1d13cff608c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -40,4 +40,4 @@ process.on('unhandledRejection', (reason, p) => { // Load all the gulpfiles only if running tasks other than the editor tasks const build = path.join(__dirname, 'build'); require('glob').sync('gulpfile.*.js', { cwd: build }) - .forEach(f => require(`./build/${f}`)); \ No newline at end of file + .forEach(f => require(`./build/${f}`)); From 13081e6541cb1a6637b8b52a1a0e4ce83852ee1d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 19 Oct 2020 16:29:56 -0700 Subject: [PATCH 057/121] get into a dogfooding state --- .../browser/terminalTypeAheadAddon.ts | 123 +++++++++++++----- .../terminal/browser/xterm-private.d.ts | 2 - .../terminal/common/terminalConfiguration.ts | 2 +- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index f55b970a811..f24e9a096e3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -14,9 +14,9 @@ const SHOW_CURSOR = `${CSI}?25h`; const HIDE_CURSOR = `${CSI}?25l`; const DELETE_CHAR = `${CSI}X`; const CSI_STYLE_RE = /^\x1b\[[0-9;]*m/; -const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;5)?([DC])/; -const PASSWORD_INPUT_RE = /(password|passphrase|passwd).*: /i; -const WHITESPACE_RE = /\s/; +const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;[35])?O?([DC])/; +const PASSWORD_INPUT_RE = /(password|passphrase|passwd).*:/i; +const NOT_WORD_RE = /\W/; /** * Codes that should be omitted from sending to the prediction engine and @@ -55,7 +55,7 @@ const moveToWordBoundary = (b: IBuffer, cursor: ICoordinate, direction: -1 | 1) } const chars = cell.getChars(); - if (WHITESPACE_RE.test(chars)) { + if (NOT_WORD_RE.test(chars)) { if (ateLeadingWhitespace) { break; } @@ -87,6 +87,8 @@ interface IPrediction { * Returns a sequence to apply the prediction. * @param buffer to write to * @param cursor position to write the data. Should advance the cursor. + * @returns a string to be written to the user terminal, or optionally a + * string for the user terminal and real pty. */ apply(buffer: IBuffer, cursor: ICoordinate): string; @@ -156,14 +158,14 @@ class StringReader { */ public eatGradually(substr: string): MatchResult { let prevIndex = this.index; - for (const char of substr) { - if (!this.eatChar(char)) { - this.index = prevIndex; - return MatchResult.Failure; + for (let i = 0; i < substr.length; i++) { + if (i > 0 && this.eof) { + return MatchResult.Buffer; } - if (this.eof) { - return MatchResult.Buffer; + if (!this.eatChar(substr[i])) { + this.index = prevIndex; + return MatchResult.Failure; } } @@ -215,6 +217,27 @@ class HardBoundary implements IPrediction { } } +/** + * Wraps another prediction. Does not apply the prediction, but will pass + * through its `matches` request. + */ +class TentativeBoundary implements IPrediction { + constructor(private readonly inner: IPrediction) { } + + public apply(buffer: IBuffer, cursor: ICoordinate) { + this.inner.apply(buffer, cursor); + return ''; + } + + public rollback() { + return ''; + } + + public matches(input: StringReader) { + return this.inner.matches(input); + } +} + /** * Prediction for a single alphanumeric character. */ @@ -289,7 +312,7 @@ class BackspacePrediction extends CharacterPrediction { } } - return input.eatChar('\b') ? MatchResult.Success : MatchResult.Failure; + return input.eatGradually('\b'); } } @@ -382,8 +405,9 @@ class CursorMovePrediction implements IPrediction { } // \b is the equivalent to moving one character back - if (direction === CursorMoveDirection.Back && input.eatChar('\b')) { - return MatchResult.Success; + const r2 = input.eatGradually(`\b`); + if (r2 !== MatchResult.Failure) { + return r2; } } @@ -534,15 +558,17 @@ class PredictionTimeline { this.expected.push({ gen: this.currentGen, p: prediction }); if (this.currentGen === this.expected[0].gen) { const text = prediction.apply(buffer, this.getCursor(buffer)); - console.log('prediction:', JSON.stringify(text)); this.terminal.write(text); } } /** - * Appends a boundary to the prediction. + * Appends a prediction followed by a boundary. The predictions applied + * after this one will only be displayed after the give prediction matches + * pty output/ */ - public addBoundary() { + public addBoundary(buffer: IBuffer, prediction: IPrediction) { + this.addPrediction(buffer, prediction); this.currentGen++; } @@ -594,6 +620,8 @@ const parseTypeheadStyle = (style: string | number) => { export class TypeAheadAddon implements ITerminalAddon { private disposables: IDisposable[] = []; private typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle); + private typeaheadThreshold = this.config.config.typeaheadThreshold; + private lastRow?: { y: number; startingX: number }; private timeline?: PredictionTimeline; constructor(private readonly _processManager: ITerminalProcessManager, private readonly config: TerminalConfigHelper) { @@ -602,27 +630,51 @@ export class TypeAheadAddon implements ITerminalAddon { public activate(terminal: Terminal): void { this.timeline = new PredictionTimeline(terminal); this.disposables.push(terminal.onData(e => this.onUserData(e))); - this.disposables.push(this.config.onConfigChanged(() => this.typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle))); + this.disposables.push(this.config.onConfigChanged(() => { + this.typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle); + this.typeaheadThreshold = this.config.config.typeaheadThreshold; + })); this.disposables.push(this._processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); } public dispose(): void { - // this.disposables.forEach(d => d.dispose()); + this.disposables.forEach(d => d.dispose()); } private onUserData(data: string): void { + if (this.typeaheadThreshold !== 0) { + return; + } + if (this.timeline?.terminal.buffer.active.type !== 'normal') { return; } - console.log('user data:', JSON.stringify(data)); + // console.log('user data:', JSON.stringify(data)); const terminal = this.timeline.terminal; const buffer = terminal.buffer.active; + + // the following code guards the terminal prompt to avoid being able to + // arrow or backspace-into the prompt. Record the lowest X value at which + // the user gave input, and mark all additions before that as tentative. + const actualY = buffer.baseY + buffer.cursorY; + if (actualY !== this.lastRow?.y) { + this.lastRow = { y: actualY, startingX: buffer.cursorX }; + } else { + this.lastRow.startingX = Math.min(this.lastRow.startingX, buffer.cursorX); + } + + const addLeftNavigating = (p: IPrediction) => + this.timeline!.getCursor(buffer).x <= this.lastRow!.startingX + ? this.timeline!.addBoundary(buffer, new TentativeBoundary(p)) + : this.timeline!.addPrediction(buffer, p); + + /** @see https://github.com/xtermjs/xterm.js/blob/1913e9512c048e3cf56bb5f5df51bfff6899c184/src/common/input/Keyboard.ts */ const reader = new StringReader(data); while (reader.remaining > 0) { if (reader.eatCharCode(127)) { // backspace - this.timeline.addPrediction(buffer, new BackspacePrediction()); + addLeftNavigating(new BackspacePrediction()); continue; } @@ -630,19 +682,20 @@ export class TypeAheadAddon implements ITerminalAddon { const char = data[reader.index - 1]; this.timeline.addPrediction(buffer, new CharacterPrediction(this.typeheadStyle, char)); if (this.timeline.getCursor(buffer).x === terminal.cols) { - this.timeline.addPrediction(buffer, new NewlinePrediction()); - this.timeline.addBoundary(); + this.timeline.addBoundary(buffer, new NewlinePrediction()); } continue; } const cursorMv = reader.eatRe(CSI_MOVE_RE); if (cursorMv) { - this.timeline.addPrediction(buffer, new CursorMovePrediction( - cursorMv[3] as CursorMoveDirection, - !!cursorMv[2], - Number(cursorMv[1]) || 1) - ); + const direction = cursorMv[3] as CursorMoveDirection; + const p = new CursorMovePrediction(direction, !!cursorMv[2], Number(cursorMv[1]) || 1); + if (direction === CursorMoveDirection.Back) { + addLeftNavigating(p); + } else { + this.timeline.addPrediction(buffer, p); + } continue; } @@ -652,7 +705,7 @@ export class TypeAheadAddon implements ITerminalAddon { } if (reader.eatStr(`${ESC}b`)) { - this.timeline.addPrediction(buffer, new CursorMovePrediction(CursorMoveDirection.Back, true, 1)); + addLeftNavigating(new CursorMovePrediction(CursorMoveDirection.Back, true, 1)); continue; } @@ -662,28 +715,30 @@ export class TypeAheadAddon implements ITerminalAddon { } // something else - this.timeline.addPrediction(buffer, new HardBoundary()); - this.timeline.addBoundary(); + this.timeline.addBoundary(buffer, new HardBoundary()); break; } } private onBeforeProcessData(event: IBeforeProcessDataEvent): void { + if (this.typeaheadThreshold !== 0) { + return; + } + if (!this.timeline) { return; } - console.log('incoming data:', JSON.stringify(event.data)); + // console.log('incoming data:', JSON.stringify(event.data)); event.data = this.timeline.beforeServerInput(event.data); - console.log('emitted data:', JSON.stringify(event.data)); + // console.log('emitted data:', JSON.stringify(event.data)); // If there's something that looks like a password prompt, omit giving // input. This is approximate since there's no TTY "password here" code, // but should be enough to cover common cases like sudo if (PASSWORD_INPUT_RE.test(event.data)) { const terminal = this.timeline.terminal; - this.timeline.addPrediction(terminal.buffer.active, new HardBoundary()); - this.timeline.addBoundary(); + this.timeline.addBoundary(terminal.buffer.active, new HardBoundary()); } } } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts index a46aa4477ff..c559ff7b8db 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts @@ -12,8 +12,6 @@ export interface XTermCore { height: number; }; - writeSync(input: Buffer | string): void; - _coreService: { triggerDataEvent(data: string, wasUserInput?: boolean): void; }; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 2cce39b1a95..0f3ca52b6c2 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -353,7 +353,7 @@ export const terminalConfiguration: IConfigurationNode = { default: true }, 'terminal.integrated.typeaheadThreshold': { - description: localize('terminal.integrated.typeaheadThreshold', "Experimental: length of time, in milliseconds, where typeahead will active. If '0', typeahead will always be on, and if '-1' it will be disabled."), + description: localize('terminal.integrated.typeaheadThreshold', "Experimental: length of time, in milliseconds, where typeahead will active. If '0', typeahead will always be on, and if '-1' it will be disabled. Note: currently only -1 and 0 supported."), type: 'integer', minimum: -1, default: -1, From 6ad1da9f7958beb2368d51e9054e0f6d4e76ca74 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sun, 18 Oct 2020 12:23:23 -0700 Subject: [PATCH 058/121] Pick up new TS for building VS Code --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build/package.json b/build/package.json index 7561ebc958c..9d86dbaf79e 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.1.0-dev.20200924", + "typescript": "^4.1.0-dev.20201018", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index 3cc284f20dc..09186eb00b0 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.1.0-dev.20200924: - version "4.1.0-dev.20200924" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200924.tgz#d8b2aaa6f94ec22725eafcadf0b9a17aae9c32b9" - integrity sha512-AXwqVrp2AeVZ3jaZ/gcvxb0nnvqEbDFuFFjvV5/9wfcyz7KZx5KvyJENUgGoJHywCvl1PHKasQKYjzjk1QixnQ== +typescript@^4.1.0-dev.20201018: + version "4.1.0-dev.20201018" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20201018.tgz#1a4b8e3f9b640218a44299773371354d75bcfa34" + integrity sha512-cOFYP1I+IrMWa6ZfefxcacZha1pQMxrq8DGMBLkvrl8k3CqIdD8APq9LXaMj/PWrB8IPgDprY6jHwqiHg0/oGA== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index bda7354c003..eaef8f06d68 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "style-loader": "^1.0.0", "ts-loader": "^4.4.2", "tsec": "googleinterns/tsec", - "typescript": "^4.1.0-dev.20200924", + "typescript": "^4.1.0-dev.20201018", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index b59bc29d438..6bbc9a10963 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9296,10 +9296,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.1.0-dev.20200924: - version "4.1.0-dev.20200924" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200924.tgz#d8b2aaa6f94ec22725eafcadf0b9a17aae9c32b9" - integrity sha512-AXwqVrp2AeVZ3jaZ/gcvxb0nnvqEbDFuFFjvV5/9wfcyz7KZx5KvyJENUgGoJHywCvl1PHKasQKYjzjk1QixnQ== +typescript@^4.1.0-dev.20201018: + version "4.1.0-dev.20201018" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20201018.tgz#1a4b8e3f9b640218a44299773371354d75bcfa34" + integrity sha512-cOFYP1I+IrMWa6ZfefxcacZha1pQMxrq8DGMBLkvrl8k3CqIdD8APq9LXaMj/PWrB8IPgDprY6jHwqiHg0/oGA== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From f1ffbb1f829557f646e9f9c463b76e02d4391a86 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Oct 2020 16:56:13 -0700 Subject: [PATCH 059/121] Add title to commands in release notes Fixes #94712 This enables a title that displays the command id for text of the form: ``` kb(command.bla) ``` --- .../contrib/update/browser/releaseNotesEditor.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index 8c3430fbd32..0cf1e65ecba 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -133,7 +133,19 @@ export class ReleaseNotesManager { return resolvedKeybindings[0].getLabel() || unassigned; }; + const kbCode = (match: string, binding: string) => { + const resolved = kb(match, binding); + return resolved ? `${resolved}` : resolved; + }; + + const kbstyleCode = (match: string, binding: string) => { + const resolved = kbstyle(match, binding); + return resolved ? `${resolved}` : resolved; + }; + return text + .replace(/`kb\(([a-z.\d\-]+)\)`/gi, kbCode) + .replace(/`kbstyle\(([^\)]+)\)`/gi, kbstyleCode) .replace(/kb\(([a-z.\d\-]+)\)/gi, kb) .replace(/kbstyle\(([^\)]+)\)/gi, kbstyle); }; From 8f1117bf44db5d9e38e3c57a5116532537b8aa3a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Oct 2020 16:57:01 -0700 Subject: [PATCH 060/121] Extract some functions in markdown renderer --- src/vs/base/browser/markdownRenderer.ts | 59 ++++++++++++++++--------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 1f089e2542e..2d601e975df 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -222,11 +222,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende markedOptions.sanitize = true; markedOptions.renderer = renderer; - const allowedSchemes = [Schemas.http, Schemas.https, Schemas.mailto, Schemas.data, Schemas.file, Schemas.vscodeRemote, Schemas.vscodeRemoteResource]; - if (markdown.isTrusted) { - allowedSchemes.push(Schemas.command); - } - // values that are too long will freeze the UI let value = markdown.value ?? ''; if (value.length > 100_000) { @@ -239,9 +234,43 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const renderedMarkdown = marked.parse(value, markedOptions); - // sanitize with insane - const insaneOptions = { + element.innerHTML = sanitizeRenderedMarkdown(markdown, renderedMarkdown); + + // signal that async code blocks can be now be inserted + signalInnerHTML!(); + + return element; +} + +function sanitizeRenderedMarkdown( + options: { isTrusted?: boolean }, + renderedMarkdown: string, +): string { + const insaneOptions = getInsaneOptions(options); + if (_ttpInsane) { + return _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string; + } else { + return insane(renderedMarkdown, insaneOptions); + } +} + +function getInsaneOptions(options: { readonly isTrusted?: boolean }): InsaneOptions { + const allowedSchemes = [ + Schemas.http, + Schemas.https, + Schemas.mailto, + Schemas.data, + Schemas.file, + Schemas.vscodeRemote, + Schemas.vscodeRemoteResource, + ]; + + if (options.isTrusted) { + allowedSchemes.push(Schemas.command); + } + + return { allowedSchemes, // allowedTags should included everything that markdown renders to. // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure. @@ -257,8 +286,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende 'th': ['align'], 'td': ['align'] }, - filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean { - if (token.tag === 'span' && markdown.isTrusted && (Object.keys(token.attrs).length === 1)) { + filter(token: { tag: string; attrs: { readonly [key: string]: string; }; }): boolean { + if (token.tag === 'span' && options.isTrusted && (Object.keys(token.attrs).length === 1)) { if (token.attrs['style']) { return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/); } else if (token.attrs['class']) { @@ -270,15 +299,5 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende return true; } }; - - if (_ttpInsane) { - element.innerHTML = _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string; - } else { - element.innerHTML = insane(renderedMarkdown, insaneOptions); - } - - // signal that async code blocks can be now be inserted - signalInnerHTML!(); - - return element; } + From 37c63d6ae551b5a2d4f1f6052ba645e02722f717 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Oct 2020 17:18:49 -0700 Subject: [PATCH 061/121] Don't show loading and project loading status for in-memory JS/TS files Fixes #108454 --- .../src/languageFeatures/hover.ts | 5 +++-- .../src/typescriptServiceClient.ts | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/hover.ts b/extensions/typescript-language-features/src/languageFeatures/hover.ts index a4de074897f..236ef694967 100644 --- a/extensions/typescript-language-features/src/languageFeatures/hover.ts +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -36,11 +36,12 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { } return new vscode.Hover( - this.getContents(response.body, response._serverType), + this.getContents(document.uri, response.body, response._serverType), typeConverters.Range.fromTextSpan(response.body)); } private getContents( + resource: vscode.Uri, data: Proto.QuickInfoResponseBody, source: ServerType | undefined, ) { @@ -49,7 +50,7 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { if (data.displayString) { const displayParts: string[] = []; - if (source === ServerType.Syntax && this.client.capabilities.has(ClientCapability.Semantic)) { + if (source === ServerType.Syntax && this.client.hasCapabilityForResource(resource, ClientCapability.Semantic)) { displayParts.push( localize({ key: 'loadingPrefix', diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 530e79c7fbd..3b4264f29ae 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -851,6 +851,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; } case EventName.projectsUpdatedInBackground: + this.loadingIndicator.reset(); + const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; const resources = body.openFiles.map(file => this.toResource(file)); this.bufferSyncSupport.getErr(resources); From e2490d25b322c19e4959dcc98d1939f1986b82c8 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 19 Oct 2020 17:25:57 -0700 Subject: [PATCH 062/121] fixes #99069 --- src/vs/workbench/common/theme.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index fb4a4d67db6..5193fd36e87 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -306,31 +306,31 @@ export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSectio dark: EDITOR_DRAG_AND_DROP_BACKGROUND, light: EDITOR_DRAG_AND_DROP_BACKGROUND, hc: EDITOR_DRAG_AND_DROP_BACKGROUND, -}, nls.localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, nls.localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_BACKGROUND = registerColor('panelSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), light: Color.fromHex('#808080').transparent(0.2), hc: null -}, nls.localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, nls.localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_FOREGROUND = registerColor('panelSectionHeader.foreground', { dark: null, light: null, hc: null -}, nls.localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, nls.localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_BORDER = registerColor('panelSectionHeader.border', { dark: contrastBorder, light: contrastBorder, hc: contrastBorder -}, nls.localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, nls.localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_BORDER = registerColor('panelSection.border', { dark: PANEL_BORDER, light: PANEL_BORDER, hc: PANEL_BORDER -}, nls.localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); +}, nls.localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); // < --- Status --- > @@ -521,25 +521,25 @@ export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBack dark: EDITOR_DRAG_AND_DROP_BACKGROUND, light: EDITOR_DRAG_AND_DROP_BACKGROUND, hc: EDITOR_DRAG_AND_DROP_BACKGROUND, -}, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search.")); +}, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), light: Color.fromHex('#808080').transparent(0.2), hc: null -}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search.")); +}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', { dark: SIDE_BAR_FOREGROUND, light: SIDE_BAR_FOREGROUND, hc: SIDE_BAR_FOREGROUND -}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search.")); +}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); export const SIDE_BAR_SECTION_HEADER_BORDER = registerColor('sideBarSectionHeader.border', { dark: contrastBorder, light: contrastBorder, hc: contrastBorder -}, nls.localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search.")); +}, nls.localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); // < --- Title Bar --- > From 9745766e8e9f1a31a856e269cb42e0e6ef10aac2 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 19 Oct 2020 17:27:00 -0700 Subject: [PATCH 063/121] add period --- src/vs/workbench/common/theme.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 5193fd36e87..f7451eda51d 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -527,19 +527,19 @@ export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionH dark: Color.fromHex('#808080').transparent(0.2), light: Color.fromHex('#808080').transparent(0.2), hc: null -}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); +}, nls.localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', { dark: SIDE_BAR_FOREGROUND, light: SIDE_BAR_FOREGROUND, hc: SIDE_BAR_FOREGROUND -}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); +}, nls.localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_BORDER = registerColor('sideBarSectionHeader.border', { dark: contrastBorder, light: contrastBorder, hc: contrastBorder -}, nls.localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar")); +}, nls.localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); // < --- Title Bar --- > From ff4f319e2954a49506d8142ae66cca9f20977c1b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 19 Oct 2020 17:53:00 -0700 Subject: [PATCH 064/121] fixes #102345 --- src/vs/workbench/common/views.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index cb690ac93e7..24f43b1d740 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -184,7 +184,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe } getViewContainerLocation(container: ViewContainer): ViewContainerLocation { - return [...this.viewContainers.keys()].filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer.id === container.id).length > 0)[0]; + return [...this.viewContainers.keys()].filter(location => this.getViewContainers(location).filter(viewContainer => viewContainer?.id === container.id).length > 0)[0]; } getDefaultViewContainer(location: ViewContainerLocation): ViewContainer | undefined { From 5eb46b187cf508079c085989fd406ec1ac0f8f30 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 03:36:08 +0200 Subject: [PATCH 065/121] link protection - read remotes only once on startup (#108938) * link protection - read remotes only once on startup * Reread on potential ocntent changes. Co-authored-by: Jackson Kearl --- .../contrib/url/browser/trustedDomains.ts | 50 ++++++++++++++----- .../url/browser/trustedDomainsValidator.ts | 33 ++++++++++-- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index 3e9d479633b..4d4b66c64f7 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -174,14 +174,47 @@ async function getRemotes(fileService: IFileService, textFileService: ITextFileS return [...set]; } -export async function readTrustedDomains(accessor: ServicesAccessor) { +export interface IStaticTrustedDomains { + readonly defaultTrustedDomains: string[]; + readonly trustedDomains: string[]; +} - const storageService = accessor.get(IStorageService); - const productService = accessor.get(IProductService); - const authenticationService = accessor.get(IAuthenticationService); +export interface ITrustedDomains extends IStaticTrustedDomains { + readonly userDomains: string[]; + readonly workspaceDomains: string[]; +} + +export async function readTrustedDomains(accessor: ServicesAccessor): Promise { + const { defaultTrustedDomains, trustedDomains } = readStaticTrustedDomains(accessor); + const [workspaceDomains, userDomains] = await Promise.all([readWorkspaceTrustedDomains(accessor), readAuthenticationTrustedDomains(accessor)]); + return { + workspaceDomains, + userDomains, + defaultTrustedDomains, + trustedDomains, + }; +} + +export async function readWorkspaceTrustedDomains(accessor: ServicesAccessor): Promise { + console.log('reading workspace domains'); const fileService = accessor.get(IFileService); const textFileService = accessor.get(ITextFileService); const workspaceContextService = accessor.get(IWorkspaceContextService); + return getRemotes(fileService, textFileService, workspaceContextService); +} + +export async function readAuthenticationTrustedDomains(accessor: ServicesAccessor): Promise { + console.log('reading auth domains'); + + const authenticationService = accessor.get(IAuthenticationService); + return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getSessions('github')) ?? []).length > 0 + ? [`https://github.com`] + : []; +} + +export function readStaticTrustedDomains(accessor: ServicesAccessor): IStaticTrustedDomains { + const storageService = accessor.get(IStorageService); + const productService = accessor.get(IProductService); const defaultTrustedDomains: string[] = productService.linkProtectionTrustedDomains ? [...productService.linkProtectionTrustedDomains] @@ -195,17 +228,8 @@ export async function readTrustedDomains(accessor: ServicesAccessor) { } } catch (err) { } - const userDomains = - authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getSessions('github')) ?? []).length > 0 - ? [`https://github.com`] - : []; - - const workspaceDomains = await getRemotes(fileService, textFileService, workspaceContextService); - return { defaultTrustedDomains, trustedDomains, - userDomains, - workspaceDomains }; } diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index 235931c0287..2c8428a6300 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -13,21 +13,25 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { - configureOpenerTrustedDomainsHandler, - readTrustedDomains -} from 'vs/workbench/contrib/url/browser/trustedDomains'; +import { configureOpenerTrustedDomainsHandler, readAuthenticationTrustedDomains, readStaticTrustedDomains, readWorkspaceTrustedDomains } from 'vs/workbench/contrib/url/browser/trustedDomains'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IdleValue } from 'vs/base/common/async'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; type TrustedDomainsDialogActionClassification = { action: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; export class OpenerValidatorContributions implements IWorkbenchContribution { + + private _readWorkspaceTrustedDomainsResult: IdleValue>; + private _readAuthenticationTrustedDomainsResult: IdleValue>; + constructor( @IOpenerService private readonly _openerService: IOpenerService, @IStorageService private readonly _storageService: IStorageService, @@ -39,8 +43,26 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { @ITelemetryService private readonly _telemetryService: ITelemetryService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotificationService private readonly _notificationService: INotificationService, + @IAuthenticationService private readonly _authenticationService: IAuthenticationService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, ) { this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); + + this._readAuthenticationTrustedDomainsResult = new IdleValue(() => + this._instantiationService.invokeFunction(readAuthenticationTrustedDomains)); + this._authenticationService.onDidRegisterAuthenticationProvider(() => { + this._readAuthenticationTrustedDomainsResult?.dispose(); + this._readAuthenticationTrustedDomainsResult = new IdleValue(() => + this._instantiationService.invokeFunction(readAuthenticationTrustedDomains)); + }); + + this._readWorkspaceTrustedDomainsResult = new IdleValue(() => + this._instantiationService.invokeFunction(readWorkspaceTrustedDomains)); + this._workspaceContextService.onDidChangeWorkspaceFolders(() => { + this._readWorkspaceTrustedDomainsResult?.dispose(); + this._readWorkspaceTrustedDomainsResult = new IdleValue(() => + this._instantiationService.invokeFunction(readWorkspaceTrustedDomains)); + }); } async validateLink(resource: URI | string): Promise { @@ -54,7 +76,8 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { const { scheme, authority, path, query, fragment } = resource; const domainToOpen = `${scheme}://${authority}`; - const { defaultTrustedDomains, trustedDomains, userDomains, workspaceDomains } = await this._instantiationService.invokeFunction(readTrustedDomains); + const [workspaceDomains, userDomains] = await Promise.all([this._readWorkspaceTrustedDomainsResult.value, this._readAuthenticationTrustedDomainsResult.value]); + const { defaultTrustedDomains, trustedDomains, } = this._instantiationService.invokeFunction(readStaticTrustedDomains); const allTrustedDomains = [...defaultTrustedDomains, ...trustedDomains, ...userDomains, ...workspaceDomains]; if (isURLDomainTrusted(resource, allTrustedDomains)) { From a4a4cf5ace4472bc4f5176396bb290cafa15c518 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Oct 2020 19:00:08 -0700 Subject: [PATCH 066/121] Re-resolve webview views when extension host restarts Fixes #108555 Previously webviews were left hanging when the extension host died. With this change, we now try to re-create them once the extension host restarts --- .../api/browser/mainThreadWebviewPanels.ts | 4 +- .../api/browser/mainThreadWebviewViews.ts | 15 +++++- .../webviewView/browser/webviewViewPane.ts | 53 ++++++++++++------- .../webviewView/browser/webviewViewService.ts | 20 ++++--- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index c0dbac194c1..561e3c219eb 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -128,9 +128,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc dispose() { super.dispose(); - for (const disposable of this._editorProviders.values()) { - disposable.dispose(); - } + dispose(this._editorProviders.values()); this._editorProviders.clear(); } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index 346ca9957b6..e369b4f088d 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { MainThreadWebviews, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { IWebviewViewService, WebviewView } from 'vs/workbench/contrib/webviewView/browser/webviewViewService'; @@ -28,6 +28,15 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews); } + dispose() { + super.dispose(); + + dispose(this._webviewViewProviders.values()); + this._webviewViewProviders.clear(); + + dispose(this._webviewViews.values()); + } + public $setWebviewViewTitle(handle: extHostProtocol.WebviewHandle, value: string | undefined): void { const webviewView = this.getWebviewView(handle); webviewView.title = value; @@ -54,7 +63,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc const extension = reviveWebviewExtension(extensionData); - this._webviewViewService.register(viewType, { + const registration = this._webviewViewService.register(viewType, { resolve: async (webviewView: WebviewView, cancellation: CancellationToken) => { const handle = webviewView.webview.id; @@ -93,6 +102,8 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc } } }); + + this._webviewViewProviders.set(viewType, registration); } public $unregisterWebviewViewProvider(viewType: string): void { diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index 4c0849a8821..5e877d47a2f 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -5,7 +5,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { setImmediate } from 'vs/base/common/platform'; import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -34,7 +34,8 @@ const storageKeys = { export class WebviewViewPane extends ViewPane { - private _webview?: WebviewOverlay; + private readonly _webview = this._register(new MutableDisposable()); + private readonly _webviewDisposables = this._register(new DisposableStore()); private _activated = false; private _container?: HTMLElement; @@ -71,6 +72,14 @@ export class WebviewViewPane extends ViewPane { this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); + + this._register(this.webviewViewService.onNewResolverRegistered(e => { + if (e.viewType === this.id) { + // Potentially re-activate if we have a new resolver + this.updateTreeVisibility(); + } + })); + this.updateTreeVisibility(); } @@ -83,14 +92,12 @@ export class WebviewViewPane extends ViewPane { dispose() { this._onDispose.fire(); - this._webview?.dispose(); - super.dispose(); } focus(): void { super.focus(); - this._webview?.focus(); + this._webview.value?.focus(); } renderBody(container: HTMLElement): void { @@ -102,7 +109,7 @@ export class WebviewViewPane extends ViewPane { this._resizeObserver = new ResizeObserver(() => { setImmediate(() => { if (this._container) { - this._webview?.layoutWebviewOverElement(this._container); + this._webview.value?.layoutWebviewOverElement(this._container); } }); }); @@ -115,8 +122,8 @@ export class WebviewViewPane extends ViewPane { } public saveState() { - if (this._webview) { - this.viewState[storageKeys.webviewState] = this._webview.state; + if (this._webview.value) { + this.viewState[storageKeys.webviewState] = this._webview.value.state; } this.memento.saveMemento(); @@ -126,21 +133,21 @@ export class WebviewViewPane extends ViewPane { protected layoutBody(height: number, width: number): void { super.layoutBody(height, width); - if (!this._webview) { + if (!this._webview.value) { return; } if (this._container) { - this._webview.layoutWebviewOverElement(this._container, { width, height }); + this._webview.value.layoutWebviewOverElement(this._container, { width, height }); } } private updateTreeVisibility() { if (this.isBodyVisible()) { this.activate(); - this._webview?.claim(this); + this._webview.value?.claim(this); } else { - this._webview?.release(this); + this._webview.value?.release(this); } } @@ -151,17 +158,20 @@ export class WebviewViewPane extends ViewPane { const webviewId = `webviewView-${this.id.replace(/[^a-z0-9]/gi, '-')}`.toLowerCase(); const webview = this.webviewService.createWebviewOverlay(webviewId, {}, {}, undefined); webview.state = this.viewState[storageKeys.webviewState]; - this._webview = webview; + this._webview.value = webview; - this._register(toDisposable(() => { - this._webview?.release(this); + if (this._container) { + this._webview.value?.layoutWebviewOverElement(this._container); + } + + this._webviewDisposables.add(toDisposable(() => { + this._webview.value?.release(this); })); - this._register(webview.onDidUpdateState(() => { + this._webviewDisposables.add(webview.onDidUpdateState(() => { this.viewState[storageKeys.webviewState] = webview.state; })); - - const source = this._register(new CancellationTokenSource()); + const source = this._webviewDisposables.add(new CancellationTokenSource()); this.withProgress(async () => { await this.extensionService.activateByEvent(`onView:${this.id}`); @@ -178,6 +188,13 @@ export class WebviewViewPane extends ViewPane { get description(): string | undefined { return self.titleDescription; }, set description(value: string | undefined) { self.updateTitleDescription(value); }, + dispose: () => { + // Only reset and clear the webview itself. Don't dispose of the view container + this._activated = false; + this._webview.clear(); + this._webviewDisposables.clear(); + }, + show: (preserveFocus) => { this.viewService.openView(this.id, !preserveFocus); } diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts index 68196b0f01f..663e359f7cf 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; @@ -20,6 +20,8 @@ export interface WebviewView { readonly onDidChangeVisibility: Event; readonly onDispose: Event; + dispose(): void; + show(preserveFocus: boolean): void; } @@ -31,6 +33,8 @@ export interface IWebviewViewService { readonly _serviceBrand: undefined; + readonly onNewResolverRegistered: Event<{ readonly viewType: string }>; + register(type: string, resolver: IWebviewViewResolver): IDisposable; resolve(viewType: string, webview: WebviewView, cancellation: CancellationToken): Promise; @@ -40,16 +44,20 @@ export class WebviewViewService extends Disposable implements IWebviewViewServic readonly _serviceBrand: undefined; - private readonly _views = new Map(); + private readonly _resolvers = new Map(); private readonly _awaitingRevival = new Map void }>(); + private readonly _onNewResolverRegistered = this._register(new Emitter<{ readonly viewType: string }>()); + public readonly onNewResolverRegistered = this._onNewResolverRegistered.event; + register(viewType: string, resolver: IWebviewViewResolver): IDisposable { - if (this._views.has(viewType)) { + if (this._resolvers.has(viewType)) { throw new Error(`View resolver already registered for ${viewType}`); } - this._views.set(viewType, resolver); + this._resolvers.set(viewType, resolver); + this._onNewResolverRegistered.fire({ viewType: viewType }); const pending = this._awaitingRevival.get(viewType); if (pending) { @@ -60,12 +68,12 @@ export class WebviewViewService extends Disposable implements IWebviewViewServic } return toDisposable(() => { - this._views.delete(viewType); + this._resolvers.delete(viewType); }); } resolve(viewType: string, webview: WebviewView, cancellation: CancellationToken): Promise { - const resolver = this._views.get(viewType); + const resolver = this._resolvers.get(viewType); if (!resolver) { if (this._awaitingRevival.has(viewType)) { throw new Error('View already awaiting revival'); From 13b3c937dc5e3816c79bdd2cdf2cdf6f9c727b75 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Oct 2020 20:22:46 -0700 Subject: [PATCH 067/121] Use looser check for active element when re-focusing webview Fixes #108596 Always skip refocusing a webview if a new element has been focused after we call `.focus` on it --- .../contrib/webview/electron-browser/webviewElement.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index b7cd695aa46..6ac6a86d729 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -312,8 +312,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme if (!this.isFocused || !this.element) { return; } - - if (document.activeElement?.tagName === 'INPUT') { + if (document.activeElement && document.activeElement?.tagName !== 'BODY') { return; } try { From 50bfc596da23dc853bc3e04dc49957a27756e87d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Oct 2020 08:05:33 +0200 Subject: [PATCH 068/121] use try catch --- src/vs/platform/log/common/fileLogService.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts index 3f85535c791..a6318446b88 100644 --- a/src/vs/platform/log/common/fileLogService.ts +++ b/src/vs/platform/log/common/fileLogService.ts @@ -5,7 +5,7 @@ import { ILogService, LogLevel, AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; -import { IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; +import { FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { Queue } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { dirname, joinPath, basename } from 'vs/base/common/resources'; @@ -87,8 +87,12 @@ export class FileLogService extends AbstractLogService implements ILogService { } private async initialize(): Promise { - if (!await this.fileService.exists(this.resource)) { + try { await this.fileService.createFile(this.resource); + } catch (error) { + if ((error).fileOperationResult !== FileOperationResult.FILE_MODIFIED_SINCE) { + throw error; + } } } From 60d96d72a560f0f02d67ef9d438a8a7ed534ec4a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 09:10:44 +0200 Subject: [PATCH 069/121] explorer - tweaks to upload/download --- .../contrib/files/browser/fileActions.ts | 11 +- .../files/browser/views/explorerViewer.ts | 118 +++++++++++------- 2 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index f91217259bd..c7f63c11d7a 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -1044,8 +1044,8 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { filesTotal: number; filesDownloaded: number; - totalBytesDownloaded: 0 - fileBytesDownloaded: 0 + totalBytesDownloaded: number; + fileBytesDownloaded: number; } async function pipeContents(name: string, source: IFileStreamContent, target: WebFileSystemAccess.FileSystemWritableFileStream, operation: IDownloadOperation): Promise { @@ -1142,7 +1142,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { } try { - const targetFolder: WebFileSystemAccess.FileSystemDirectoryHandle = await window.showDirectoryPicker(); + const parentFolder: WebFileSystemAccess.FileSystemDirectoryHandle = await window.showDirectoryPicker(); const operation: IDownloadOperation = { startTime: Date.now(), @@ -1154,12 +1154,13 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { }; if (stat.isDirectory) { + const targetFolder = await parentFolder.getDirectoryHandle(stat.name, { create: true }); await downloadFolder(stat, targetFolder, operation); } else { - await downloadFile(targetFolder, stat.name, stat.resource, operation); + await downloadFile(parentFolder, stat.name, stat.resource, operation); } } catch (error) { - logService.trace(error); + logService.warn(error); cts.cancel(); // `showDirectoryPicker` will throw an error when the user cancels } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 01b3dbc05a5..17a38497e9e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -999,22 +999,43 @@ export class FileDragAndDrop implements ITreeDragAndDrop { if (target.isReadonly) { return; } + const resolvedTarget = target; + if (!resolvedTarget) { + return; + } // Desktop DND (Import file) if (data instanceof NativeDragAndDropData) { - if (isWeb) { - this.handleWebExternalDrop(data, target, originalEvent).then(undefined, e => this.notificationService.warn(e)); - } else { - this.handleExternalDrop(data, target, originalEvent).then(undefined, e => this.notificationService.warn(e)); - } + const cts = new CancellationTokenSource(); + + // Indicate progress globally + const dropPromise = this.progressService.withProgress({ + location: ProgressLocation.Window, + delay: 800, + cancellable: true, + title: isWeb ? localize('uploadingFiles', "Uploading") : localize('copyingFiles', "Copying") + }, async progress => { + try { + if (isWeb) { + await this.handleWebExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); + } else { + await this.handleExternalDrop(data, resolvedTarget, originalEvent, progress, cts.token); + } + } catch (error) { + this.notificationService.warn(error); + } + }, () => cts.dispose(true)); + + // Also indicate progress in the files view + this.progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => dropPromise); } // In-Explorer DND (Move/Copy file) else { - this.handleExplorerDrop(data as ElementsDragAndDropData, target, originalEvent).then(undefined, e => this.notificationService.warn(e)); + this.handleExplorerDrop(data as ElementsDragAndDropData, resolvedTarget, originalEvent).then(undefined, e => this.notificationService.warn(e)); } } - private async handleWebExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { + private async handleWebExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { const items = (originalEvent.dataTransfer as unknown as IWebkitDataTransfer).items; // Somehow the items thing is being modified at random, maybe as a security @@ -1026,48 +1047,38 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } const results: { isFile: boolean, resource: URI }[] = []; - const cts = new CancellationTokenSource(); const operation: IUploadOperation = { filesTotal: entries.length, filesUploaded: 0, startTime: Date.now(), bytesUploaded: 0 }; - // Start upload and report progress globally - const uploadPromise = this.progressService.withProgress({ - location: ProgressLocation.Window, - delay: 800, - cancellable: true, - title: localize('uploadingFiles', "Uploading") - }, async progress => { - for (let entry of entries) { - if (cts.token.isCancellationRequested) { + for (let entry of entries) { + if (token.isCancellationRequested) { + break; + } + + // Confirm overwrite as needed + if (target && entry.name && target.getChild(entry.name)) { + const { confirmed } = await this.dialogService.confirm(getFileOverwriteConfirm(entry.name)); + if (!confirmed) { + continue; + } + + await this.workingCopyFileService.delete([joinPath(target.resource, entry.name)], { recursive: true }); + + if (token.isCancellationRequested) { break; } - - // Confirm overwrite as needed - if (target && entry.name && target.getChild(entry.name)) { - const { confirmed } = await this.dialogService.confirm(getFileOverwriteConfirm(entry.name)); - if (!confirmed) { - continue; - } - - await this.workingCopyFileService.delete([joinPath(target.resource, entry.name)], { recursive: true }); - } - - // Upload entry - const result = await this.doUploadWebFileEntry(entry, target.resource, target, progress, operation, cts.token); - if (result) { - results.push(result); - } } - }, () => cts.dispose(true)); - // Also indicate progress in the files view - this.progressService.withProgress({ location: VIEW_ID, delay: 800 }, () => uploadPromise); - - // Wait until upload is done - await uploadPromise; + // Upload entry + const result = await this.doUploadWebFileEntry(entry, target.resource, target, progress, operation, token); + if (result) { + results.push(result); + } + } // Open uploaded file in editor only if we upload just one - if (!cts.token.isCancellationRequested && results.length === 1 && results[0].isFile) { - await this.editorService.openEditor({ resource: results[0].resource, options: { pinned: true } }); + const firstUploadedFile = results[0]; + if (!token.isCancellationRequested && firstUploadedFile?.isFile) { + await this.editorService.openEditor({ resource: firstUploadedFile.resource, options: { pinned: true } }); } } @@ -1234,12 +1245,16 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - private async handleExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { + private async handleExternalDrop(data: NativeDragAndDropData, target: ExplorerItem, originalEvent: DragEvent, progress: IProgress, token: CancellationToken): Promise { // Check for dropped external files to be folders const droppedResources = extractResources(originalEvent, true); const result = await this.fileService.resolveAll(droppedResources.map(droppedResource => ({ resource: droppedResource.resource }))); + if (token.isCancellationRequested) { + return; + } + // Pass focus to window this.hostService.focus(); @@ -1264,7 +1279,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return this.workspaceEditingService.addFolders(folders); } if (choice === buttons.length - 2) { - return this.addResources(target, droppedResources.map(res => res.resource)); + return this.addResources(target, droppedResources.map(res => res.resource), progress, token); } return undefined; @@ -1272,16 +1287,20 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Handle dropped files (only support FileStat as target) else if (target instanceof ExplorerItem) { - return this.addResources(target, droppedResources.map(res => res.resource)); + return this.addResources(target, droppedResources.map(res => res.resource), progress, token); } } - private async addResources(target: ExplorerItem, resources: URI[]): Promise { + private async addResources(target: ExplorerItem, resources: URI[], progress: IProgress, token: CancellationToken): Promise { if (resources && resources.length > 0) { // Resolve target to check for name collisions and ask user const targetStat = await this.fileService.resolve(target.resource); + if (token.isCancellationRequested) { + return; + } + // Check for name collisions const targetNames = new Set(); const caseSensitive = this.fileService.hasCapability(target.resource, FileSystemProviderCapabilities.PathCaseSensitive); @@ -1302,8 +1321,15 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } addPromisesFactory.push(async () => { + if (token.isCancellationRequested) { + return; + } + const sourceFile = resource; - const targetFile = joinPath(target.resource, basename(sourceFile)); + const sourceFileName = basename(sourceFile); + const targetFile = joinPath(target.resource, sourceFileName); + + progress.report({ message: sourceFileName }); const stat = (await this.workingCopyFileService.copy([{ source: sourceFile, target: targetFile }], { overwrite: true }))[0]; // if we only add one file, just open it directly From 73cd1f193fc45f5fbe330a29d365196a78516bce Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 09:10:55 +0200 Subject: [PATCH 070/121] trusted domains - remove console.logs //cc @JacksonKearl --- src/vs/workbench/contrib/url/browser/trustedDomains.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index 4d4b66c64f7..81ec10aea95 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -196,7 +196,6 @@ export async function readTrustedDomains(accessor: ServicesAccessor): Promise { - console.log('reading workspace domains'); const fileService = accessor.get(IFileService); const textFileService = accessor.get(ITextFileService); const workspaceContextService = accessor.get(IWorkspaceContextService); @@ -204,8 +203,6 @@ export async function readWorkspaceTrustedDomains(accessor: ServicesAccessor): P } export async function readAuthenticationTrustedDomains(accessor: ServicesAccessor): Promise { - console.log('reading auth domains'); - const authenticationService = accessor.get(IAuthenticationService); return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getSessions('github')) ?? []).length > 0 ? [`https://github.com`] From 3c463a2076ef0f2c8ef838bfc4e31fa7e3ca119a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 09:46:48 +0200 Subject: [PATCH 071/121] Fixes #108880 --- .../services/keybinding/common/macLinuxKeyboardMapper.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index 86f63f04b56..7e2d9d6d17c 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -956,6 +956,12 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { } } + // See https://github.com/microsoft/vscode/issues/108880 + if (binding.ctrlKey && !binding.metaKey && !binding.altKey && constantKeyCode === KeyCode.US_MINUS) { + // ctrl+- and ctrl+shift+- render very similarly in native macOS menus, leading to confusion + return null; + } + if (constantKeyCode !== -1) { return this._getElectronLabelForKeyCode(constantKeyCode); } From 4af42491069dcbf0881b7993ee18a180b790f861 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 20 Oct 2020 04:04:33 -0400 Subject: [PATCH 072/121] SCM: Support past commit message navigation (#107619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * logging prior commit messages * Logging prior commit messages * Logging prior commit messages * now works in forward and backward directions * reset index on empty input * cleaning up code * introduce historyNavigator, working but not persisting on window reload * introduce historyNavigator, working but not persisting on window reload * add history * saves search entries on window reload but doesn't save last typed message in input box * save input * change where the save occurs * working * add remove last method * now replaces most recent input * remove check for null value * now at least lets you see most recent commit * before adding objects * add scmi value class * add scmi value class * new commit * fix removal / insertion order * change function modifiers * working correctly * change conditional * undo inadvertant changes * Update README.md * fix tricky bug * working and removed unnecessary conditional * fix another bug * make elements private again * change order of save * now working as expected, about to add context keys * hook up context keys * save on shutdown * improve variable name * disable show prior/next commit when there's no history and ensure that input is last in history * formatting * add new history navigator * fix bad == * rename scm input history methods * adopt HistoryNavigator2 in SCMInput * remove unnecessary method * revert history.ts * :lipstick: * change size of history required to be valid * revert change * on reload, display latest input * remove rogue console.log * fix issue with saving uncommitted message Co-authored-by: João Moreno --- src/vs/base/common/history.ts | 92 +++++++++++++++++++ src/vs/workbench/api/browser/mainThreadSCM.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 29 +++++- .../contrib/scm/browser/scmViewPane.ts | 16 +++- src/vs/workbench/contrib/scm/common/scm.ts | 6 +- .../contrib/scm/common/scmService.ts | 81 ++++++++++++---- 6 files changed, 202 insertions(+), 24 deletions(-) diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index f7b8d5ed64d..781067fe863 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -97,3 +97,95 @@ export class HistoryNavigator implements INavigator { return elements; } } + +interface HistoryNode { + value: T; + previous: HistoryNode | undefined; + next: HistoryNode | undefined; +} + +export class HistoryNavigator2 { + + private head: HistoryNode; + private tail: HistoryNode; + private cursor: HistoryNode; + private size: number; + + constructor(history: readonly T[], private capacity: number = 10) { + if (history.length < 1) { + throw new Error('not supported'); + } + + this.size = 1; + this.head = this.tail = this.cursor = { + value: history[0], + previous: undefined, + next: undefined + }; + + for (let i = 1; i < history.length; i++) { + this.add(history[i]); + } + } + + add(value: T): void { + const node: HistoryNode = { + value, + previous: this.tail, + next: undefined + }; + + this.tail.next = node; + this.tail = node; + this.cursor = this.tail; + this.size++; + + while (this.size > this.capacity) { + this.head = this.head.next!; + this.head.previous = undefined; + this.size--; + } + } + + replaceLast(value: T): void { + this.tail.value = value; + } + + isAtEnd(): boolean { + return this.cursor === this.tail; + } + + current(): T { + return this.cursor.value; + } + + previous(): T { + if (this.cursor.previous) { + this.cursor = this.cursor.previous; + } + + return this.cursor.value; + } + + next(): T { + if (this.cursor.next) { + this.cursor = this.cursor.next; + } + + return this.cursor.value; + } + + resetCursor(): T { + this.cursor = this.tail; + return this.cursor.value; + } + + *[Symbol.iterator](): Iterator { + let node: HistoryNode | undefined = this.head; + + while (node) { + yield node.value; + node = node.next; + } + } +} diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 9594c9137d6..0b70be0ca26 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -397,7 +397,7 @@ export class MainThreadSCM implements MainThreadSCMShape { return; } - repository.input.value = value; + repository.input.setValue(value, false); } $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index bf905250784..fd9bffda2f2 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -221,7 +221,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ if (!repository || !repository.provider.acceptInputCommand) { return Promise.resolve(null); } - const id = repository.provider.acceptInputCommand.id; const args = repository.provider.acceptInputCommand.arguments; @@ -230,6 +229,34 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'scm.viewNextCommit', + description: { description: localize('scm view next commit', "SCM: View Next Commit"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.has('scmInputIsInLastLine'), + primary: KeyCode.DownArrow, + handler: accessor => { + const contextKeyService = accessor.get(IContextKeyService); + const context = contextKeyService.getContext(document.activeElement); + const repository = context.getValue('scmRepository'); + repository?.input.showNextHistoryValue(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'scm.viewPriorCommit', + description: { description: localize('scm view prior commit', "SCM: View Prior Commit"), args: [] }, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.has('scmInputIsInFirstLine'), + primary: KeyCode.UpArrow, + handler: accessor => { + const contextKeyService = accessor.get(IContextKeyService); + const context = contextKeyService.getContext(document.activeElement); + const repository = context.getValue('scmRepository'); + repository?.input.showPreviousHistoryValue(); + } +}); + CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider: ISCMProvider) => { if (!provider || !provider.rootUri) { return; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 89ae5f47db2..f175011ae4f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1307,14 +1307,14 @@ class SCMInputWidget extends Disposable { if (value === textModel.getValue()) { // circuit breaker return; } - textModel.setValue(input.value); + textModel.setValue(value); this.inputEditor.setPosition(textModel.getFullModelRange().getEndPosition()); })); // Keep API in sync with model, update placeholder visibility and validate const updatePlaceholderVisibility = () => this.placeholderTextContainer.classList.toggle('hidden', textModel.getValueLength() > 0); this.repositoryDisposables.add(textModel.onDidChangeContent(() => { - input.value = textModel.getValue(); + input.setValue(textModel.getValue(), true); updatePlaceholderVisibility(); triggerValidation(); })); @@ -1433,6 +1433,18 @@ class SCMInputWidget extends Disposable { this.validationDisposable.dispose(); })); + const firstLineKey = contextKeyService2.createKey('scmInputIsInFirstLine', false); + const lastLineKey = contextKeyService2.createKey('scmInputIsInLastLine', false); + + this._register(this.inputEditor.onDidChangeCursorPosition(({ position }) => { + const viewModel = this.inputEditor._getViewModel()!; + const lastLineNumber = viewModel.getLineCount(); + const viewPosition = viewModel.coordinatesConverter.convertModelPositionToViewPosition(position); + + firstLineKey.set(viewPosition.lineNumber === 1); + lastLineKey.set(viewPosition.lineNumber === lastLineNumber); + })); + const onInputFontFamilyChanged = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.inputFontFamily')); this._register(onInputFontFamilyChanged(() => this.inputEditor.updateOptions({ fontFamily: this.getInputEditorFontFamily() }))); diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index aa3f57f7849..0f4640b971b 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -86,7 +86,8 @@ export interface IInputValidator { export interface ISCMInput { readonly repository: ISCMRepository; - value: string; + readonly value: string; + setValue(value: string, fromKeyboard: boolean): void; readonly onDidChange: Event; placeholder: string; @@ -97,6 +98,9 @@ export interface ISCMInput { visible: boolean; readonly onDidChangeVisibility: Event; + + showNextHistoryValue(): void; + showPreviousHistoryValue(): void; } export interface ISCMRepository extends IDisposable { diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 50282ded800..b6dcb82722f 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -8,7 +8,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { HistoryNavigator2 } from 'vs/base/common/history'; class SCMInput implements ISCMInput { @@ -18,21 +19,6 @@ class SCMInput implements ISCMInput { return this._value; } - set value(value: string) { - if (value === this._value) { - return; - } - - this._value = value; - - if (this.repository.provider.rootUri) { - const key = `scm/input:${this.repository.provider.label}:${this.repository.provider.rootUri.path}`; - this.storageService.store(key, value, StorageScope.WORKSPACE); - } - - this._onDidChange.fire(value); - } - private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; @@ -79,14 +65,71 @@ class SCMInput implements ISCMInput { private readonly _onDidChangeValidateInput = new Emitter(); readonly onDidChangeValidateInput: Event = this._onDidChangeValidateInput.event; + private historyNavigator: HistoryNavigator2; + constructor( readonly repository: ISCMRepository, @IStorageService private storageService: IStorageService ) { - if (this.repository.provider.rootUri) { - const key = `scm/input:${this.repository.provider.label}:${this.repository.provider.rootUri.path}`; - this._value = this.storageService.get(key, StorageScope.WORKSPACE, ''); + const historyKey = `scm/input:${this.repository.provider.label}:${this.repository.provider.rootUri?.path}`; + let history: string[] | undefined; + let rawHistory = this.storageService.get(historyKey, StorageScope.WORKSPACE, ''); + + if (rawHistory) { + try { + history = JSON.parse(rawHistory); + } catch { + // noop + } } + + if (!history || history.length === 0) { + history = [this._value]; + } else { + this._value = history[history.length - 1]; + } + + this.historyNavigator = new HistoryNavigator2(history, 50); + + this.storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + if (this.historyNavigator.isAtEnd()) { + this.historyNavigator.replaceLast(this._value); + } + + if (this.repository.provider.rootUri) { + this.storageService.store(historyKey, JSON.stringify([...this.historyNavigator]), StorageScope.WORKSPACE); + } + } + }); + } + + setValue(value: string, transient: boolean) { + if (value === this._value) { + return; + } + + if (!transient) { + this.historyNavigator.replaceLast(this._value); + this.historyNavigator.add(value); + } + + this._value = value; + this._onDidChange.fire(value); + } + + showNextHistoryValue(): void { + const value = this.historyNavigator.next(); + this.setValue(value, true); + } + + showPreviousHistoryValue(): void { + if (this.historyNavigator.isAtEnd()) { + this.historyNavigator.replaceLast(this._value); + } + + const value = this.historyNavigator.previous(); + this.setValue(value, true); } } From 0ccd7a95a3852713f70a1c89ddc9da78ee3e1d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 20 Oct 2020 10:29:36 +0200 Subject: [PATCH 073/121] remove deprecated code usages --- .../editor/contrib/parameterHints/parameterHintsWidget.ts | 6 +++--- src/vs/workbench/contrib/views/browser/treeView.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index e7ef8cad81a..2d0d4bcd349 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -192,7 +192,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } const multiple = hints.signatures.length > 1; - dom.toggleClass(this.domNodes.element, 'multiple', multiple); + this.domNodes.element.classList.toggle('multiple', multiple); this.keyMultipleSignatures.set(multiple); this.domNodes.signature.innerText = ''; @@ -243,8 +243,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { const hasDocs = this.hasDocs(signature, activeParameter); - dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs); - dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); + this.domNodes.signature.classList.toggle('has-docs', hasDocs); + this.domNodes.docs.classList.toggle('empty', !hasDocs); this.domNodes.overloads.textContent = String(hints.activeSignature + 1).padStart(hints.signatures.length.toString().length, '0') + '/' + hints.signatures.length; diff --git a/src/vs/workbench/contrib/views/browser/treeView.ts b/src/vs/workbench/contrib/views/browser/treeView.ts index f11e9d412a5..2df022d0558 100644 --- a/src/vs/workbench/contrib/views/browser/treeView.ts +++ b/src/vs/workbench/contrib/views/browser/treeView.ts @@ -896,7 +896,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer Date: Tue, 20 Oct 2020 10:53:52 +0200 Subject: [PATCH 074/121] web - allow to dispose the workbench This will remove any unload veto handlers. --- src/vs/platform/lifecycle/common/lifecycle.ts | 12 ++++- .../lifecycle/common/lifecycleService.ts | 5 ++ src/vs/workbench/browser/web.main.ts | 7 ++- .../contrib/backup/browser/backupTracker.ts | 2 +- .../host/browser/browserHostService.ts | 2 +- .../lifecycle/browser/lifecycleService.ts | 48 ++++++++++++++----- .../electron-sandbox/lifecycleService.ts | 4 ++ .../browser/browserTextFileService.ts | 2 +- .../test/browser/workbenchTestServices.ts | 4 ++ src/vs/workbench/workbench.web.api.ts | 26 +++++++--- 10 files changed, 88 insertions(+), 24 deletions(-) diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 5d047b97813..b61ad20aac0 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -163,6 +163,15 @@ export interface ILifecycleService { * has started. */ when(phase: LifecyclePhase): Promise; + + /** + * Triggers a shutdown of the workbench. Depending on native or web, this can have + * different implementations and behaviour. + * + * **Note:** this should normally not be called. See related methods in `IHostService` + * and `INativeHostService` to close a window or quit the application. + */ + shutdown(): void; } export const NullLifecycleService: ILifecycleService = { @@ -176,7 +185,8 @@ export const NullLifecycleService: ILifecycleService = { phase: LifecyclePhase.Restored, startupKind: StartupKind.NewWindow, - when() { return Promise.resolve(); } + async when() { }, + shutdown() { } }; // Shared veto handling across main and renderer diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/platform/lifecycle/common/lifecycleService.ts index fed0b6580e5..c19c0d81500 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/platform/lifecycle/common/lifecycleService.ts @@ -71,4 +71,9 @@ export abstract class AbstractLifecycleService extends Disposable implements ILi await barrier.wait(); } + + /** + * Subclasses to implement the explicit shutdown method. + */ + abstract shutdown(): void; } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 50755cf112a..704a1a90faf 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -52,6 +52,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; import { UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; class BrowserMain extends Disposable { @@ -97,11 +98,13 @@ class BrowserMain extends Disposable { // Return API Facade return instantiationService.invokeFunction(accessor => { const commandService = accessor.get(ICommandService); + const lifecycleService = accessor.get(ILifecycleService); return { commands: { executeCommand: (command, ...args) => commandService.executeCommand(command, ...args) - } + }, + shutdown: () => lifecycleService.shutdown() }; }); } @@ -127,7 +130,7 @@ class BrowserMain extends Disposable { // Workbench Lifecycle this._register(workbench.onBeforeShutdown(event => { if (storageService.hasPendingUpdate) { - console.warn('Unload prevented: pending storage update'); + console.warn('Unload veto: pending storage update'); event.veto(true); // prevent data loss from pending storage update } })); diff --git a/src/vs/workbench/contrib/backup/browser/backupTracker.ts b/src/vs/workbench/contrib/backup/browser/backupTracker.ts index f4be13d34b9..12b675bd654 100644 --- a/src/vs/workbench/contrib/backup/browser/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/browser/backupTracker.ts @@ -72,7 +72,7 @@ export class BrowserBackupTracker extends BackupTracker implements IWorkbenchCon for (const dirtyWorkingCopy of dirtyWorkingCopies) { if (!this.backupFileService.hasBackupSync(dirtyWorkingCopy.resource, this.getContentVersion(dirtyWorkingCopy))) { - console.warn('Unload prevented: pending backups'); + console.warn('Unload veto: pending backups'); return true; // dirty without backup: veto } } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index dea4121181d..589f6d3be39 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -99,7 +99,7 @@ export class BrowserHostService extends Disposable implements IHostService { // Veto is setting is configured as such and we are not // expecting a navigation that was triggered by the user if (!this.signalExpectedShutdown && this.configurationService.getValue('window.confirmBeforeClose')) { - console.warn('Unload prevented: window.confirmBeforeClose=true'); + console.warn('Unload veto: window.confirmBeforeClose=true'); e.veto(true); } diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index af642f25aee..8c69068f3fc 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -8,11 +8,15 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; import { localize } from 'vs/nls'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { addDisposableListener } from 'vs/base/browser/dom'; export class BrowserLifecycleService extends AbstractLifecycleService { declare readonly _serviceBrand: undefined; + private beforeUnloadDisposable: IDisposable | undefined = undefined; + constructor( @ILogService readonly logService: ILogService ) { @@ -22,32 +26,54 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - window.addEventListener('beforeunload', e => this.onBeforeUnload(e)); + + // beforeUnload + this.beforeUnloadDisposable = addDisposableListener(window, 'beforeunload', (e: BeforeUnloadEvent) => this.onBeforeUnload(e)); } private onBeforeUnload(event: BeforeUnloadEvent): void { + this.logService.info('[lifecycle] onBeforeUnload triggered'); + + this.doShutdown(() => { + // Veto handling + event.preventDefault(); + event.returnValue = localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again."); + }); + } + + shutdown(): void { + this.logService.info('[lifecycle] shutdown triggered'); + + // Remove beforeunload listener that would prevent shutdown + this.beforeUnloadDisposable?.dispose(); + + // Handle shutdown without veto support + this.doShutdown(); + } + + private doShutdown(handleVeto?: () => void): void { const logService = this.logService; - logService.info('[lifecycle] onBeforeUnload triggered'); let veto = false; // Before Shutdown this._onBeforeShutdown.fire({ veto(value) { - if (value === true) { - veto = true; - } else if (value instanceof Promise && !veto) { - logService.error('[lifecycle] Long running onBeforeShutdown currently not supported in the web'); - veto = true; + if (typeof handleVeto === 'function') { + if (value === true) { + veto = true; + } else if (value instanceof Promise && !veto) { + logService.error('[lifecycle] Long running onBeforeShutdown currently not supported in the web'); + veto = true; + } } }, reason: ShutdownReason.QUIT }); - // Veto: signal back to browser by returning a non-falsify return value - if (veto) { - event.preventDefault(); - event.returnValue = localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again."); + // Veto: handle if provided + if (veto && typeof handleVeto === 'function') { + handleVeto(); return; } diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index 1d92faacfc8..cd7d99f09ad 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -156,6 +156,10 @@ export class NativeLifecycleService extends AbstractLifecycleService { onUnexpectedError(error); } + + shutdown(): void { + this.nativeHostService.closeWindow(); + } } registerSingleton(ILifecycleService, NativeLifecycleService); diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index e53764eb2ef..589876c2cab 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -19,7 +19,7 @@ export class BrowserTextFileService extends AbstractTextFileService { protected onBeforeShutdown(reason: ShutdownReason): boolean { if (this.files.models.some(model => model.hasState(TextFileEditorModelState.PENDING_SAVE))) { - console.warn('Unload prevented: pending file saves'); + console.warn('Unload veto: pending file saves'); return true; // files are pending to be saved: veto } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 3527de197e4..1249176064e 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -932,6 +932,10 @@ export class TestLifecycleService implements ILifecycleService { } fireWillShutdown(event: BeforeShutdownEvent): void { this._onBeforeShutdown.fire(event); } + + shutdown(): void { + this.fireShutdown(); + } } export class TestTextResourceConfigurationService implements ITextResourceConfigurationService { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index b21da0e7956..16accfae70b 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -13,7 +13,7 @@ import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlServi import { LogLevel } from 'vs/platform/log/common/log'; import { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/browser/updateService'; import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IProductConfiguration } from 'vs/platform/product/common/productService'; @@ -444,7 +444,8 @@ interface IWorkbenchConstructionOptions { interface IWorkbench { commands: { executeCommand(command: string, ...args: any[]): Promise; - } + }, + shutdown: () => void; } /** @@ -456,7 +457,7 @@ interface IWorkbench { let created = false; let workbenchPromiseResolve: Function; const workbenchPromise = new Promise(resolve => workbenchPromiseResolve = resolve); -async function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { +function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): IDisposable { // Mark start of workbench mark('didLoadWorkbenchMain'); @@ -470,10 +471,6 @@ async function create(domElement: HTMLElement, options: IWorkbenchConstructionOp created = true; } - // Startup workbench and resolve waiters - const workbench = await main(domElement, options); - workbenchPromiseResolve(workbench); - // Register commands if any if (Array.isArray(options.commands)) { for (const command of options.commands) { @@ -484,6 +481,21 @@ async function create(domElement: HTMLElement, options: IWorkbenchConstructionOp }); } } + + // Startup workbench and resolve waiters + let instantiatedWorkbench: IWorkbench | undefined = undefined; + main(domElement, options).then(workbench => { + instantiatedWorkbench = workbench; + workbenchPromiseResolve(workbench); + }); + + return toDisposable(() => { + if (instantiatedWorkbench) { + instantiatedWorkbench.shutdown(); + } else { + workbenchPromise.then(instantiatedWorkbench => instantiatedWorkbench.shutdown()); + } + }); } From cf9ac85214c3f1d3d0b80cc503ff7498f2b3ea2f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 10:55:42 +0200 Subject: [PATCH 075/121] Remove usage of dom.addClasses (for #103454) --- src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index ed645f8a069..64b8e8c98ee 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -8,7 +8,6 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Widget } from 'vs/base/browser/ui/widget'; import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; -import { addClasses } from 'vs/base/browser/dom'; /** * The arrow image size. @@ -62,7 +61,7 @@ export class ScrollbarArrow extends Widget { this.domNode = document.createElement('div'); this.domNode.className = opts.className; - addClasses(this.domNode, opts.icon.classNames); + this.domNode.classList.add(...opts.icon.classNamesArray); this.domNode.style.position = 'absolute'; this.domNode.style.width = ARROW_IMG_SIZE + 'px'; From 0f82a7a9f218f1bd87087bc1fa3aab7d3a1d0bd6 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Oct 2020 12:06:30 +0200 Subject: [PATCH 076/121] call stack: show running when call stack empty and only one session present fixes #108351 --- .../contrib/debug/browser/callStackView.ts | 30 +++++++++++-------- .../debug/browser/media/debugViewlet.css | 4 +-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 272a59189ad..9766d9e88b9 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -111,8 +111,8 @@ async function expandTo(session: IDebugSession, tree: WorkbenchCompressibleAsync } export class CallStackView extends ViewPane { - private pauseMessage!: HTMLSpanElement; - private pauseMessageLabel!: HTMLSpanElement; + private stateMessage!: HTMLSpanElement; + private stateMessageLabel!: HTMLSpanElement; private onCallStackChangeScheduler: RunOnceScheduler; private needsRefresh = false; private ignoreSelectionChangedEvent = false; @@ -156,15 +156,19 @@ export class CallStackView extends ViewPane { const thread = sessions.length === 1 && sessions[0].getAllThreads().length === 1 ? sessions[0].getAllThreads()[0] : undefined; if (thread && thread.stoppedDetails) { - this.pauseMessageLabel.textContent = thread.stateLabel; - this.pauseMessageLabel.title = thread.stateLabel; - this.pauseMessageLabel.classList.toggle('exception', thread.stoppedDetails.reason === 'exception'); - this.pauseMessage.hidden = false; - this.updateActions(); + this.stateMessageLabel.textContent = thread.stateLabel; + this.stateMessageLabel.title = thread.stateLabel; + this.stateMessageLabel.classList.toggle('exception', thread.stoppedDetails.reason === 'exception'); + this.stateMessage.hidden = false; + } else if (sessions.length === 1 && sessions[0].state === State.Running) { + this.stateMessageLabel.textContent = nls.localize({ key: 'running', comment: ['indicates state'] }, "Running"); + this.stateMessageLabel.title = sessions[0].getLabel(); + this.stateMessageLabel.classList.remove('exception'); + this.stateMessage.hidden = false; } else { - this.pauseMessage.hidden = true; - this.updateActions(); + this.stateMessage.hidden = true; } + this.updateActions(); this.needsRefresh = false; this.dataSource.deemphasizedStackFramesToShow = []; @@ -195,13 +199,13 @@ export class CallStackView extends ViewPane { const titleContainer = dom.append(container, $('.debug-call-stack-title')); super.renderHeaderTitle(titleContainer, this.options.title); - this.pauseMessage = dom.append(titleContainer, $('span.pause-message')); - this.pauseMessage.hidden = true; - this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); + this.stateMessage = dom.append(titleContainer, $('span.state-message')); + this.stateMessage.hidden = true; + this.stateMessageLabel = dom.append(this.stateMessage, $('span.label')); } getActions(): IAction[] { - if (this.pauseMessage.hidden) { + if (this.stateMessage.hidden) { return [new CollapseAction(() => this.tree, true, 'explorer-action codicon-collapse-all')]; } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 78195e5fd2c..e0b6acb323a 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -98,7 +98,7 @@ width: 100%; } -.debug-pane .debug-call-stack-title > .pause-message { +.debug-pane .debug-call-stack-title > .state-message { flex: 1; text-align: right; text-overflow: ellipsis; @@ -107,7 +107,7 @@ margin: 0px 10px; } -.debug-pane .debug-call-stack-title > .pause-message > .label { +.debug-pane .debug-call-stack-title > .state-message > .label { border-radius: 3px; padding: 1px 2px; font-size: 9px; From 083bdb3241163c8ecb14877ab38e5501fb8e933a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Oct 2020 12:27:30 +0200 Subject: [PATCH 077/121] Port display polish --- .../contrib/remote/browser/tunnelView.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 92a5455dc2b..5ef37918355 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { URI } from 'vs/base/common/uri'; -import { isLocalhost, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -403,17 +403,20 @@ class TunnelItem implements ITunnelItem { get label(): string { if (this.name) { return nls.localize('remote.tunnelsView.forwardedPortLabel0', "{0}", this.name); - } else if (this.localAddress && !isLocalhost(this.remoteHost)) { - return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}:{1} \u2192 {2}", this.remoteHost, this.remotePort, this.localAddress); } else if (this.localAddress) { - return nls.localize('remote.tunnelsView.forwardedPortLabel3', "{0} \u2192 {1}", this.remotePort, this.localAddress); - } else if (!isLocalhost(this.remoteHost)) { - return nls.localize('remote.tunnelsView.forwardedPortLabel4', "{0}:{1}", this.remoteHost, this.remotePort); + return nls.localize('remote.tunnelsView.forwardedPortLabel1', "{0} \u2192 {1}", this.remotePort, TunnelItem.compactLongAddress(this.localAddress)); } else { - return nls.localize('remote.tunnelsView.forwardedPortLabel5', "{0}", this.remotePort); + return nls.localize('remote.tunnelsView.forwardedPortLabel2', "{0}", this.remotePort); } } + private static compactLongAddress(address: string): string { + if (address.length < 15) { + return address; + } + return new URL(address).host; + } + set description(description: string | undefined) { this._description = description; } @@ -422,7 +425,7 @@ class TunnelItem implements ITunnelItem { if (this._description) { return this._description; } else if (this.name) { - return nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} to {1}", this.remotePort, this.localAddress); + return nls.localize('remote.tunnelsView.forwardedPortDescription0', "{0} \u2192 {1}", this.remotePort, this.localAddress); } return undefined; } From ad8de4eb13bcf1da0d82048f3fd30fe7d8684ac8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Oct 2020 12:32:56 +0200 Subject: [PATCH 078/121] Fix auto port forwarding when not desktop The setting for disabling auto forwarding was desktop only, which is a bug! --- .../workbench/contrib/remote/common/remote.contribution.ts | 5 +++++ .../contrib/remote/electron-sandbox/remote.contribution.ts | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index ca725098357..6192bb88193 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -124,5 +124,10 @@ Registry.as(ConfigurationExtensions.Configuration) 'pub.name': ['ui'] } }, + 'remote.autoForwardPorts': { + type: 'boolean', + markdownDescription: localize('remote.autoForwardPorts', "When enabled, URLs with ports (ex. `http://127.0.0.1:3000`) that are printed to your terminals are automatically forwarded."), + default: true + } } }); diff --git a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts index 039ec302a61..ac7c706301a 100644 --- a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts @@ -173,11 +173,6 @@ Registry.as(ConfigurationExtensions.Configuration) markdownDescription: nls.localize('remote.restoreForwardedPorts', "Restores the ports you forwarded in a workspace."), default: false }, - 'remote.autoForwardPorts': { - type: 'boolean', - markdownDescription: nls.localize('remote.autoForwardPorts', "When enabled, URLs with ports (ex. `http://127.0.0.1:3000`) that are printed to your terminals are automatically forwarded."), - default: true - } } }); From 861b7a989a1adc3ee76b4545a62b561516f281e1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Oct 2020 12:34:42 +0200 Subject: [PATCH 079/121] Move restoreForwardedPorts setting to common It was never desktop specific, this is a bug --- .../workbench/contrib/remote/common/remote.contribution.ts | 5 +++++ .../contrib/remote/electron-sandbox/remote.contribution.ts | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 6192bb88193..6ead6198b14 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -124,6 +124,11 @@ Registry.as(ConfigurationExtensions.Configuration) 'pub.name': ['ui'] } }, + 'remote.restoreForwardedPorts': { + type: 'boolean', + markdownDescription: localize('remote.restoreForwardedPorts', "Restores the ports you forwarded in a workspace."), + default: false + }, 'remote.autoForwardPorts': { type: 'boolean', markdownDescription: localize('remote.autoForwardPorts', "When enabled, URLs with ports (ex. `http://127.0.0.1:3000`) that are printed to your terminals are automatically forwarded."), diff --git a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts index ac7c706301a..f0d171d7ed6 100644 --- a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts @@ -168,11 +168,6 @@ Registry.as(ConfigurationExtensions.Configuration) markdownDescription: nls.localize('remote.downloadExtensionsLocally', "When enabled extensions are downloaded locally and installed on remote."), default: false }, - 'remote.restoreForwardedPorts': { - type: 'boolean', - markdownDescription: nls.localize('remote.restoreForwardedPorts', "Restores the ports you forwarded in a workspace."), - default: false - }, } }); From 71832b5b09c5bb6caba5eda3641c55ae7858751a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 15:03:26 +0200 Subject: [PATCH 080/121] web - disable linear-gradient in safari for now (#108996) --- src/vs/workbench/browser/parts/editor/tabsTitleControl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 8a7695ce6bd..0bcf1dee370 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -47,6 +47,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPath, win32, posix } from 'vs/base/common/path'; import { insert } from 'vs/base/common/arrays'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { isSafari } from 'vs/base/browser/browser'; interface IEditorInputLabel { name?: string; @@ -1631,7 +1632,10 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = } // Fade out styles via linear gradient (when tabs are set to shrink) - if (theme.type !== 'hc') { + // But not when: + // - in high contrast theme + // - on Safari (https://github.com/microsoft/vscode/issues/108996) + if (theme.type !== 'hc' && !isSafari) { const workbenchBackground = WORKBENCH_BACKGROUND(theme); const editorBackgroundColor = theme.getColor(editorBackground); const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND); From b3f4f13920b9d9e80348c287eacd778fcc27c999 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 15:29:35 +0200 Subject: [PATCH 081/121] Fix unit test --- .vscode/settings.json | 3 +++ .../services/keybinding/common/macLinuxKeyboardMapper.ts | 2 +- .../services/keybinding/test/electron-browser/mac_en_us.txt | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index ec4b9ec2be4..4b2a9059553 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,6 +67,9 @@ }, "gulp.autoDetect": "off", "files.insertFinalNewline": true, + "[plaintext]": { + "files.insertFinalNewline": false, + }, "[typescript]": { "editor.defaultFormatter": "vscode.typescript-language-features" }, diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index 7e2d9d6d17c..c1b995107de 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -957,7 +957,7 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { } // See https://github.com/microsoft/vscode/issues/108880 - if (binding.ctrlKey && !binding.metaKey && !binding.altKey && constantKeyCode === KeyCode.US_MINUS) { + if (this._OS === OperatingSystem.Macintosh && binding.ctrlKey && !binding.metaKey && !binding.altKey && constantKeyCode === KeyCode.US_MINUS) { // ctrl+- and ctrl+shift+- render very similarly in native macOS menus, leading to confusion return null; } diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/mac_en_us.txt b/src/vs/workbench/services/keybinding/test/electron-browser/mac_en_us.txt index 7a83477293f..5881abce1cf 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/mac_en_us.txt +++ b/src/vs/workbench/services/keybinding/test/electron-browser/mac_en_us.txt @@ -345,9 +345,9 @@ isUSStandard: true | HW Code combination | Key | KeyCode combination | Pri | UI label | User settings | Electron accelerator | Dispatching string | WYSIWYG | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | Minus | - | - | | - | - | - | [Minus] | | -| Ctrl+Minus | - | Ctrl+- | | Ctrl+- | ctrl+- | Ctrl+- | ctrl+[Minus] | | +| Ctrl+Minus | - | Ctrl+- | | Ctrl+- | ctrl+- | null | ctrl+[Minus] | | | Shift+Minus | _ | Shift+- | | Shift+- | shift+- | Shift+- | shift+[Minus] | | -| Ctrl+Shift+Minus | _ | Ctrl+Shift+- | | Ctrl+Shift+- | ctrl+shift+- | Ctrl+Shift+- | ctrl+shift+[Minus] | | +| Ctrl+Shift+Minus | _ | Ctrl+Shift+- | | Ctrl+Shift+- | ctrl+shift+- | null | ctrl+shift+[Minus] | | | Alt+Minus | - | Alt+- | | Alt+- | alt+- | Alt+- | alt+[Minus] | | | Ctrl+Alt+Minus | – | Ctrl+Alt+- | | Ctrl+Alt+- | ctrl+alt+- | Ctrl+Alt+- | ctrl+alt+[Minus] | | | Shift+Alt+Minus | _ | Shift+Alt+- | | Shift+Alt+- | shift+alt+- | Shift+Alt+- | shift+alt+[Minus] | | From 8fcf778ba251131780f2a110d311161f39b97b03 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 15:34:03 +0200 Subject: [PATCH 082/121] debt - lift lifecycle service to workbench layer --- src/vs/platform/lifecycle/common/lifecycle.ts | 184 ----------------- .../api/browser/extensionHost.contribution.ts | 2 +- .../api/browser/viewsExtensionPoint.ts | 2 +- .../browser/actions/navigationActions.ts | 2 +- .../browser/actions/textInputActions.ts | 2 +- src/vs/workbench/browser/layout.ts | 2 +- .../parts/editor/editor.contribution.ts | 2 +- .../notifications/notificationsToasts.ts | 2 +- src/vs/workbench/browser/web.main.ts | 2 +- src/vs/workbench/browser/workbench.ts | 2 +- src/vs/workbench/common/actions.ts | 2 +- src/vs/workbench/common/contributions.ts | 2 +- .../backup/browser/backup.web.contribution.ts | 2 +- .../contrib/backup/browser/backupTracker.ts | 2 +- .../backup/common/backup.contribution.ts | 2 +- .../contrib/backup/common/backupRestorer.ts | 2 +- .../contrib/backup/common/backupTracker.ts | 2 +- .../electron-sandbox/backup.contribution.ts | 2 +- .../backup/electron-sandbox/backupTracker.ts | 2 +- .../electron-browser/backupTracker.test.ts | 2 +- .../browser/preview/bulkEdit.contribution.ts | 2 +- .../common/codeActions.contribution.ts | 2 +- .../codeEditor/browser/saveParticipants.ts | 2 +- .../browser/toggleMultiCursorModifier.ts | 2 +- .../electron-sandbox/selectionClipboard.ts | 2 +- .../sleepResumeRepaintMinimap.ts | 2 +- .../configurationExportHelper.contribution.ts | 2 +- .../browser/customEditor.contribution.ts | 2 +- .../debug/browser/debug.contribution.ts | 2 +- .../contrib/debug/browser/debugService.ts | 2 +- .../contrib/debug/browser/debugSession.ts | 2 +- .../browser/experiments.contribution.ts | 2 +- .../experiments/common/experimentService.ts | 2 +- .../experimentService.test.ts | 2 +- .../experimentalPrompts.test.ts | 2 +- .../extensionRecommendationsService.ts | 2 +- .../browser/extensions.contribution.ts | 2 +- .../extensions/common/extensionsUtils.ts | 2 +- .../extensions.contribution.ts | 2 +- .../extensionRecommendationsService.test.ts | 2 +- .../extensionsActions.test.ts | 2 +- .../extensionsWorkbenchService.test.ts | 2 +- .../browser/externalTerminal.contribution.ts | 2 +- .../feedback/browser/feedback.contribution.ts | 4 +- .../browser/editors/textFileEditorTracker.ts | 2 +- .../files/browser/files.contribution.ts | 2 +- .../files/common/dirtyFilesIndicator.ts | 2 +- .../format/browser/formatActionsMultiple.ts | 2 +- .../issue/browser/issue.web.contribution.ts | 2 +- .../browser/localizations.contribution.ts | 2 +- .../contrib/logs/common/logs.contribution.ts | 2 +- .../contrib/logs/common/logsDataCleaner.ts | 2 +- .../markers/browser/markers.contribution.ts | 2 +- .../markers/browser/markersFileDecorations.ts | 2 +- .../browser/contrib/marker/markerProvider.ts | 2 +- .../browser/contrib/status/editorStatus.ts | 2 +- .../notebook/browser/notebook.contribution.ts | 2 +- .../output/browser/output.contribution.ts | 2 +- .../contrib/output/browser/outputServices.ts | 2 +- .../browser/performance.contribution.ts | 2 +- .../performance/browser/perfviewEditor.ts | 2 +- .../performance.contribution.ts | 2 +- .../electron-browser/startupProfiler.ts | 2 +- .../electron-browser/startupTimings.ts | 2 +- .../browser/keyboardLayoutPicker.ts | 2 +- .../browser/preferences.contribution.ts | 2 +- .../browser/relauncher.contribution.ts | 2 +- .../contrib/remote/browser/remote.ts | 2 +- .../remote/common/remote.contribution.ts | 2 +- .../electron-sandbox/remote.contribution.ts | 2 +- .../contrib/sash/browser/sash.contribution.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../search/browser/replaceContributions.ts | 2 +- .../search/browser/search.contribution.ts | 2 +- .../browser/searchEditor.contribution.ts | 2 +- .../snippets/browser/snippetsService.ts | 2 +- .../partsSplash.contribution.ts | 2 +- .../browser/languageSurveys.contribution.ts | 2 +- .../surveys/browser/nps.contribution.ts | 2 +- .../electron-browser/tags.contribution.ts | 2 +- .../tasks/browser/abstractTaskService.ts | 2 +- .../tasks/browser/task.contribution.ts | 2 +- .../browser/telemetry.contribution.ts | 2 +- .../terminal/browser/terminalService.ts | 2 +- .../electron-browser/terminal.contribution.ts | 2 +- .../update/browser/update.contribution.ts | 2 +- .../contrib/url/browser/url.contribution.ts | 2 +- .../browser/userDataSync.contribution.ts | 2 +- .../userDataSync.contribution.ts | 2 +- .../contrib/watermark/browser/watermark.ts | 2 +- .../webview/browser/webviewIconManager.ts | 2 +- .../common/viewsWelcome.contribution.ts | 2 +- .../page/browser/welcomePage.contribution.ts | 2 +- .../welcome/page/browser/welcomePage.ts | 2 +- .../browser/telemetryOptOut.contribution.ts | 2 +- .../telemetryOptOut.contribution.ts | 2 +- .../browser/walkThrough.contribution.ts | 2 +- src/vs/workbench/electron-sandbox/window.ts | 2 +- .../electron-sandbox/accessibilityService.ts | 2 +- .../browser/configurationService.ts | 2 +- .../browser/extensionEnablementService.ts | 2 +- .../extensionEnablementService.test.ts | 2 +- .../extensions/browser/extensionService.ts | 2 +- .../extensions/browser/extensionUrlHandler.ts | 2 +- .../extensions/common/remoteExtensionHost.ts | 2 +- .../electron-browser/extensionService.ts | 2 +- .../localProcessExtensionHost.ts | 2 +- .../host/browser/browserHostService.ts | 2 +- .../integrity/node/integrityService.ts | 2 +- .../keybindingEditing.test.ts | 2 +- .../services/label/common/labelService.ts | 2 +- .../lifecycle/browser/lifecycleService.ts | 4 +- .../services/lifecycle/common/lifecycle.ts | 189 ++++++++++++++++++ .../lifecycle/common/lifecycleService.ts | 2 +- .../electron-sandbox/lifecycleService.ts | 5 +- .../log/electron-browser/logService.ts | 2 +- .../common/abstractRemoteAgentService.ts | 2 +- .../browser/browserTextFileService.ts | 2 +- .../textfile/browser/textFileService.ts | 2 +- .../common/textFileEditorModelManager.ts | 2 +- .../electron-browser/nativeTextFileService.ts | 2 +- .../services/timer/browser/timerService.ts | 2 +- .../timer/electron-sandbox/timerService.ts | 2 +- .../services/userData/browser/userDataInit.ts | 2 +- .../browser/userDataSyncWorkbenchService.ts | 2 +- .../workspaceEditingService.ts | 2 +- .../browser/parts/editor/editorGroups.test.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- .../electron-browser/workbenchTestServices.ts | 2 +- 129 files changed, 320 insertions(+), 314 deletions(-) create mode 100644 src/vs/workbench/services/lifecycle/common/lifecycle.ts rename src/vs/{platform => workbench/services}/lifecycle/common/lifecycleService.ts (98%) diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index b61ad20aac0..c67d56a0f16 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -3,192 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { isThenable } from 'vs/base/common/async'; -export const ILifecycleService = createDecorator('lifecycleService'); - -/** - * An event that is send out when the window is about to close. Clients have a chance to veto - * the closing by either calling veto with a boolean "true" directly or with a promise that - * resolves to a boolean. Returning a promise is useful in cases of long running operations - * on shutdown. - * - * Note: It is absolutely important to avoid long running promises if possible. Please try hard - * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! - */ -export interface BeforeShutdownEvent { - - /** - * Allows to veto the shutdown. The veto can be a long running operation but it - * will block the application from closing. - */ - veto(value: boolean | Promise): void; - - /** - * The reason why the application will be shutting down. - */ - readonly reason: ShutdownReason; -} - -/** - * An event that is send out when the window closes. Clients have a chance to join the closing - * by providing a promise from the join method. Returning a promise is useful in cases of long - * running operations on shutdown. - * - * Note: It is absolutely important to avoid long running promises if possible. Please try hard - * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! - */ -export interface WillShutdownEvent { - - /** - * Allows to join the shutdown. The promise can be a long running operation but it - * will block the application from closing. - */ - join(promise: Promise): void; - - /** - * The reason why the application is shutting down. - */ - readonly reason: ShutdownReason; -} - -export const enum ShutdownReason { - - /** Window is closed */ - CLOSE = 1, - - /** Application is quit */ - QUIT = 2, - - /** Window is reloaded */ - RELOAD = 3, - - /** Other configuration loaded into window */ - LOAD = 4 -} - -export const enum StartupKind { - NewWindow = 1, - ReloadedWindow = 3, - ReopenedWindow = 4, -} - -export function StartupKindToString(startupKind: StartupKind): string { - switch (startupKind) { - case StartupKind.NewWindow: return 'NewWindow'; - case StartupKind.ReloadedWindow: return 'ReloadedWindow'; - case StartupKind.ReopenedWindow: return 'ReopenedWindow'; - } -} - -export const enum LifecyclePhase { - - /** - * The first phase signals that we are about to startup getting ready. - */ - Starting = 1, - - /** - * Services are ready and the view is about to restore its state. - */ - Ready = 2, - - /** - * Views, panels and editors have restored. For editors this means, that - * they show their contents fully. - */ - Restored = 3, - - /** - * The last phase after views, panels and editors have restored and - * some time has passed (few seconds). - */ - Eventually = 4 -} - -export function LifecyclePhaseToString(phase: LifecyclePhase) { - switch (phase) { - case LifecyclePhase.Starting: return 'Starting'; - case LifecyclePhase.Ready: return 'Ready'; - case LifecyclePhase.Restored: return 'Restored'; - case LifecyclePhase.Eventually: return 'Eventually'; - } -} - -/** - * A lifecycle service informs about lifecycle events of the - * application, such as shutdown. - */ -export interface ILifecycleService { - - readonly _serviceBrand: undefined; - - /** - * Value indicates how this window got loaded. - */ - readonly startupKind: StartupKind; - - /** - * A flag indicating in what phase of the lifecycle we currently are. - */ - phase: LifecyclePhase; - - /** - * Fired before shutdown happens. Allows listeners to veto against the - * shutdown to prevent it from happening. - * - * The event carries a shutdown reason that indicates how the shutdown was triggered. - */ - readonly onBeforeShutdown: Event; - - /** - * Fired when no client is preventing the shutdown from happening (from onBeforeShutdown). - * Can be used to save UI state even if that is long running through the WillShutdownEvent#join() - * method. - * - * The event carries a shutdown reason that indicates how the shutdown was triggered. - */ - readonly onWillShutdown: Event; - - /** - * Fired when the shutdown is about to happen after long running shutdown operations - * have finished (from onWillShutdown). This is the right place to dispose resources. - */ - readonly onShutdown: Event; - - /** - * Returns a promise that resolves when a certain lifecycle phase - * has started. - */ - when(phase: LifecyclePhase): Promise; - - /** - * Triggers a shutdown of the workbench. Depending on native or web, this can have - * different implementations and behaviour. - * - * **Note:** this should normally not be called. See related methods in `IHostService` - * and `INativeHostService` to close a window or quit the application. - */ - shutdown(): void; -} - -export const NullLifecycleService: ILifecycleService = { - - _serviceBrand: undefined, - - onBeforeShutdown: Event.None, - onWillShutdown: Event.None, - onShutdown: Event.None, - - phase: LifecyclePhase.Restored, - startupKind: StartupKind.NewWindow, - - async when() { }, - shutdown() { } -}; - // Shared veto handling across main and renderer export function handleVetos(vetos: (boolean | Promise)[], onError: (error: Error) => void): Promise { if (vetos.length === 0) { diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 9e264fb33b9..a4df8523631 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -6,7 +6,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; // --- other interested parties import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint'; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index f88bdb42230..d6a1d810a08 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -13,7 +13,7 @@ import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'; diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index b9198a1e02f..7344a3a29b3 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -18,7 +18,7 @@ import { Direction } from 'vs/base/browser/ui/grid/grid'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isAncestor } from 'vs/base/browser/dom'; diff --git a/src/vs/workbench/browser/actions/textInputActions.ts b/src/vs/workbench/browser/actions/textInputActions.ts index 33856f6f6cf..8b63f052b9c 100644 --- a/src/vs/workbench/browser/actions/textInputActions.ts +++ b/src/vs/workbench/browser/actions/textInputActions.ts @@ -11,7 +11,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { EventHelper } from 'vs/base/browser/dom'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isNative } from 'vs/base/common/platform'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 02e4ac93844..7dda73f66c7 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -22,7 +22,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, IPath } from 'vs/platform/windows/common/windows'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEditor } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 31f947d8e1b..05f02021e20 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -46,7 +46,7 @@ import { OpenWorkspaceButtonContribution } from 'vs/workbench/browser/parts/edit import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { toLocalResource } from 'vs/base/common/resources'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { EditorAutoSave } from 'vs/workbench/browser/parts/editor/editorAutoSave'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 3ed4c64890f..a950e35d92a 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -19,7 +19,7 @@ import { NotificationsToastsVisibleContext, INotificationsToastController } from import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Severity, NotificationsFilter } from 'vs/platform/notification/common/notification'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IntervalCounter, timeout } from 'vs/base/common/async'; import { assertIsDefined } from 'vs/base/common/types'; diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 704a1a90faf..a5bd0cdb1bf 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -52,7 +52,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; import { UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; class BrowserMain extends Disposable { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index a340d1aff08..dce86a91142 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -21,7 +21,7 @@ import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { LifecyclePhase, ILifecycleService, WillShutdownEvent, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase, ILifecycleService, WillShutdownEvent, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter'; diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index f739d5d4b8e..de82ce88e15 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -10,7 +10,7 @@ import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/c import { SyncActionDescriptor, MenuRegistry, MenuId, ICommandAction } from 'vs/platform/actions/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index 9625606b070..56ab4b25eb4 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { runWhenIdle, IdleDeadline } from 'vs/base/common/async'; diff --git a/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts b/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts index 50f14d80aea..c68942985a5 100644 --- a/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts +++ b/src/vs/workbench/contrib/backup/browser/backup.web.contribution.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { BrowserBackupTracker } from 'vs/workbench/contrib/backup/browser/backupTracker'; // Register Backup Tracker diff --git a/src/vs/workbench/contrib/backup/browser/backupTracker.ts b/src/vs/workbench/contrib/backup/browser/backupTracker.ts index 12b675bd654..e28ef9cd9a3 100644 --- a/src/vs/workbench/contrib/backup/browser/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/browser/backupTracker.ts @@ -7,7 +7,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopy, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; diff --git a/src/vs/workbench/contrib/backup/common/backup.contribution.ts b/src/vs/workbench/contrib/backup/common/backup.contribution.ts index e51ea2d1aba..0ae3302ffaf 100644 --- a/src/vs/workbench/contrib/backup/common/backup.contribution.ts +++ b/src/vs/workbench/contrib/backup/common/backup.contribution.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { BackupRestorer } from 'vs/workbench/contrib/backup/common/backupRestorer'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; // Register Backup Restorer Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupRestorer, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/backup/common/backupRestorer.ts b/src/vs/workbench/contrib/backup/common/backupRestorer.ts index c32a30eeca2..cc08328c69c 100644 --- a/src/vs/workbench/contrib/backup/common/backupRestorer.ts +++ b/src/vs/workbench/contrib/backup/common/backupRestorer.ts @@ -9,7 +9,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { Schemas } from 'vs/base/common/network'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IUntitledTextResourceEditorInput, IEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IEditorInputWithOptions } from 'vs/workbench/common/editor'; import { toLocalResource, isEqual } from 'vs/base/common/resources'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/contrib/backup/common/backupTracker.ts b/src/vs/workbench/contrib/backup/common/backupTracker.ts index b79002d2009..d8f35fc9dae 100644 --- a/src/vs/workbench/contrib/backup/common/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/common/backupTracker.ts @@ -7,7 +7,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; -import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; export abstract class BackupTracker extends Disposable { diff --git a/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts index 43a574d8bf4..cf529c7e698 100644 --- a/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backup.contribution.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker'; // Register Backup Tracker diff --git a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts index 51cc964f498..d2c4f92fbd5 100644 --- a/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts +++ b/src/vs/workbench/contrib/backup/electron-sandbox/backupTracker.ts @@ -8,7 +8,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { ILifecycleService, LifecyclePhase, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ConfirmResult, IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index ae5b2e4a3f6..f2e134c7c25 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -31,7 +31,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura import { IWorkingCopyBackup, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; -import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index feeaaabdce3..139ee6ddbc3 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; diff --git a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts index c7969c9d1c0..36a0a7d2a17 100644 --- a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts +++ b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts @@ -5,7 +5,7 @@ import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { CodeActionsContribution, editorConfiguration } from 'vs/workbench/contrib/codeActions/common/codeActionsContribution'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 698828c06d7..f2770354531 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -28,7 +28,7 @@ import { SaveReason } from 'vs/workbench/common/editor'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution, Extensions as WorkbenchContributionsExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { getModifiedRanges } from 'vs/workbench/contrib/format/browser/formatModified'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 33d7253576c..2c340ac02b3 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -9,7 +9,7 @@ import * as platform from 'vs/base/common/platform'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts index 592605c0759..015984db6ab 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts @@ -16,7 +16,7 @@ import { IEditorContribution, Handler } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts index 46019e55ba7..f0e5225baa5 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/sleepResumeRepaintMinimap.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; diff --git a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts index e780491f3cf..127283add02 100644 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts +++ b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts @@ -6,7 +6,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { DefaultConfigurationExportHelper } from 'vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper'; diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts index 63dfa598903..3c6118f6f33 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditor.contribution.ts @@ -6,7 +6,7 @@ import { Schemas } from 'vs/base/common/network'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 009fe56029d..a26cea18ba4 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -29,7 +29,7 @@ import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; import { DebugStatusContribution } from 'vs/workbench/contrib/debug/browser/debugStatus'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; import { ADD_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID, RunToCursorAction, registerEditorActions } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 5a648e644f2..8c472ae7e4e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -11,7 +11,7 @@ import * as errors from 'vs/base/common/errors'; import severity from 'vs/base/common/severity'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 26ccaab1f06..663ff7109de 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -31,7 +31,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { localize } from 'vs/nls'; import { canceled } from 'vs/base/common/errors'; import { filterExceptionsFromTelemetry } from 'vs/workbench/contrib/debug/common/debugUtils'; diff --git a/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts index cc24a8695e8..b13961ea6b6 100644 --- a/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts +++ b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts @@ -8,7 +8,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index fa09f280e86..ee883ca582e 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -7,7 +7,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { language, OperatingSystem, OS } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index 2990fcce10f..12454df7f2a 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -21,7 +21,7 @@ import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/teleme import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index 474a88e30c5..b0e18778a2e 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, IPromptChoice, IPromptOptions, Severity } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 02e53565da8..7df6cf1b99e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -13,7 +13,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { distinct, shuffle } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { DynamicWorkspaceRecommendations } from 'vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations'; import { ExeBasedRecommendations } from 'vs/workbench/contrib/extensions/browser/exeBasedRecommendations'; import { ExperimentalRecommendations } from 'vs/workbench/contrib/extensions/browser/experimentalRecommendations'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 205ca37d121..401c1897061 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -31,7 +31,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtensionActivationProgress } from 'vs/workbench/contrib/extensions/browser/extensionsActivationProgress'; import { onUnexpectedError } from 'vs/base/common/errors'; diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 6caca15aa7b..7887fc066ba 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 21d2f7844d3..f7fb0e3bda3 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -13,7 +13,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { RuntimeExtensionsEditor, ShowRuntimeExtensionsAction, IExtensionHostProfileService, DebugExtensionHostAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction, CONTEXT_PROFILE_SESSION_STATE, SaveExtensionHostProfileAction, CONTEXT_EXTENSION_HOST_PROFILE_RECORDED } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor'; import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, ActiveEditorContext } from 'vs/workbench/common/editor'; import { ExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-browser/extensionProfileService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 40a1185bb6d..3c38a6761d5 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -37,7 +37,7 @@ import { TestExtensionEnablementService } from 'vs/workbench/services/extensionM import { IURLService } from 'vs/platform/url/common/url'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; import { NativeURLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 39582fe948d..aab73adb67d 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -47,7 +47,7 @@ import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 74a838c980f..dc1e3e16768 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -41,7 +41,7 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedPr import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; diff --git a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts index 3fd4c0fe013..7b38b373bea 100644 --- a/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution.ts @@ -25,7 +25,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { Disposable } from 'vs/base/common/lifecycle'; import { isWeb, isWindows } from 'vs/base/common/platform'; import { dirname, basename } from 'vs/base/common/path'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; const OPEN_IN_TERMINAL_COMMAND_ID = 'openInTerminal'; diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts b/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts index b6d1fa758f6..94485ad4969 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts @@ -6,6 +6,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { FeedbackStatusbarConribution } from 'vs/workbench/contrib/feedback/browser/feedbackStatusbarItem'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FeedbackStatusbarConribution, LifecyclePhase.Starting); \ No newline at end of file +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FeedbackStatusbarConribution, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts index 415393da1ef..b260fdf04b3 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts @@ -6,7 +6,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { URI } from 'vs/base/common/uri'; import { ITextFileService, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { IHostService } from 'vs/workbench/services/host/browser/host'; diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index d2e8299e07a..2c7af040d36 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -27,7 +27,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; import { ExplorerViewletViewsContribution } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ILabelService } from 'vs/platform/label/common/label'; diff --git a/src/vs/workbench/contrib/files/common/dirtyFilesIndicator.ts b/src/vs/workbench/contrib/files/common/dirtyFilesIndicator.ts index 94d0d2a6ef7..f947c9200be 100644 --- a/src/vs/workbench/contrib/files/common/dirtyFilesIndicator.ts +++ b/src/vs/workbench/contrib/files/common/dirtyFilesIndicator.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { VIEWLET_ID } from 'vs/workbench/contrib/files/common/files'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index db5448308c4..a36b0947db6 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -19,7 +19,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts index cac0f93652b..190c892525f 100644 --- a/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts +++ b/src/vs/workbench/contrib/issue/browser/issue.web.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { ICommandAction, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/productService'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES } from 'vs/workbench/common/actions'; diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index a03c99e966c..446ad446f07 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -11,7 +11,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { ConfigureLocaleAction } from 'vs/workbench/contrib/localizations/browser/localizationsActions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { IExtensionManagementService, DidInstallExtensionEvent, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index ef517f03d49..5d04d1b973f 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -18,7 +18,7 @@ import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/se import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { dirname } from 'vs/base/common/resources'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; diff --git a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts index 4f7faa55ebe..bd51bcfb753 100644 --- a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts +++ b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts @@ -8,7 +8,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { basename, dirname } from 'vs/base/common/resources'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { URI } from 'vs/base/common/uri'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; export class LogsDataCleaner extends Disposable { diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 2b89de3e117..e046732264e 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -20,7 +20,7 @@ import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } from 'vs/workbench/contrib/markers/browser/markers'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; diff --git a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts index c6eea1a6b89..c82688d11ad 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts @@ -14,7 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; class MarkersDecorationsProvider implements IDecorationsProvider { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts b/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts index 6d7d9c8a64f..dd92f64d1fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IMarkerListProvider, MarkerList, IMarkerNavigationService } from 'vs/editor/contrib/gotoError/markerNavigationService'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index 2c2c937e6cf..7d7abed7f52 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -16,7 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 920a3688e11..7cd9088308f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -20,7 +20,7 @@ import { IEditorOptions, ITextEditorOptions, IResourceEditorInput } from 'vs/pla import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index a12cfb4a6a9..7b0283691df 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -18,7 +18,7 @@ import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } fro import { LogViewer, LogViewerInput } from 'vs/workbench/contrib/output/browser/logViewer'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ViewContainer, IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 7af6a57b013..f1f11105233 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -15,7 +15,7 @@ import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLin import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; import { ILogService } from 'vs/platform/log/common/log'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; import { IViewsService } from 'vs/workbench/common/views'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts index 1262e65ffd4..a1f68bc0f03 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index ec7143e8e40..4d0b7970c90 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; -import { ILifecycleService, LifecyclePhase, StartupKindToString } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase, StartupKindToString } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; diff --git a/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts index 5a2a4182404..27611018578 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/performance.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { StartupProfiler } from './startupProfiler'; diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index 4023c6493c0..78832061e97 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -9,7 +9,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { PerfviewInput } from 'vs/workbench/contrib/performance/browser/perfviewEditor'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index 99db644f386..52f82c64dab 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -9,7 +9,7 @@ import { promisify } from 'util'; import { onUnexpectedError } from 'vs/base/common/errors'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { ILifecycleService, StartupKind, StartupKindToString } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, StartupKind, StartupKindToString } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService } from 'vs/platform/update/common/update'; diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index c8abae6ade6..cdf16c2451e 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IKeymapService, areKeyboardLayoutsEqual, parseKeyboardLayoutDescription, getKeyboardLayoutId, IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 0e1aaa73bfd..8c59d791150 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -18,7 +18,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 4c0cd2c2370..9f8bede88ca 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -16,7 +16,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; import { isMacintosh, isNative, isLinux } from 'vs/base/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProductService } from 'vs/platform/product/common/productService'; diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 2dc4800d78c..c6d98b601a3 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -38,7 +38,7 @@ import { ReconnectionWaitEvent, PersistentConnectionEventType } from 'vs/platfor import Severity from 'vs/base/common/severity'; import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems'; import { Action, IActionViewItem, IAction } from 'vs/base/common/actions'; import { isStringArray } from 'vs/base/common/types'; diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 6ead6198b14..1728f76eab2 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -5,7 +5,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILabelService, ResourceLabelFormatting } from 'vs/platform/label/common/label'; import { OperatingSystem, isWeb } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts index f0d171d7ed6..5a750342697 100644 --- a/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-sandbox/remote.contribution.ts @@ -11,7 +11,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILabelService } from 'vs/platform/label/common/label'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts index 1dfb6be879b..c4b13519c08 100644 --- a/src/vs/workbench/contrib/sash/browser/sash.contribution.ts +++ b/src/vs/workbench/contrib/sash/browser/sash.contribution.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index fd9bffda2f2..4f8c578406d 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -11,7 +11,7 @@ import { VIEWLET_ID, ISCMRepository, ISCMService, VIEW_PANE_ID, ISCMProvider, IS import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { SCMStatusController } from './activity'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/contrib/search/browser/replaceContributions.ts b/src/vs/workbench/contrib/search/browser/replaceContributions.ts index 23077d8d20a..3c32a737f8b 100644 --- a/src/vs/workbench/contrib/search/browser/replaceContributions.ts +++ b/src/vs/workbench/contrib/search/browser/replaceContributions.ts @@ -7,7 +7,7 @@ import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; import { ReplaceService, ReplacePreviewContentProvider } from 'vs/workbench/contrib/search/browser/replaceService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; export function registerContributions(): void { registerSingleton(IReplaceService, ReplaceService, true); diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index c0b8a580b54..caf6809563d 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -21,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IListService, WorkbenchListFocusContextKey, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { Registry } from 'vs/platform/registry/common/platform'; import { defaultQuickAccessContextKeyValue } from 'vs/workbench/browser/quickaccess'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index fb00b6d69e9..ad6f7917b50 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -16,7 +16,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 93b7f9272f7..3976dd126b7 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index 1777327a092..dc2cdaeaf94 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -10,7 +10,7 @@ import { getTotalHeight, getTotalWidth } from 'vs/base/browser/dom'; import { Color } from 'vs/base/common/color'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { ColorIdentifier, editorBackground, foreground } from 'vs/platform/theme/common/colorRegistry'; import { getThemeTypeSelector, IThemeService } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index fe63c8a2b3d..273f4cc82a7 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -12,7 +12,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IOpenerService } from 'vs/platform/opener/common/opener'; diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index 51ff6db0b40..7f8e32d2842 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IProductService } from 'vs/platform/product/common/productService'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/tags/electron-browser/tags.contribution.ts b/src/vs/workbench/contrib/tags/electron-browser/tags.contribution.ts index 28995ce564e..209e2690dc9 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/tags.contribution.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/tags.contribution.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { WorkspaceTags } from 'vs/workbench/contrib/tags/electron-browser/workspaceTags'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; // Register Workspace Tags Contribution Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceTags, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index c211f46c15a..7011a47f060 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -20,7 +20,7 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { LRUCache, Touch } from 'vs/base/common/map'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 190091c80f2..d938abe46af 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ProblemMatcherRegistry } from 'vs/workbench/contrib/tasks/common/problemMatcher'; diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 2b418979187..004f9217c13 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase, ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase, ILifecycleService, StartupKind } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 92b03fff2ab..f6500de4078 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, ITerminalLaunchError, ITerminalNativeWindowsDelegate } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index 263c15cb666..ca107b15e38 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -12,7 +12,7 @@ import { TerminalNativeContribution } from 'vs/workbench/contrib/terminal/electr import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { getTerminalShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; // This file contains additional desktop-only contributions on top of those in browser/ diff --git a/src/vs/workbench/contrib/update/browser/update.contribution.ts b/src/vs/workbench/contrib/update/browser/update.contribution.ts index f842ef50965..d10d7d865a1 100644 --- a/src/vs/workbench/contrib/update/browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/browser/update.contribution.ts @@ -10,7 +10,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, CheckForVSCodeUpdateAction, CONTEXT_UPDATE_STATE, SwitchProductQualityContribution } from 'vs/workbench/contrib/update/browser/update'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { StateType } from 'vs/platform/update/common/update'; diff --git a/src/vs/workbench/contrib/url/browser/url.contribution.ts b/src/vs/workbench/contrib/url/browser/url.contribution.ts index d52dd4fa67f..949c9980357 100644 --- a/src/vs/workbench/contrib/url/browser/url.contribution.ts +++ b/src/vs/workbench/contrib/url/browser/url.contribution.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { MenuId, MenuRegistry, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { IURLService } from 'vs/platform/url/common/url'; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 8701765c53c..51517c3e562 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -5,7 +5,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; import { IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts index 1f199d19b0a..bad65d90f44 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts @@ -6,7 +6,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IUserDataSyncUtilService, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { UserDataSycnUtilServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index ba50cde5456..30340ca4663 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -12,7 +12,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OpenFolderAction, OpenFileFolderAction, OpenFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ShowAllCommandsAction } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts index cf1f762e488..ba0c8561448 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewIconManager.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview'; export class WebviewIconManager { diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts index 9153e3c1a4d..3f3d30665f6 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { ViewsWelcomeContribution } from 'vs/workbench/contrib/welcome/common/viewsWelcomeContribution'; diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index e2a76c54ed0..396871dc396 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -11,7 +11,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; Registry.as(ConfigurationExtensions.Configuration) diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 6f909ab86ae..4a61a79fe44 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -25,7 +25,7 @@ import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapE import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, StartupKind } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts index d0440e20402..ac2b9b11c09 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut.contribution.ts @@ -6,6 +6,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { BrowserTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/browser/telemetryOptOut'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BrowserTelemetryOptOut, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts index 5c4f1ccf06e..9551df300cc 100644 --- a/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts +++ b/src/vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut.contribution.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryOptOut/electron-sandbox/telemetryOptOut'; Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeTelemetryOptOut, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts index 24fc64ae597..d1bdbc2db74 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -16,7 +16,7 @@ import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/c import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; Registry.as(EditorExtensions.Editors) diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 49bb6f5b93e..c847975f9da 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -27,7 +27,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts index bc250461016..cdf58500913 100644 --- a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts @@ -14,7 +14,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; interface AccessibilityMetrics { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index a2c5b9f443b..2e23db80ae7 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -29,7 +29,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index cfc85669558..d99ed62c1ab 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -21,7 +21,7 @@ import { StorageManager } from 'vs/platform/extensionManagement/common/extension import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; const SOURCE = 'IWorkbenchExtensionEnablementService'; diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 8fecb410ea6..3bf8237b1fc 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -24,7 +24,7 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; // import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 5eaec3499a3..4eb57711e6c 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -23,7 +23,7 @@ import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browse import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index ef4ffab918f..207a43ec779 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -21,7 +21,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index 1d26bf01091..9f03d2be9e6 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -19,7 +19,7 @@ import { IRemoteAuthorityResolverService, IRemoteConnectionData } from 'vs/platf import * as platform from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index f940a3610f1..49afb84a569 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -17,7 +17,7 @@ import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtension import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IHostService } from 'vs/workbench/services/host/browser/host'; diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index a41bafbb44a..5ec4c4bbae8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -23,7 +23,7 @@ import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 589f6d3be39..adc90ab9de1 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -23,7 +23,7 @@ import { parseLineAndColumnAware } from 'vs/base/common/extpath'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { BeforeShutdownEvent, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { BeforeShutdownEvent, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; /** * A workspace to open in the workbench can either be: diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 7d50386ce6f..516484cb126 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -9,7 +9,7 @@ import * as fs from 'fs'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { ChecksumPair, IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/productService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 30c1951cf4e..9efe8d8bf18 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -27,7 +27,7 @@ import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybindin import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 66b354a5a71..74dc864bf51 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -18,7 +18,7 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderW import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting, IFormatterChangeEvent } from 'vs/platform/label/common/label'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { match } from 'vs/base/common/glob'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index 8c69068f3fc..d9e5aed88ce 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; +import { AbstractLifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycleService'; import { localize } from 'vs/nls'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/services/lifecycle/common/lifecycle.ts b/src/vs/workbench/services/lifecycle/common/lifecycle.ts new file mode 100644 index 00000000000..876edba7db3 --- /dev/null +++ b/src/vs/workbench/services/lifecycle/common/lifecycle.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ILifecycleService = createDecorator('lifecycleService'); + +/** + * An event that is send out when the window is about to close. Clients have a chance to veto + * the closing by either calling veto with a boolean "true" directly or with a promise that + * resolves to a boolean. Returning a promise is useful in cases of long running operations + * on shutdown. + * + * Note: It is absolutely important to avoid long running promises if possible. Please try hard + * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! + */ +export interface BeforeShutdownEvent { + + /** + * Allows to veto the shutdown. The veto can be a long running operation but it + * will block the application from closing. + */ + veto(value: boolean | Promise): void; + + /** + * The reason why the application will be shutting down. + */ + readonly reason: ShutdownReason; +} + +/** + * An event that is send out when the window closes. Clients have a chance to join the closing + * by providing a promise from the join method. Returning a promise is useful in cases of long + * running operations on shutdown. + * + * Note: It is absolutely important to avoid long running promises if possible. Please try hard + * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence! + */ +export interface WillShutdownEvent { + + /** + * Allows to join the shutdown. The promise can be a long running operation but it + * will block the application from closing. + */ + join(promise: Promise): void; + + /** + * The reason why the application is shutting down. + */ + readonly reason: ShutdownReason; +} + +export const enum ShutdownReason { + + /** Window is closed */ + CLOSE = 1, + + /** Application is quit */ + QUIT = 2, + + /** Window is reloaded */ + RELOAD = 3, + + /** Other configuration loaded into window */ + LOAD = 4 +} + +export const enum StartupKind { + NewWindow = 1, + ReloadedWindow = 3, + ReopenedWindow = 4, +} + +export function StartupKindToString(startupKind: StartupKind): string { + switch (startupKind) { + case StartupKind.NewWindow: return 'NewWindow'; + case StartupKind.ReloadedWindow: return 'ReloadedWindow'; + case StartupKind.ReopenedWindow: return 'ReopenedWindow'; + } +} + +export const enum LifecyclePhase { + + /** + * The first phase signals that we are about to startup getting ready. + */ + Starting = 1, + + /** + * Services are ready and the view is about to restore its state. + */ + Ready = 2, + + /** + * Views, panels and editors have restored. For editors this means, that + * they show their contents fully. + */ + Restored = 3, + + /** + * The last phase after views, panels and editors have restored and + * some time has passed (few seconds). + */ + Eventually = 4 +} + +export function LifecyclePhaseToString(phase: LifecyclePhase) { + switch (phase) { + case LifecyclePhase.Starting: return 'Starting'; + case LifecyclePhase.Ready: return 'Ready'; + case LifecyclePhase.Restored: return 'Restored'; + case LifecyclePhase.Eventually: return 'Eventually'; + } +} + +/** + * A lifecycle service informs about lifecycle events of the + * application, such as shutdown. + */ +export interface ILifecycleService { + + readonly _serviceBrand: undefined; + + /** + * Value indicates how this window got loaded. + */ + readonly startupKind: StartupKind; + + /** + * A flag indicating in what phase of the lifecycle we currently are. + */ + phase: LifecyclePhase; + + /** + * Fired before shutdown happens. Allows listeners to veto against the + * shutdown to prevent it from happening. + * + * The event carries a shutdown reason that indicates how the shutdown was triggered. + */ + readonly onBeforeShutdown: Event; + + /** + * Fired when no client is preventing the shutdown from happening (from onBeforeShutdown). + * Can be used to save UI state even if that is long running through the WillShutdownEvent#join() + * method. + * + * The event carries a shutdown reason that indicates how the shutdown was triggered. + */ + readonly onWillShutdown: Event; + + /** + * Fired when the shutdown is about to happen after long running shutdown operations + * have finished (from onWillShutdown). This is the right place to dispose resources. + */ + readonly onShutdown: Event; + + /** + * Returns a promise that resolves when a certain lifecycle phase + * has started. + */ + when(phase: LifecyclePhase): Promise; + + /** + * Triggers a shutdown of the workbench. Depending on native or web, this can have + * different implementations and behaviour. + * + * **Note:** this should normally not be called. See related methods in `IHostService` + * and `INativeHostService` to close a window or quit the application. + */ + shutdown(): void; +} + +export const NullLifecycleService: ILifecycleService = { + + _serviceBrand: undefined, + + onBeforeShutdown: Event.None, + onWillShutdown: Event.None, + onShutdown: Event.None, + + phase: LifecyclePhase.Restored, + startupKind: StartupKind.NewWindow, + + async when() { }, + shutdown() { } +}; diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/workbench/services/lifecycle/common/lifecycleService.ts similarity index 98% rename from src/vs/platform/lifecycle/common/lifecycleService.ts rename to src/vs/workbench/services/lifecycle/common/lifecycleService.ts index c19c0d81500..a7e52e488bc 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/common/lifecycleService.ts @@ -6,7 +6,7 @@ import { Emitter } from 'vs/base/common/event'; import { Barrier } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILifecycleService, BeforeShutdownEvent, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, BeforeShutdownEvent, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { mark } from 'vs/base/common/performance'; diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index cd7d99f09ad..2cfbb367437 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -5,13 +5,14 @@ import { localize } from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { ShutdownReason, StartupKind, handleVetos, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason, StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; +import { AbstractLifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycleService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import Severity from 'vs/base/common/severity'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; diff --git a/src/vs/workbench/services/log/electron-browser/logService.ts b/src/vs/workbench/services/log/electron-browser/logService.ts index 6d896159197..c230f8920bf 100644 --- a/src/vs/workbench/services/log/electron-browser/logService.ts +++ b/src/vs/workbench/services/log/electron-browser/logService.ts @@ -12,7 +12,7 @@ import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; export class NativeLogService extends DelegatedLogService { diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 8f2b82d7c7a..ec221545d26 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -11,7 +11,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { connectRemoteAgentManagement, IConnectionOptions, ISocketFactory, PersistentConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 589876c2cab..f498d7ba281 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -6,7 +6,7 @@ import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; import { ITextFileService, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; export class BrowserTextFileService extends AbstractTextFileService { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 83974e2c424..39a25cb2b11 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { ITextFileService, ITextFileStreamContent, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions, ITextFileEditorModelManager, IResourceEncoding, stringToSnapshot, ITextFileSaveAsOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, IFileStreamContent } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 9e84791aaf5..7899cdab6da 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ITextFileEditorModel, ITextFileEditorModelManager, ITextFileEditorModelLoadOrCreateOptions, ITextFileLoadEvent, ITextFileSaveEvent, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; import { IFileService, FileChangesEvent, FileOperation, FileChangeType } from 'vs/platform/files/common/files'; diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index f7980b5848a..d1f83007990 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -16,7 +16,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/tex import { UTF8, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index 65126d9699e..cab6c54f0b9 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts index fd16b7da21c..1192fb67378 100644 --- a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts +++ b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts @@ -8,7 +8,7 @@ import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/enviro import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index f15d240c489..0f876c6daeb 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -21,7 +21,7 @@ import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authe import { getSyncAreaLabel } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; export const IUserDataInitializationService = createDecorator('IUserDataInitializationService'); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 720d17e3b0a..4a8df1c52fb 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -29,7 +29,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; type UserAccountClassification = { id: { classification: 'EndUserPseudonymizedInformation', purpose: 'BusinessInsight' }; diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index 6a4cf6d99e5..9f6279a0d89 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -18,7 +18,7 @@ import { basename } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts index a06dce40d02..2059826812e 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts @@ -11,7 +11,7 @@ import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestSer import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Registry } from 'vs/platform/registry/common/platform'; import { IEditorModel } from 'vs/platform/editor/common/editor'; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 1249176064e..13a752ecace 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -21,7 +21,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorOptions, IResourceEditorInput, IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { FileOperationEvent, IFileService, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, ICreateFileOptions, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions, IWriteFileOptions, IReadFileOptions, IFileContent, IFileStreamContent, FileOperationError, IFileSystemProviderWithFileReadStreamCapability } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index ed43a453f08..950219dd483 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -11,7 +11,7 @@ import { NativeTextFileService, } from 'vs/workbench/services/textfile/electron- import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { FileOperationError, IFileService } from 'vs/platform/files/common/files'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModelService } from 'vs/editor/common/services/modelService'; import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; From 49de785cb3e618e7d13e840c19d1bcb37230aa09 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 15:45:08 +0200 Subject: [PATCH 083/121] fix bad import location //cc @RMacfarlane --- .../encryption/{electron-main => }/common/encryptionService.ts | 0 .../platform/encryption/electron-main/encryptionMainService.ts | 2 +- .../workbench/services/encryption/common/encryptionService.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/vs/platform/encryption/{electron-main => }/common/encryptionService.ts (100%) diff --git a/src/vs/platform/encryption/electron-main/common/encryptionService.ts b/src/vs/platform/encryption/common/encryptionService.ts similarity index 100% rename from src/vs/platform/encryption/electron-main/common/encryptionService.ts rename to src/vs/platform/encryption/common/encryptionService.ts diff --git a/src/vs/platform/encryption/electron-main/encryptionMainService.ts b/src/vs/platform/encryption/electron-main/encryptionMainService.ts index d91b6b96b61..bf5041ff784 100644 --- a/src/vs/platform/encryption/electron-main/encryptionMainService.ts +++ b/src/vs/platform/encryption/electron-main/encryptionMainService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ICommonEncryptionService } from 'vs/platform/encryption/electron-main/common/encryptionService'; +import { ICommonEncryptionService } from 'vs/platform/encryption/common/encryptionService'; export const IEncryptionMainService = createDecorator('encryptionMainService'); diff --git a/src/vs/workbench/services/encryption/common/encryptionService.ts b/src/vs/workbench/services/encryption/common/encryptionService.ts index ed4d0c23b4e..d264a3b9dc0 100644 --- a/src/vs/workbench/services/encryption/common/encryptionService.ts +++ b/src/vs/workbench/services/encryption/common/encryptionService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ICommonEncryptionService } from 'vs/platform/encryption/electron-main/common/encryptionService'; +import { ICommonEncryptionService } from 'vs/platform/encryption/common/encryptionService'; export const IEncryptionService = createDecorator('encryptionService'); From c57b7cd3d65a5f1404ec5bdd1d17252f65b9cf21 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Oct 2020 15:52:26 +0200 Subject: [PATCH 084/121] debt - lift resource identity service to workbench where it is used --- src/vs/workbench/browser/web.main.ts | 2 +- src/vs/workbench/electron-browser/desktop.main.ts | 4 ++-- src/vs/workbench/electron-sandbox/desktop.main.ts | 2 +- src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts | 2 +- .../resourceIdentity}/common/resourceIdentityService.ts | 0 .../resourceIdentity}/node/resourceIdentityServiceImpl.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename src/vs/{platform/resource => workbench/services/resourceIdentity}/common/resourceIdentityService.ts (100%) rename src/vs/{platform/resource => workbench/services/resourceIdentity}/node/resourceIdentityServiceImpl.ts (95%) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index a5bd0cdb1bf..5481f5e2f1d 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -44,7 +44,7 @@ import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/wi import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; import { coalesce } from 'vs/base/common/arrays'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { WebResourceIdentityService, IResourceIdentityService } from 'vs/workbench/services/resourceIdentity/common/resourceIdentityService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IndexedDB, INDEXEDDB_LOGS_OBJECT_STORE, INDEXEDDB_USERDATA_OBJECT_STORE } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; import { BrowserRequestService } from 'vs/workbench/services/request/browser/requestService'; diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 42115bba46d..9edf976b57c 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -45,8 +45,8 @@ import { FileUserDataProvider } from 'vs/workbench/services/userData/common/file import { basename } from 'vs/base/common/resources'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; -import { NativeResourceIdentityService } from 'vs/platform/resource/node/resourceIdentityServiceImpl'; -import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { NativeResourceIdentityService } from 'vs/workbench/services/resourceIdentity/node/resourceIdentityServiceImpl'; +import { IResourceIdentityService } from 'vs/workbench/services/resourceIdentity/common/resourceIdentityService'; import { NativeLogService } from 'vs/workbench/services/log/electron-browser/logService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index a5536518ca6..75a6eaa0c77 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -29,7 +29,7 @@ import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; -import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { IResourceIdentityService } from 'vs/workbench/services/resourceIdentity/common/resourceIdentityService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleRemoteAgentService, SimpleResourceIdentityService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 502e6437e6f..efa338d250b 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -7,7 +7,7 @@ /* eslint-disable code-import-patterns */ import { ConsoleLogService } from 'vs/platform/log/common/log'; -import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { IResourceIdentityService } from 'vs/workbench/services/resourceIdentity/common/resourceIdentityService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { hash } from 'vs/base/common/hash'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/platform/resource/common/resourceIdentityService.ts b/src/vs/workbench/services/resourceIdentity/common/resourceIdentityService.ts similarity index 100% rename from src/vs/platform/resource/common/resourceIdentityService.ts rename to src/vs/workbench/services/resourceIdentity/common/resourceIdentityService.ts diff --git a/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts b/src/vs/workbench/services/resourceIdentity/node/resourceIdentityServiceImpl.ts similarity index 95% rename from src/vs/platform/resource/node/resourceIdentityServiceImpl.ts rename to src/vs/workbench/services/resourceIdentity/node/resourceIdentityServiceImpl.ts index fe7cd857a5f..6ce05e46225 100644 --- a/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts +++ b/src/vs/workbench/services/resourceIdentity/node/resourceIdentityServiceImpl.ts @@ -8,7 +8,7 @@ import { stat } from 'vs/base/node/pfs'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService'; +import { IResourceIdentityService } from 'vs/workbench/services/resourceIdentity/common/resourceIdentityService'; import { Disposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; From 49d9c8c79b6fd9aaeeb5e869e37d93cfd0e7b703 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Oct 2020 16:05:41 +0200 Subject: [PATCH 085/121] accessibilty: always enable show accessibility help command fixes #108850 --- .../standalone/browser/accessibilityHelp/accessibilityHelp.ts | 1 - .../contrib/codeEditor/browser/accessibility/accessibility.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index 274ad8a2060..9da4eb8dacb 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -328,7 +328,6 @@ class ShowAccessibilityHelpAction extends EditorAction { alias: 'Show Accessibility Help', precondition: undefined, kbOpts: { - kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.F1, weight: KeybindingWeight.EditorContrib, linux: { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index f76fc93385e..cd181b13ca3 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -282,7 +282,6 @@ class ShowAccessibilityHelpAction extends EditorAction { alias: 'Show Accessibility Help', precondition: undefined, kbOpts: { - kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.F1, weight: KeybindingWeight.EditorContrib, linux: { From 9be5ce54ec1ad06746d6b9c68a82d171e70201a3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 20 Oct 2020 16:26:20 +0200 Subject: [PATCH 086/121] add notes --- .../services/userData/common/fileUserDataProvider.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts index 91158913e25..0baaa4afde3 100644 --- a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts @@ -29,6 +29,11 @@ export class FileUserDataProvider extends Disposable implements private extUri: ExtUri; constructor( + /* + Original userdata and backup home locations. Used to + - listen to changes and trigger change events + - Compute UserData URIs from original URIs and vice-versa + */ private readonly fileSystemUserDataHome: URI, private readonly fileSystemBackupsHome: URI | undefined, private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, From 125f83c3f68d19805ecf5df0ed83efc7e12e3b7f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Oct 2020 08:03:58 -0700 Subject: [PATCH 087/121] Use classList For #103454 --- .../workbench/contrib/webview/browser/baseWebviewElement.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 6f19e4548e7..6b84fbcb776 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addClass } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -103,9 +102,7 @@ export abstract class BaseWebview extends Disposable { const subscription = this._register(this.on(WebviewMessageChannels.webviewReady, () => { this._logService.debug(`Webview(${this.id}): webview ready`); - if (this.element) { - addClass(this.element, 'ready'); - } + this.element?.classList.add('ready'); if (this._state.type === WebviewState.Type.Initializing) { this._state.pendingMessages.forEach(({ channel, data }) => this.doPostMessage(channel, data)); From cb1dce0ef30d48c4259eb7d0a923926b3e6e16af Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Oct 2020 17:03:00 +0200 Subject: [PATCH 088/121] Finalize theme icon color API Fixes #103120 --- src/vs/vscode.d.ts | 13 ++++++++++- src/vs/vscode.proposed.d.ts | 22 ------------------- .../workbench/api/common/extHostTreeViews.ts | 3 --- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 4a1a51b934c..a0fd1ff7a04 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -808,11 +808,22 @@ declare module 'vscode' { */ static readonly Folder: ThemeIcon; + /** + * The id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + */ + readonly id: string; + + /** + * The optional ThemeColor of the icon. The color is currently only used in [TreeItem](#TreeItem). + */ + readonly themeColor?: ThemeColor; + /** * Creates a reference to a theme icon. * @param id id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. + * @param color optional `ThemeColor` for the icon. The color is currently only used in [TreeItem](#TreeItem). */ - constructor(id: string); + constructor(id: string, color?: ThemeColor); } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 63712708d62..3b0258815ff 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2159,28 +2159,6 @@ declare module 'vscode' { //#endregion - //#region https://github.com/microsoft/vscode/issues/103120 @alexr00 - export class ThemeIcon2 extends ThemeIcon { - - /** - * The id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. - */ - readonly id: string; - - /** - * The optional ThemeColor of the icon. The color is currently only used in [TreeItem](#TreeItem). - */ - readonly themeColor?: ThemeColor; - - /** - * Creates a reference to a theme icon. - * @param id id of the icon. The available icons are listed in https://microsoft.github.io/vscode-codicons/dist/codicon.html. - * @param color optional `ThemeColor` for the icon. The color is currently only used in [TreeItem](#TreeItem). - */ - constructor(id: string, color?: ThemeColor); - } - //#endregion - //#region https://github.com/microsoft/vscode/issues/102665 Comment API @rebornix export interface CommentThread { /** diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 5406cfa39d1..d7194fc2317 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -601,9 +601,6 @@ class ExtHostTreeView extends Disposable { } private getThemeIcon(extensionTreeItem: vscode.TreeItem2): ThemeIcon | undefined { - if ((extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath.themeColor) { - checkProposedApiEnabled(this.extension); - } return extensionTreeItem.iconPath instanceof ThemeIcon ? extensionTreeItem.iconPath : undefined; } From 65dddce20e2591669fb019c19d9778c96629dad1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Oct 2020 17:11:13 +0200 Subject: [PATCH 089/121] Fix back quick input button --- src/vs/workbench/api/common/extHost.api.impl.ts | 1 - src/vs/workbench/api/common/extHostTypes.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d4393a332bd..284c6aff854 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1173,7 +1173,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind, ThemeColor: extHostTypes.ThemeColor, ThemeIcon: extHostTypes.ThemeIcon, - ThemeIcon2: extHostTypes.ThemeIcon, TreeItem: extHostTypes.TreeItem, TreeItem2: extHostTypes.TreeItem, TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index ef8a0019524..f02347835f8 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2729,7 +2729,7 @@ export enum DebugConfigurationProviderTriggerKind { @es5ClassCompat export class QuickInputButtons { - static readonly Back: vscode.QuickInputButton = { iconPath: 'back.svg' }; + static readonly Back: vscode.QuickInputButton = { iconPath: { id: 'back.svg' } }; private constructor() { } } From d5e80397cb38f665449c6c7f3740d38fbf030cc7 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 20 Oct 2020 16:02:09 +0200 Subject: [PATCH 090/121] [themeService] avoid locations in storage --- .../services/themes/common/colorThemeData.ts | 14 ++------------ .../themes/common/themeExtensionPoints.ts | 17 ++++++++--------- .../themes/common/workbenchThemeService.ts | 8 +++----- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 6b9fa724b74..b4cef7e7368 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -10,8 +10,6 @@ import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IWorkbe import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; -import * as objects from 'vs/base/common/objects'; -import * as arrays from 'vs/base/common/arrays'; import * as resources from 'vs/base/common/resources'; import { Extensions as ColorRegistryExtensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { ITokenStyle, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; @@ -56,7 +54,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { settingsId: string; description?: string; isLoaded: boolean; - location?: URI; + location?: URI; // only set for extension from the registry, not for themes restored from the storage watch?: boolean; extensionData?: ExtensionData; @@ -521,7 +519,6 @@ export class ColorThemeData implements IWorkbenchColorTheme { themeTokenColors: this.themeTokenColors, semanticTokenRules: this.semanticTokenRules.map(SemanticTokenRule.toJSONObject), extensionData: ExtensionData.toJSONObject(this.extensionData), - location: this.location?.toJSON(), themeSemanticHighlighting: this.themeSemanticHighlighting, colorMap: colorMapData, watch: this.watch @@ -529,13 +526,6 @@ export class ColorThemeData implements IWorkbenchColorTheme { storageService.store(PERSISTED_THEME_STORAGE_KEY, value, StorageScope.GLOBAL); } - hasEqualData(other: ColorThemeData) { - return objects.equals(this.colorMap, other.colorMap) - && objects.equals(this.themeTokenColors, other.themeTokenColors) - && arrays.equals(this.semanticTokenRules, other.semanticTokenRules, SemanticTokenRule.equals) - && this.themeSemanticHighlighting === other.themeSemanticHighlighting; - } - get baseTheme(): string { return this.id.split(' ')[0]; } @@ -607,7 +597,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { } break; case 'location': - theme.location = URI.revive(data.location); + // ignore, no longer restore break; case 'extensionData': theme.extensionData = ExtensionData.fromJSONObject(data.extensionData); diff --git a/src/vs/workbench/services/themes/common/themeExtensionPoints.ts b/src/vs/workbench/services/themes/common/themeExtensionPoints.ts index 20e496a4fe3..b3aa420553d 100644 --- a/src/vs/workbench/services/themes/common/themeExtensionPoints.ts +++ b/src/vs/workbench/services/themes/common/themeExtensionPoints.ts @@ -113,7 +113,7 @@ export interface ThemeChangeEvent { export interface IThemeData { id: string; settingsId: string | null; - extensionData?: ExtensionData; + location?: URI; } export class ThemeRegistry { @@ -152,10 +152,9 @@ export class ThemeRegistry { extensionId: ext.description.identifier.value, extensionPublisher: ext.description.publisher, extensionName: ext.description.name, - extensionIsBuiltin: ext.description.isBuiltin, - extensionLocation: ext.description.extensionLocation + extensionIsBuiltin: ext.description.isBuiltin }; - this.onThemes(extensionData, ext.value, ext.collector); + this.onThemes(extensionData, ext.description.extensionLocation, ext.value, ext.collector); } for (const theme of this.extensionThemes) { if (!previousIds[theme.id]) { @@ -169,7 +168,7 @@ export class ThemeRegistry { }); } - private onThemes(extensionData: ExtensionData, themes: IThemeExtensionPoint[], collector: ExtensionMessageCollector): void { + private onThemes(extensionData: ExtensionData, extensionLocation: URI, themes: IThemeExtensionPoint[], collector: ExtensionMessageCollector): void { if (!Array.isArray(themes)) { collector.error(nls.localize( 'reqarray', @@ -198,9 +197,9 @@ export class ThemeRegistry { return; } - const themeLocation = resources.joinPath(extensionData.extensionLocation, theme.path); - if (!resources.isEqualOrParent(themeLocation, extensionData.extensionLocation)) { - collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", this.themesExtPoint.name, themeLocation.path, extensionData.extensionLocation.path)); + const themeLocation = resources.joinPath(extensionLocation, theme.path); + if (!resources.isEqualOrParent(themeLocation, extensionLocation)) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", this.themesExtPoint.name, themeLocation.path, extensionLocation.path)); } let themeData = this.create(theme, themeLocation, extensionData); @@ -245,7 +244,7 @@ export class ThemeRegistry { public findThemeByExtensionLocation(extLocation: URI | undefined): Promise { if (extLocation) { return this.getThemes().then(allThemes => { - return allThemes.filter(t => t.extensionData && resources.isEqual(t.extensionData.extensionLocation, extLocation)); + return allThemes.filter(t => t.location && resources.isEqualOrParent(t.location, extLocation)); }); } return Promise.resolve([]); diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9186681075f..53e7674b1ae 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -8,7 +8,6 @@ import { Event } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; import { IColorTheme, IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { URI } from 'vs/base/common/uri'; import { isBoolean, isString } from 'vs/base/common/types'; export const IWorkbenchThemeService = createDecorator('themeService'); @@ -135,16 +134,15 @@ export interface ExtensionData { extensionPublisher: string; extensionName: string; extensionIsBuiltin: boolean; - extensionLocation: URI; } export namespace ExtensionData { export function toJSONObject(d: ExtensionData | undefined): any { - return d && { _extensionId: d.extensionId, _extensionIsBuiltin: d.extensionIsBuiltin, _extensionLocation: d.extensionLocation.toJSON(), _extensionName: d.extensionName, _extensionPublisher: d.extensionPublisher }; + return d && { _extensionId: d.extensionId, _extensionIsBuiltin: d.extensionIsBuiltin, _extensionName: d.extensionName, _extensionPublisher: d.extensionPublisher }; } export function fromJSONObject(o: any): ExtensionData | undefined { - if (o && isString(o._extensionId) && isBoolean(o._extensionIsBuiltin) && isString(o._extensionLocation) && isString(o._extensionName) && isString(o._extensionPublisher)) { - return { extensionId: o._extensionId, extensionIsBuiltin: o._extensionIsBuiltin, extensionLocation: URI.revive(o._extensionLocation), extensionName: o._extensionName, extensionPublisher: o._extensionPublisher }; + if (o && isString(o._extensionId) && isBoolean(o._extensionIsBuiltin) && isString(o._extensionName) && isString(o._extensionPublisher)) { + return { extensionId: o._extensionId, extensionIsBuiltin: o._extensionIsBuiltin, extensionName: o._extensionName, extensionPublisher: o._extensionPublisher }; } return undefined; } From 05c579b7ba023e542a71b8a89db083a7ca7bfa2d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 20 Oct 2020 18:04:10 +0200 Subject: [PATCH 091/121] Remove some deprecated code in ./src/vs/base. For #103454 --- src/vs/base/browser/dom.ts | 20 ------------------- .../contrib/codeAction/lightBulbWidget.ts | 8 ++++---- .../themes/browser/workbenchThemeService.ts | 12 +++++------ .../services/themes/common/colorThemeData.ts | 7 +++++-- 4 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 2d753c91e78..7d5f15fc6e6 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -35,33 +35,17 @@ export function isInDOM(node: Node | null): boolean { interface IDomClassList { addClass(node: HTMLElement | SVGElement, className: string): void; - addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; - removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void; } const _classList: IDomClassList = new class implements IDomClassList { - addClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.addClass(node, name))); - } - addClass(node: HTMLElement, className: string): void { if (className && node.classList) { node.classList.add(className); } } - removeClass(node: HTMLElement, className: string): void { - if (className && node.classList) { - node.classList.remove(className); - } - } - - removeClasses(node: HTMLElement, ...classNames: string[]): void { - classNames.forEach(nameValue => nameValue.split(' ').forEach(name => this.removeClass(node, name))); - } - toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void { if (node.classList) { node.classList.toggle(className, shouldHaveIt); @@ -72,10 +56,6 @@ const _classList: IDomClassList = new class implements IDomClassList { /** @deprecated ES6 - use classList*/ export function addClass(node: HTMLElement | SVGElement, className: string): void { return _classList.addClass(node, className); } /** @deprecated ES6 - use classList*/ -export function addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void { return _classList.addClasses(node, ...classNames); } -/** @deprecated ES6 - use classList*/ -export function removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void { return _classList.removeClasses(node, ...classNames); } -/** @deprecated ES6 - use classList*/ export function toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void { return _classList.toggleClass(node, className, shouldHaveIt); } class DomListener implements IDisposable { diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 5caf1cd6a60..2d95a32a5ae 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -204,8 +204,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { private _updateLightBulbTitleAndIcon(): void { if (this.state.type === LightBulbState.Type.Showing && this.state.actions.hasAutoFix) { // update icon - dom.removeClasses(this._domNode, Codicon.lightBulb.classNames); - dom.addClasses(this._domNode, Codicon.lightbulbAutofix.classNames); + this._domNode.classList.remove(...Codicon.lightBulb.classNamesArray); + this._domNode.classList.add(...Codicon.lightbulbAutofix.classNamesArray); const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId); if (preferredKb) { @@ -215,8 +215,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } // update icon - dom.removeClasses(this._domNode, Codicon.lightbulbAutofix.classNames); - dom.addClasses(this._domNode, Codicon.lightBulb.classNames); + this._domNode.classList.remove(...Codicon.lightbulbAutofix.classNamesArray); + this._domNode.classList.add(...Codicon.lightBulb.classNamesArray); const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId); if (kb) { diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index eb96d29d5cd..cde9e21cf7e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -18,7 +18,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; -import { removeClasses, addClasses, createStyleSheet } from 'vs/base/browser/dom'; +import { createStyleSheet } from 'vs/base/browser/dom'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; @@ -456,11 +456,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.updateDynamicCSSRules(newTheme); if (this.currentColorTheme.id) { - removeClasses(this.container, this.currentColorTheme.id); + this.container.classList.remove(...this.currentColorTheme.classNames); } else { - removeClasses(this.container, VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME); + this.container.classList.remove(VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME); } - addClasses(this.container, newTheme.id); + this.container.classList.add(...newTheme.classNames); this.currentColorTheme.clearCaches(); this.currentColorTheme = newTheme; @@ -575,9 +575,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _applyRules(iconThemeData.styleSheetContent!, fileIconThemeRulesClassName); if (iconThemeData.id) { - addClasses(this.container, fileIconsEnabledClass); + this.container.classList.add(fileIconsEnabledClass); } else { - removeClasses(this.container, fileIconsEnabledClass); + this.container.classList.remove(fileIconsEnabledClass); } this.fileIconThemeWatcher.update(iconThemeData); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index b4cef7e7368..73ede2fd1f3 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -515,7 +515,6 @@ export class ColorThemeData implements IWorkbenchColorTheme { id: this.id, label: this.label, settingsId: this.settingsId, - selector: this.id.split(' ').join('.'), // to not break old clients themeTokenColors: this.themeTokenColors, semanticTokenRules: this.semanticTokenRules.map(SemanticTokenRule.toJSONObject), extensionData: ExtensionData.toJSONObject(this.extensionData), @@ -527,7 +526,11 @@ export class ColorThemeData implements IWorkbenchColorTheme { } get baseTheme(): string { - return this.id.split(' ')[0]; + return this.classNames[0]; + } + + get classNames(): string[] { + return this.id.split(' '); } get type(): ColorScheme { From 747ed274094a5f989444d490df3591a8c3bf1911 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 20 Oct 2020 10:50:33 -0700 Subject: [PATCH 092/121] Tweaks to comment reaction styling --- .../contrib/comments/browser/commentThreadWidget.ts | 6 ++++++ .../contrib/comments/browser/media/review.css | 13 ++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 25b63f19e78..9e66d94a6ae 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -47,6 +47,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { PANEL_BORDER } from 'vs/workbench/common/theme'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; const COLLAPSE_ACTION_CLASS = 'expand-review-action codicon-chevron-up'; @@ -916,6 +917,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget content.push(`.monaco-editor .review-widget .body .review-comment blockquote { border-color: ${blockQuoteBOrder}; }`); } + const border = theme.getColor(PANEL_BORDER); + if (border) { + content.push(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label { border-color: ${border}; }`); + } + const hcBorder = theme.getColor(contrastBorder); if (hcBorder) { content.push(`.monaco-editor .review-widget .body .comment-form .review-thread-reply-button { outline-color: ${hcBorder}; }`); diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index 584c433d6c3..da9c9f970d8 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -119,18 +119,17 @@ white-space: pre; text-align: center; font-size: 12px; + display: flex; } .monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item .action-label .reaction-icon { - background-size: 12px; + background-size: 14px; background-position: left center; background-repeat: no-repeat; - width: 16px; - height: 12px; + width: 14px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; display: inline-block; - margin-top: 3px; margin-right: 4px; } @@ -165,11 +164,7 @@ } .monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label { - border: 1px solid transparent; -} - -.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active { - border: 1px solid grey; + border: 1px solid; } .monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.disabled { From d2a825f57e422e7b177fdb4ec4650e46f738240d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 20 Oct 2020 10:53:00 -0700 Subject: [PATCH 093/121] fixes #102954 --- .../browser/actions/layoutActions.ts | 86 ++++++++++++++++++- src/vs/workbench/browser/layout.ts | 6 +- .../contrib/terminal/browser/terminalTab.ts | 2 +- .../services/layout/browser/layoutService.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- 5 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index b62d60bf29e..376a2a46884 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -28,6 +28,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { Codicon } from 'vs/base/common/codicons'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; const registry = Registry.as(WorkbenchExtensions.WorkbenchActions); @@ -807,7 +808,7 @@ export abstract class BaseResizeViewAction extends Action { super(id, label); } - protected resizePart(sizeChange: number): void { + protected resizePart(widthChange: number, heightChange: number): void { const isEditorFocus = this.layoutService.hasFocus(Parts.EDITOR_PART); const isSidebarFocus = this.layoutService.hasFocus(Parts.SIDEBAR_PART); const isPanelFocus = this.layoutService.hasFocus(Parts.PANEL_PART); @@ -822,7 +823,7 @@ export abstract class BaseResizeViewAction extends Action { } if (part) { - this.layoutService.resizePart(part, sizeChange); + this.layoutService.resizePart(part, widthChange, heightChange); } } } @@ -841,7 +842,43 @@ export class IncreaseViewSizeAction extends BaseResizeViewAction { } async run(): Promise { - this.resizePart(BaseResizeViewAction.RESIZE_INCREMENT); + this.resizePart(BaseResizeViewAction.RESIZE_INCREMENT, BaseResizeViewAction.RESIZE_INCREMENT); + } +} + +export class IncreaseViewWidthAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.increaseViewWidth'; + static readonly LABEL = nls.localize('increaseViewWidth', "Increase Current View Width"); + + constructor( + id: string, + label: string, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService + ) { + super(id, label, layoutService); + } + + async run(): Promise { + this.resizePart(BaseResizeViewAction.RESIZE_INCREMENT, 0); + } +} + +export class IncreaseViewHeightAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.increaseViewHeight'; + static readonly LABEL = nls.localize('increaseViewHeight', "Increase Current View Height"); + + constructor( + id: string, + label: string, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService + ) { + super(id, label, layoutService); + } + + async run(): Promise { + this.resizePart(0, BaseResizeViewAction.RESIZE_INCREMENT); } } @@ -860,9 +897,50 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { } async run(): Promise { - this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT); + this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT, -BaseResizeViewAction.RESIZE_INCREMENT); + } +} + +export class DecreaseViewWidthAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.decreaseViewWidth'; + static readonly LABEL = nls.localize('decreaseViewWidth', "Decrease Current View Width"); + + constructor( + id: string, + label: string, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService + ) { + super(id, label, layoutService); + } + + async run(): Promise { + this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT, 0); + } +} + + +export class DecreaseViewHeightAction extends BaseResizeViewAction { + + static readonly ID = 'workbench.action.decreaseViewHeight'; + static readonly LABEL = nls.localize('decreaseViewHeight', "Decrease Current View Height"); + + constructor( + id: string, + label: string, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService + ) { + super(id, label, layoutService); + } + + async run(): Promise { + this.resizePart(0, -BaseResizeViewAction.RESIZE_INCREMENT); } } registry.registerWorkbenchAction(SyncActionDescriptor.from(IncreaseViewSizeAction, undefined), 'View: Increase Current View Size', CATEGORIES.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(IncreaseViewWidthAction, undefined), 'View: Increase Editor Width', CATEGORIES.View.value, EditorContextKeys.focus); +registry.registerWorkbenchAction(SyncActionDescriptor.from(IncreaseViewHeightAction, undefined), 'View: Increase Editor Height', CATEGORIES.View.value, EditorContextKeys.focus); registry.registerWorkbenchAction(SyncActionDescriptor.from(DecreaseViewSizeAction, undefined), 'View: Decrease Current View Size', CATEGORIES.View.value); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DecreaseViewWidthAction, undefined), 'View: Decrease Editor Width', CATEGORIES.View.value, EditorContextKeys.focus); +registry.registerWorkbenchAction(SyncActionDescriptor.from(DecreaseViewHeightAction, undefined), 'View: Decrease Editor Height', CATEGORIES.View.value, EditorContextKeys.focus); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7dda73f66c7..ba017d4b9fc 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1397,9 +1397,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._onCenteredLayoutChange.fire(this.state.editor.centered); } - resizePart(part: Parts, sizeChange: number): void { - const sizeChangePxWidth = this.workbenchGrid.width * sizeChange / 100; - const sizeChangePxHeight = this.workbenchGrid.height * sizeChange / 100; + resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void { + const sizeChangePxWidth = this.workbenchGrid.width * sizeChangeWidth / 100; + const sizeChangePxHeight = this.workbenchGrid.height * sizeChangeHeight / 100; let viewSize: IViewSize; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 360f0c4a8ec..eb45ee52381 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -56,7 +56,7 @@ class SplitPaneContainer extends Disposable { (this.orientation === Orientation.VERTICAL && direction === Direction.Right)) { amount *= -1; } - this._layoutService.resizePart(Parts.PANEL_PART, amount); + this._layoutService.resizePart(Parts.PANEL_PART, amount, amount); return; } diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 75434b06573..a405e8973c6 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -232,7 +232,7 @@ export interface IWorkbenchLayoutService extends ILayoutService { /** * Resizes currently focused part on main access */ - resizePart(part: Parts, sizeChange: number): void; + resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void; /** * Register a part to participate in the layout. diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 13a752ecace..34e84902831 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -460,7 +460,7 @@ export class TestLayoutService implements IWorkbenchLayoutService { toggleZenMode(): void { } isEditorLayoutCentered(): boolean { return false; } centerEditorLayout(_active: boolean): void { } - resizePart(_part: Parts, _sizeChange: number): void { } + resizePart(_part: Parts, _sizeChangeWidth: number, _sizeChangeHeight: number): void { } registerPart(part: Part): void { } isWindowMaximized() { return false; } updateWindowMaximizedState(maximized: boolean): void { } From efe6a1e4d76f6922aeed0cd9b2b9b1abe5f3e110 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 20 Oct 2020 11:29:16 -0700 Subject: [PATCH 094/121] terminal: move typeahead latency into predictor, add dynamic predictor toggle --- .../terminal/browser/terminalInstance.ts | 7 +- .../browser/terminalLatencyTelemetryAddon.ts | 104 --------- .../browser/terminalTypeAheadAddon.ts | 209 ++++++++++++++++-- .../terminal/common/terminalConfiguration.ts | 2 +- 4 files changed, 191 insertions(+), 131 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 707572a0506..cb3b9f19a07 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -44,7 +44,6 @@ import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs import { EnvironmentVariableInfoWidget } from 'vs/workbench/contrib/terminal/browser/widgets/environmentVariableInfoWidget'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { LatencyTelemetryAddon } from 'vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon'; import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; // How long in milliseconds should an average frame take to render for a notification to appear @@ -450,10 +449,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } })); - const latencyAddon = this._register(this._instantiationService.createInstance(LatencyTelemetryAddon, this._processManager)); - this._xterm.loadAddon(latencyAddon); - - this._xterm.loadAddon(new TypeAheadAddon(this._processManager, this._configHelper)); + const typeaheadAddon = this._register(this._instantiationService.createInstance(TypeAheadAddon, this._processManager, this._configHelper)); + this._xterm.loadAddon(typeaheadAddon); return xterm; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts deleted file mode 100644 index 563f3f55251..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalLatencyTelemetryAddon.ts +++ /dev/null @@ -1,104 +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 { Disposable } from 'vs/base/common/lifecycle'; -import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { ITerminalAddon, Terminal } from 'xterm'; - -interface ITypedChar { - char: string; - time: number; -} - -// Collect data in 5 minute chunks -const TELEMETRY_TIMEOUT = 1000 * 60 * 5; - -export class LatencyTelemetryAddon extends Disposable implements ITerminalAddon { - private _terminal!: Terminal; - private _typedQueue: ITypedChar[] = []; - private _activeTimer: any; - private _unprocessedLatencies: number[] = []; - - constructor( - private readonly _processManager: ITerminalProcessManager, - @ITelemetryService private readonly _telemetryService: ITelemetryService - ) { - super(); - } - - public activate(terminal: Terminal): void { - this._terminal = terminal; - this._register(terminal.onData(e => this._onData(e))); - this._register(this._processManager.onBeforeProcessData(e => this._onBeforeProcessData(e))); - } - - private async _triggerTelemetryReport(): Promise { - if (!this._activeTimer) { - this._activeTimer = setTimeout(() => { - this._sendTelemetryReport(); - this._activeTimer = undefined; - }, TELEMETRY_TIMEOUT); - } - } - - private _sendTelemetryReport(): void { - if (this._unprocessedLatencies.length < 10) { - return; - } - - /* __GDPR__ - "terminalLatencyStats" : { - "min" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "max" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "median" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } - } - */ - const median = this._unprocessedLatencies.sort()[Math.floor(this._unprocessedLatencies.length / 2)]; - this._telemetryService.publicLog('terminalLatencyStats', { - min: Math.min(...this._unprocessedLatencies), - max: Math.max(...this._unprocessedLatencies), - median, - count: this._unprocessedLatencies.length - }); - this._unprocessedLatencies.length = 0; - } - - private _onData(data: string): void { - if (this._terminal.buffer.active.type === 'alternate') { - return; - } - - const code = data.charCodeAt(0); - if (data.length === 1 && code >= 32 && code <= 126) { - const typed: ITypedChar = { - char: data, - time: Date.now() - }; - this._typedQueue.push(typed); - } - } - - private _onBeforeProcessData(event: IBeforeProcessDataEvent): void { - if (!this._typedQueue.length) { - return; - } - - const cleanText = removeAnsiEscapeCodes(event.data); - for (let i = 0; i < cleanText.length; i++) { - if (this._typedQueue[0] && this._typedQueue[0].char === cleanText[i]) { - const success = this._typedQueue.shift()!; - const latency = Date.now() - success.time; - this._unprocessedLatencies.push(latency); - this._triggerTelemetryReport(); - } else { - this._typedQueue.length = 0; - break; - } - } - } -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index f24e9a096e3..24d0148e9ec 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -4,6 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; @@ -18,6 +21,12 @@ const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;[35])?O?([DC])/; const PASSWORD_INPUT_RE = /(password|passphrase|passwd).*:/i; const NOT_WORD_RE = /\W/; +const statsBufferSize = 25; +const statsSendTelemetryEvery = 1000 * 60 * 5; // how often to collect stats +const statsMinSamplesToTurnOn = 5; +const statsMinAccuracyToTurnOn = 0.3; +const statsToggleOffThreshold = 0.5; // if latency is less than `threshold * this`, turn off + /** * Codes that should be omitted from sending to the prediction engine and * insted omitted directly: @@ -424,6 +433,68 @@ class CursorMovePrediction implements IPrediction { } } +class PredictionStats implements IDisposable { + private readonly stats: [latency: number, correct: boolean][] = []; + private index = 0; + private readonly addedAtTime = new WeakMap(); + private readonly disposables: IDisposable[] = []; + private readonly changeEmitter = new Emitter(); + public readonly onChange = this.changeEmitter.event; + + /** + * Gets the percent (0-1) of predictions that were accurate. + */ + public get accuracy() { + let correctCount = 0; + for (const [, correct] of this.stats) { + if (correct) { + correctCount++; + } + } + + return correctCount / this.stats.length; + } + + /** + * Gets the number of recorded stats. + */ + public get sampleSize() { + return this.stats.length; + } + + /** + * Gets latency stats of successful predictions. + */ + public get latency() { + const latencies = this.stats.filter(([, correct]) => correct).map(([s]) => s).sort(); + + return { + count: latencies.length, + min: latencies[0], + median: latencies[Math.floor(latencies.length / 2)], + max: latencies[latencies.length - 1], + }; + } + + constructor(timeline: PredictionTimeline) { + this.disposables = [ + timeline.onPredictionAdded(p => this.addedAtTime.set(p, Date.now())), + timeline.onPredictionSucceeded(this.pushStat.bind(this, true)), + timeline.onPredictionFailed(this.pushStat.bind(this, false)), + ]; + } + + public dispose() { + this.disposables.forEach(d => d.dispose()); + } + + private pushStat(correct: boolean, prediction: IPrediction) { + const started = this.addedAtTime.get(prediction)!; + this.stats[this.index] = [Date.now() - started, correct]; + this.index = (this.index + 1) % statsBufferSize; + this.changeEmitter.fire(); + } +} class PredictionTimeline { /** @@ -449,8 +520,44 @@ class PredictionTimeline { */ private inputBuffer?: string; + /** + * Whether predictions are echoed to the terminal. If false, predictions + * will still be computed internally for latency metrics, but input will + * never be adjusted. + */ + private showPredictions = false; + + private readonly addedEmitter = new Emitter(); + public readonly onPredictionAdded = this.addedEmitter.event; + private readonly failedEmitter = new Emitter(); + public readonly onPredictionFailed = this.failedEmitter.event; + private readonly succeededEmitter = new Emitter(); + public readonly onPredictionSucceeded = this.succeededEmitter.event; + constructor(public readonly terminal: Terminal) { } + public setShowPredictions(show: boolean) { + if (show === this.showPredictions) { + return; + } + + console.log('set predictions:', show); + this.showPredictions = show; + + const buffer = this.getActiveBuffer(); + if (!buffer) { + return; + } + + const toApply = this.expected.filter(({ gen }) => gen === this.expected[0].gen).map(({ p }) => p); + if (show) { + this.cursor = undefined; + this.terminal.write(toApply.map(p => p.apply(buffer, this.getCursor(buffer))).join('')); + } else { + this.terminal.write(toApply.reverse().map(p => p.rollback(buffer)).join('')); + } + } + /** * Should be called when input is incoming to the temrinal. */ @@ -494,6 +601,7 @@ class PredictionTimeline { const eaten = input.slice(beforeTestReaderIndex, reader.index); output += prediction.rollForwards?.(buffer, eaten) ?? (prediction.rollback(buffer) + input.slice(beforeTestReaderIndex, reader.index)); + this.succeededEmitter.fire(prediction); this.expected.shift(); break; case MatchResult.Buffer: @@ -511,6 +619,7 @@ class PredictionTimeline { .join(''); this.expected = []; this.cursor = undefined; + this.failedEmitter.fire(prediction); break ReadLoop; } } @@ -536,6 +645,10 @@ class PredictionTimeline { } } + if (!this.showPredictions) { + return input; + } + if (output.length === 0) { return ''; } @@ -556,9 +669,13 @@ class PredictionTimeline { */ public addPrediction(buffer: IBuffer, prediction: IPrediction) { this.expected.push({ gen: this.currentGen, p: prediction }); + this.addedEmitter.fire(prediction); + if (this.currentGen === this.expected[0].gen) { const text = prediction.apply(buffer, this.getCursor(buffer)); - this.terminal.write(text); + if (this.showPredictions) { + this.terminal.write(text); + } } } @@ -617,40 +734,94 @@ const parseTypeheadStyle = (style: string | number) => { return `${CSI}32;${r};${g};${b}m`; }; -export class TypeAheadAddon implements ITerminalAddon { - private disposables: IDisposable[] = []; +export class TypeAheadAddon extends Disposable implements ITerminalAddon { private typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle); private typeaheadThreshold = this.config.config.typeaheadThreshold; private lastRow?: { y: number; startingX: number }; private timeline?: PredictionTimeline; - constructor(private readonly _processManager: ITerminalProcessManager, private readonly config: TerminalConfigHelper) { + constructor( + private readonly processManager: ITerminalProcessManager, + private readonly config: TerminalConfigHelper, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { + super(); } public activate(terminal: Terminal): void { - this.timeline = new PredictionTimeline(terminal); - this.disposables.push(terminal.onData(e => this.onUserData(e))); - this.disposables.push(this.config.onConfigChanged(() => { + const timeline = this.timeline = new PredictionTimeline(terminal); + const stats = this._register(new PredictionStats(this.timeline)); + + timeline.setShowPredictions(this.typeaheadThreshold === 0); + this._register(terminal.onData(e => this.onUserData(e))); + this._register(this.config.onConfigChanged(() => { this.typeheadStyle = parseTypeheadStyle(this.config.config.typeaheadStyle); this.typeaheadThreshold = this.config.config.typeaheadThreshold; + this.reevaluatePredictorState(stats, timeline); + })); + this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); + + let nextStatsSend: any; + let reevaluateTimer: any; + this._register(stats.onChange(() => { + if (!nextStatsSend) { + nextStatsSend = setTimeout(() => { + this.sendLatencyStats(stats); + nextStatsSend = undefined; + }, statsSendTelemetryEvery); + } + + // We want to toggle the state only when the user has a pause in their + // typing. Otherwise, we could turn this on when the PTY sent data but + // the terminal cursor is not updated, causes issues. + if (reevaluateTimer) { + clearTimeout(reevaluateTimer); + } + + reevaluateTimer = setTimeout(() => { + this.reevaluatePredictorState(stats, timeline); + reevaluateTimer = undefined; + }, 100); })); - this.disposables.push(this._processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); } - public dispose(): void { - this.disposables.forEach(d => d.dispose()); + private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { + if (this.typeaheadThreshold < 0) { + timeline.setShowPredictions(false); + } else if (this.typeaheadThreshold === 0) { + timeline.setShowPredictions(true); + } else if (stats.sampleSize > statsMinSamplesToTurnOn && stats.accuracy > statsMinAccuracyToTurnOn) { + const latency = stats.latency.median; + if (latency >= this.typeaheadThreshold) { + timeline.setShowPredictions(true); + } else if (latency < this.typeaheadThreshold / statsToggleOffThreshold) { + timeline.setShowPredictions(false); + } + } + } + + private sendLatencyStats(stats: PredictionStats) { + /* __GDPR__ + "terminalLatencyStats" : { + "min" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "max" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "median" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + "predictionAccuracy" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('terminalLatencyStats', { + ...stats.latency, + predictionAccuracy: stats.accuracy, + }); } private onUserData(data: string): void { - if (this.typeaheadThreshold !== 0) { - return; - } - if (this.timeline?.terminal.buffer.active.type !== 'normal') { return; } - // console.log('user data:', JSON.stringify(data)); + console.log('user data:', JSON.stringify(data)); const terminal = this.timeline.terminal; const buffer = terminal.buffer.active; @@ -721,17 +892,13 @@ export class TypeAheadAddon implements ITerminalAddon { } private onBeforeProcessData(event: IBeforeProcessDataEvent): void { - if (this.typeaheadThreshold !== 0) { - return; - } - if (!this.timeline) { return; } - // console.log('incoming data:', JSON.stringify(event.data)); + console.log('incoming data:', JSON.stringify(event.data)); event.data = this.timeline.beforeServerInput(event.data); - // console.log('emitted data:', JSON.stringify(event.data)); + console.log('emitted data:', JSON.stringify(event.data)); // If there's something that looks like a password prompt, omit giving // input. This is approximate since there's no TTY "password here" code, diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 0f3ca52b6c2..87c812cdcf0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -353,7 +353,7 @@ export const terminalConfiguration: IConfigurationNode = { default: true }, 'terminal.integrated.typeaheadThreshold': { - description: localize('terminal.integrated.typeaheadThreshold', "Experimental: length of time, in milliseconds, where typeahead will active. If '0', typeahead will always be on, and if '-1' it will be disabled. Note: currently only -1 and 0 supported."), + description: localize('terminal.integrated.typeaheadThreshold', "Experimental: length of time, in milliseconds, where typeahead will activate. If '0', typeahead will always be on, and if '-1' it will be disabled."), type: 'integer', minimum: -1, default: -1, From b048c2bad1c3dc12e5cb345b2127e90b9801dccb Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 20 Oct 2020 11:32:38 -0700 Subject: [PATCH 095/121] typeahead: comment out debug logs --- .../contrib/terminal/browser/terminalTypeAheadAddon.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 24d0148e9ec..8105cbddbc9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -541,7 +541,7 @@ class PredictionTimeline { return; } - console.log('set predictions:', show); + // console.log('set predictions:', show); this.showPredictions = show; const buffer = this.getActiveBuffer(); @@ -821,7 +821,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { return; } - console.log('user data:', JSON.stringify(data)); + // console.log('user data:', JSON.stringify(data)); const terminal = this.timeline.terminal; const buffer = terminal.buffer.active; @@ -896,9 +896,9 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { return; } - console.log('incoming data:', JSON.stringify(event.data)); + // console.log('incoming data:', JSON.stringify(event.data)); event.data = this.timeline.beforeServerInput(event.data); - console.log('emitted data:', JSON.stringify(event.data)); + // console.log('emitted data:', JSON.stringify(event.data)); // If there's something that looks like a password prompt, omit giving // input. This is approximate since there's no TTY "password here" code, From 2fb8ea0ad1699ca4ed40c1f8f3bf6d1a0794e78d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 20 Oct 2020 11:33:48 -0700 Subject: [PATCH 096/121] typeahead: update gdpr fragment --- .../contrib/terminal/browser/terminalTypeAheadAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 8105cbddbc9..0e2be946c43 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -806,7 +806,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { "min" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "max" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "median" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "predictionAccuracy" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ From 1892ef16f34cd1fae5f228e1d74821e5c5654fac Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 21:31:58 +0200 Subject: [PATCH 097/121] Add Remote Terminals Restoring Co-authored-by: Rob Lourens --- .../api/browser/mainThreadTerminalService.ts | 2 +- .../api/common/extHostTerminalService.ts | 6 +- .../terminal/browser/remoteTerminalService.ts | 160 +++++++++++------- .../contrib/terminal/browser/terminal.ts | 6 +- .../terminal/browser/terminalActions.ts | 14 -- .../terminal/browser/terminalInstance.ts | 56 ++++-- .../browser/terminalProcessExtHostProxy.ts | 6 +- .../browser/terminalProcessManager.ts | 16 +- .../terminal/browser/terminalService.ts | 45 ++++- .../contrib/terminal/browser/terminalTab.ts | 26 ++- .../contrib/terminal/browser/terminalView.ts | 14 +- .../terminal/browser/xterm-private.d.ts | 2 + .../terminal/common/remoteTerminalChannel.ts | 66 ++++++-- .../contrib/terminal/common/terminal.ts | 32 +++- .../terminal/common/terminalDataBuffering.ts | 10 +- 15 files changed, 321 insertions(+), 140 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index c5c03a86786..17b4458a107 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -63,7 +63,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._toDispose.add(_terminalService.onInstanceRequestSpawnExtHostProcess(request => this._onRequestSpawnExtHostProcess(request))); this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e))); this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null))); - this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title))); + this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.id, instance.title))); this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed))); this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e))); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index d722f2b428e..97d30a82f61 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -9,7 +9,7 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalChildProcess, EXT_HOST_CREATION_DELAY, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; import { timeout } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; @@ -245,8 +245,8 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess { public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } private readonly _onProcessTitleChanged = new Emitter(); public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; - private readonly _onProcessOverrideDimensions = new Emitter(); - public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } + private readonly _onProcessOverrideDimensions = new Emitter(); + public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } constructor(private readonly _pty: vscode.Pseudoterminal) { } diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts index 959f572ed2d..68aa3c4b42a 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalService.ts @@ -8,24 +8,21 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { URI } from 'vs/base/common/uri'; -import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IRemoteTerminalService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IRemoteTerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteTerminalProcessExecCommandEvent, IShellLaunchConfigDto, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; -import { IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IProcessDataEvent, IRemoteTerminalAttachTarget, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class RemoteTerminalService extends Disposable implements IRemoteTerminalService { public _serviceBrand: undefined; private readonly _remoteTerminalChannel: RemoteTerminalChannelClient | null; - private _hasConnectedToRemote = false; constructor( - @ITerminalService _terminalService: ITerminalService, @ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @ILogService private readonly _logService: ILogService, @@ -46,34 +43,42 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal throw new Error(`Cannot create remote terminal when there is no remote!`); } - let isPreconnectionTerminal = false; - if (!this._hasConnectedToRemote) { - isPreconnectionTerminal = true; - this._remoteAgentService.getEnvironment().then(() => { - this._hasConnectedToRemote = true; - }); - } + return new RemoteTerminalProcess(terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper, this._remoteTerminalChannel, this._remoteAgentService, this._logService, this._commandService); + } - return new RemoteTerminalProcess(terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper, isPreconnectionTerminal, this._remoteTerminalChannel, this._remoteAgentService, this._logService, this._commandService); + public async listTerminals(): Promise { + const terms = this._remoteTerminalChannel ? await this._remoteTerminalChannel.listTerminals() : []; + return terms.map(termDto => { + return { + id: termDto.id, + pid: termDto.pid, + title: termDto.title, + cwd: termDto.cwd + }; + }); } } export class RemoteTerminalProcess extends Disposable implements ITerminalChildProcess { - public readonly _onProcessData = this._register(new Emitter()); - public readonly onProcessData: Event = this._onProcessData.event; + public readonly _onProcessData = this._register(new Emitter()); + public readonly onProcessData: Event = this._onProcessData.event; private readonly _onProcessExit = this._register(new Emitter()); public readonly onProcessExit: Event = this._onProcessExit.event; public readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } private readonly _onProcessTitleChanged = this._register(new Emitter()); public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); + public readonly onProcessOverrideDimensions: Event = this._onProcessOverrideDimensions.event; private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } private _startBarrier: Barrier; private _remoteTerminalId: number; + private _inReplay = false; + constructor( private readonly _terminalId: number, private readonly _shellLaunchConfig: IShellLaunchConfig, @@ -81,7 +86,6 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP private readonly _cols: number, private readonly _rows: number, private readonly _configHelper: ITerminalConfigHelper, - private readonly _isPreconnectionTerminal: boolean, private readonly _remoteTerminalChannel: RemoteTerminalChannelClient, private readonly _remoteAgentService: IRemoteAgentService, private readonly _logService: ILogService, @@ -94,11 +98,6 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP public async start(): Promise { - // Add a loading title only if this terminal is instantiated before a connection is up and running - if (this._isPreconnectionTerminal) { - setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0); - } - // Fetch the environment to check shell permissions const env = await this._remoteAgentService.getEnvironment(); if (!env) { @@ -106,49 +105,45 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP throw new Error('Could not fetch remote environment'); } - const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(env.os); + if (!this._shellLaunchConfig.remoteAttach) { + const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(env.os); - const shellLaunchConfigDto: IShellLaunchConfigDto = { - name: this._shellLaunchConfig.name, - executable: this._shellLaunchConfig.executable, - args: this._shellLaunchConfig.args, - cwd: this._shellLaunchConfig.cwd, - env: this._shellLaunchConfig.env - }; + const shellLaunchConfigDto: IShellLaunchConfigDto = { + name: this._shellLaunchConfig.name, + executable: this._shellLaunchConfig.executable, + args: this._shellLaunchConfig.args, + cwd: this._shellLaunchConfig.cwd, + env: this._shellLaunchConfig.env + }; - this._logService.trace('Spawning remote agent process', { terminalId: this._terminalId, shellLaunchConfigDto }); + this._logService.trace('Spawning remote agent process', { terminalId: this._terminalId, shellLaunchConfigDto }); - const result = await this._remoteTerminalChannel.createTerminalProcess( - shellLaunchConfigDto, - this._activeWorkspaceRootUri, - this._cols, - this._rows, - isWorkspaceShellAllowed, - ); + const result = await this._remoteTerminalChannel.createTerminalProcess( + shellLaunchConfigDto, + this._activeWorkspaceRootUri, + this._cols, + this._rows, + isWorkspaceShellAllowed, + ); - this._remoteTerminalId = result.terminalId; - this._register(this._remoteTerminalChannel.onTerminalProcessEvent(this._remoteTerminalId)(event => { - switch (event.type) { - case 'ready': - return this._onProcessReady.fire({ pid: event.pid, cwd: event.cwd }); - case 'titleChanged': - return this._onProcessTitleChanged.fire(event.title); - case 'data': - return this._onProcessData.fire(event.data); - case 'exit': - return this._onProcessExit.fire(event.exitCode); - case 'execCommand': - return this._execCommand(event); + this._remoteTerminalId = result.terminalId; + this.setupTerminalEventListener(); + this._onProcessResolvedShellLaunchConfig.fire(reviveIShellLaunchConfig(result.resolvedShellLaunchConfig)); + + const startResult = await this._remoteTerminalChannel.startTerminalProcess(this._remoteTerminalId); + + if (typeof startResult !== 'undefined') { + // An error occurred + return startResult; } - })); + } else { + this._remoteTerminalId = this._shellLaunchConfig.remoteAttach.id; + this._onProcessReady.fire({ pid: this._shellLaunchConfig.remoteAttach.pid, cwd: this._shellLaunchConfig.remoteAttach.cwd }); + this.setupTerminalEventListener(); - this._onProcessResolvedShellLaunchConfig.fire(reviveIShellLaunchConfig(result.resolvedShellLaunchConfig)); - - const startResult = await this._remoteTerminalChannel.startTerminalProcess(this._remoteTerminalId); - - if (typeof startResult !== 'undefined') { - // An error occurred - return startResult; + setTimeout(() => { + this._onProcessTitleChanged.fire(this._shellLaunchConfig.remoteAttach!.title); + }, 0); } this._startBarrier.open(); @@ -162,13 +157,62 @@ export class RemoteTerminalProcess extends Disposable implements ITerminalChildP } public input(data: string): void { + if (this._inReplay) { + return; + } + this._startBarrier.wait().then(_ => { this._remoteTerminalChannel.sendInputToTerminalProcess(this._remoteTerminalId, data); }); } + private setupTerminalEventListener(): void { + this._register(this._remoteTerminalChannel.onTerminalProcessEvent(this._remoteTerminalId)(event => { + switch (event.type) { + case 'ready': + return this._onProcessReady.fire({ pid: event.pid, cwd: event.cwd }); + case 'titleChanged': + return this._onProcessTitleChanged.fire(event.title); + case 'data': + return this._onProcessData.fire({ data: event.data, sync: false }); + case 'replay': { + try { + this._inReplay = true; + + for (const e of event.events) { + if (e.cols !== 0 || e.rows !== 0) { + // never override with 0x0 as that is a marker for an unknown initial size + this._onProcessOverrideDimensions.fire({ cols: e.cols, rows: e.rows, forceExactSize: true }); + } + this._onProcessData.fire({ data: e.data, sync: true }); + } + } finally { + this._inReplay = false; + } + + // remove size override + this._onProcessOverrideDimensions.fire(undefined); + + return; + } + case 'exit': + return this._onProcessExit.fire(event.exitCode); + case 'execCommand': + return this._execCommand(event); + case 'orphan?': { + this._remoteTerminalChannel.orphanQuestionReply(this._remoteTerminalId); + return; + } + } + })); + } + public resize(cols: number, rows: number): void { + if (this._inReplay) { + return; + } this._startBarrier.wait().then(_ => { + this._remoteTerminalChannel.resizeTerminalProcess(this._remoteTerminalId, cols, rows); }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 1e8c415f41e..2ba4774be33 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -7,7 +7,7 @@ import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl'; -import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IWindowsShellHelper, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig, IDefaultShellAndArgsRequest, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, ITerminalProcessExtHostProxy, ICommandTracker, INavigationMode, TitleEventSource, ITerminalDimensions, ITerminalLaunchError, ITerminalNativeWindowsDelegate, LinuxDistro, IRemoteTerminalAttachTarget } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment, Platform } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; @@ -79,6 +79,7 @@ export interface ITerminalService { terminalTabs: ITerminalTab[]; isProcessSupportRegistered: boolean; + initializeTerminals(): Promise; onActiveTabChanged: Event; onTabDisposed: Event; onInstanceCreated: Event; @@ -89,7 +90,7 @@ export interface ITerminalService { onInstanceRequestSpawnExtHostProcess: Event; onInstanceRequestStartExtensionTerminal: Event; onInstancesChanged: Event; - onInstanceTitleChanged: Event; + onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; onRequestAvailableShells: Event; onDidRegisterProcessSupport: Event; @@ -180,6 +181,7 @@ export interface IRemoteTerminalService { dispose(): void; + listTerminals(): Promise; createRemoteTerminalProcess(terminalId: number, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, configHelper: ITerminalConfigHelper,): Promise; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 002bfbabf9c..23bf8cf8cd1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -79,23 +79,9 @@ export class ToggleTerminalAction extends ToggleViewAction { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITerminalService private readonly terminalService: ITerminalService ) { super(id, label, TERMINAL_VIEW_ID, viewsService, viewDescriptorService, contextKeyService, layoutService); } - - async run() { - if (this.terminalService.isProcessSupportRegistered && this.terminalService.terminalInstances.length === 0) { - // If there is not yet an instance attempt to create it here so that we can suggest a - // new shell on Windows (and not do so when the panel is restored on reload). - const newTerminalInstance = this.terminalService.createTerminal(undefined); - const toDispose = newTerminalInstance.onProcessIdReady(() => { - newTerminalInstance.focus(); - toDispose.dispose(); - }); - } - return super.run(); - } } export class KillTerminalAction extends Action { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index cb3b9f19a07..544318a4085 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_VIEW_ID, IWindowsShellHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource, DEFAULT_COMMANDS_TO_SKIP_SHELL, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; @@ -94,7 +94,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _terminalA11yTreeFocusContextKey: IContextKey; private _cols: number = 0; private _rows: number = 0; - private _dimensionsOverride: ITerminalDimensions | undefined; + private _dimensionsOverride: ITerminalDimensionsOverride | undefined; private _windowsShellHelper: IWindowsShellHelper | undefined; private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; @@ -117,12 +117,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get id(): number { return this._id; } public get cols(): number { if (this._dimensionsOverride && this._dimensionsOverride.cols) { + if (this._dimensionsOverride.forceExactSize) { + return this._dimensionsOverride.cols; + } return Math.min(Math.max(this._dimensionsOverride.cols, 2), this._cols); } return this._cols; } public get rows(): number { if (this._dimensionsOverride && this._dimensionsOverride.rows) { + if (this._dimensionsOverride.forceExactSize) { + return this._dimensionsOverride.rows; + } return Math.min(Math.max(this._dimensionsOverride.rows, 2), this._rows); } return this._rows; @@ -415,7 +421,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._xterm.onKey(e => this._onKey(e.key, e.domEvent)); this._xterm.onSelectionChange(async () => this._onSelectionChange()); - this._processManager.onProcessData(data => this._onProcessData(data)); + this._processManager.onProcessData(e => this._onProcessData(e)); this._xterm.onData(data => this._processManager.write(data)); this.processReady.then(async () => { if (this._linkManager) { @@ -882,11 +888,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); - this._processManager.onProcessData(data => { - this._initialDataEvents?.push(data); - this._onData.fire(data); + this._processManager.onProcessData(ev => { + this._initialDataEvents?.push(ev.data); + this._onData.fire(ev.data); }); - this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e)); + this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e, true)); this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e)); this._processManager.onEnvironmentVariableInfoChanged(e => this._onEnvironmentVariableInfoChanged(e)); @@ -959,9 +965,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - private _onProcessData(data: string): void { - const messageId = ++this._latestXtermWriteData; - this._xterm?.write(data, () => this._latestXtermParseData = messageId); + private _onProcessData(ev: IProcessDataEvent): void { + if (ev.sync) { + this._xtermCore?.writeSync(ev.data); + } else { + const messageId = ++this._latestXtermWriteData; + this._xterm?.write(ev.data, () => this._latestXtermParseData = messageId); + } } /** @@ -1349,6 +1359,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { @debounce(50) private async _resize(): Promise { + this._resizeNow(false); + } + + private async _resizeNow(immediate: boolean): Promise { let cols = this.cols; let rows = this.rows; @@ -1398,8 +1412,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - await this._processManager.ptyProcessReady; - this._processManager.setDimensions(cols, rows); + if (immediate) { + // do not await, call setDimensions synchronously + this._processManager.setDimensions(cols, rows); + } else { + await this._processManager.ptyProcessReady; + this._processManager.setDimensions(cols, rows); + } } public setShellType(shellType: TerminalShellType) { @@ -1442,9 +1461,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return this._titleReadyPromise; } - public setDimensions(dimensions: ITerminalDimensions | undefined): void { + public setDimensions(dimensions: ITerminalDimensionsOverride | undefined, immediate: boolean = false): void { + if (this._dimensionsOverride && this._dimensionsOverride.forceExactSize && !dimensions && this._rows === 0 && this._cols === 0) { + // this terminal never had a real size => keep the last dimensions override exact size + this._cols = this._dimensionsOverride.cols; + this._rows = this._dimensionsOverride.rows; + } this._dimensionsOverride = dimensions; - this._resize(); + if (immediate) { + this._resizeNow(true); + } else { + this._resize(); + } } private _setResolvedShellLaunchConfig(shellLaunchConfig: IShellLaunchConfig): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 84f18d3c998..c7069f89ff0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper, ITerminalDimensions, ITerminalLaunchError, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -23,8 +23,8 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } private readonly _onProcessTitleChanged = this._register(new Emitter()); public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; - private readonly _onProcessOverrideDimensions = this._register(new Emitter()); - public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); + public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 0dd449b36f4..52fb61c0ca7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -6,7 +6,7 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { env as processEnv } from 'vs/base/common/process'; -import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalDimensions, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalLaunchError, IProcessDataEvent, ITerminalDimensionsOverride } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -69,14 +69,14 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce public get onProcessReady(): Event { return this._onProcessReady.event; } private readonly _onBeforeProcessData = this._register(new Emitter()); public get onBeforeProcessData(): Event { return this._onBeforeProcessData.event; } - private readonly _onProcessData = this._register(new Emitter()); - public get onProcessData(): Event { return this._onProcessData.event; } + private readonly _onProcessData = this._register(new Emitter()); + public get onProcessData(): Event { return this._onProcessData.event; } private readonly _onProcessTitle = this._register(new Emitter()); public get onProcessTitle(): Event { return this._onProcessTitle.event; } private readonly _onProcessExit = this._register(new Emitter()); public get onProcessExit(): Event { return this._onProcessExit.event; } - private readonly _onProcessOverrideDimensions = this._register(new Emitter()); - public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); + public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } private readonly _onProcessOverrideShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessOverrideShellLaunchConfig.event; } private readonly _onEnvironmentVariableInfoChange = this._register(new Emitter()); @@ -171,11 +171,13 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this.processState = ProcessState.LAUNCHING; - this._process.onProcessData(data => { + this._process.onProcessData(ev => { + const data = (typeof ev === 'string' ? ev : ev.data); + const sync = (typeof ev === 'string' ? false : ev.sync); const beforeProcessDataEvent: IBeforeProcessDataEvent = { data }; this._onBeforeProcessData.fire(beforeProcessDataEvent); if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) { - this._onProcessData.fire(beforeProcessDataEvent.data); + this._onProcessData.fire({ data: beforeProcessDataEvent.data, sync }); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index f6500de4078..59e19ad211a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -14,7 +14,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, ITerminalExternalLinkProvider, IRemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; @@ -85,8 +85,8 @@ export class TerminalService implements ITerminalService { public get onInstanceMaximumDimensionsChanged(): Event { return this._onInstanceMaximumDimensionsChanged.event; } private readonly _onInstancesChanged = new Emitter(); public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } - private readonly _onInstanceTitleChanged = new Emitter(); - public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } + private readonly _onInstanceTitleChanged = new Emitter(); + public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } private readonly _onActiveInstanceChanged = new Emitter(); public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } private readonly _onTabDisposed = new Emitter(); @@ -108,7 +108,8 @@ export class TerminalService implements ITerminalService { @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -221,7 +222,7 @@ export class TerminalService implements ITerminalService { } public getTabLabels(): string[] { - return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); + return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); } public getFindState(): FindReplaceState { @@ -339,6 +340,28 @@ export class TerminalService implements ITerminalService { } } + public async initializeTerminals(): Promise { + if (!!this._environmentService.remoteAuthority) { + const emptyTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, undefined); + this._terminalTabs.push(emptyTab); + this._onInstanceTitleChanged.fire(undefined); + const remoteTerms = await this._remoteTerminalService.listTerminals(); + if (remoteTerms.length > 0) { + // Reattach to all remote terms + this.createTerminal({ remoteAttach: remoteTerms[0] }, emptyTab); + for (let term of remoteTerms.slice(1)) { + this.createTerminal({ remoteAttach: term }); + } + } else { + // Remote, no terminals to attach to + this.createTerminal(undefined, emptyTab); + } + } else { + // Local, just create a terminal + this.createTerminal(); + } + } + private _getInstanceFromGlobalInstanceIndex(index: number): { tab: ITerminalTab, tabIndex: number, instance: ITerminalInstance, localInstanceIndex: number } | null { let currentTabIndex = 0; while (index >= 0 && currentTabIndex < this._terminalTabs.length) { @@ -610,7 +633,7 @@ export class TerminalService implements ITerminalService { return instance; } - public createTerminal(shell: IShellLaunchConfig = {}): ITerminalInstance { + public createTerminal(shell: IShellLaunchConfig = {}, terminalTab?: TerminalTab): ITerminalInstance { if (!this.isProcessSupportRegistered) { throw new Error('Could not create terminal when process support is not registered'); } @@ -620,8 +643,14 @@ export class TerminalService implements ITerminalService { this._initInstanceListeners(instance); return instance; } - const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); - this._terminalTabs.push(terminalTab); + + if (terminalTab) { + terminalTab.addInstance(shell); + } else { + terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); + this._terminalTabs.push(terminalTab); + } + const instance = terminalTab.terminalInstances[0]; terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index eb45ee52381..a798660b2f7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IShellLaunchConfig, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -228,7 +229,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { constructor( private _container: HTMLElement | undefined, - shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance, + shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance | undefined, @ITerminalService private readonly _terminalService: ITerminalService, @IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, @@ -236,6 +237,18 @@ export class TerminalTab extends Disposable implements ITerminalTab { ) { super(); + if (shellLaunchConfigOrInstance) { + this.addInstance(shellLaunchConfigOrInstance); + } + + this._activeInstanceIndex = 0; + + if (this._container) { + this.attachToElement(this._container); + } + } + + public addInstance(shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance): void { let instance: ITerminalInstance; if ('id' in shellLaunchConfigOrInstance) { instance = shellLaunchConfigOrInstance; @@ -244,11 +257,12 @@ export class TerminalTab extends Disposable implements ITerminalTab { } this._terminalInstances.push(instance); this._initInstanceListeners(instance); - this._activeInstanceIndex = 0; - if (this._container) { - this.attachToElement(this._container); + if (this._splitPaneContainer) { + this._splitPaneContainer!.split(instance); } + + this._onInstancesChanged.fire(); } public dispose(): void { @@ -358,6 +372,10 @@ export class TerminalTab extends Disposable implements ITerminalTab { } public get title(): string { + if (!this.terminalInstances.length) { + return nls.localize('terminal.integrated.starting', "Starting..."); + } + let title = this.terminalInstances[0].title; for (let i = 1; i < this.terminalInstances.length; i++) { if (this.terminalInstances[i].title) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index ae87e42d990..8d9a84d5116 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -41,6 +41,7 @@ export class TerminalViewPane extends ViewPane { private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; private _splitTerminalAction: IAction | undefined; + private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; constructor( @@ -109,14 +110,21 @@ export class TerminalViewPane extends ViewPane { this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { - const hadTerminals = this._terminalService.terminalInstances.length > 0; - if (!hadTerminals) { - this._terminalService.createTerminal(); + const hadTerminals = !!this._terminalService.terminalTabs.length; + if (this._terminalsInitialized) { + if (!hadTerminals) { + this._terminalService.createTerminal(); + } + } else { + this._terminalsInitialized = true; + this._terminalService.initializeTerminals(); } + this._updateTheme(); if (hadTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } else { + // TODO@Tyriar - this call seems unnecessary this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width); } } else { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts index c559ff7b8db..3ae0210352d 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts @@ -26,6 +26,8 @@ export interface XTermCore { }; _onIntersectionChange: any; }; + + writeSync(data: string | Uint8Array): void; } export interface IEventEmitter { diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts index c84ca3c0819..7ee9b719d9c 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts @@ -116,6 +116,21 @@ export interface ISendCommandResultToTerminalProcessArguments { payload: any; } +export interface IOrphanQuestionReplyArgs { + id: number; +} + +export interface IRemoteTerminalDescriptionDto { + id: number; + pid: number; + title: string; + cwd: string; +} + +export interface ITriggerTerminalDataReplayArguments { + id: number; +} + export interface IRemoteTerminalProcessReadyEvent { type: 'ready'; pid: number; @@ -126,11 +141,16 @@ export interface IRemoteTerminalProcessTitleChangedEvent { title: string; } export interface IRemoteTerminalProcessDataEvent { - type: 'data' + type: 'data'; data: string; } +export interface ReplayEntry { cols: number; rows: number; data: string; } +export interface IRemoteTerminalProcessReplayEvent { + type: 'replay'; + events: ReplayEntry[]; +} export interface IRemoteTerminalProcessExitEvent { - type: 'exit' + type: 'exit'; exitCode: number | undefined; } export interface IRemoteTerminalProcessExecCommandEvent { @@ -139,12 +159,17 @@ export interface IRemoteTerminalProcessExecCommandEvent { commandId: string; commandArgs: any[]; } +export interface IRemoteTerminalProcessOrphanQuestionEvent { + type: 'orphan?'; +} export type IRemoteTerminalProcessEvent = ( IRemoteTerminalProcessReadyEvent | IRemoteTerminalProcessTitleChangedEvent | IRemoteTerminalProcessDataEvent + | IRemoteTerminalProcessReplayEvent | IRemoteTerminalProcessExitEvent | IRemoteTerminalProcessExecCommandEvent + | IRemoteTerminalProcessOrphanQuestionEvent ); export interface IOnTerminalProcessEventArguments { @@ -166,7 +191,7 @@ export class RemoteTerminalChannelClient { ) { } - private _readSingleTemrinalConfiguration(key: string): ISingleTerminalConfiguration { + private _readSingleTerminalConfiguration(key: string): ISingleTerminalConfiguration { const result = this._configurationService.inspect(key); return { userValue: result.userValue, @@ -178,18 +203,18 @@ export class RemoteTerminalChannelClient { public async createTerminalProcess(shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { const terminalConfig = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); const configuration: ICompleteTerminalConfiguration = { - 'terminal.integrated.automationShell.windows': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.windows'), - 'terminal.integrated.automationShell.osx': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.osx'), - 'terminal.integrated.automationShell.linux': this._readSingleTemrinalConfiguration('terminal.integrated.automationShell.linux'), - 'terminal.integrated.shell.windows': this._readSingleTemrinalConfiguration('terminal.integrated.shell.windows'), - 'terminal.integrated.shell.osx': this._readSingleTemrinalConfiguration('terminal.integrated.shell.osx'), - 'terminal.integrated.shell.linux': this._readSingleTemrinalConfiguration('terminal.integrated.shell.linux'), - 'terminal.integrated.shellArgs.windows': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.windows'), - 'terminal.integrated.shellArgs.osx': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.osx'), - 'terminal.integrated.shellArgs.linux': this._readSingleTemrinalConfiguration('terminal.integrated.shellArgs.linux'), - 'terminal.integrated.env.windows': this._readSingleTemrinalConfiguration('terminal.integrated.env.windows'), - 'terminal.integrated.env.osx': this._readSingleTemrinalConfiguration('terminal.integrated.env.osx'), - 'terminal.integrated.env.linux': this._readSingleTemrinalConfiguration('terminal.integrated.env.linux'), + 'terminal.integrated.automationShell.windows': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.windows'), + 'terminal.integrated.automationShell.osx': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.osx'), + 'terminal.integrated.automationShell.linux': this._readSingleTerminalConfiguration('terminal.integrated.automationShell.linux'), + 'terminal.integrated.shell.windows': this._readSingleTerminalConfiguration('terminal.integrated.shell.windows'), + 'terminal.integrated.shell.osx': this._readSingleTerminalConfiguration('terminal.integrated.shell.osx'), + 'terminal.integrated.shell.linux': this._readSingleTerminalConfiguration('terminal.integrated.shell.linux'), + 'terminal.integrated.shellArgs.windows': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.windows'), + 'terminal.integrated.shellArgs.osx': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.osx'), + 'terminal.integrated.shellArgs.linux': this._readSingleTerminalConfiguration('terminal.integrated.shellArgs.linux'), + 'terminal.integrated.env.windows': this._readSingleTerminalConfiguration('terminal.integrated.env.windows'), + 'terminal.integrated.env.osx': this._readSingleTerminalConfiguration('terminal.integrated.env.osx'), + 'terminal.integrated.env.linux': this._readSingleTerminalConfiguration('terminal.integrated.env.linux'), 'terminal.integrated.inheritEnv': terminalConfig.inheritEnv, 'terminal.integrated.cwd': terminalConfig.cwd, 'terminal.integrated.detectLocale': terminalConfig.detectLocale, @@ -306,4 +331,15 @@ export class RemoteTerminalChannelClient { }; return this._channel.call('$sendCommandResultToTerminalProcess', args); } + + public orphanQuestionReply(id: number): Promise { + const args: IOrphanQuestionReplyArgs = { + id + }; + return this._channel.call('$orphanQuestionReply', args); + } + + public listTerminals(): Promise { + return this._channel.call('$listTerminals'); + } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index c8e63273704..83f5b682d17 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -165,6 +165,13 @@ export interface ITerminalEnvironment { [key: string]: string | null; } +export interface IRemoteTerminalAttachTarget { + id: number; + pid: number; + title: string; + cwd: string; +} + export interface IShellLaunchConfig { /** * The name of the terminal, if this is not set the name of the process will be used. @@ -217,6 +224,11 @@ export interface IShellLaunchConfig { */ isExtensionTerminal?: boolean; + /** + * This is a terminal that attaches to an already running remote terminal. + */ + remoteAttach?: { id: number; pid: number; title: string; cwd: string; }; + /** * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the @@ -268,6 +280,13 @@ export interface ITerminalDimensions { readonly rows: number; } +export interface ITerminalDimensionsOverride extends ITerminalDimensions { + /** + * indicate that xterm must receive these exact dimensions, even if they overflow the ui! + */ + forceExactSize?: boolean; +} + export interface ICommandTracker { scrollToPreviousCommand(): void; scrollToNextCommand(): void; @@ -291,6 +310,11 @@ export interface IBeforeProcessDataEvent { data: string; } +export interface IProcessDataEvent { + data: string; + sync: boolean; +} + export interface ITerminalProcessManager extends IDisposable { readonly processState: ProcessState; readonly ptyProcessReady: Promise; @@ -302,10 +326,10 @@ export interface ITerminalProcessManager extends IDisposable { readonly onProcessReady: Event; readonly onBeforeProcessData: Event; - readonly onProcessData: Event; + readonly onProcessData: Event; readonly onProcessTitle: Event; readonly onProcessExit: Event; - readonly onProcessOverrideDimensions: Event; + readonly onProcessOverrideDimensions: Event; readonly onProcessResolvedShellLaunchConfig: Event; readonly onEnvironmentVariableInfoChanged: Event; @@ -416,11 +440,11 @@ export interface ITerminalLaunchError { * child_process.ChildProcess node.js interface. */ export interface ITerminalChildProcess { - onProcessData: Event; + onProcessData: Event; onProcessExit: Event; onProcessReady: Event<{ pid: number, cwd: string }>; onProcessTitleChanged: Event; - onProcessOverrideDimensions?: Event; + onProcessOverrideDimensions?: Event; onProcessResolvedShellLaunchConfig?: Event; /** diff --git a/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts b/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts index 82f3fe6f1d0..db8c7f57d9b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalDataBuffering.ts @@ -5,6 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; interface TerminalDataBuffer extends IDisposable { data: string[]; @@ -23,19 +24,20 @@ export class TerminalDataBufferer implements IDisposable { } } - startBuffering(id: number, event: Event, throttleBy: number = 5): IDisposable { + startBuffering(id: number, event: Event, throttleBy: number = 5): IDisposable { let disposable: IDisposable; - disposable = event((e: string) => { + disposable = event((e: string | IProcessDataEvent) => { + const data = (typeof e === 'string' ? e : e.data); let buffer = this._terminalBufferMap.get(id); if (buffer) { - buffer.data.push(e); + buffer.data.push(data); return; } const timeoutId = setTimeout(() => this._flushBuffer(id), throttleBy); buffer = { - data: [e], + data: [data], timeoutId: timeoutId, dispose: () => { clearTimeout(timeoutId); From 709d8bbcda5b3a6cb9ad90d77269f53171ac5c88 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 21:58:38 +0200 Subject: [PATCH 098/121] Fix integration tests --- .../workbench/contrib/terminal/browser/terminalService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 59e19ad211a..ab95ad81762 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -357,8 +357,10 @@ export class TerminalService implements ITerminalService { this.createTerminal(undefined, emptyTab); } } else { - // Local, just create a terminal - this.createTerminal(); + if (this.terminalInstances.length === 0) { + // Local, just create a terminal + this.createTerminal(); + } } } From 8684c6d87533ccd8c7562f58feb0a2aad3f2a26e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 22:06:18 +0200 Subject: [PATCH 099/121] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eaef8f06d68..282a55d8e88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.51.0", - "distro": "2efd579dedb12e7e9ed9ebd112120fa8b3b0922e", + "distro": "5a2a07b357e780fb7130743e24cf814126485ef9", "author": { "name": "Microsoft Corporation" }, From 99b0fba46ee461889bd6e2acc439548124dcaef6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Oct 2020 22:11:16 +0200 Subject: [PATCH 100/121] Server spawn by default --- .../contrib/terminal/browser/terminalProcessManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 52fb61c0ca7..d9bb4929c97 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -158,8 +158,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce } const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); - const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue('terminal.integrated.serverSpawn'); - if (enableRemoteAgentTerminals) { + const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue('terminal.integrated.serverSpawn'); + if (enableRemoteAgentTerminals !== false) { this._process = await this._remoteTerminalService.createRemoteTerminalProcess(this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper); } else { this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper); From f0ba6e4a6b4f3939e7fc54f94c1c8c1ecedeeb4e Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 20 Oct 2020 21:50:26 +0000 Subject: [PATCH 101/121] Add background color to lightbulb widget --- src/vs/editor/contrib/codeAction/lightBulbWidget.css | 12 ++++-------- src/vs/editor/contrib/codeAction/lightBulbWidget.ts | 6 +++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.css b/src/vs/editor/contrib/codeAction/lightBulbWidget.css index aadcb4fd6e6..29c4fe0c043 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.css +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.css @@ -3,18 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .lightbulb-glyph, -.monaco-editor .codicon-lightbulb { +.monaco-editor .contentWidgets .codicon-light-bulb, +.monaco-editor .contentWidgets .codicon-lightbulb-autofix { display: flex; align-items: center; justify-content: center; - height: 16px; - width: 20px; - padding-left: 2px; } -.monaco-editor .lightbulb-glyph:hover, -.monaco-editor .codicon-lightbulb:hover { +.monaco-editor .contentWidgets .codicon-light-bulb:hover, +.monaco-editor .contentWidgets .codicon-lightbulb-autofix:hover { cursor: pointer; - /* transform: scale(1.3, 1.3); */ } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 2d95a32a5ae..69906d79d32 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -16,7 +16,7 @@ import * as nls from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorLightBulbForeground, editorLightBulbAutoFixForeground, editorBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { Gesture } from 'vs/base/browser/touch'; import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { Codicon } from 'vs/base/common/codicons'; @@ -233,12 +233,15 @@ export class LightBulbWidget extends Disposable implements IContentWidget { registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const editorBackgroundColor = theme.getColor(editorBackground)?.transparent(0.7); + // Lightbulb Icon const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground); if (editorLightBulbForegroundColor) { collector.addRule(` .monaco-editor .contentWidgets ${Codicon.lightBulb.cssSelector} { color: ${editorLightBulbForegroundColor}; + background-color: ${editorBackgroundColor}; }`); } @@ -248,6 +251,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = collector.addRule(` .monaco-editor .contentWidgets ${Codicon.lightbulbAutofix.cssSelector} { color: ${editorLightBulbAutoFixForegroundColor}; + background-color: ${editorBackgroundColor}; }`); } From 434bbca013960e3ebbf1fd6b8e992947965fc32e Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 20 Oct 2020 14:59:19 -0700 Subject: [PATCH 102/121] Remove unused variable --- src/vs/editor/contrib/codeAction/lightBulbWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 69906d79d32..8eac58026d9 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -16,7 +16,7 @@ import * as nls from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { editorLightBulbForeground, editorLightBulbAutoFixForeground, editorBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { editorLightBulbForeground, editorLightBulbAutoFixForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { Gesture } from 'vs/base/browser/touch'; import type { CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; import { Codicon } from 'vs/base/common/codicons'; From a6067d7f3d7be2a8339ac10d331e8e149ee6e2f1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 20 Oct 2020 15:20:49 -0700 Subject: [PATCH 103/121] Only reattach to terminals when serverSpawn is enabled --- src/vs/workbench/contrib/terminal/browser/terminalService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index ab95ad81762..9f8a4350bb2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -108,6 +108,7 @@ export class TerminalService implements ITerminalService { @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService ) { @@ -341,7 +342,8 @@ export class TerminalService implements ITerminalService { } public async initializeTerminals(): Promise { - if (!!this._environmentService.remoteAuthority) { + const enableRemoteAgentTerminals = this._workspaceConfigurationService.getValue('terminal.integrated.serverSpawn'); + if (!!this._environmentService.remoteAuthority && enableRemoteAgentTerminals !== false) { const emptyTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, undefined); this._terminalTabs.push(emptyTab); this._onInstanceTitleChanged.fire(undefined); From 9e3af791ff6e29f9fad34e4131ae6c759bd6a63b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 20 Oct 2020 15:53:59 -0700 Subject: [PATCH 104/121] fixes #107752 --- src/vs/workbench/browser/parts/views/treeView.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index ebe6736547c..3669114bf2a 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -46,6 +46,9 @@ export class TreeViewPane extends ViewPane { if (options.title !== this.treeView.title) { this.updateTitle(this.treeView.title); } + if (options.titleDescription !== this.treeView.description) { + this.updateTitleDescription(this.treeView.description); + } this.updateTreeVisibility(); } From 3ccf7adcf7d4a171581c491cb0f306dc724991ed Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 20 Oct 2020 23:09:14 +0000 Subject: [PATCH 105/121] terminal: add some unit tests for typeahead --- .../browser/terminalTypeAheadAddon.ts | 79 ++--- .../test/browser/terminalTypeahead.test.ts | 316 ++++++++++++++++++ 2 files changed, 356 insertions(+), 39 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 0e2be946c43..923cf9bae3c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; +import { debounce } from 'vs/base/common/decorators'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from 'xterm'; +import type { IBuffer, IBufferCell, ITerminalAddon, Terminal } from 'xterm'; const ESC = '\x1b'; const CSI = `${ESC}[`; @@ -21,7 +22,7 @@ const CSI_MOVE_RE = /^\x1b\[([0-9]*)(;[35])?O?([DC])/; const PASSWORD_INPUT_RE = /(password|passphrase|passwd).*:/i; const NOT_WORD_RE = /\W/; -const statsBufferSize = 25; +const statsBufferSize = 24; const statsSendTelemetryEvery = 1000 * 60 * 5; // how often to collect stats const statsMinSamplesToTurnOn = 5; const statsMinAccuracyToTurnOn = 0.3; @@ -91,7 +92,7 @@ const enum MatchResult { Buffer, } -interface IPrediction { +export interface IPrediction { /** * Returns a sequence to apply the prediction. * @param buffer to write to @@ -265,7 +266,7 @@ class CharacterPrediction implements IPrediction { : { ...cursor, oldAttributes: '', oldChar: '' }; cursor.x++; - return this.style + this.char + `${CSI}0m`; + return this.style + this.char + this.appliedAt.oldAttributes; } public rollback(buffer: IBuffer) { @@ -312,16 +313,25 @@ class BackspacePrediction extends CharacterPrediction { return setCursorCoordinate(buffer, cursor) + DELETE_CHAR; } + public rollForwards() { + return ''; + } + public matches(input: StringReader) { - // if at end of line, allow backspace + clear line. Zsh does this. - if (this.appliedAt?.oldChar === '') { - const r = input.eatGradually(`\b${CSI}K`); - if (r !== MatchResult.Failure) { - return r; + const isEOL = this.appliedAt?.oldChar === ''; + if (isEOL) { + const r1 = input.eatGradually(`\b${CSI}K`); + if (r1 !== MatchResult.Failure) { + return r1; + } + + const r2 = input.eatGradually(`\b \b`); + if (r2 !== MatchResult.Failure) { + return r2; } } - return input.eatGradually('\b'); + return MatchResult.Failure; } } @@ -433,11 +443,10 @@ class CursorMovePrediction implements IPrediction { } } -class PredictionStats implements IDisposable { +export class PredictionStats extends Disposable { private readonly stats: [latency: number, correct: boolean][] = []; private index = 0; private readonly addedAtTime = new WeakMap(); - private readonly disposables: IDisposable[] = []; private readonly changeEmitter = new Emitter(); public readonly onChange = this.changeEmitter.event; @@ -452,7 +461,7 @@ class PredictionStats implements IDisposable { } } - return correctCount / this.stats.length; + return correctCount / (this.stats.length || 1); } /** @@ -477,15 +486,10 @@ class PredictionStats implements IDisposable { } constructor(timeline: PredictionTimeline) { - this.disposables = [ - timeline.onPredictionAdded(p => this.addedAtTime.set(p, Date.now())), - timeline.onPredictionSucceeded(this.pushStat.bind(this, true)), - timeline.onPredictionFailed(this.pushStat.bind(this, false)), - ]; - } - - public dispose() { - this.disposables.forEach(d => d.dispose()); + super(); + this._register(timeline.onPredictionAdded(p => this.addedAtTime.set(p, Date.now()))); + this._register(timeline.onPredictionSucceeded(this.pushStat.bind(this, true))); + this._register(timeline.onPredictionFailed(this.pushStat.bind(this, false))); } private pushStat(correct: boolean, prediction: IPrediction) { @@ -496,7 +500,7 @@ class PredictionStats implements IDisposable { } } -class PredictionTimeline { +export class PredictionTimeline { /** * Expected queue of events. Only predictions for the lowest are * written into the terminal. @@ -649,16 +653,15 @@ class PredictionTimeline { return input; } - if (output.length === 0) { - return ''; + if (output.length === 0 || output === input) { + return output; } if (this.cursor) { output += setCursorCoordinate(buffer, this.cursor); } - // prevent cursor flickering while typing, since output will *always* - // contains cursor moves if we did anything with predictions: + // prevent cursor flickering while typing output = HIDE_CURSOR + output + SHOW_CURSOR; return output; @@ -739,6 +742,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { private typeaheadThreshold = this.config.config.typeaheadThreshold; private lastRow?: { y: number; startingX: number }; private timeline?: PredictionTimeline; + public stats?: PredictionStats; constructor( private readonly processManager: ITerminalProcessManager, @@ -750,7 +754,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { public activate(terminal: Terminal): void { const timeline = this.timeline = new PredictionTimeline(terminal); - const stats = this._register(new PredictionStats(this.timeline)); + const stats = this.stats = this._register(new PredictionStats(this.timeline)); timeline.setShowPredictions(this.typeaheadThreshold === 0); this._register(terminal.onData(e => this.onUserData(e))); @@ -762,7 +766,6 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); let nextStatsSend: any; - let reevaluateTimer: any; this._register(stats.onChange(() => { if (!nextStatsSend) { nextStatsSend = setTimeout(() => { @@ -771,20 +774,18 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { }, statsSendTelemetryEvery); } - // We want to toggle the state only when the user has a pause in their - // typing. Otherwise, we could turn this on when the PTY sent data but - // the terminal cursor is not updated, causes issues. - if (reevaluateTimer) { - clearTimeout(reevaluateTimer); - } - - reevaluateTimer = setTimeout(() => { this.reevaluatePredictorState(stats, timeline); - reevaluateTimer = undefined; - }, 100); })); } + /** + * Note on debounce: + * + * We want to toggle the state only when the user has a pause in their + * typing. Otherwise, we could turn this on when the PTY sent data but the + * terminal cursor is not updated, causes issues. + */ + @debounce(100) private reevaluatePredictorState(stats: PredictionStats, timeline: PredictionTimeline) { if (this.typeaheadThreshold < 0) { timeline.setShowPredictions(false); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts new file mode 100644 index 00000000000..6fdd2cbbc78 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalTypeahead.test.ts @@ -0,0 +1,316 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Terminal } from 'xterm'; +import { SinonStub, stub, useFakeTimers } from 'sinon'; +import { Emitter } from 'vs/base/common/event'; +import { IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon'; +import { IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +const CSI = `\x1b[`; + +suite('Workbench - Terminal Typeahead', () => { + suite('PredictionStats', () => { + let stats: PredictionStats; + const add = new Emitter(); + const succeed = new Emitter(); + const fail = new Emitter(); + + setup(() => { + stats = new PredictionStats({ + onPredictionAdded: add.event, + onPredictionSucceeded: succeed.event, + onPredictionFailed: fail.event, + } as any); + }); + + test('creates sane data', () => { + const stubs = createPredictionStubs(5); + const clock = useFakeTimers(); + try { + for (const s of stubs) { add.fire(s); } + + for (let i = 0; i < stubs.length; i++) { + clock.tick(100); + (i % 2 ? fail : succeed).fire(stubs[i]); + } + + assert.strictEqual(stats.accuracy, 3 / 5); + assert.strictEqual(stats.sampleSize, 5); + assert.deepStrictEqual(stats.latency, { + count: 3, + min: 100, + max: 500, + median: 300 + }); + } finally { + clock.restore(); + } + }); + + test('circular buffer', () => { + const bufferSize = 24; + const stubs = createPredictionStubs(bufferSize * 2); + + for (const s of stubs.slice(0, bufferSize)) { add.fire(s); succeed.fire(s); } + assert.strictEqual(stats.accuracy, 1); + + for (const s of stubs.slice(bufferSize, bufferSize * 3 / 2)) { add.fire(s); fail.fire(s); } + assert.strictEqual(stats.accuracy, 0.5); + + for (const s of stubs.slice(bufferSize * 3 / 2)) { add.fire(s); fail.fire(s); } + assert.strictEqual(stats.accuracy, 0); + }); + }); + + suite('timeline', () => { + const onBeforeProcessData = new Emitter(); + const onConfigChanged = new Emitter(); + let publicLog: SinonStub; + let config: ITerminalConfiguration; + let addon: TypeAheadAddon; + + + const predictedHelloo = [ + `${CSI}?25l`, // hide cursor + `${CSI}2;7H`, // move cursor cursor + `${CSI}X`, // delete character + 'o', // new character + `${CSI}2;8H`, // place cursor back at end of line + `${CSI}?25h`, // show cursor + ].join(''); + + const expectProcessed = (input: string, output: string) => { + const evt = { data: input }; + onBeforeProcessData.fire(evt); + assert.strictEqual(JSON.stringify(evt.data), JSON.stringify(output)); + }; + + setup(() => { + config = upcastPartial({ + typeaheadStyle: 3, + typeaheadThreshold: 0 + }); + publicLog = stub(); + addon = new TypeAheadAddon( + upcastPartial({ onBeforeProcessData: onBeforeProcessData.event }), + upcastPartial({ config, onConfigChanged: onConfigChanged.event }), + upcastPartial({ publicLog }) + ); + }); + + teardown(() => { + addon.dispose(); + }); + + test('predicts a single character', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('o'); + t.expectWritten(`${CSI}3mo`); + }); + + test('validates character prediction', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('o'); + expectProcessed('o', predictedHelloo); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('rolls back character prediction', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('o'); + + expectProcessed('q', [ + `${CSI}?25l`, // hide cursor + `${CSI}2;7H`, // move cursor cursor + `${CSI}X`, // delete character + 'q', // new character + `${CSI}?25h`, // show cursor + ].join('')); + assert.strictEqual(addon.stats?.accuracy, 0); + }); + + test('validates against and applies graphics mode on predicted', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('o'); + expectProcessed(`${CSI}4mo`, [ + `${CSI}?25l`, // hide cursor + `${CSI}2;7H`, // move cursor cursor + `${CSI}X`, // delete character + `${CSI}4m`, // PTY's style + 'o', // new character + `${CSI}2;8H`, // place cursor back at end of line + `${CSI}?25h`, // show cursor + ].join('')); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('ignores cursor hides or shows', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('o'); + expectProcessed(`${CSI}?25lo${CSI}?25h`, [ + `${CSI}?25l`, // hide cursor from PTY + `${CSI}?25l`, // hide cursor + `${CSI}2;7H`, // move cursor cursor + `${CSI}X`, // delete character + 'o', // new character + `${CSI}?25h`, // show cursor from PTY + `${CSI}2;8H`, // place cursor back at end of line + `${CSI}?25h`, // show cursor + ].join('')); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('matches backspace at EOL (bash style)', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('\x7F'); + expectProcessed(`\b${CSI}K`, `\b${CSI}K`); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('matches backspace at EOL (zsh style)', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('\x7F'); + expectProcessed('\b \b', '\b \b'); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('gradually matches backspace', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + t.onData('\x7F'); + expectProcessed('\b', ''); + expectProcessed(' \b', '\b \b'); + assert.strictEqual(addon.stats?.accuracy, 1); + }); + + test('waits for validation before deleting to left of cursor', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + + // initially should not backspace (until the server confirms it) + t.onData('\x7F'); + t.expectWritten(''); + expectProcessed('\b \b', '\b \b'); + t.cursor.x--; + + // enter input on the column... + t.onData('o'); + onBeforeProcessData.fire({ data: 'o' }); + t.cursor.x++; + t.clearWritten(); + + // now that the column is 'unlocked', we should be able to predict backspace on it + t.onData('\x7F'); + t.expectWritten(`${CSI}2;6H${CSI}X`); + }); + + test('avoids predicting password input', () => { + const t = createMockTerminal('hello|'); + addon.activate(t.terminal); + expectProcessed('Your password: ', 'Your password: '); + + t.onData('mellon\r\n'); + t.expectWritten(''); + expectProcessed('\r\n', '\r\n'); + + t.onData('o'); // back to normal mode + t.expectWritten(`${CSI}3mo`); + }); + }); +}); + +function upcastPartial(v: Partial): T { + return v as T; +} + +function createPredictionStubs(n: number) { + return new Array(n).fill(0).map(stubPrediction); +} + +function stubPrediction(): IPrediction { + return { + apply: () => '', + rollback: () => '', + matches: () => 0, + }; +} + +function createMockTerminal(...lines: string[]) { + const written: string[] = []; + const cursor = { y: 1, x: 1 }; + const onData = new Emitter(); + + for (let y = 0; y < lines.length; y++) { + const line = lines[y]; + if (line.includes('|')) { + cursor.y = y + 1; + cursor.x = line.indexOf('|') + 1; + lines[y] = line.replace('|', ''); + break; + } + } + + return { + written, + cursor, + expectWritten: (s: string) => { + assert.strictEqual(JSON.stringify(written.join('')), JSON.stringify(s)); + written.splice(0, written.length); + }, + clearWritten: () => written.splice(0, written.length), + onData: (s: string) => onData.fire(s), + terminal: { + cols: 80, + rows: 5, + onData: onData.event, + write(line: string) { + written.push(line); + }, + buffer: { + active: { + type: 'normal', + baseY: 0, + get cursorY() { return cursor.y; }, + get cursorX() { return cursor.x; }, + getLine(y: number) { + const s = lines[y - 1] || ''; + return { + length: s.length, + getCell: (x: number) => mockCell(s[x - 1] || ''), + }; + }, + } + } + } as unknown as Terminal + }; +} + +function mockCell(char: string) { + return new Proxy({}, { + get(_, prop) { + switch (prop) { + case 'getWidth': + return () => 1; + case 'getChars': + return () => char; + case 'getCode': + return () => char.charCodeAt(0) || 0; + default: + return String(prop).startsWith('is') ? (() => false) : (() => 0); + } + }, + }); +} From 65bb0ec307376d04de0c61baaf28bca63e164249 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 20 Oct 2020 16:36:32 -0700 Subject: [PATCH 106/121] Fix remote integration tests --- .../contrib/terminal/browser/terminalService.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 9f8a4350bb2..9a46e058c68 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -354,15 +354,13 @@ export class TerminalService implements ITerminalService { for (let term of remoteTerms.slice(1)) { this.createTerminal({ remoteAttach: term }); } - } else { + } else if (this.terminalInstances.length === 0) { // Remote, no terminals to attach to this.createTerminal(undefined, emptyTab); } - } else { - if (this.terminalInstances.length === 0) { - // Local, just create a terminal - this.createTerminal(); - } + } else if (this.terminalInstances.length === 0) { + // Local, just create a terminal + this.createTerminal(); } } From 5df95abf42e9c4a5decf9a4170ac39f9cbaaea1e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 20 Oct 2020 16:55:54 -0700 Subject: [PATCH 107/121] Fix hygiene --- .../contrib/terminal/browser/terminalTypeAheadAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 923cf9bae3c..c0551e32c31 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -774,7 +774,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { }, statsSendTelemetryEvery); } - this.reevaluatePredictorState(stats, timeline); + this.reevaluatePredictorState(stats, timeline); })); } From 3d6b06b3416fcb22c16d448a64f74371871345c6 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 21 Oct 2020 00:12:00 +0000 Subject: [PATCH 108/121] terminal: format issue on linux? --- .../contrib/terminal/browser/terminalTypeAheadAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 923cf9bae3c..c0551e32c31 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -774,7 +774,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { }, statsSendTelemetryEvery); } - this.reevaluatePredictorState(stats, timeline); + this.reevaluatePredictorState(stats, timeline); })); } From 2fdc8c336c601e385fadfaa1802cdc67fea96241 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 20 Oct 2020 17:24:04 -0700 Subject: [PATCH 109/121] Move to consistent naming for search args. Closes #108750. --- .../browser/searchEditor.contribution.ts | 55 +++++++++++++++---- .../searchEditor/browser/searchEditor.ts | 44 +++++++-------- .../searchEditor/browser/searchEditorInput.ts | 12 ++-- .../browser/searchEditorSerialization.ts | 50 ++++++++--------- 4 files changed, 96 insertions(+), 65 deletions(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index ad6f7917b50..08518d3e33f 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -228,6 +228,37 @@ CommandsRegistry.registerCommand( //#region Actions const category = { value: localize('search', "Search Editor"), original: 'Search Editor' }; +export type LegacySearchEditorArgs = Partial<{ + query: string, + includes: string, + excludes: string, + contextLines: number, + wholeWord: boolean, + caseSensitive: boolean, + regexp: boolean, + useIgnores: boolean, + showIncludesExcludes: boolean, + triggerSearch: boolean, + focusResults: boolean, + location: 'reuse' | 'new' +}>; + +const translateLegacyConfig = (legacyConfig: LegacySearchEditorArgs & OpenSearchEditorArgs = {}): OpenSearchEditorArgs => { + const config: OpenSearchEditorArgs = {}; + const overrides: { [K in keyof LegacySearchEditorArgs]: keyof OpenSearchEditorArgs } = { + includes: 'filesToInclude', + excludes: 'filesToExclude', + wholeWord: 'matchWholeWord', + caseSensitive: 'isCaseSensitive', + regexp: 'isRegexp', + useIgnores: 'useExcludeSettingsAndIgnoreFiles', + }; + Object.entries(legacyConfig).forEach(([key, value]) => { + (config as any)[(overrides as any)[key] ?? key] = value; + }); + return config; +}; + export type OpenSearchEditorArgs = Partial; const openArgDescription = { description: 'Open a new search editor. Arguments passed can include variables like ${relativeFileDirname}.', @@ -236,13 +267,13 @@ const openArgDescription = { schema: { properties: { query: { type: 'string' }, - includes: { type: 'string' }, - excludes: { type: 'string' }, + filesToInclude: { type: 'string' }, + filesToExclude: { type: 'string' }, contextLines: { type: 'number' }, - wholeWord: { type: 'boolean' }, - caseSensitive: { type: 'boolean' }, - regexp: { type: 'boolean' }, - useIgnores: { type: 'boolean' }, + matchWholeWord: { type: 'boolean' }, + isCaseSensitive: { type: 'boolean' }, + isRegexp: { type: 'boolean' }, + useExcludeSettingsAndIgnoreFiles: { type: 'boolean' }, showIncludesExcludes: { type: 'boolean' }, triggerSearch: { type: 'boolean' }, focusResults: { type: 'boolean' }, @@ -285,8 +316,8 @@ registerAction2(class extends Action2 { description: openArgDescription }); } - async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { - await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, { ...args, location: 'new' }); + async run(accessor: ServicesAccessor, args: LegacySearchEditorArgs | OpenSearchEditorArgs) { + await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, translateLegacyConfig({ ...args, location: 'new' })); } }); @@ -300,8 +331,8 @@ registerAction2(class extends Action2 { description: openArgDescription }); } - async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { - await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, { ...args, location: 'reuse' }); + async run(accessor: ServicesAccessor, args: LegacySearchEditorArgs | OpenSearchEditorArgs) { + await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, translateLegacyConfig({ ...args, location: 'reuse' })); } }); @@ -315,8 +346,8 @@ registerAction2(class extends Action2 { description: openArgDescription }); } - async run(accessor: ServicesAccessor, args: OpenSearchEditorArgs) { - await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, args, true); + async run(accessor: ServicesAccessor, args: LegacySearchEditorArgs | OpenSearchEditorArgs) { + await accessor.get(IInstantiationService).invokeFunction(openNewSearchEditor, translateLegacyConfig(args), true); } }); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 1d575614d74..820f50030eb 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -439,16 +439,16 @@ export class SearchEditor extends BaseTextEditor { } } - private readConfigFromWidget() { + private readConfigFromWidget(): SearchConfiguration { return { - caseSensitive: this.queryEditorWidget.searchInput.getCaseSensitive(), + isCaseSensitive: this.queryEditorWidget.searchInput.getCaseSensitive(), contextLines: this.queryEditorWidget.getContextLines(), - excludes: this.inputPatternExcludes.getValue(), - includes: this.inputPatternIncludes.getValue(), + filesToExclude: this.inputPatternExcludes.getValue(), + filesToInclude: this.inputPatternIncludes.getValue(), query: this.queryEditorWidget.searchInput.getValue(), - regexp: this.queryEditorWidget.searchInput.getRegex(), - wholeWord: this.queryEditorWidget.searchInput.getWholeWords(), - useIgnores: this.inputPatternExcludes.useExcludesAndIgnoreFiles(), + isRegexp: this.queryEditorWidget.searchInput.getRegex(), + matchWholeWord: this.queryEditorWidget.searchInput.getWholeWords(), + useExcludeSettingsAndIgnoreFiles: this.inputPatternExcludes.useExcludesAndIgnoreFiles(), showIncludesExcludes: this.showingIncludesExcludes }; } @@ -464,25 +464,25 @@ export class SearchEditor extends BaseTextEditor { this.inputPatternIncludes.onSearchSubmit(); }); - const config: SearchConfiguration = this.readConfigFromWidget(); + const config = this.readConfigFromWidget(); if (!config.query) { return; } const content: IPatternInfo = { pattern: config.query, - isRegExp: config.regexp, - isCaseSensitive: config.caseSensitive, - isWordMatch: config.wholeWord, + isRegExp: config.isRegexp, + isCaseSensitive: config.isCaseSensitive, + isWordMatch: config.matchWholeWord, }; const options: ITextQueryBuilderOptions = { _reason: 'searchEditor', extraFileResources: this.instantiationService.invokeFunction(getOutOfWorkspaceEditorResources), maxResults: 10000, - disregardIgnoreFiles: !config.useIgnores || undefined, - disregardExcludeSettings: !config.useIgnores || undefined, - excludePattern: config.excludes, - includePattern: config.includes, + disregardIgnoreFiles: !config.useExcludeSettingsAndIgnoreFiles || undefined, + disregardExcludeSettings: !config.useExcludeSettingsAndIgnoreFiles || undefined, + excludePattern: config.filesToExclude, + includePattern: config.filesToInclude, previewOptions: { matchLines: 1, charsPerLine: 1000 @@ -527,7 +527,7 @@ export class SearchEditor extends BaseTextEditor { const controller = ReferencesController.get(this.searchResultEditor); controller.closeWidget(false); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, sortOrder, exit?.limitHit); + const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.filesToInclude, config.filesToExclude, config.contextLines, labelFormatter, sortOrder, exit?.limitHit); const { body } = await input.getModels(); this.modelService.updateModel(body, results.text); input.config = config; @@ -582,13 +582,13 @@ export class SearchEditor extends BaseTextEditor { this.toggleRunAgainMessage(body.getLineCount() === 1 && body.getValue() === '' && config.query !== ''); this.queryEditorWidget.setValue(config.query); - this.queryEditorWidget.searchInput.setCaseSensitive(config.caseSensitive); - this.queryEditorWidget.searchInput.setRegex(config.regexp); - this.queryEditorWidget.searchInput.setWholeWords(config.wholeWord); + this.queryEditorWidget.searchInput.setCaseSensitive(config.isCaseSensitive); + this.queryEditorWidget.searchInput.setRegex(config.isRegexp); + this.queryEditorWidget.searchInput.setWholeWords(config.matchWholeWord); this.queryEditorWidget.setContextLines(config.contextLines); - this.inputPatternExcludes.setValue(config.excludes); - this.inputPatternIncludes.setValue(config.includes); - this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(config.useIgnores); + this.inputPatternExcludes.setValue(config.filesToExclude); + this.inputPatternIncludes.setValue(config.filesToInclude); + this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(config.useExcludeSettingsAndIgnoreFiles); this.toggleIncludesExcludes(config.showIncludesExcludes); this.restoreViewState(); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index 7a265a1f7a1..a75dbd61ed5 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -32,13 +32,13 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export type SearchConfiguration = { query: string, - includes: string, - excludes: string, + filesToInclude: string, + filesToExclude: string, contextLines: number, - wholeWord: boolean, - caseSensitive: boolean, - regexp: boolean, - useIgnores: boolean, + matchWholeWord: boolean, + isCaseSensitive: boolean, + isRegexp: boolean, + useExcludeSettingsAndIgnoreFiles: boolean, showIncludesExcludes: boolean, }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index 91cf43f0323..951be05422f 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -108,12 +108,12 @@ function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x: const contentPatternToSearchConfiguration = (pattern: ITextQuery, includes: string, excludes: string, contextLines: number): SearchConfiguration => { return { query: pattern.contentPattern.pattern, - regexp: !!pattern.contentPattern.isRegExp, - caseSensitive: !!pattern.contentPattern.isCaseSensitive, - wholeWord: !!pattern.contentPattern.isWordMatch, - excludes, includes, + isRegexp: !!pattern.contentPattern.isRegExp, + isCaseSensitive: !!pattern.contentPattern.isCaseSensitive, + matchWholeWord: !!pattern.contentPattern.isWordMatch, + filesToExclude: excludes, filesToInclude: includes, showIncludesExcludes: !!(includes || excludes || pattern?.userDisabledExcludesAndIgnoreFiles), - useIgnores: (pattern?.userDisabledExcludesAndIgnoreFiles === undefined ? true : !pattern.userDisabledExcludesAndIgnoreFiles), + useExcludeSettingsAndIgnoreFiles: (pattern?.userDisabledExcludesAndIgnoreFiles === undefined ? true : !pattern.userDisabledExcludesAndIgnoreFiles), contextLines, }; }; @@ -126,15 +126,15 @@ export const serializeSearchConfiguration = (config: Partial ({ query: '', - includes: '', - excludes: '', - regexp: false, - caseSensitive: false, - useIgnores: true, - wholeWord: false, + filesToInclude: '', + filesToExclude: '', + isRegexp: false, + isCaseSensitive: false, + useExcludeSettingsAndIgnoreFiles: true, + matchWholeWord: false, contextLines: 0, showIncludesExcludes: false, }); @@ -189,19 +189,19 @@ export const extractSearchQueryFromLines = (lines: string[]): SearchConfiguratio const [, key, value] = parsed; switch (key) { case 'Query': query.query = unescapeNewlines(value); break; - case 'Including': query.includes = value; break; - case 'Excluding': query.excludes = value; break; + case 'Including': query.filesToInclude = value; break; + case 'Excluding': query.filesToExclude = value; break; case 'ContextLines': query.contextLines = +value; break; case 'Flags': { - query.regexp = value.indexOf('RegExp') !== -1; - query.caseSensitive = value.indexOf('CaseSensitive') !== -1; - query.useIgnores = value.indexOf('IgnoreExcludeSettings') === -1; - query.wholeWord = value.indexOf('WordMatch') !== -1; + query.isRegexp = value.indexOf('RegExp') !== -1; + query.isCaseSensitive = value.indexOf('CaseSensitive') !== -1; + query.useExcludeSettingsAndIgnoreFiles = value.indexOf('IgnoreExcludeSettings') === -1; + query.matchWholeWord = value.indexOf('WordMatch') !== -1; } } } - query.showIncludesExcludes = !!(query.includes || query.excludes || !query.useIgnores); + query.showIncludesExcludes = !!(query.filesToInclude || query.filesToExclude || !query.useExcludeSettingsAndIgnoreFiles); return query; }; From 6c8c6008ef9d86e46b086a00f273b1fee5bd8c51 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 20 Oct 2020 17:37:14 -0700 Subject: [PATCH 110/121] findInFiles: excludeSettingAndIgnoreFiles => useExcludeSettingsAndIgnoreFiles Add args to intelisense registry Ref #107588. --- .../workbench/contrib/search/browser/search.contribution.ts | 2 ++ src/vs/workbench/contrib/search/browser/searchActions.ts | 2 +- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index caf6809563d..041daae2594 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -571,12 +571,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ properties: { query: { 'type': 'string' }, replace: { 'type': 'string' }, + preserveCase: { 'type': 'boolean' }, triggerSearch: { 'type': 'boolean' }, filesToInclude: { 'type': 'string' }, filesToExclude: { 'type': 'string' }, isRegex: { 'type': 'boolean' }, isCaseSensitive: { 'type': 'boolean' }, matchWholeWord: { 'type': 'boolean' }, + useExcludeSettingsAndIgnoreFiles: { 'type': 'boolean' }, } } }, diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 23c915b19f6..d2f10840868 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -169,7 +169,7 @@ export interface IFindInFilesArgs { isRegex?: boolean; isCaseSensitive?: boolean; matchWholeWord?: boolean; - excludeSettingAndIgnoreFiles?: boolean; + useExcludeSettingsAndIgnoreFiles?: boolean; } export const FindInFilesCommand: ICommandHandler = (accessor, args: IFindInFilesArgs = {}) => { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 9afc71db8f5..414f7501adc 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1197,8 +1197,8 @@ export class SearchView extends ViewPane { if (typeof args.preserveCase === 'boolean') { this.searchWidget.replaceInput.setPreserveCase(args.preserveCase); } - if (typeof args.excludeSettingAndIgnoreFiles === 'boolean') { - this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(args.excludeSettingAndIgnoreFiles); + if (typeof args.useExcludeSettingsAndIgnoreFiles === 'boolean') { + this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(args.useExcludeSettingsAndIgnoreFiles); } } From ad219036a5e216e014e827e75fbecbcea3bb9e44 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Oct 2020 10:28:18 +0200 Subject: [PATCH 111/121] extract UserDataAutoSyncEnablementService from UserDataAutoSyncService --- .../sharedProcess/sharedProcessMain.ts | 4 +- .../common/userDataAutoSyncService.ts | 53 ++++++++++++------- .../userDataSync/common/userDataSync.ts | 11 ++-- .../userDataAutoSyncService.ts | 7 ++- .../test/common/userDataSyncClient.ts | 4 +- .../preferences/browser/settingsEditor2.ts | 12 ++--- .../preferences/browser/settingsTree.ts | 6 +-- .../userDataSync/browser/userDataSync.ts | 27 +++++----- .../userDataSync/browser/userDataSyncViews.ts | 6 +-- .../browser/extensionEnablementService.ts | 6 +-- .../extensionEnablementService.test.ts | 6 +-- ...s => userDataAutoSyncEnablementService.ts} | 7 ++- .../browser/userDataSyncWorkbenchService.ts | 13 ++--- .../userDataAutoSyncService.ts | 11 +--- src/vs/workbench/workbench.sandbox.main.ts | 5 ++ src/vs/workbench/workbench.web.main.ts | 10 ++-- 16 files changed, 105 insertions(+), 83 deletions(-) rename src/vs/workbench/services/userDataSync/browser/{userDataAutoSyncService.ts => userDataAutoSyncEnablementService.ts} (79%) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 45c871d9aa9..f6389d25d26 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -48,7 +48,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; @@ -70,6 +70,7 @@ import { IExtensionRecommendationNotificationService } from 'vs/platform/extensi import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; import { ActiveWindowManager } from 'vs/platform/windows/common/windowTracker'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -212,6 +213,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService)); + services.set(IUserDataAutoSyncEnablementService, new SyncDescriptor(UserDataAutoSyncEnablementService)); services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); registerConfiguration(); diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index aa678e17ef7..cb2342a2eae 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -6,7 +6,7 @@ import { Delayer, disposableTimeout, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, toDisposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, UserDataAutoSyncError, ISyncTask, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncLogService, IUserDataSyncService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, UserDataAutoSyncError, ISyncTask, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isPromiseCanceledError } from 'vs/base/common/errors'; @@ -37,15 +37,22 @@ const disableMachineEventuallyKey = 'sync.disableMachineEventually'; const sessionIdKey = 'sync.sessionId'; const storeUrlKey = 'sync.storeUrl'; -export class UserDataAutoSyncEnablementService extends Disposable { +interface _IUserDataAutoSyncEnablementService extends IUserDataAutoSyncEnablementService { + canToggleEnablement(): boolean; + setEnablement(enabled: boolean): void; +} + +export class UserDataAutoSyncEnablementService extends Disposable implements _IUserDataAutoSyncEnablementService { + + _serviceBrand: any; private _onDidChangeEnablement = new Emitter(); readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; constructor( - @IStorageService protected readonly storageService: IStorageService, + @IStorageService private readonly storageService: IStorageService, @IEnvironmentService protected readonly environmentService: IEnvironmentService, - @IUserDataSyncStoreManagementService protected readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, ) { super(); this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); @@ -65,24 +72,29 @@ export class UserDataAutoSyncEnablementService extends Disposable { return this.userDataSyncStoreManagementService.userDataSyncStore !== undefined && this.environmentService.sync === undefined; } - protected setEnablement(enabled: boolean): void { + setEnablement(enabled: boolean): void { this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL); } private onDidStorageChange(workspaceStorageChangeEvent: IWorkspaceStorageChangeEvent): void { - if (workspaceStorageChangeEvent.scope === StorageScope.GLOBAL) { - if (enablementKey === workspaceStorageChangeEvent.key) { - this._onDidChangeEnablement.fire(this.isEnabled()); - } + if (workspaceStorageChangeEvent.scope !== StorageScope.GLOBAL) { + return; + } + + if (enablementKey === workspaceStorageChangeEvent.key) { + this._onDidChangeEnablement.fire(this.isEnabled()); + return; } } } -export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { +export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService { _serviceBrand: any; + private readonly userDataAutoSyncEnablementService: _IUserDataAutoSyncEnablementService; + private readonly autoSync = this._register(new MutableDisposable()); private successiveFailures: number = 0; private lastSyncTriggerTime: number | undefined = undefined; @@ -105,7 +117,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } constructor( - @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, + @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @@ -113,10 +125,11 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, - @IStorageService storageService: IStorageService, - @IEnvironmentService environmentService: IEnvironmentService + @IStorageService private readonly storageService: IStorageService, + @IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService ) { - super(storageService, environmentService, userDataSyncStoreManagementService); + super(); + this.userDataAutoSyncEnablementService = userDataAutoSyncEnablementService as _IUserDataAutoSyncEnablementService; this.syncTriggerDelayer = this._register(new Delayer(0)); this.lastSyncUrl = this.syncUrl; @@ -135,7 +148,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } })); - if (this.isEnabled()) { + if (this.userDataAutoSyncEnablementService.isEnabled()) { this.logService.info('Auto Sync is enabled.'); } else { this.logService.info('Auto Sync is disabled.'); @@ -174,7 +187,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } /* log message when auto sync is not disabled by user */ - else if (message && this.isEnabled()) { + else if (message && this.userDataAutoSyncEnablementService.isEnabled()) { this.logService.info(message); } } @@ -184,7 +197,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i protected startAutoSync(): boolean { return true; } private isAutoSyncEnabled(): { enabled: boolean, message?: string } { - if (!this.isEnabled()) { + if (!this.userDataAutoSyncEnablementService.isEnabled()) { return { enabled: false, message: 'Auto Sync: Disabled.' }; } if (!this.userDataSyncAccountService.account) { @@ -234,9 +247,9 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i } private updateEnablement(enabled: boolean): void { - if (this.isEnabled() !== enabled) { + if (this.userDataAutoSyncEnablementService.isEnabled() !== enabled) { this.telemetryService.publicLog2<{ enabled: boolean }, AutoSyncEnablementClassification>(enablementKey, { enabled }); - this.setEnablement(enabled); + this.userDataAutoSyncEnablementService.setEnablement(enabled); this.updateAutoSync(); } } @@ -323,7 +336,7 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i this.stopDisableMachineEventually(); // disable only if sync is disabled - if (!this.isEnabled() && this.userDataSyncAccountService.account) { + if (!this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncAccountService.account) { await this.userDataSyncMachinesService.removeCurrentMachine(); } } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 52f721f88ad..e654f0dc867 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -460,13 +460,18 @@ export interface IUserDataSyncService { getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise; } +export const IUserDataAutoSyncEnablementService = createDecorator('IUserDataAutoSyncEnablementService'); +export interface IUserDataAutoSyncEnablementService { + _serviceBrand: any; + readonly onDidChangeEnablement: Event; + isEnabled(): boolean; + canToggleEnablement(): boolean; +} + export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService'); export interface IUserDataAutoSyncService { _serviceBrand: any; readonly onError: Event; - readonly onDidChangeEnablement: Event; - isEnabled(): boolean; - canToggleEnablement(): boolean; turnOn(): Promise; turnOff(everywhere: boolean): Promise; triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise; diff --git a/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts index 6c9d0f00e2a..115f5ff011f 100644 --- a/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // -import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncStoreService, IUserDataSyncStoreManagementService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @@ -26,9 +25,9 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @ITelemetryService telemetryService: ITelemetryService, @IUserDataSyncMachinesService userDataSyncMachinesService: IUserDataSyncMachinesService, @IStorageService storageService: IStorageService, - @IEnvironmentService environmentService: IEnvironmentService, + @IUserDataAutoSyncEnablementService userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, ) { - super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, environmentService); + super(userDataSyncStoreManagementService, userDataSyncStoreService, userDataSyncResourceEnablementService, userDataSyncService, logService, authTokenService, telemetryService, userDataSyncMachinesService, storageService, userDataAutoSyncEnablementService); this._register(Event.debounce(Event.any( Event.map(nativeHostService.onDidFocusWindow, () => 'windowFocus'), diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index a329a554820..bc8a0cbbca5 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -6,7 +6,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource, ServerResource, IUserDataSyncStoreManagementService, registerConfiguration, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { bufferToStream, VSBuffer } from 'vs/base/common/buffer'; import { generateUuid } from 'vs/base/common/uuid'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -38,6 +38,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; export class UserDataSyncClient extends Disposable { @@ -115,6 +116,7 @@ export class UserDataSyncClient extends Disposable { async getCompatibleExtension() { return null; } }); + this.instantiationService.stub(IUserDataAutoSyncEnablementService, this.instantiationService.createInstance(UserDataAutoSyncEnablementService)); this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService)); if (!empty) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 068b01de440..904c2198b35 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -37,7 +37,7 @@ import { badgeBackground, badgeForeground, contrastBorder, editorForeground } fr import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { IUserDataAutoSyncService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncEnablementService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; @@ -175,7 +175,7 @@ export class SettingsEditor2 extends EditorPane { @IEditorGroupsService protected editorGroupService: IEditorGroupsService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService ) { super(SettingsEditor2.ID, telemetryService, themeService, storageService); this.delayedFilterLogging = new Delayer(1000); @@ -491,7 +491,7 @@ export class SettingsEditor2 extends EditorPane { } })); - if (this.userDataSyncWorkbenchService.enabled && this.userDataAutoSyncService.canToggleEnablement()) { + if (this.userDataSyncWorkbenchService.enabled && this.userDataAutoSyncEnablementService.canToggleEnablement()) { const syncControls = this._register(this.instantiationService.createInstance(SyncControls, headerControlsContainer)); this._register(syncControls.onDidChangeLastSyncedLabel(lastSyncedLabel => { this.lastSyncedLabel = lastSyncedLabel; @@ -1416,7 +1416,7 @@ class SyncControls extends Disposable { container: HTMLElement, @ICommandService private readonly commandService: ICommandService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IThemeService themeService: IThemeService, ) { super(); @@ -1449,7 +1449,7 @@ class SyncControls extends Disposable { this.update(); })); - this._register(this.userDataAutoSyncService.onDidChangeEnablement(() => { + this._register(this.userDataAutoSyncEnablementService.onDidChangeEnablement(() => { this.update(); })); } @@ -1473,7 +1473,7 @@ class SyncControls extends Disposable { return; } - if (this.userDataAutoSyncService.isEnabled() || this.userDataSyncService.status !== SyncStatus.Idle) { + if (this.userDataAutoSyncEnablementService.isEnabled() || this.userDataSyncService.status !== SyncStatus.Idle) { DOM.show(this.lastSyncedLabel); DOM.hide(this.turnOnSyncButton.element); } else { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index e5eb4697ce9..7c2dc4aa457 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -46,7 +46,7 @@ import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSetti import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; -import { getDefaultIgnoredSettings, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { getDefaultIgnoredSettings, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { getInvalidTypeError } from 'vs/workbench/services/preferences/common/preferencesValidation'; import { Codicon } from 'vs/base/common/codicons'; import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; @@ -1525,7 +1525,7 @@ export class SettingTreeRenderers { @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IContextViewService private readonly _contextViewService: IContextViewService, - @IUserDataAutoSyncService private readonly _userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncEnablementService private readonly _userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, ) { this.settingActions = [ new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, (context: SettingsTreeSettingElement) => { @@ -1569,7 +1569,7 @@ export class SettingTreeRenderers { } private getActionsForSetting(setting: ISetting): IAction[] { - const enableSync = this._userDataAutoSyncService.isEnabled(); + const enableSync = this._userDataAutoSyncEnablementService.isEnabled(); return enableSync && !setting.disallowSyncIgnore ? [ new Separator(), diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index caeba8ce960..f41dbb1c2cd 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -30,7 +30,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUserDataAutoSyncService, IUserDataSyncService, registerConfiguration, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncResourceEnablementService, - getSyncResourceFromLocalPreview, IResourcePreview, IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStore + getSyncResourceFromLocalPreview, IResourcePreview, IUserDataSyncStoreManagementService, UserDataSyncStoreType, IUserDataSyncStore, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -110,6 +110,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IInstantiationService private readonly instantiationService: IInstantiationService, @IOutputService private readonly outputService: IOutputService, @IUserDataSyncAccountService readonly authTokenService: IUserDataSyncAccountService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, @ITextModelService textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @@ -135,14 +136,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this._register(Event.any( Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500), - this.userDataAutoSyncService.onDidChangeEnablement, + this.userDataAutoSyncEnablementService.onDidChangeEnablement, this.userDataSyncWorkbenchService.onDidChangeAccountStatus )(() => { this.updateAccountBadge(); this.updateGlobalActivityBadge(); })); this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts))); - this._register(userDataAutoSyncService.onDidChangeEnablement(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts))); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts))); this._register(userDataSyncService.onSyncErrors(errors => this.onSynchronizerErrors(errors))); this._register(userDataAutoSyncService.onError(error => this.onAutoSyncError(error))); @@ -152,7 +153,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo textModelResolverService.registerTextModelContentProvider(USER_DATA_SYNC_SCHEME, instantiationService.createInstance(UserDataRemoteContentProvider)); registerEditorContribution(AcceptChangesContribution.ID, AcceptChangesContribution); - this._register(Event.any(userDataSyncService.onDidChangeStatus, userDataAutoSyncService.onDidChangeEnablement)(() => this.turningOnSync = !userDataAutoSyncService.isEnabled() && userDataSyncService.status !== SyncStatus.Idle)); + this._register(Event.any(userDataSyncService.onDidChangeStatus, userDataAutoSyncEnablementService.onDidChangeEnablement)(() => this.turningOnSync = !userDataAutoSyncEnablementService.isEnabled() && userDataSyncService.status !== SyncStatus.Idle)); } } @@ -167,7 +168,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private readonly conflictsDisposables = new Map(); private onDidChangeConflicts(conflicts: [SyncResource, IResourcePreview[]][]) { - if (!this.userDataAutoSyncService.isEnabled()) { + if (!this.userDataAutoSyncEnablementService.isEnabled()) { return; } this.updateGlobalActivityBadge(); @@ -255,7 +256,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async acceptRemote(syncResource: SyncResource, conflicts: IResourcePreview[]) { try { for (const conflict of conflicts) { - await this.userDataSyncService.accept(syncResource, conflict.remoteResource, undefined, this.userDataAutoSyncService.isEnabled()); + await this.userDataSyncService.accept(syncResource, conflict.remoteResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { this.notificationService.error(e); @@ -265,7 +266,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async acceptLocal(syncResource: SyncResource, conflicts: IResourcePreview[]): Promise { try { for (const conflict of conflicts) { - await this.userDataSyncService.accept(syncResource, conflict.localResource, undefined, this.userDataAutoSyncService.isEnabled()); + await this.userDataSyncService.accept(syncResource, conflict.localResource, undefined, this.userDataAutoSyncEnablementService.isEnabled()); } } catch (e) { this.notificationService.error(e); @@ -401,7 +402,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo let clazz: string | undefined; let priority: number | undefined = undefined; - if (this.userDataSyncService.conflicts.length && this.userDataAutoSyncService.isEnabled()) { + if (this.userDataSyncService.conflicts.length && this.userDataAutoSyncEnablementService.isEnabled()) { badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, [, conflicts]) => { return result + conflicts.length; }, 0), () => localize('has conflicts', "{0}: Conflicts Detected", SYNC_TITLE)); } else if (this.turningOnSync) { badge = new ProgressBadge(() => localize('turning on syncing', "Turning on Settings Sync...")); @@ -419,7 +420,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo let badge: IBadge | undefined = undefined; - if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataAutoSyncService.isEnabled() && this.userDataSyncWorkbenchService.accountStatus === AccountStatus.Unavailable) { + if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncWorkbenchService.accountStatus === AccountStatus.Unavailable) { badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync Settings")); } @@ -713,7 +714,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private registerActions(): void { - if (this.userDataAutoSyncService.canToggleEnablement()) { + if (this.userDataAutoSyncEnablementService.canToggleEnablement()) { this.registerTurnOnSyncAction(); this.registerTurnOffSyncAction(); } @@ -966,7 +967,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo items.push({ id: showSyncedDataCommand.id, label: showSyncedDataCommand.title }); items.push({ type: 'separator' }); items.push({ id: syncNowCommand.id, label: syncNowCommand.title, description: syncNowCommand.description(that.userDataSyncService) }); - if (that.userDataAutoSyncService.canToggleEnablement()) { + if (that.userDataAutoSyncEnablementService.canToggleEnablement()) { const account = that.userDataSyncWorkbenchService.current; items.push({ id: turnOffSyncCommand.id, label: turnOffSyncCommand.title, description: account ? `${account.accountName} (${that.authenticationService.getLabel(account.authenticationProviderId)})` : undefined }); } @@ -1166,7 +1167,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio @IDialogService private readonly dialogService: IDialogService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, ) { super(); @@ -1195,7 +1196,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio return false; // we need a model } - if (!this.userDataAutoSyncService.isEnabled()) { + if (!this.userDataAutoSyncEnablementService.isEnabled()) { return false; } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 8b801947f8d..1bf03ec3a5b 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { ALL_SYNC_RESOURCES, SyncResource, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncResourceEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; @@ -79,7 +79,7 @@ export class UserDataSyncDataViews extends Disposable { constructor( container: ViewContainer, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService, @IUserDataSyncMachinesService private readonly userDataSyncMachinesService: IUserDataSyncMachinesService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @@ -194,7 +194,7 @@ export class UserDataSyncDataViews extends Disposable { } }); this._register(Event.any(this.userDataSyncResourceEnablementService.onDidChangeResourceEnablement, - this.userDataAutoSyncService.onDidChangeEnablement, + this.userDataAutoSyncEnablementService.onDidChangeEnablement, this.userDataSyncService.onDidResetLocal, this.userDataSyncService.onDidResetRemote)(() => treeView.refresh())); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index d99ed62c1ab..4d115067de7 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -20,7 +20,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -44,7 +44,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IProductService private readonly productService: IProductService, - @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @INotificationService private readonly notificationService: INotificationService, @@ -104,7 +104,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench throw new Error(localize('cannot disable language pack extension', "Cannot change enablement of {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id)); } - if (this.userDataAutoSyncService.isEnabled() && this.userDataSyncAccountService.account && + if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncAccountService.account && isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) { throw new Error(localize('cannot disable auth extension', "Cannot change enablement {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id)); } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 3bf8237b1fc..32de64cf104 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -22,7 +22,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { productService, TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; // import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -56,7 +56,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IConfigurationService), extensionManagementServerService, productService, - instantiationService.get(IUserDataAutoSyncService) || instantiationService.stub(IUserDataAutoSyncService, >{ isEnabled() { return false; } }), + instantiationService.get(IUserDataAutoSyncEnablementService) || instantiationService.stub(IUserDataAutoSyncEnablementService, >{ isEnabled() { return false; } }), instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService), instantiationService.get(ILifecycleService) || instantiationService.stub(ILifecycleService, new TestLifecycleService()), instantiationService.get(INotificationService) || instantiationService.stub(INotificationService, new TestNotificationService()), @@ -401,7 +401,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test canChangeEnablement return false for auth extension and user data sync account depends on it and auto sync is on', () => { - instantiationService.stub(IUserDataAutoSyncService, >{ isEnabled() { return true; } }); + instantiationService.stub(IUserDataAutoSyncEnablementService, >{ isEnabled() { return true; } }); instantiationService.stub(IUserDataSyncAccountService, >{ account: { authenticationProviderId: 'a' } }); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts similarity index 79% rename from src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts rename to src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts index 50f385e3269..d0e5a506853 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -export class WebUserDataAutoSyncService extends UserDataAutoSyncService implements IUserDataAutoSyncService { +export class WebUserDataAutoSyncEnablementService extends UserDataAutoSyncEnablementService { private get workbenchEnvironmentService(): IWorkbenchEnvironmentService { return this.environmentService; } private enabled: boolean | undefined = undefined; @@ -22,7 +21,7 @@ export class WebUserDataAutoSyncService extends UserDataAutoSyncService implemen return this.enabled; } - protected setEnablement(enabled: boolean) { + setEnablement(enabled: boolean) { if (this.enabled !== enabled) { this.enabled = enabled; if (this.workbenchEnvironmentService.options?.settingsSyncOptions) { diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 4a8df1c52fb..f6d93e4f9eb 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, IAuthenticationProvider, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask, IUserDataSyncStoreManagementService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IAuthenticationProvider, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask, IUserDataSyncStoreManagementService, SyncStatus, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_SYNC_MERGES_VIEW, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; @@ -93,6 +93,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IStorageService private readonly storageService: IStorageService, + @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ILogService private readonly logService: ILogService, @@ -118,8 +119,8 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat if (this.userDataSyncStoreManagementService.userDataSyncStore) { this.syncStatusContext.set(this.userDataSyncService.status); this._register(userDataSyncService.onDidChangeStatus(status => this.syncStatusContext.set(status))); - this.syncEnablementContext.set(userDataAutoSyncService.isEnabled()); - this._register(userDataAutoSyncService.onDidChangeEnablement(enabled => this.syncEnablementContext.set(enabled))); + this.syncEnablementContext.set(userDataAutoSyncEnablementService.isEnabled()); + this._register(userDataAutoSyncEnablementService.onDidChangeEnablement(enabled => this.syncEnablementContext.set(enabled))); this.waitAndInitialize(); } @@ -247,7 +248,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat if (!this.authenticationProviders.length) { throw new Error(localize('no authentication providers', "Settings sync cannot be turned on because there are no authentication providers available.")); } - if (this.userDataAutoSyncService.isEnabled()) { + if (this.userDataAutoSyncEnablementService.isEnabled()) { return; } if (this.userDataSyncService.status !== SyncStatus.Idle) { @@ -552,7 +553,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private async switch(sessionId: string, accountName: string, accountId: string): Promise { const currentAccount = this.current; - if (this.userDataAutoSyncService.isEnabled() && (currentAccount && currentAccount.accountName !== accountName)) { + if (this.userDataAutoSyncEnablementService.isEnabled() && (currentAccount && currentAccount.accountName !== accountName)) { // accounts are switched while sync is enabled. } this.currentSessionId = sessionId; @@ -565,7 +566,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this.currentSessionId = undefined; await this.update(); - if (this.userDataAutoSyncService.isEnabled()) { + if (this.userDataAutoSyncEnablementService.isEnabled()) { this.notificationService.notify({ severity: Severity.Error, message: localize('successive auth failures', "Settings sync is suspended because of successive authorization failures. Please sign in again to continue synchronizing"), diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts index d48dbbdab49..7e381a0446b 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -3,16 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataAutoSyncService, UserDataSyncError, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataAutoSyncService, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implements IUserDataAutoSyncService { +class UserDataAutoSyncService implements IUserDataAutoSyncService { declare readonly _serviceBrand: undefined; @@ -20,12 +17,8 @@ class UserDataAutoSyncService extends UserDataAutoSyncEnablementService implemen get onError(): Event { return Event.map(this.channel.listen('onError'), e => UserDataSyncError.toUserDataSyncError(e)); } constructor( - @IStorageService storageService: IStorageService, - @IEnvironmentService environmentService: IEnvironmentService, - @IUserDataSyncStoreManagementService userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @ISharedProcessService sharedProcessService: ISharedProcessService, ) { - super(storageService, environmentService, userDataSyncStoreManagementService); this.channel = sharedProcessService.getChannel('userDataAutoSync'); } diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index aa3f59f402a..3dfbe3d9b27 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -16,6 +16,11 @@ import 'vs/workbench/workbench.common.main'; //#endregion +import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; + +registerSingleton(IUserDataAutoSyncEnablementService, UserDataAutoSyncEnablementService); + //#region --- workbench services diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index f738609dfde..22d0ae14060 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -65,14 +65,15 @@ import { ITunnelService, TunnelService } from 'vs/platform/remote/common/tunnel' import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLogService'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; -import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService, IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { StorageKeysSyncRegistryService, IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; -import { WebUserDataAutoSyncService } from 'vs/workbench/services/userDataSync/browser/userDataAutoSyncService'; +import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; +import { WebUserDataAutoSyncEnablementService } from 'vs/workbench/services/userDataSync/browser/userDataAutoSyncEnablementService'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -90,8 +91,9 @@ registerSingleton(IUserDataSyncMachinesService, UserDataSyncMachinesService); registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService); registerSingleton(IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService); registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService); -registerSingleton(IUserDataSyncService, UserDataSyncService, true); -registerSingleton(IUserDataAutoSyncService, WebUserDataAutoSyncService, true); +registerSingleton(IUserDataSyncService, UserDataSyncService); +registerSingleton(IUserDataAutoSyncEnablementService, WebUserDataAutoSyncEnablementService); +registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService); registerSingleton(ITitleService, TitlebarPart); registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(ITimerService, TimerService); From 90132aee33fa8c7b0ab2ea16bd580922d0b37b98 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Oct 2020 11:44:57 +0200 Subject: [PATCH 112/121] #108522 use ReloadWindowAction --- .../browser/extensionEnablementService.ts | 9 +++++---- .../test/browser/extensionEnablementService.test.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 4d115067de7..d67f433f8c9 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -23,6 +23,8 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; const SOURCE = 'IWorkbenchExtensionEnablementService'; @@ -48,7 +50,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @INotificationService private readonly notificationService: INotificationService, - // @IHostService private readonly hostService: IHostService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -60,9 +62,8 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench this.lifecycleService.when(LifecyclePhase.Restored).then(() => { this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ label: localize('Reload', "Reload"), - run: () => { - //this.hostService.reload(); - } + // Using ReloadWindowAction because depending on IHostService causes cyclic dependency - #108522 + run: () => instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL).run() }]); }); } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 32de64cf104..8080ace1e78 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -60,7 +60,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService), instantiationService.get(ILifecycleService) || instantiationService.stub(ILifecycleService, new TestLifecycleService()), instantiationService.get(INotificationService) || instantiationService.stub(INotificationService, new TestNotificationService()), - // instantiationService.get(IHostService), + instantiationService, ); } From 724769268e49e3cb11d04e3b79981cd7458e5722 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Oct 2020 11:53:49 +0200 Subject: [PATCH 113/121] web - change window.confirmBeforeClose to check for keyboard press only by default --- .../browser/actions/windowActions.ts | 6 +- .../browser/workbench.contribution.ts | 12 +++- .../host/browser/browserHostService.ts | 57 ++++++++++++++++--- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 1115d77f027..e5fdcdbca0a 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -403,9 +403,9 @@ KeybindingsRegistry.registerKeybindingRule({ CommandsRegistry.registerCommand('workbench.action.toggleConfirmBeforeClose', accessor => { const configurationService = accessor.get(IConfigurationService); - const setting = configurationService.inspect('window.confirmBeforeClose').userValue; + const setting = configurationService.inspect<'always' | 'keyboardOnly' | 'never'>('window.confirmBeforeClose').userValue; - return configurationService.updateValue('window.confirmBeforeClose', setting === false ? true : false, ConfigurationTarget.USER); + return configurationService.updateValue('window.confirmBeforeClose', setting === 'never' ? 'keyboardOnly' : 'never', ConfigurationTarget.USER); }); // --- Menu Registration @@ -415,7 +415,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { command: { id: 'workbench.action.toggleConfirmBeforeClose', title: nls.localize('miConfirmClose', "Confirm Before Close"), - toggled: ContextKeyExpr.equals('config.window.confirmBeforeClose', true) + toggled: ContextKeyExpr.notEquals('config.window.confirmBeforeClose', 'never') }, order: 1, when: IsWebContext diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index c28dada37fb..255f9cac587 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -404,9 +404,15 @@ import { isStandalone } from 'vs/base/browser/browser'; 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, 'window.confirmBeforeClose': { - 'type': 'boolean', - 'default': isWeb && !isStandalone, // on by default in web, unless PWA - 'description': nls.localize('confirmBeforeCloseWeb', "Controls whether to ask for confirmation before closing the browser tab or window."), + 'type': 'string', + 'enum': ['always', 'keyboardOnly', 'never'], + 'enumDescriptions': [ + nls.localize('window.confirmBeforeClose.always', "Always ask for confirmation."), + nls.localize('window.confirmBeforeClose.keyboardOnly', "Only ask for confirmation if the browser tab or window was closed by pressing a keybinding."), + nls.localize('window.confirmBeforeClose.never', "Never explicitly ask for confirmation unless data loss is imminent.") + ], + 'default': isWeb && !isStandalone ? 'keyboardOnly' : 'never', // on by default in web, unless PWA + 'description': nls.localize('confirmBeforeCloseWeb', "Controls whether to ask for confirmation before closing the browser tab or window. Independent of this setting, there will always be a confirmation to prevent data loss."), 'scope': ConfigurationScope.APPLICATION, 'included': isWeb } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index adc90ab9de1..bc224d31267 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -13,7 +13,7 @@ import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, i import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; -import { trackFocus } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -58,13 +58,31 @@ export interface IWorkspaceProvider { open(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise; } +enum HostShutdownReason { + + /** + * An unknown shutdown reason. + */ + Unknown = 1, + + /** + * A shutdown that was potentially triggered by keyboard use. + */ + Keyboard = 2, + + /** + * An explicit shutdown via code. + */ + Api = 3 +} + export class BrowserHostService extends Disposable implements IHostService { declare readonly _serviceBrand: undefined; private workspaceProvider: IWorkspaceProvider; - private signalExpectedShutdown = false; + private shutdownReason = HostShutdownReason.Unknown; constructor( @ILayoutService private readonly layoutService: ILayoutService, @@ -91,20 +109,37 @@ export class BrowserHostService extends Disposable implements IHostService { } private registerListeners(): void { + + // Veto shutdown depending on `window.confirmBeforeClose` setting this._register(this.lifecycleService.onBeforeShutdown(e => this.onBeforeShutdown(e))); + + // Track certain DOM events to detect keybinding usage + this._register(addDisposableListener(this.layoutService.container, EventType.KEY_DOWN, e => this.updateShutdownReasonFromEvent(e))); + this._register(addDisposableListener(this.layoutService.container, EventType.KEY_UP, () => this.updateShutdownReasonFromEvent(undefined))); + this._register(addDisposableListener(this.layoutService.container, EventType.MOUSE_DOWN, () => this.updateShutdownReasonFromEvent(undefined))); + this._register(addDisposableListener(this.layoutService.container, EventType.MOUSE_UP, () => this.updateShutdownReasonFromEvent(undefined))); + this._register(this.onDidChangeFocus(() => this.updateShutdownReasonFromEvent(undefined))); } private onBeforeShutdown(e: BeforeShutdownEvent): void { - // Veto is setting is configured as such and we are not - // expecting a navigation that was triggered by the user - if (!this.signalExpectedShutdown && this.configurationService.getValue('window.confirmBeforeClose')) { + // Veto the shutdown depending on `window.confirmBeforeClose` setting + const confirmBeforeClose = this.configurationService.getValue<'always' | 'keyboardOnly' | 'never'>('window.confirmBeforeClose'); + if (confirmBeforeClose === 'always' || (this.shutdownReason === HostShutdownReason.Keyboard && confirmBeforeClose === 'keyboardOnly')) { console.warn('Unload veto: window.confirmBeforeClose=true'); e.veto(true); } // Unset for next shutdown - this.signalExpectedShutdown = false; + this.shutdownReason = HostShutdownReason.Unknown; + } + + private updateShutdownReasonFromEvent(e: KeyboardEvent | undefined): void { + if (this.shutdownReason === HostShutdownReason.Api) { + return; // do not overwrite any explicitly set shutdown reason + } + + this.shutdownReason = e ? HostShutdownReason.Keyboard : HostShutdownReason.Unknown; } //#region Focus @@ -317,8 +352,11 @@ export class BrowserHostService extends Disposable implements IHostService { } private doOpen(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise { + + // We know that `workspaceProvider.open` will trigger a shutdown + // with `options.reuse` so we update `shutdownReason` to reflect that if (options?.reuse) { - this.signalExpectedShutdown = true; + this.shutdownReason = HostShutdownReason.Api; } return this.workspaceProvider.open(workspace, options); @@ -367,7 +405,10 @@ export class BrowserHostService extends Disposable implements IHostService { } async reload(): Promise { - this.signalExpectedShutdown = true; + + // We know that `window.location.reload` will trigger a shutdown + // so we update `shutdownReason` to reflect that + this.shutdownReason = HostShutdownReason.Api; window.location.reload(); } From 55dd6cc5191db572a9cdc69705bbdd189989c41b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Oct 2020 12:24:46 +0200 Subject: [PATCH 114/121] fix #108977 --- .../services/dialogs/electron-sandbox/dialogService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts index d28b6b5e923..c6886ea84e7 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/dialogService.ts @@ -215,7 +215,7 @@ class NativeDialogService implements IDialogService { const osProps = await this.nativeHostService.getOSProperties(); const detailString = (useAgo: boolean): string => { - return nls.localize('aboutDetail', + return nls.localize({ key: 'aboutDetail', comment: ['Electron, Chrome, Node.js and V8 are product names that need no translation'] }, "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", version, this.productService.commit || 'Unknown', From 55bd429f3182351e4abcfe1c195cf2ef81fe83be Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 21 Oct 2020 13:24:27 +0200 Subject: [PATCH 115/121] Fix file:// URIs in terminal when remote OS is different Fixes #109076 --- .../contrib/terminal/browser/links/terminalLinkManager.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index c2a23641883..10c3aca946e 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -17,7 +17,7 @@ import type { Terminal, IViewportRange, ILinkProvider } from 'xterm'; import { Schemas } from 'vs/base/common/network'; import { posix, win32 } from 'vs/base/common/path'; import { ITerminalExternalLinkProvider, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { OperatingSystem, isMacintosh, OS } from 'vs/base/common/platform'; +import { OperatingSystem, isMacintosh, OS, isWindows } from 'vs/base/common/platform'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { TerminalProtocolLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider'; import { TerminalValidatedLocalLinkProvider, lineAndColumnClause, unixLocalLinkClause, winLocalLinkClause, winDrivePrefix, winLineAndColumnMatchIndex, unixLineAndColumnMatchIndex, lineAndColumnClauseGroupCount } from 'vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider'; @@ -34,6 +34,7 @@ export type XtermLinkMatcherValidationCallback = (uri: string, callback: (isVali interface IPath { join(...paths: string[]): string; normalize(path: string): string; + sep: '\\' | '/'; } /** @@ -192,7 +193,9 @@ export class TerminalLinkManager extends DisposableStore { // respect line/col attachment const uri = URI.parse(link); if (uri.scheme === Schemas.file) { - this._handleLocalLink(uri.fsPath); + // Just using fsPath here is unsafe: https://github.com/microsoft/vscode/issues/109076 + const fsPath = uri.fsPath; + this._handleLocalLink(((this.osPath.sep === posix.sep) && isWindows) ? fsPath.replace(/\\/g, posix.sep) : fsPath); return; } From 0b148e83db9b7b2870ae1897605d07a5ad57b48e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 21 Oct 2020 15:27:18 +0200 Subject: [PATCH 116/121] Provide command to get package manager Fixes #109071 --- extensions/npm/package.json | 8 ++++++++ extensions/npm/package.nls.json | 3 ++- extensions/npm/src/npmMain.ts | 9 +++++++-- extensions/npm/src/tasks.ts | 14 +++++++------- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 85d7d9b0b64..0c363cb2935 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -95,6 +95,10 @@ { "command": "npm.runScriptFromFolder", "title": "%command.runScriptFromFolder%" + }, + { + "command": "npm.packageManager", + "title": "%command.packageManager" } ], "menus": { @@ -126,6 +130,10 @@ { "command": "npm.runScriptFromFolder", "when": "false" + }, + { + "command": "npm.packageManager", + "when": "false" } ], "editor/context": [ diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 51756d241f1..8ecf3746281 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -19,5 +19,6 @@ "command.openScript": "Open", "command.runInstall": "Run Install", "command.runSelectedScript": "Run Script", - "command.runScriptFromFolder": "Run NPM Script in Folder..." + "command.runScriptFromFolder": "Run NPM Script in Folder...", + "command.packageManager": "Get Configured Package Manager" } diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index fc0f8a5c557..f5c4b419e53 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { runSelectedScript, selectAndRunScriptFromFolder } from './commands'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { invalidateTasksCache, NpmTaskProvider } from './tasks'; +import { getPackageManager, invalidateTasksCache, NpmTaskProvider } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; let treeDataProvider: NpmScriptsTreeDataProvider | undefined; @@ -56,7 +56,12 @@ export async function activate(context: vscode.ExtensionContext): Promise context.subscriptions.push(vscode.commands.registerCommand('npm.refresh', () => { invalidateScriptCaches(); })); - + context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { + if (args instanceof vscode.Uri) { + return getPackageManager(args, true); + } + return ''; + })); } function canRunNpmInCurrentWorkspace() { diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index f0681b489c6..4b51fd395de 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -108,15 +108,15 @@ export function isWorkspaceFolder(value: any): value is WorkspaceFolder { return value && typeof value !== 'number'; } -export async function getPackageManager(folder: WorkspaceFolder): Promise { - let packageManagerName = workspace.getConfiguration('npm', folder.uri).get('packageManager', 'npm'); +export async function getPackageManager(folder: Uri, silent: boolean = false): Promise { + let packageManagerName = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); if (packageManagerName === 'auto') { - const { name, multiplePMDetected } = await findPreferredPM(folder.uri.fsPath); + const { name, multiplePMDetected } = await findPreferredPM(folder.fsPath); packageManagerName = name; - if (multiplePMDetected) { - const multiplePMWarning = localize('npm.multiplePMWarning', 'Found multiple lockfiles for {0}. Using {1} as the preferred package manager.', folder.uri.fsPath, packageManagerName); + if (multiplePMDetected && !silent) { + const multiplePMWarning = localize('npm.multiplePMWarning', 'Found multiple lockfiles for {0}. Using {1} as the preferred package manager.', folder.fsPath, packageManagerName); window.showWarningMessage(multiplePMWarning); } } @@ -291,7 +291,7 @@ export async function createTask(script: NpmTaskDefinition | string, cmd: string kind = script; } - const packageManager = await getPackageManager(folder); + const packageManager = await getPackageManager(folder.uri); async function getCommandLine(cmd: string): Promise { if (workspace.getConfiguration('npm', folder.uri).get('runSilent')) { return `${packageManager} --silent ${cmd}`; @@ -378,7 +378,7 @@ export async function startDebugging(scriptName: string, cwd: string, folder: Wo request: 'launch', name: `Debug ${scriptName}`, cwd, - runtimeExecutable: await getPackageManager(folder), + runtimeExecutable: await getPackageManager(folder.uri), runtimeArgs: [ 'run', scriptName, From 522391c3b5a04e2b31ca2a03d31658b79c4b8ba3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 21 Oct 2020 15:35:43 +0200 Subject: [PATCH 117/121] Show an error if the web worker extension host does not start in 10s --- .../extensions/browser/extensionService.ts | 18 +++++++++++++++++- .../browser/webWorkerExtensionHost.ts | 16 +++++++++++----- .../extensions/common/extensionHostProtocol.ts | 7 +++++++ .../electron-browser/extensionService.ts | 3 ++- .../node/extensionHostProcessSetup.ts | 4 ++-- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 4eb57711e6c..1dff19bf177 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -9,7 +9,7 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IExtensionService, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, IExtensionHost, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -26,6 +26,8 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; +import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -79,6 +81,20 @@ export class ExtensionService extends AbstractExtensionService implements IExten super.dispose(); } + protected _onExtensionHostCrashed(extensionHost: ExtensionHostManager, code: number, signal: string | null): void { + super._onExtensionHostCrashed(extensionHost, code, signal); + if (extensionHost.kind === ExtensionHostKind.LocalWebWorker) { + if (code === ExtensionHostExitCode.StartTimeout10s) { + this._notificationService.prompt( + Severity.Error, + nls.localize('extensionService.startTimeout', "The Web Worker Extension Host did not start in 10s."), + [] + ); + return; + } + } + } + protected async _scanSingleExtension(extension: IExtension): Promise { if (extension.location.scheme === Schemas.vscodeRemote) { return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index f0f7cb8f676..026e286977d 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; -import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { createMessageOfType, MessageType, isMessageOfType, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -121,6 +121,9 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost `; const iframeContent = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; iframe.setAttribute('src', iframeContent); + const timeout = setTimeout(() => { + this._onDidExit.fire([ExtensionHostExitCode.StartTimeout10s, 'The Web Worker Extension Host did not start in 10s']); + }, 10000); const barrier = new Barrier(); let port!: MessagePort; @@ -139,16 +142,19 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost err.name = name; err.stack = stack; onUnexpectedError(err); - this._onDidExit.fire([18, err.message]); + clearTimeout(timeout); + this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, err.message]); return; } const { data } = event.data; if (barrier.isOpen() || !(data instanceof MessagePort)) { console.warn('UNEXPECTED message', event); - this._onDidExit.fire([81, 'UNEXPECTED message']); + clearTimeout(timeout); + this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, 'UNEXPECTED message']); return; } port = data; + clearTimeout(timeout); barrier.open(); })); @@ -193,7 +199,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const { data } = event; if (barrier.isOpen() || !(data instanceof MessagePort)) { console.warn('UNEXPECTED message', event); - this._onDidExit.fire([81, 'UNEXPECTED message']); + this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, 'UNEXPECTED message']); return; } port = data; @@ -217,7 +223,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost worker.onerror = (event) => { console.error(event.message, event.error); - this._onDidExit.fire([81, event.message || event.error]); + this._onDidExit.fire([ExtensionHostExitCode.UnexpectedError, event.message || event.error]); }; // keep for cleanup diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 1889832dd7b..c60299a9a42 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -5,6 +5,13 @@ import { VSBuffer } from 'vs/base/common/buffer'; +export const enum ExtensionHostExitCode { + // nodejs uses codes 1-13 and exit codes >128 are signal exits + VersionMismatch = 55, + StartTimeout10s = 56, + UnexpectedError = 81, +} + export interface IExtHostReadyMessage { type: 'VSCODE_EXTHOST_IPC_READY'; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 49afb84a569..e76e7793de5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -39,6 +39,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ILogService } from 'vs/platform/log/common/log'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { Schemas } from 'vs/base/common/network'; +import { ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -199,7 +200,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten super._onExtensionHostCrashed(extensionHost, code, signal); if (extensionHost.kind === ExtensionHostKind.LocalProcess) { - if (code === 55) { + if (code === ExtensionHostExitCode.VersionMismatch) { this._notificationService.prompt( Severity.Error, nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."), diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 4eb8204bf5a..e39d131fe7b 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -13,7 +13,7 @@ import { PersistentProtocol, ProtocolConstants, BufferedEmitter } from 'vs/base/ import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/common/product'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; -import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; @@ -225,7 +225,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise Date: Wed, 21 Oct 2020 16:14:21 +0200 Subject: [PATCH 118/121] Fix quick input back button --- src/vs/workbench/api/common/extHostTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index f02347835f8..ab6d9a7f6de 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2729,7 +2729,7 @@ export enum DebugConfigurationProviderTriggerKind { @es5ClassCompat export class QuickInputButtons { - static readonly Back: vscode.QuickInputButton = { iconPath: { id: 'back.svg' } }; + static readonly Back: vscode.QuickInputButton | { iconPath: string } = { iconPath: 'back.svg' }; private constructor() { } } From 14cd152991fda43e9cde9ebb65a87e6111135811 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Oct 2020 16:20:28 +0200 Subject: [PATCH 119/121] Offer to open workspace in root even when telemetry has been disabled (fix #108669) --- build/lib/i18n.resources.json | 4 + .../electron-browser/workspaceTagsService.ts | 63 +------------- .../browser/workspaces.contribution.ts | 84 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 3 + 4 files changed, 94 insertions(+), 60 deletions(-) create mode 100644 src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 0d34d1cea63..47f0155d8a5 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -210,6 +210,10 @@ "name": "vs/workbench/contrib/webviewPanel", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/workspaces", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/customEditor", "project": "vscode-workbench" diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts index 616f6e4255f..35b943f8750 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts @@ -7,16 +7,9 @@ import * as crypto from 'crypto'; import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { INotificationService, NeverShowAgainScope, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; -import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; -import { localize } from 'vs/nls'; -import Severity from 'vs/base/common/severity'; -import { joinPath } from 'vs/base/common/resources'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/tags/electron-browser/workspaceTags'; @@ -133,15 +126,12 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IProductService private readonly productService: IProductService, - @IHostService private readonly hostService: IHostService, - @INotificationService private readonly notificationService: INotificationService, - @IQuickInputService private readonly quickInputService: IQuickInputService, @ITextFileService private readonly textFileService: ITextFileService ) { } async getTags(): Promise { if (!this._tags) { - this._tags = await this.resolveWorkspaceTags(rootFiles => this.handleWorkspaceFiles(rootFiles)); + this._tags = await this.resolveWorkspaceTags(); } return this._tags; @@ -301,7 +291,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.playwright" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ - private resolveWorkspaceTags(participant?: (rootFiles: string[]) => void): Promise { + private resolveWorkspaceTags(): Promise { const tags: Tags = Object.create(null); const state = this.contextService.getWorkbenchState(); @@ -318,7 +308,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { tags['workspace.empty'] = isEmpty; const folders = !isEmpty ? workspace.folders.map(folder => folder.uri) : this.productService.quality !== 'stable' && this.findFolders(); - if (!folders || !folders.length || !this.fileService) { + if (!folders || !folders.length) { return Promise.resolve(tags); } @@ -326,10 +316,6 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { const names = ([]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set()); - if (participant) { - participant(names); - } - tags['workspace.grunt'] = nameSet.has('gruntfile.js'); tags['workspace.gulp'] = nameSet.has('gulpfile.js'); tags['workspace.jake'] = nameSet.has('jakefile.js'); @@ -485,49 +471,6 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { }); } - private handleWorkspaceFiles(rootFiles: string[]): void { - const state = this.contextService.getWorkbenchState(); - const workspace = this.contextService.getWorkspace(); - - // Handle top-level workspace files for local single folder workspace - if (state === WorkbenchState.FOLDER) { - const workspaceFiles = rootFiles.filter(hasWorkspaceFileExtension); - if (workspaceFiles.length > 0) { - this.doHandleWorkspaceFiles(workspace.folders[0].uri, workspaceFiles); - } - } - } - - private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void { - const neverShowAgain: INeverShowAgainOptions = { id: 'workspaces.dontPromptToOpen', scope: NeverShowAgainScope.WORKSPACE, isSecondary: true }; - - // Prompt to open one workspace - if (workspaces.length === 1) { - const workspaceFile = workspaces[0]; - - this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ - label: localize('openWorkspace', "Open Workspace"), - run: () => this.hostService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }]) - }], { neverShowAgain }); - } - - // Prompt to select a workspace from many - else if (workspaces.length > 1) { - this.notificationService.prompt(Severity.Info, localize('workspacesFound', "This folder contains multiple workspace files. Do you want to open one? [Learn more]({0}) about workspace files.", 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ - label: localize('selectWorkspace', "Select Workspace"), - run: () => { - this.quickInputService.pick( - workspaces.map(workspace => ({ label: workspace } as IQuickPickItem)), - { placeHolder: localize('selectToOpen', "Select a workspace to open") }).then(pick => { - if (pick) { - this.hostService.openWindow([{ workspaceUri: joinPath(folder, pick.label) }]); - } - }); - } - }], { neverShowAgain }); - } - } - private findFolders(): URI[] | undefined { const folder = this.findFolder(); return folder && [folder]; diff --git a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts new file mode 100644 index 00000000000..eee904f92fa --- /dev/null +++ b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IFileService } from 'vs/platform/files/common/files'; +import { INeverShowAgainOptions, INotificationService, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification'; +import { URI } from 'vs/base/common/uri'; +import { joinPath } from 'vs/base/common/resources'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; + +/** + * A workbench contribution that will look for `.code-workspace` files in the root of the + * workspace folder and open a notification to suggest to open one of the workspaces. + */ +export class WorkspacesFinderContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @INotificationService private readonly notificationService: INotificationService, + @IFileService private readonly fileService: IFileService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IHostService private readonly hostService: IHostService + ) { + super(); + + this.findWorkspaces(); + } + + private async findWorkspaces(): Promise { + const folder = this.contextService.getWorkspace().folders[0]; + if (!folder || this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) { + return; // require a single root folder + } + + const rootFileNames = (await this.fileService.resolve(folder.uri)).children?.map(child => child.name); + if (Array.isArray(rootFileNames)) { + const workspaceFiles = rootFileNames.filter(hasWorkspaceFileExtension); + if (workspaceFiles.length > 0) { + this.doHandleWorkspaceFiles(folder.uri, workspaceFiles); + } + } + } + + private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void { + const neverShowAgain: INeverShowAgainOptions = { id: 'workspaces.dontPromptToOpen', scope: NeverShowAgainScope.WORKSPACE, isSecondary: true }; + + // Prompt to open one workspace + if (workspaces.length === 1) { + const workspaceFile = workspaces[0]; + + this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ + label: localize('openWorkspace', "Open Workspace"), + run: () => this.hostService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }]) + }], { neverShowAgain }); + } + + // Prompt to select a workspace from many + else if (workspaces.length > 1) { + this.notificationService.prompt(Severity.Info, localize('workspacesFound', "This folder contains multiple workspace files. Do you want to open one? [Learn more]({0}) about workspace files.", 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ + label: localize('selectWorkspace', "Select Workspace"), + run: () => { + this.quickInputService.pick( + workspaces.map(workspace => ({ label: workspace } as IQuickPickItem)), + { placeHolder: localize('selectToOpen', "Select a workspace to open") }).then(pick => { + if (pick) { + this.hostService.openWindow([{ workspaceUri: joinPath(folder, pick.label) }]); + } + }); + } + }], { neverShowAgain }); + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspacesFinderContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 95a13d61a16..fa0ea82dcf2 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -295,4 +295,7 @@ import 'vs/workbench/contrib/welcome/common/viewsWelcome.contribution'; // Timeline import 'vs/workbench/contrib/timeline/browser/timeline.contribution'; +// Workspaces +import 'vs/workbench/contrib/workspaces/browser/workspaces.contribution'; + //#endregion From 58e1c6dcaec930446648faeacef7a4f1c0bd95c6 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Oct 2020 16:36:26 +0200 Subject: [PATCH 120/121] fixes #108972 --- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7fe44b35aaf..9aa9f44be57 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -461,7 +461,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { if (action.id === SelectReplAction.ID) { return this.instantiationService.createInstance(SelectReplActionViewItem, this.selectReplAction); } else if (action.id === FILTER_ACTION_ID) { - this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize('workbench.debug.filter.placeholder', "Filter (e.g. text, !exclude)"), this.filterState); + this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action, localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState); return this.filterActionViewItem; } From a27961d843748707c909b8a22926e1e9de3daef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 21 Oct 2020 15:51:55 +0200 Subject: [PATCH 121/121] git: improve status bar message --- extensions/git/src/repository.ts | 22 ++++++++++++++++++++++ extensions/git/src/statusbar.ts | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c9ac43f8960..80d185a8360 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1787,6 +1787,28 @@ export class Repository implements Disposable { return `${this.HEAD.behind}↓ ${this.HEAD.ahead}↑`; } + get syncTooltip(): string { + if (!this.HEAD + || !this.HEAD.name + || !this.HEAD.commit + || !this.HEAD.upstream + || !(this.HEAD.ahead || this.HEAD.behind) + ) { + return localize('sync changes', "Synchronize Changes"); + } + + const remoteName = this.HEAD && this.HEAD.remote || this.HEAD.upstream.remote; + const remote = this.remotes.find(r => r.name === remoteName); + + if ((remote && remote.isReadOnly) || !this.HEAD.ahead) { + return localize('pull n', "Pull {0} commits from {1}/{2}", this.HEAD.behind, this.HEAD.upstream.remote, this.HEAD.upstream.name); + } else if (!this.HEAD.behind) { + return localize('push n', "Push {0} commits to {1}/{2}", this.HEAD.ahead, this.HEAD.upstream.remote, this.HEAD.upstream.name); + } else { + return localize('pull push n', "Pull {0} and push {1} commits between {2}/{3}", this.HEAD.behind, this.HEAD.ahead, this.HEAD.upstream.remote, this.HEAD.upstream.name); + } + } + private updateInputBoxPlaceholder(): void { const branchName = this.headShortName; diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index 8a108ae1b45..fd556024f83 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -28,7 +28,7 @@ class CheckoutStatusBar { return { command: 'git.checkout', - tooltip: `${this.repository.headLabel}`, + tooltip: localize('checkout', "Checkout branch/tag..."), title, arguments: [this.repository.sourceControl] }; @@ -150,7 +150,7 @@ class SyncStatusBar { const rebaseWhenSync = config.get('rebaseWhenSync'); command = rebaseWhenSync ? 'git.syncRebase' : 'git.sync'; - tooltip = localize('sync changes', "Synchronize Changes"); + tooltip = this.repository.syncTooltip; } else { icon = '$(cloud-upload)'; command = 'git.publish';