From addd6f19f26550a7caeacb61e1b394a4e44fa62b Mon Sep 17 00:00:00 2001 From: Rasmus Moorats Date: Fri, 3 Jan 2020 15:10:20 +0200 Subject: [PATCH 001/192] avoid polluting shell history with debugger commands fixes #88038 --- src/vs/workbench/contrib/debug/node/terminals.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index e4e0abd2280..ebfb917c820 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -108,7 +108,8 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } let quote: (s: string) => string; - let command = ''; + // begin command with a space to avoid polluting shell history + let command = ' '; switch (shellType) { From 18c6615ee2beabe75a173edb651af7212d75d1f6 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 10 Feb 2020 21:03:09 -0800 Subject: [PATCH 002/192] Add a command to open the EH logs folder Fix microsoft/vscode-remote-release#412 --- .../electron-browser/logs.contribution.ts | 3 ++- .../logs/electron-browser/logsActions.ts | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts index 5aa0badb11e..f98f5653b1b 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts @@ -7,8 +7,9 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser/logsActions'; +import { OpenLogsFolderAction, OpenExtensionLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser/logsActions'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); +workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenExtensionLogsFolderAction, OpenExtensionLogsFolderAction.ID, OpenExtensionLogsFolderAction.LABEL), 'Developer: Open Extension Logs Folder', devCategory); diff --git a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts index f2287b85265..50823a6f793 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts @@ -9,6 +9,7 @@ import { join } from 'vs/base/common/path'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; export class OpenLogsFolderAction extends Action { @@ -26,3 +27,22 @@ export class OpenLogsFolderAction extends Action { return this.electronService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')).fsPath); } } + +export class OpenExtensionLogsFolderAction extends Action { + + static readonly ID = 'workbench.action.openExtensionLogsFolder'; + static readonly LABEL = nls.localize('openExtensionLogsFolder', "Open Extension Logs Folder"); + + constructor(id: string, label: string, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IElectronService private readonly electronService: IElectronService, + @IElectronEnvironmentService private readonly electronEnvironmentService: IElectronEnvironmentService, + ) { + super(id, label); + } + + run(): Promise { + const extensionLogsPath = URI.file(join(this.environmentService.logsPath, `exthost${this.electronEnvironmentService.windowId}`, 'exthost.log')).fsPath; + return this.electronService.showItemInFolder(extensionLogsPath); + } +} From df74b406337daa141f2c6b4a12d780dce9676b09 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 11 Feb 2020 12:04:09 +0100 Subject: [PATCH 003/192] Introduce a MinimapModel --- .../browser/viewParts/minimap/minimap.ts | 211 ++++++++++++++---- 1 file changed, 165 insertions(+), 46 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 5c8b974989d..d949a6984a8 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import { CharCode } from 'vs/base/common/charCode'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer'; @@ -24,7 +24,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ViewModelDecoration, MinimapLinesRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; @@ -32,7 +32,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { Color } from 'vs/base/common/color'; import { GestureEvent, EventType, Gesture } from 'vs/base/browser/touch'; import { MinimapCharRendererFactory } from 'vs/editor/browser/viewParts/minimap/minimapCharRendererFactory'; -import { MinimapPosition } from 'vs/editor/common/model'; +import { MinimapPosition, TextModelResolvedOptions } from 'vs/editor/common/model'; import { once } from 'vs/base/common/functional'; function getMinimapLineHeight(renderMinimap: RenderMinimap, scale: number): number { @@ -460,6 +460,132 @@ class MinimapBuffers { export class Minimap extends ViewPart { + private _model: MinimapModel; + private _actual: InnerMinimap; + + constructor(context: ViewContext) { + super(context); + + this._model = new MinimapModel(context); + this._actual = new InnerMinimap(context.configuration, context.theme, this._model); + } + + public dispose(): void { + this._actual.dispose(); + super.dispose(); + } + + public getDomNode(): FastDomNode { + return this._actual.getDomNode(); + } + + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return this._actual.onConfigurationChanged(e); + } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + return this._actual.onCursorStateChanged(e); + } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return this._actual.onDecorationsChanged(e); + } + public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return this._actual.onFlushed(e); + } + public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return this._actual.onLinesChanged(e); + } + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return this._actual.onLinesDeleted(e); + } + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return this._actual.onLinesInserted(e); + } + public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return this._actual.onScrollChanged(e); + } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this._context.model.invalidateMinimapColorCache(); + this._actual.onThemeChanged(e); + return true; + } + public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + return this._actual.onTokensChanged(e); + } + public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + return this._actual.onTokensColorsChanged(e); + } + public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return this._actual.onZonesChanged(e); + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + this._actual.prepareRender(ctx); + } + + public render(ctx: RestrictedRenderingContext): void { + this._actual.render(ctx); + } +} + +class MinimapModel { + + private readonly _context: ViewContext; + + constructor(context: ViewContext) { + this._context = context; + } + + public getLineCount(): number { + return this._context.model.getLineCount(); + } + + public getLineContent(lineNumber: number): string { + return this._context.model.getLineContent(lineNumber); + } + + public getLineMaxColumn(lineNumber: number): number { + return this._context.model.getLineMaxColumn(lineNumber); + } + + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { + return this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); + } + + public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] { + return this._context.model.getDecorationsInViewport(visibleRange); + } + + public getOptions(): TextModelResolvedOptions { + return this._context.model.getOptions(); + } + + public revealLineNumber(lineNumber: number): void { + this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( + 'mouse', + new Range(lineNumber, 1, lineNumber, 1), + viewEvents.VerticalRevealType.Center, + false, + ScrollType.Smooth + )); + } + + public setScrollTop(scrollTop: number): void { + this._context.viewLayout.setScrollPositionNow({ + scrollTop: scrollTop + }); + } +} + +class InnerMinimap extends Disposable { + + private readonly _configuration: IConfiguration; + private readonly _theme: EditorTheme; + private readonly _model: MinimapModel; + private readonly _tokensColorTracker: MinimapTokensColorTracker; private readonly _domNode: FastDomNode; private readonly _shadow: FastDomNode; @@ -483,14 +609,22 @@ export class Minimap extends ViewPart { private _gestureInProgress: boolean = false; private _buffers: MinimapBuffers | null; - constructor(context: ViewContext) { - super(context); + constructor( + configuration: IConfiguration, + theme: EditorTheme, + model: MinimapModel + ) { + super(); + + this._configuration = configuration; + this._theme = theme; + this._model = model; this._tokensColorTracker = MinimapTokensColorTracker.getInstance(); - this._options = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker); + this._options = new MinimapOptions(this._configuration, this._theme, this._tokensColorTracker); this._lastRenderData = null; this._buffers = null; - this._selectionColor = this._context.theme.getColor(minimapSelection); + this._selectionColor = this._theme.getColor(minimapSelection); this._domNode = createFastDomNode(document.createElement('div')); PartFingerprints.write(this._domNode, PartFingerprint.Minimap); @@ -543,15 +677,9 @@ export class Minimap extends ViewPart { const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber; - lineNumber = Math.min(lineNumber, this._context.model.getLineCount()); + lineNumber = Math.min(lineNumber, this._model.getLineCount()); - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( - 'mouse', - new Range(lineNumber, 1, lineNumber, 1), - viewEvents.VerticalRevealType.Center, - false, - ScrollType.Smooth - )); + this._model.revealLineNumber(lineNumber); }); this._sliderMouseMoveMonitor = new GlobalMouseMoveMonitor(); @@ -575,16 +703,12 @@ export class Minimap extends ViewPart { if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { // The mouse has wondered away from the scrollbar => reset dragging - this._context.viewLayout.setScrollPositionNow({ - scrollTop: initialSliderState.scrollTop - }); + this._model.setScrollTop(initialSliderState.scrollTop); return; } const mouseDelta = mouseMoveData.posy - initialMousePosition; - this._context.viewLayout.setScrollPositionNow({ - scrollTop: initialSliderState.getDesiredScrollTopFromDelta(mouseDelta) - }); + this._model.setScrollTop(initialSliderState.getDesiredScrollTopFromDelta(mouseDelta)); }, () => { this._slider.toggleClassName('active', false); @@ -623,9 +747,7 @@ export class Minimap extends ViewPart { private scrollDueToTouchEvent(touch: GestureEvent) { const startY = this._domNode.domNode.getBoundingClientRect().top; const scrollTop = this._lastRenderData!.renderedLayout.getDesiredScrollTopFromTouchLocation(touch.pageY - startY); - this._context.viewLayout.setScrollPositionNow({ - scrollTop: scrollTop - }); + this._model.setScrollTop(scrollTop); } public dispose(): void { @@ -684,7 +806,7 @@ export class Minimap extends ViewPart { } private _onOptionsMaybeChanged(): boolean { - const opts = new MinimapOptions(this._context.configuration, this._context.theme, this._tokensColorTracker); + const opts = new MinimapOptions(this._configuration, this._theme, this._tokensColorTracker); if (this._options.equals(opts)) { return false; } @@ -706,6 +828,10 @@ export class Minimap extends ViewPart { this._renderDecorations = true; return true; } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + this._renderDecorations = true; + return true; + } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { this._lastRenderData = null; return true; @@ -732,6 +858,12 @@ export class Minimap extends ViewPart { this._renderDecorations = true; return true; } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + this._selectionColor = this._theme.getColor(minimapSelection); + this._renderDecorations = true; + this._onOptionsMaybeChanged(); + return true; + } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { if (this._lastRenderData) { return this._lastRenderData.onTokensChanged(e); @@ -748,19 +880,6 @@ export class Minimap extends ViewPart { return true; } - public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - this._renderDecorations = true; - return true; - } - - public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - this._context.model.invalidateMinimapColorCache(); - this._selectionColor = this._context.theme.getColor(minimapSelection); - this._renderDecorations = true; - this._onOptionsMaybeChanged(); - return true; - } - // --- end event handlers public prepareRender(ctx: RenderingContext): void { @@ -787,7 +906,7 @@ export class Minimap extends ViewPart { renderingCtx.visibleRange.endLineNumber, renderingCtx.viewportHeight, (renderingCtx.viewportData.whitespaceViewportData.length > 0), - this._context.model.getLineCount(), + this._model.getLineCount(), renderingCtx.scrollTop, renderingCtx.scrollHeight, this._lastRenderData ? this._lastRenderData.renderedLayout : null @@ -810,12 +929,12 @@ export class Minimap extends ViewPart { private renderDecorations(layout: MinimapLayout) { if (this._renderDecorations) { this._renderDecorations = false; - const decorations = this._context.model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._context.model.getLineMaxColumn(layout.endLineNumber))); + const decorations = this._model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._model.getLineMaxColumn(layout.endLineNumber))); const { renderMinimap, canvasInnerWidth, canvasInnerHeight } = this._options; const lineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); const characterWidth = getMinimapCharWidth(renderMinimap, this._options.fontScale); - const tabSize = this._context.model.getOptions().tabSize; + const tabSize = this._model.getOptions().tabSize; const canvasContext = this._decorationsCanvas.domNode.getContext('2d')!; canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight); @@ -837,7 +956,7 @@ export class Minimap extends ViewPart { continue; } - const decorationColor = (decoration.options.minimap).getColor(this._context.theme); + const decorationColor = (decoration.options.minimap).getColor(this._theme); for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) { switch (decoration.options.minimap.position) { @@ -877,7 +996,7 @@ export class Minimap extends ViewPart { let lineIndexToXOffset = lineOffsetMap.get(lineNumber); const isFirstDecorationForLine = !lineIndexToXOffset; if (!lineIndexToXOffset) { - const lineData = this._context.model.getLineContent(lineNumber); + const lineData = this._model.getLineContent(lineNumber); lineIndexToXOffset = [MINIMAP_GUTTER_WIDTH]; for (let i = 1; i < lineData.length + 1; i++) { const charCode = lineData.charCodeAt(i - 1); @@ -944,7 +1063,7 @@ export class Minimap extends ViewPart { } // Render untouched lines by using last rendered data. - let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines( + let [_dirtyY1, _dirtyY2, needed] = InnerMinimap._renderUntouchedLines( imageData, startLineNumber, endLineNumber, @@ -953,7 +1072,7 @@ export class Minimap extends ViewPart { ); // Fetch rendering info from view model for rest of lines that need rendering. - const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); + const lineInfo = this._model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); const tabSize = lineInfo.tabSize; const background = this._options.backgroundColor; const useLighterFont = this._tokensColorTracker.backgroundIsLight(); @@ -963,7 +1082,7 @@ export class Minimap extends ViewPart { const renderedLines: MinimapLine[] = []; for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { if (needed[lineIndex]) { - Minimap._renderLine( + InnerMinimap._renderLine( imageData, background, useLighterFont, From 461d7b8d9c86313b7759cb70c4f729d77dd48e61 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 11 Feb 2020 17:05:23 +0100 Subject: [PATCH 004/192] wip: extract documentation ext point --- build/lib/i18n.resources.json | 4 +++ .../common/codeActions.contribution.ts | 4 --- .../common/documentation.contribution.ts | 25 +++++++++++++++++++ .../common/documentationContribution.ts | 3 +-- .../common/documentationExtensionPoint.ts | 0 src/vs/workbench/workbench.common.main.ts | 3 +++ 6 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/documentation/common/documentation.contribution.ts rename src/vs/workbench/contrib/{codeActions => documentation}/common/documentationContribution.ts (95%) rename src/vs/workbench/contrib/{codeActions => documentation}/common/documentationExtensionPoint.ts (100%) diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 31446bfe4d9..4ec85f30c56 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -62,6 +62,10 @@ "name": "vs/workbench/contrib/debug", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/documentation", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" diff --git a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts index c7969c9d1c0..ccdbb435b25 100644 --- a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts +++ b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts @@ -10,12 +10,9 @@ 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'; import { CodeActionsExtensionPoint, codeActionsExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint'; -import { CodeActionDocumentationContribution } from 'vs/workbench/contrib/codeActions/common/documentationContribution'; -import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/documentationExtensionPoint'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; const codeActionsExtensionPoint = ExtensionsRegistry.registerExtensionPoint(codeActionsExtensionPointDescriptor); -const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint(documentationExtensionPointDescriptor); Registry.as(Extensions.Configuration) .registerConfiguration(editorConfiguration); @@ -25,7 +22,6 @@ class WorkbenchConfigurationContribution { @IInstantiationService instantiationService: IInstantiationService, ) { instantiationService.createInstance(CodeActionsContribution, codeActionsExtensionPoint); - instantiationService.createInstance(CodeActionDocumentationContribution, documentationExtensionPoint); } } diff --git a/src/vs/workbench/contrib/documentation/common/documentation.contribution.ts b/src/vs/workbench/contrib/documentation/common/documentation.contribution.ts new file mode 100644 index 00000000000..13f09eee187 --- /dev/null +++ b/src/vs/workbench/contrib/documentation/common/documentation.contribution.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { DocumentationContribution } from 'vs/workbench/contrib/documentation/common/documentationContribution'; +import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/documentation/common/documentationExtensionPoint'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; + +const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint(documentationExtensionPointDescriptor); + +class WorkbenchConfigurationContribution { + constructor( + @IInstantiationService instantiationService: IInstantiationService, + ) { + instantiationService.createInstance(DocumentationContribution, documentationExtensionPoint); + } +} + +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(WorkbenchConfigurationContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts similarity index 95% rename from src/vs/workbench/contrib/codeActions/common/documentationContribution.ts rename to src/vs/workbench/contrib/documentation/common/documentationContribution.ts index 9c2b75c277d..57547e37c1d 100644 --- a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts +++ b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts @@ -15,8 +15,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { DocumentationExtensionPoint } from './documentationExtensionPoint'; - -export class CodeActionDocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { +export class DocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { private contributions: { title: string; diff --git a/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint.ts b/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts similarity index 100% rename from src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint.ts rename to src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 9debc489ee1..ee4edc24c59 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -272,6 +272,9 @@ import 'vs/workbench/contrib/userDataSync/browser/userDataSync.contribution'; // Code Actions import 'vs/workbench/contrib/codeActions/common/codeActions.contribution'; +// Documentation +import 'vs/workbench/contrib/documentation/common/documentation.contribution'; + // Timeline import 'vs/workbench/contrib/timeline/browser/timeline.contribution'; From 8d9be7fb505b8b5accc8e32c747abaf3abf82ae3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 12 Feb 2020 00:15:57 +0100 Subject: [PATCH 005/192] First cut at minimap sampling lines --- .../browser/viewParts/minimap/minimap.ts | 444 ++++++++++++------ .../common/viewModel/splitLinesCollection.ts | 2 +- src/vs/editor/common/viewModel/viewModel.ts | 15 +- .../editor/common/viewModel/viewModelImpl.ts | 10 +- 4 files changed, 311 insertions(+), 160 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index d949a6984a8..f8b400f8161 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -24,7 +24,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewLineData, ViewModelDecoration, MinimapLinesRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; @@ -35,22 +35,6 @@ import { MinimapCharRendererFactory } from 'vs/editor/browser/viewParts/minimap/ import { MinimapPosition, TextModelResolvedOptions } from 'vs/editor/common/model'; import { once } from 'vs/base/common/functional'; -function getMinimapLineHeight(renderMinimap: RenderMinimap, scale: number): number { - if (renderMinimap === RenderMinimap.Text) { - return Constants.BASE_CHAR_HEIGHT * scale; - } - // RenderMinimap.Blocks - return (Constants.BASE_CHAR_HEIGHT + 1) * scale; -} - -function getMinimapCharWidth(renderMinimap: RenderMinimap, scale: number): number { - if (renderMinimap === RenderMinimap.Text) { - return Constants.BASE_CHAR_WIDTH * scale; - } - // RenderMinimap.Blocks - return Constants.BASE_CHAR_WIDTH * scale; -} - /** * The orthogonal distance to the slider at which dragging "resets". This implements "snapping" */ @@ -60,6 +44,8 @@ const GUTTER_DECORATION_WIDTH = 2; class MinimapOptions { + public readonly isSampling: boolean; + public readonly renderMinimap: RenderMinimap; public readonly scrollBeyondLastLine: boolean; @@ -109,17 +95,21 @@ class MinimapOptions { public readonly backgroundColor: RGBA8; - constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker) { + public readonly minimapLineHeight: number; + public readonly minimapCharWidth: number; + + constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker, isSampling: boolean) { const options = configuration.options; const pixelRatio = options.get(EditorOption.pixelRatio); const layoutInfo = options.get(EditorOption.layoutInfo); const fontInfo = options.get(EditorOption.fontInfo); + this.isSampling = isSampling; this.renderMinimap = layoutInfo.renderMinimap | 0; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); const minimapOpts = options.get(EditorOption.minimap); this.showSlider = minimapOpts.showSlider; - this.fontScale = Math.round(minimapOpts.scale * pixelRatio); + this.fontScale = (isSampling ? 1 : Math.round(minimapOpts.scale * pixelRatio)); this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); this.pixelRatio = pixelRatio; this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; @@ -135,6 +125,10 @@ class MinimapOptions { this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker); + + const baseCharHeight = (isSampling ? 1 : this.renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); + this.minimapLineHeight = baseCharHeight * this.fontScale; + this.minimapCharWidth = Constants.BASE_CHAR_WIDTH * this.fontScale; } private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 { @@ -146,7 +140,8 @@ class MinimapOptions { } public equals(other: MinimapOptions): boolean { - return (this.renderMinimap === other.renderMinimap + return (this.isSampling === other.isSampling + && this.renderMinimap === other.renderMinimap && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider && this.pixelRatio === other.pixelRatio @@ -229,6 +224,7 @@ class MinimapLayout { public static create( options: MinimapOptions, + samplingRatio: number, viewportStartLineNumber: number, viewportEndLineNumber: number, viewportHeight: number, @@ -238,11 +234,23 @@ class MinimapLayout { scrollHeight: number, previousLayout: MinimapLayout | null ): MinimapLayout { + // console.log(`create - ${viewportStartLineNumber}, ${viewportEndLineNumber}, ${viewportHeight}, ${viewportContainsWhitespaceGaps}, ${lineCount}, ${scrollTop}, ${scrollHeight}`); const pixelRatio = options.pixelRatio; - const minimapLineHeight = getMinimapLineHeight(options.renderMinimap, options.fontScale); + const minimapLineHeight = options.minimapLineHeight; const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); const lineHeight = options.lineHeight; + if (options.isSampling) { + const expectedViewportLineCount = viewportHeight / lineHeight; + const sliderHeight = Math.max(1, Math.floor(expectedViewportLineCount * minimapLineHeight / pixelRatio / samplingRatio)); + const maxMinimapSliderTop = Math.max(0, options.minimapHeight - sliderHeight); + // The slider can move from 0 to `maxMinimapSliderTop` + // in the same way `scrollTop` can move from 0 to `scrollHeight` - `viewportHeight`. + const computedSliderRatio = (maxMinimapSliderTop) / (scrollHeight - viewportHeight); + const sliderTop = (scrollTop * computedSliderRatio); + return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, 1, lineCount); + } + // The visible line count in a viewport can change due to a number of reasons: // a) with the same viewport width, different scroll positions can result in partial lines being visible: // e.g. for a line height of 20, and a viewport height of 600 @@ -400,8 +408,8 @@ class RenderData { public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { this._renderedLines.onLinesInserted(e.fromLineNumber, e.toLineNumber); } - public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { - return this._renderedLines.onTokensChanged(e.ranges); + public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { + return this._renderedLines.onTokensChanged(ranges); } } @@ -458,16 +466,63 @@ class MinimapBuffers { } } -export class Minimap extends ViewPart { +export interface IMinimapModel { + readonly tokensColorTracker: MinimapTokensColorTracker; + readonly options: MinimapOptions; + + getLineCount(): number; + getLineContent(lineNumber: number): string; + getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; + getSelections(): Selection[]; + getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[]; + getOptions(): TextModelResolvedOptions; + revealLineNumber(lineNumber: number): void; + setScrollTop(scrollTop: number): void; +} + +export interface IMinimapRenderingContext { + readonly samplingRatio: number; + readonly viewportContainsWhitespaceGaps: boolean; + + readonly scrollWidth: number; + readonly scrollHeight: number; + + readonly viewportStartLineNumber: number; + readonly viewportEndLineNumber: number; + + readonly scrollTop: number; + readonly scrollLeft: number; + + readonly viewportWidth: number; + readonly viewportHeight: number; +} + +export class Minimap extends ViewPart implements IMinimapModel { + + private _selections: Selection[]; + private _minimapSelections: Selection[]; + + private _samplingRatio!: number; + private _minimapLines!: number[]; + private _isSampling = false;//true; + + public readonly tokensColorTracker: MinimapTokensColorTracker; + public options: MinimapOptions; - private _model: MinimapModel; private _actual: InnerMinimap; constructor(context: ViewContext) { super(context); - this._model = new MinimapModel(context); - this._actual = new InnerMinimap(context.configuration, context.theme, this._model); + this._selections = []; + this._minimapSelections = []; + + this._recreateLineMapping(); + + this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); + this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); + + this._actual = new InnerMinimap(context.theme, this); } public dispose(): void { @@ -479,84 +534,213 @@ export class Minimap extends ViewPart { return this._actual.getDomNode(); } + private _onOptionsMaybeChanged(): boolean { + const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); + if (this.options.equals(opts)) { + return false; + } + this.options = opts; + this._actual.onDidChangeOptions(); + return true; + } + // ---- begin view event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - return this._actual.onConfigurationChanged(e); + return this._onOptionsMaybeChanged(); } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - return this._actual.onCursorStateChanged(e); + this._selections = e.selections; + this._recomputeMinimapSelections(); + return this._actual.onSelectionChanged(); } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - return this._actual.onDecorationsChanged(e); + return this._actual.onDecorationsChanged(); } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { - return this._actual.onFlushed(e); + return this._actual.onFlushed(); } public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { - return this._actual.onLinesChanged(e); + return this._actual.onLinesChanged(e); // TODO } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { - return this._actual.onLinesDeleted(e); + return this._actual.onLinesDeleted(e); // TODO } public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { - return this._actual.onLinesInserted(e); + return this._actual.onLinesInserted(e); // TODO } public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { - return this._actual.onScrollChanged(e); + return this._actual.onScrollChanged(); } public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { this._context.model.invalidateMinimapColorCache(); - this._actual.onThemeChanged(e); + this._actual.onThemeChanged(); + this._onOptionsMaybeChanged(); return true; } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { - return this._actual.onTokensChanged(e); + if (this._isSampling) { + let ranges: { fromLineNumber: number; toLineNumber: number; }[] = []; + for (const range of e.ranges) { + ranges.push({ + fromLineNumber: this._modelLineToMinimapLine(range.fromLineNumber), + toLineNumber: this._modelLineToMinimapLine(range.toLineNumber) + }); + } + return this._actual.onTokensChanged(ranges); + } else { + return this._actual.onTokensChanged(e.ranges); + } } public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { - return this._actual.onTokensColorsChanged(e); + return this._actual.onTokensColorsChanged(); } public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { - return this._actual.onZonesChanged(e); + return this._actual.onZonesChanged(); } // --- end event handlers public prepareRender(ctx: RenderingContext): void { - this._actual.prepareRender(ctx); + // nothing to read } public render(ctx: RestrictedRenderingContext): void { - this._actual.render(ctx); + let viewportStartLineNumber = ctx.visibleRange.startLineNumber; + let viewportEndLineNumber = ctx.visibleRange.endLineNumber; + + if (this._isSampling) { + viewportStartLineNumber = this._modelLineToMinimapLine(viewportStartLineNumber); + viewportEndLineNumber = this._modelLineToMinimapLine(viewportEndLineNumber); + } + + const minimapCtx: IMinimapRenderingContext = { + samplingRatio: this._samplingRatio, + viewportContainsWhitespaceGaps: (ctx.viewportData.whitespaceViewportData.length > 0), + + scrollWidth: ctx.scrollWidth, + scrollHeight: ctx.scrollHeight, + + viewportStartLineNumber: viewportStartLineNumber, + viewportEndLineNumber: viewportEndLineNumber, + + scrollTop: ctx.scrollTop, + scrollLeft: ctx.scrollLeft, + + viewportWidth: ctx.viewportWidth, + viewportHeight: ctx.viewportHeight, + }; + this._actual.render(minimapCtx); } -} -class MinimapModel { + //#region IMinimapModel - private readonly _context: ViewContext; + private _recreateLineMapping(): void { + const options = this._context.configuration.options; + const layoutInfo = options.get(EditorOption.layoutInfo); + const pixelRatio = options.get(EditorOption.pixelRatio); - constructor(context: ViewContext) { - this._context = context; + const minimapLineCount = Math.round(pixelRatio * layoutInfo.height); + const modelLineCount = this._context.model.getLineCount(); + const ratio = modelLineCount / minimapLineCount; + + let result: number[] = []; + for (let i = 0; i < minimapLineCount; i++) { + const desiredLine = Math.min(modelLineCount, Math.round((i + 1) * ratio)); + result[i] = desiredLine; + } + + this._samplingRatio = ratio; + this._minimapLines = result; + } + + private _modelLineToMinimapLine(lineNumber: number): number { + return Math.min(this._minimapLines.length, Math.max(1, Math.round(lineNumber / this._samplingRatio))); + } + + private _recomputeMinimapSelections(): void { + this._minimapSelections = []; + for (const selection of this._selections) { + if (this._isSampling) { + const isMultiline = (selection.startLineNumber !== selection.endLineNumber); + + let minimapLineStart = this._modelLineToMinimapLine(selection.startLineNumber); + let minimapLineEnd = this._modelLineToMinimapLine(selection.endLineNumber); + if (isMultiline && minimapLineEnd === minimapLineStart) { + if (minimapLineEnd === this._minimapLines.length) { + if (minimapLineStart > 1) { + minimapLineStart--; + } + } else { + minimapLineEnd++; + } + } + + this._minimapSelections.push(new Selection(minimapLineStart, selection.startColumn, minimapLineEnd, selection.endColumn)); + } else { + this._minimapSelections.push(selection); + } + } } public getLineCount(): number { + if (this._isSampling) { + return this._minimapLines.length; + } return this._context.model.getLineCount(); } public getLineContent(lineNumber: number): string { + if (this._isSampling) { + return this._context.model.getLineContent(this._minimapLines[lineNumber - 1]); + } return this._context.model.getLineContent(lineNumber); } - public getLineMaxColumn(lineNumber: number): number { - return this._context.model.getLineMaxColumn(lineNumber); - } - - public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[] { + if (this._isSampling) { + let result: (ViewLineData | null)[] = []; + for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { + if (needed[lineIndex]) { + result[lineIndex] = this._context.model.getViewLineData(this._minimapLines[startLineNumber + lineIndex - 1]); + } else { + result[lineIndex] = null; + } + } + return result; + } return this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); } - public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] { - return this._context.model.getDecorationsInViewport(visibleRange); + public getSelections(): Selection[] { + return this._minimapSelections; + } + + public getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[] { + let visibleRange: Range; + if (this._isSampling) { + const modelStartLineNumber = this._minimapLines[startLineNumber - 1]; + const modelEndLineNumber = this._minimapLines[endLineNumber - 1]; + visibleRange = new Range(modelStartLineNumber, 1, modelEndLineNumber, this._context.model.getLineMaxColumn(modelEndLineNumber)); + } else { + visibleRange = new Range(startLineNumber, 1, endLineNumber, this._context.model.getLineMaxColumn(endLineNumber)); + } + const decorations = this._context.model.getDecorationsInViewport(visibleRange); + + if (this._isSampling) { + let result: ViewModelDecoration[] = []; + for (const decoration of decorations) { + if (!decoration.options.minimap) { + continue; + } + const range = decoration.range; + const minimapStartLineNumber = this._modelLineToMinimapLine(range.startLineNumber); + const minimapEndLineNumber = this._modelLineToMinimapLine(range.endLineNumber); + result.push(new ViewModelDecoration(new Range(minimapStartLineNumber, range.startColumn, minimapEndLineNumber, range.endColumn), decoration.options)); + } + return result; + } + return decorations; } public getOptions(): TextModelResolvedOptions { @@ -564,6 +748,9 @@ class MinimapModel { } public revealLineNumber(lineNumber: number): void { + if (this._isSampling) { + lineNumber = this._minimapLines[lineNumber - 1]; + } this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( 'mouse', new Range(lineNumber, 1, lineNumber, 1), @@ -578,15 +765,15 @@ class MinimapModel { scrollTop: scrollTop }); } + + //#endregion } class InnerMinimap extends Disposable { - private readonly _configuration: IConfiguration; private readonly _theme: EditorTheme; - private readonly _model: MinimapModel; + private readonly _model: IMinimapModel; - private readonly _tokensColorTracker: MinimapTokensColorTracker; private readonly _domNode: FastDomNode; private readonly _shadow: FastDomNode; private readonly _canvas: FastDomNode; @@ -601,27 +788,21 @@ class InnerMinimap extends Disposable { private readonly _sliderTouchMoveListener: IDisposable; private readonly _sliderTouchEndListener: IDisposable; - private _options: MinimapOptions; private _lastRenderData: RenderData | null; - private _selections: Selection[] = []; private _selectionColor: Color | undefined; private _renderDecorations: boolean = false; private _gestureInProgress: boolean = false; private _buffers: MinimapBuffers | null; constructor( - configuration: IConfiguration, theme: EditorTheme, - model: MinimapModel + model: IMinimapModel ) { super(); - this._configuration = configuration; this._theme = theme; this._model = model; - this._tokensColorTracker = MinimapTokensColorTracker.getInstance(); - this._options = new MinimapOptions(this._configuration, this._theme, this._tokensColorTracker); this._lastRenderData = null; this._buffers = null; this._selectionColor = this._theme.getColor(minimapSelection); @@ -665,15 +846,15 @@ class InnerMinimap extends Disposable { this._mouseDownListener = dom.addStandardDisposableListener(this._domNode.domNode, 'mousedown', (e) => { e.preventDefault(); - const renderMinimap = this._options.renderMinimap; + const renderMinimap = this._model.options.renderMinimap; if (renderMinimap === RenderMinimap.None) { return; } if (!this._lastRenderData) { return; } - const minimapLineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); - const internalOffsetY = this._options.pixelRatio * e.browserEvent.offsetY; + const minimapLineHeight = this._model.options.minimapLineHeight; + const internalOffsetY = this._model.options.pixelRatio * e.browserEvent.offsetY; const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber; @@ -762,7 +943,7 @@ class InnerMinimap extends Disposable { } private _getMinimapDomNodeClassName(): string { - if (this._options.showSlider === 'always') { + if (this._model.options.showSlider === 'always') { return 'minimap slider-always'; } return 'minimap slider-mouseover'; @@ -773,66 +954,55 @@ class InnerMinimap extends Disposable { } private _applyLayout(): void { - this._domNode.setLeft(this._options.minimapLeft); - this._domNode.setWidth(this._options.minimapWidth); - this._domNode.setHeight(this._options.minimapHeight); - this._shadow.setHeight(this._options.minimapHeight); + this._domNode.setLeft(this._model.options.minimapLeft); + this._domNode.setWidth(this._model.options.minimapWidth); + this._domNode.setHeight(this._model.options.minimapHeight); + this._shadow.setHeight(this._model.options.minimapHeight); - this._canvas.setWidth(this._options.canvasOuterWidth); - this._canvas.setHeight(this._options.canvasOuterHeight); - this._canvas.domNode.width = this._options.canvasInnerWidth; - this._canvas.domNode.height = this._options.canvasInnerHeight; + this._canvas.setWidth(this._model.options.canvasOuterWidth); + this._canvas.setHeight(this._model.options.canvasOuterHeight); + this._canvas.domNode.width = this._model.options.canvasInnerWidth; + this._canvas.domNode.height = this._model.options.canvasInnerHeight; - this._decorationsCanvas.setWidth(this._options.canvasOuterWidth); - this._decorationsCanvas.setHeight(this._options.canvasOuterHeight); - this._decorationsCanvas.domNode.width = this._options.canvasInnerWidth; - this._decorationsCanvas.domNode.height = this._options.canvasInnerHeight; + this._decorationsCanvas.setWidth(this._model.options.canvasOuterWidth); + this._decorationsCanvas.setHeight(this._model.options.canvasOuterHeight); + this._decorationsCanvas.domNode.width = this._model.options.canvasInnerWidth; + this._decorationsCanvas.domNode.height = this._model.options.canvasInnerHeight; - this._slider.setWidth(this._options.minimapWidth); + this._slider.setWidth(this._model.options.minimapWidth); } private _getBuffer(): ImageData | null { if (!this._buffers) { - if (this._options.canvasInnerWidth > 0 && this._options.canvasInnerHeight > 0) { + if (this._model.options.canvasInnerWidth > 0 && this._model.options.canvasInnerHeight > 0) { this._buffers = new MinimapBuffers( this._canvas.domNode.getContext('2d')!, - this._options.canvasInnerWidth, - this._options.canvasInnerHeight, - this._options.backgroundColor + this._model.options.canvasInnerWidth, + this._model.options.canvasInnerHeight, + this._model.options.backgroundColor ); } } return this._buffers ? this._buffers.getBuffer() : null; } - private _onOptionsMaybeChanged(): boolean { - const opts = new MinimapOptions(this._configuration, this._theme, this._tokensColorTracker); - if (this._options.equals(opts)) { - return false; - } - this._options = opts; + // ---- begin view event handlers + + public onDidChangeOptions(): void { this._lastRenderData = null; this._buffers = null; this._applyLayout(); this._domNode.setClassName(this._getMinimapDomNodeClassName()); - return true; } - - // ---- begin view event handlers - - public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - return this._onOptionsMaybeChanged(); - } - public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - this._selections = e.selections; + public onSelectionChanged(): boolean { this._renderDecorations = true; return true; } - public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + public onDecorationsChanged(): boolean { this._renderDecorations = true; return true; } - public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + public onFlushed(): boolean { this._lastRenderData = null; return true; } @@ -854,40 +1024,35 @@ class InnerMinimap extends Disposable { } return true; } - public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + public onScrollChanged(): boolean { this._renderDecorations = true; return true; } - public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + public onThemeChanged(): boolean { this._selectionColor = this._theme.getColor(minimapSelection); this._renderDecorations = true; - this._onOptionsMaybeChanged(); return true; } - public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { + public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { if (this._lastRenderData) { - return this._lastRenderData.onTokensChanged(e); + return this._lastRenderData.onTokensChanged(ranges); } return false; } - public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean { + public onTokensColorsChanged(): boolean { this._lastRenderData = null; this._buffers = null; return true; } - public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + public onZonesChanged(): boolean { this._lastRenderData = null; return true; } // --- end event handlers - public prepareRender(ctx: RenderingContext): void { - // Nothing to read - } - - public render(renderingCtx: RestrictedRenderingContext): void { - const renderMinimap = this._options.renderMinimap; + public render(renderingCtx: IMinimapRenderingContext): void { + const renderMinimap = this._model.options.renderMinimap; if (renderMinimap === RenderMinimap.None) { this._shadow.setClassName('minimap-shadow-hidden'); this._sliderHorizontal.setWidth(0); @@ -901,11 +1066,12 @@ class InnerMinimap extends Disposable { } const layout = MinimapLayout.create( - this._options, - renderingCtx.visibleRange.startLineNumber, - renderingCtx.visibleRange.endLineNumber, + this._model.options, + renderingCtx.samplingRatio, + renderingCtx.viewportStartLineNumber, + renderingCtx.viewportEndLineNumber, renderingCtx.viewportHeight, - (renderingCtx.viewportData.whitespaceViewportData.length > 0), + renderingCtx.viewportContainsWhitespaceGaps, this._model.getLineCount(), renderingCtx.scrollTop, renderingCtx.scrollHeight, @@ -915,10 +1081,10 @@ class InnerMinimap extends Disposable { this._slider.setHeight(layout.sliderHeight); // Compute horizontal slider coordinates - const scrollLeftChars = renderingCtx.scrollLeft / this._options.typicalHalfwidthCharacterWidth; - const horizontalSliderLeft = Math.min(this._options.minimapWidth, Math.round(scrollLeftChars * getMinimapCharWidth(this._options.renderMinimap, this._options.fontScale) / this._options.pixelRatio)); + const scrollLeftChars = renderingCtx.scrollLeft / this._model.options.typicalHalfwidthCharacterWidth; + const horizontalSliderLeft = Math.min(this._model.options.minimapWidth, Math.round(scrollLeftChars * this._model.options.minimapCharWidth / this._model.options.pixelRatio)); this._sliderHorizontal.setLeft(horizontalSliderLeft); - this._sliderHorizontal.setWidth(this._options.minimapWidth - horizontalSliderLeft); + this._sliderHorizontal.setWidth(this._model.options.minimapWidth - horizontalSliderLeft); this._sliderHorizontal.setTop(0); this._sliderHorizontal.setHeight(layout.sliderHeight); @@ -929,19 +1095,20 @@ class InnerMinimap extends Disposable { private renderDecorations(layout: MinimapLayout) { if (this._renderDecorations) { this._renderDecorations = false; - const decorations = this._model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._model.getLineMaxColumn(layout.endLineNumber))); + const selections = this._model.getSelections(); + const decorations = this._model.getMinimapDecorationsInViewport(layout.startLineNumber, layout.endLineNumber); - const { renderMinimap, canvasInnerWidth, canvasInnerHeight } = this._options; - const lineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); - const characterWidth = getMinimapCharWidth(renderMinimap, this._options.fontScale); + const { canvasInnerWidth, canvasInnerHeight } = this._model.options; + const lineHeight = this._model.options.minimapLineHeight; + const characterWidth = this._model.options.minimapCharWidth; const tabSize = this._model.getOptions().tabSize; const canvasContext = this._decorationsCanvas.domNode.getContext('2d')!; canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight); const lineOffsetMap = new Map(); - for (let i = 0; i < this._selections.length; i++) { - const selection = this._selections[i]; + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; for (let line = selection.startLineNumber; line <= selection.endLineNumber; line++) { this.renderDecorationOnLine(canvasContext, lineOffsetMap, selection, this._selectionColor, layout, line, lineHeight, lineHeight, tabSize, characterWidth); @@ -988,7 +1155,7 @@ class InnerMinimap extends Disposable { const y = (lineNumber - layout.startLineNumber) * lineHeight; // Skip rendering the line if it's vertically outside our viewport - if (y + height < 0 || y > this._options.canvasInnerHeight) { + if (y + height < 0 || y > this._model.options.canvasInnerHeight) { return; } @@ -1041,11 +1208,11 @@ class InnerMinimap extends Disposable { } private renderLines(layout: MinimapLayout): RenderData | null { - const renderMinimap = this._options.renderMinimap; - const charRenderer = this._options.charRenderer(); + const renderMinimap = this._model.options.renderMinimap; + const charRenderer = this._model.options.charRenderer(); const startLineNumber = layout.startLineNumber; const endLineNumber = layout.endLineNumber; - const minimapLineHeight = getMinimapLineHeight(renderMinimap, this._options.fontScale); + const minimapLineHeight = this._model.options.minimapLineHeight; // Check if nothing changed w.r.t. lines from last frame if (this._lastRenderData && this._lastRenderData.linesEquals(layout)) { @@ -1073,9 +1240,9 @@ class InnerMinimap extends Disposable { // Fetch rendering info from view model for rest of lines that need rendering. const lineInfo = this._model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); - const tabSize = lineInfo.tabSize; - const background = this._options.backgroundColor; - const useLighterFont = this._tokensColorTracker.backgroundIsLight(); + const tabSize = this._model.getOptions().tabSize; + const background = this._model.options.backgroundColor; + const useLighterFont = this._model.tokensColorTracker.backgroundIsLight(); // Render the rest of lines let dy = 0; @@ -1087,12 +1254,13 @@ class InnerMinimap extends Disposable { background, useLighterFont, renderMinimap, - this._tokensColorTracker, + this._model.options.minimapCharWidth, + this._model.tokensColorTracker, charRenderer, dy, tabSize, - lineInfo.data[lineIndex]!, - this._options.fontScale + lineInfo[lineIndex]!, + this._model.options.fontScale ); } renderedLines[lineIndex] = new MinimapLine(dy); @@ -1212,6 +1380,7 @@ class InnerMinimap extends Disposable { backgroundColor: RGBA8, useLighterFont: boolean, renderMinimap: RenderMinimap, + charWidth: number, colorTracker: MinimapTokensColorTracker, minimapCharRenderer: MinimapCharRenderer, dy: number, @@ -1221,7 +1390,6 @@ class InnerMinimap extends Disposable { ): void { const content = lineData.content; const tokens = lineData.tokens; - const charWidth = getMinimapCharWidth(renderMinimap, fontScale); const maxDx = target.width - charWidth; let dx = MINIMAP_GUTTER_WIDTH; diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 3a3eab70a1c..07c22199f39 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -129,7 +129,7 @@ export interface IViewModelLinesCollection extends IDisposable { getViewLineMinColumn(viewLineNumber: number): number; getViewLineMaxColumn(viewLineNumber: number): number; getViewLineData(viewLineNumber: number): ViewLineData; - getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array; + getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations; getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[]; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 529f386a135..82051bbeb9b 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -113,7 +113,7 @@ export interface IViewModel { getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; getViewLineData(lineNumber: number): ViewLineData; - getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData; + getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; getCompletelyVisibleViewRange(): Range; getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; @@ -142,19 +142,6 @@ export interface IViewModel { getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null; } -export class MinimapLinesRenderingData { - public readonly tabSize: number; - public readonly data: Array; - - constructor( - tabSize: number, - data: Array - ) { - this.tabSize = tabSize; - this.data = data; - } -} - export class ViewLineData { _viewLineDataBrand: void; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index ed3b3382535..ebd64856397 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -19,7 +19,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; @@ -587,12 +587,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.lines.getViewLineData(lineNumber); } - public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { - let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); - return new MinimapLinesRenderingData( - this.getTabSize(), - result - ); + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[] { + return this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); } public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations { From 47e69381e368760bd152530b891293778d2ada61 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 12 Feb 2020 15:44:37 +0100 Subject: [PATCH 006/192] wip: empty view api --- .../common/documentationContribution.ts | 53 ++++++++++--------- .../common/documentationExtensionPoint.ts | 35 ++++++++++++ 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/documentation/common/documentationContribution.ts b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts index 57547e37c1d..97a97463f01 100644 --- a/src/vs/workbench/contrib/documentation/common/documentationContribution.ts +++ b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts @@ -15,18 +15,16 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { DocumentationExtensionPoint } from './documentationExtensionPoint'; +interface ICodeActionContribution { + title: string; + when: ContextKeyExpr; + command: string; +} + export class DocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { - private contributions: { - title: string; - when: ContextKeyExpr; - command: string; - }[] = []; - - private readonly emptyCodeActionsList = { - actions: [], - dispose: () => { } - }; + private codeActionContributions: ICodeActionContribution[] = []; + private readonly emptyCodeActionsList = { actions: [], dispose: () => { } }; constructor( extensionPoint: IExtensionPoint, @@ -37,24 +35,29 @@ export class DocumentationContribution extends Disposable implements IWorkbenchC this._register(modes.CodeActionProviderRegistry.register('*', this)); extensionPoint.setHandler(points => { - this.contributions = []; + this.codeActionContributions = []; for (const documentation of points) { - if (!documentation.value.refactoring) { - continue; + if (documentation.value.refactoring) { + for (const contribution of documentation.value.refactoring) { + const precondition = ContextKeyExpr.deserialize(contribution.when); + if (!precondition) { + continue; + } + + this.codeActionContributions.push({ + title: contribution.title, + when: precondition, + command: contribution.command + }); + } } - for (const contribution of documentation.value.refactoring) { - const precondition = ContextKeyExpr.deserialize(contribution.when); - if (!precondition) { - continue; + if (documentation.value.view) { + for (const contribution of documentation.value.view) { + const precondition = ContextKeyExpr.deserialize(contribution.when); + + // TODO } - - this.contributions.push({ - title: contribution.title, - when: precondition, - command: contribution.command - }); - } } }); @@ -71,7 +74,7 @@ export class DocumentationContribution extends Disposable implements IWorkbenchC } } - return this.contributions + return this.codeActionContributions .filter(contribution => this.contextKeyService.contextMatchesRules(contribution.when)) .map(contribution => { return { diff --git a/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts b/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts index bb848f8d64d..d2099c4a8bc 100644 --- a/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts +++ b/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts @@ -11,6 +11,8 @@ export enum DocumentationExtensionPointFields { when = 'when', title = 'title', command = 'command', + view = 'view', + contents = 'contents', } export interface RefactoringDocumentationExtensionPoint { @@ -19,8 +21,15 @@ export interface RefactoringDocumentationExtensionPoint { readonly [DocumentationExtensionPointFields.command]: string; } +export interface ViewDocumentationExtensionPoint { + readonly [DocumentationExtensionPointFields.view]: string; + readonly [DocumentationExtensionPointFields.contents]: string; + readonly [DocumentationExtensionPointFields.when]: string; +} + export interface DocumentationExtensionPoint { readonly refactoring?: readonly RefactoringDocumentationExtensionPoint[]; + readonly view?: readonly ViewDocumentationExtensionPoint[]; } const documentationExtensionPointSchema = Object.freeze({ @@ -53,6 +62,32 @@ const documentationExtensionPointSchema = Object.freeze Date: Wed, 12 Feb 2020 16:17:54 +0100 Subject: [PATCH 007/192] fixes #90177 --- .../browser/parts/views/viewPaneContainer.ts | 2 +- .../contrib/debug/browser/callStackView.ts | 17 ++++++++++------- .../contrib/debug/browser/variablesView.ts | 8 ++++---- .../debug/browser/watchExpressionsView.ts | 15 ++++++++------- .../contrib/files/browser/views/explorerView.ts | 4 ---- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index f50f32cc0ae..7c37ed3075a 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -88,7 +88,7 @@ export abstract class ViewPane extends Pane implements IView { private readonly menuActions: ViewMenuActions; protected actionRunner?: IActionRunner; - protected toolbar?: ToolBar; + private toolbar?: ToolBar; private readonly showActionsAlways: boolean = false; private headerContainer?: HTMLElement; private titleContainer?: HTMLElement; diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 6d195c8220f..4358be1522e 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -119,16 +119,11 @@ export class CallStackView extends ViewPane { this.pauseMessageLabel.title = thread.stoppedDetails.text || ''; dom.toggleClass(this.pauseMessageLabel, 'exception', thread.stoppedDetails.reason === 'exception'); this.pauseMessage.hidden = false; - if (this.toolbar) { - this.toolbar.setActions([])(); - } + this.updateActions(); } else { this.pauseMessage.hidden = true; - if (this.toolbar) { - const collapseAction = new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all'); - this.toolbar.setActions([collapseAction])(); - } + this.updateActions(); } this.needsRefresh = false; @@ -153,6 +148,14 @@ export class CallStackView extends ViewPane { this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); } + getActions(): IAction[] { + if (this.pauseMessage.hidden) { + return [new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all')]; + } + + return []; + } + renderBody(container: HTMLElement): void { super.renderBody(container); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 4184deb5dcd..30fb675b178 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -109,10 +109,6 @@ export class VariablesView extends ViewPane { CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService); - if (this.toolbar) { - const collapseAction = new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all'); - this.toolbar.setActions([collapseAction])(); - } this.tree.updateChildren(); this._register(this.debugService.getViewModel().onDidFocusStackFrame(sf => { @@ -154,6 +150,10 @@ export class VariablesView extends ViewPane { })); } + getActions(): IAction[] { + return [new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all')]; + } + layoutBody(width: number, height: number): void { this.tree.layout(width, height); } diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index f95c6728825..0fb5dfb061b 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -95,13 +95,6 @@ export class WatchExpressionsView extends ViewPane { this.tree.setInput(this.debugService); CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService); - if (this.toolbar) { - const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService); - const collapseAction = new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all'); - const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService); - this.toolbar.setActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])(); - } - this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); this._register(this.debugService.getModel().onDidChangeWatchExpressions(async we => { @@ -160,6 +153,14 @@ export class WatchExpressionsView extends ViewPane { this.tree.domFocus(); } + getActions(): IAction[] { + return [ + new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService), + new CollapseAction(this.tree, true, 'explorer-action codicon-collapse-all'), + new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService) + ]; + } + private onMouseDblClick(e: ITreeMouseEvent): void { if ((e.browserEvent.target as HTMLElement).className.indexOf('twistie') >= 0) { // Ignore double click events on twistie diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 8cd40c0c99b..4f5ec72cb90 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -250,10 +250,6 @@ export class ExplorerView extends ViewPane { this.createTree(treeContainer); - if (this.toolbar) { - this.toolbar.setActions(this.getActions(), this.getSecondaryActions())(); - } - this._register(this.labelService.onDidChangeFormatters(() => { this._onDidChangeTitleArea.fire(); })); From b5ae3a367f6a2289271658f3b5978866803375e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Feb 2020 07:31:08 -0800 Subject: [PATCH 008/192] Remove experimentalRefreshOnResume This never actually worked, the problem is that the terminal is still hanging onto the old texture instead of disposing it which was the intent. Some way to indicate to re-create the texture atlas would be needed to fix this, but the hope is to remove the canvas renderer eventually anyway. Part of #69665 --- .../contrib/terminal/browser/terminal.contribution.ts | 5 ----- .../contrib/terminal/browser/terminalInstance.ts | 9 --------- src/vs/workbench/contrib/terminal/common/terminal.ts | 1 - 3 files changed, 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index c6f0946e123..9ad11415ffe 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -332,11 +332,6 @@ configurationRegistry.registerConfiguration({ type: 'boolean', default: true }, - 'terminal.integrated.experimentalRefreshOnResume': { - description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."), - type: 'boolean', - default: false - }, 'terminal.integrated.experimentalUseTitleEvent': { description: nls.localize('terminal.integrated.experimentalUseTitleEvent', "An experimental setting that will use the terminal title event for the dropdown title. This setting will only apply to new terminals."), type: 'boolean', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index fd41e75e93c..cec072d72b3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -847,15 +847,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (!this._xterm) { return; } - if (this._configHelper.config.experimentalRefreshOnResume) { - if (this._xterm.getOption('rendererType') !== 'dom') { - this._xterm.setOption('rendererType', 'dom'); - // Do this asynchronously to clear our the texture atlas as all terminals will not - // be using canvas - const xterm = this._xterm; - setTimeout(() => xterm.setOption('rendererType', 'canvas'), 0); - } - } this._xterm.refresh(0, this._xterm.rows - 1); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index a0a28c39e8f..e58b63cc000 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -119,7 +119,6 @@ export interface ITerminalConfiguration { showExitAlert: boolean; splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; windowsEnableConpty: boolean; - experimentalRefreshOnResume: boolean; experimentalUseTitleEvent: boolean; enableFileLinks: boolean; unicodeVersion: '6' | '11'; From baf92de6225c54cc09c3e92245f98ad313f1b458 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 12 Feb 2020 08:05:38 -0800 Subject: [PATCH 009/192] debug: update js-debug-nightly to "2020.2.1017" @ 2020-02-12T01:06:27.1Z --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 5960fa7fda1..c2755a8478b 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -46,7 +46,7 @@ }, { "name": "ms-vscode.js-debug-nightly", - "version": "2020.2.1017", + "version": "2020.2.1117", "forQualities": [ "insider" ], From 6a04aa0477a0cf6d32e4c5c8db92d23ebdebea14 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 12 Feb 2020 08:35:57 -0800 Subject: [PATCH 010/192] debug: always set environment variables per-process in integrated terminal Fixes #90375 --- src/vs/workbench/api/node/extHostDebugService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index ee2d2c45bb1..dab72ef96b4 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -96,10 +96,8 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { // shellArgs: this._terminalService._getDefaultShellArgs(configProvider), cwd: args.cwd, name: args.title || nls.localize('debug.terminal.title', "debuggee"), - env: args.env }; delete args.cwd; - delete args.env; this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options); } From 29e124a938ee20670bbda585673b9c4188c5b8f1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 12 Feb 2020 17:40:01 +0100 Subject: [PATCH 011/192] web - open in desktop should use opener service --- .../browser/openInDesktop.web.contribution.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts b/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts index 2dc49482c8d..0b959f3abd4 100644 --- a/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts +++ b/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts @@ -18,6 +18,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IApplicationLink } from 'vs/workbench/workbench.web.api'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class OpenInDesktopIndicator extends Disposable implements IWorkbenchContribution { @@ -63,7 +64,8 @@ export class OpenInDesktopAction extends Action { id: string, label: string, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } @@ -72,7 +74,7 @@ export class OpenInDesktopAction extends Action { const links = this.environmentService.options?.applicationLinkProvider?.(); if (Array.isArray(links)) { if (links.length === 1) { - return this.runWithoutPicker(links[0]); + return this.openApplicationLink(links[0]); } return this.runWithPicker(links); @@ -91,7 +93,7 @@ export class OpenInDesktopAction extends Action { quickPick.onDidAccept(() => { const selectedItems = quickPick.selectedItems; if (selectedItems.length === 1) { - this.runWithoutPicker(selectedItems[0]); + this.openApplicationLink(selectedItems[0]); } quickPick.hide(); }); @@ -101,10 +103,8 @@ export class OpenInDesktopAction extends Action { return true; } - private async runWithoutPicker(link: IApplicationLink): Promise { - - // Open directly - window.location.href = link.uri.toString(); + private async openApplicationLink(link: IApplicationLink): Promise { + this.openerService.open(link.uri, { openExternal: true }); return true; } From 614a837995878e0de96ffb65a972cfce98e4acfc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 12 Feb 2020 18:00:58 +0100 Subject: [PATCH 012/192] opener - do not open blank windows for non-http(s) links --- src/vs/editor/browser/services/openerService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index a33a4ec776f..4dfb0035522 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -103,7 +103,15 @@ export class OpenerService implements IOpenerService { // Default external opener is going through window.open() this._externalOpener = { openExternal: href => { - dom.windowOpenNoOpener(href); + // ensure to open HTTP/HTTPS links into new windows + // to not trigger a navigation. Any other link is + // safe to be set as HREF to prevent a blank window + // from opening. + if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) { + dom.windowOpenNoOpener(href); + } else { + window.location.href = href; + } return Promise.resolve(true); } }; From 50bc5d71aa4f5ea728ace3daaa4574f12fcc4d9a Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Feb 2020 10:51:25 -0800 Subject: [PATCH 013/192] migrate comment panel to use view container --- .../api/browser/mainThreadComments.ts | 63 ++++++++++------- .../contrib/comments/browser/commentsPanel.ts | 67 ++++++++++--------- .../comments/browser/commentsTreeViewer.ts | 6 +- 3 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index bbe5edd3b6b..3a3afc745fd 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -14,12 +14,13 @@ import * as modes from 'vs/editor/common/modes'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol'; -import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; export class MainThreadCommentThread implements modes.CommentThread { @@ -343,13 +344,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments private _activeCommentThread?: MainThreadCommentThread; private readonly _activeCommentThreadDisposables = this._register(new DisposableStore()); - private _openPanelListener: IDisposable | null = null; + private _openViewListener: IDisposable | null = null; constructor( extHostContext: IExtHostContext, @ICommentService private readonly _commentService: ICommentService, - @IPanelService private readonly _panelService: IPanelService + @IViewsService private readonly _viewsService: IViewsService, + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments); @@ -376,10 +378,10 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._commentService.registerCommentController(providerId, provider); this._commentControllers.set(handle, provider); - const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID); + const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptor(COMMENTS_VIEW_ID); if (!commentsPanelAlreadyConstructed) { - this.registerPanel(commentsPanelAlreadyConstructed); - this.registerOpenPanelListener(commentsPanelAlreadyConstructed); + this.registerView(commentsPanelAlreadyConstructed); + this.registerViewOpenedListener(commentsPanelAlreadyConstructed); } this._commentService.setWorkspaceComments(String(handle), []); } @@ -444,27 +446,36 @@ export class MainThreadComments extends Disposable implements MainThreadComments return provider.deleteCommentThread(commentThreadHandle); } - private registerPanel(commentsPanelAlreadyConstructed: boolean) { - if (!commentsPanelAlreadyConstructed) { - Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( - CommentsPanel, - COMMENTS_PANEL_ID, - COMMENTS_PANEL_TITLE, - 'commentsPanel', - 10 - )); + private registerView(commentsViewAlreadyRegistered: boolean) { + if (!commentsViewAlreadyRegistered) { + const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ + id: COMMENTS_VIEW_ID, + name: COMMENTS_VIEW_TITLE, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + order: 10, + }, ViewContainerLocation.Panel); + + Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ + id: COMMENTS_VIEW_ID, + name: COMMENTS_VIEW_TITLE, + canToggleVisibility: false, + ctorDescriptor: new SyncDescriptor(CommentsPanel), + focusCommand: { + id: 'workbench.action.focusCommentsPanel' + } + }], VIEW_CONTAINER); } } /** - * If the comments panel has never been opened, the constructor for it has not yet run so it has - * no listeners for comment threads being set or updated. Listen for the panel opening for the + * If the comments view has never been opened, the constructor for it has not yet run so it has + * no listeners for comment threads being set or updated. Listen for the view opening for the * first time and send it comments then. */ - private registerOpenPanelListener(commentsPanelAlreadyConstructed: boolean) { - if (!commentsPanelAlreadyConstructed && !this._openPanelListener) { - this._openPanelListener = this._panelService.onDidPanelOpen(e => { - if (e.panel.getId() === COMMENTS_PANEL_ID) { + private registerViewOpenedListener(commentsPanelAlreadyConstructed: boolean) { + if (!commentsPanelAlreadyConstructed && !this._openViewListener) { + this._openViewListener = this._viewsService.onDidChangeViewVisibility(e => { + if (e.id === COMMENTS_VIEW_ID && e.visible) { keys(this._commentControllers).forEach(handle => { let threads = this._commentControllers.get(handle)!.getAllComments(); @@ -474,9 +485,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments } }); - if (this._openPanelListener) { - this._openPanelListener.dispose(); - this._openPanelListener = null; + if (this._openViewListener) { + this._openViewListener.dispose(); + this._openViewListener = null; } } }); diff --git a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts index 923fb4dcd0b..ebd2b1ae5de 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts @@ -11,22 +11,25 @@ import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TreeResourceNavigator } from 'vs/platform/list/browser/listService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { Panel } from 'vs/workbench/browser/panel'; import { CommentNode, CommentsModel, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel'; import { CommentController } from 'vs/workbench/contrib/comments/browser/commentsEditorContribution'; -import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/contrib/comments/browser/commentService'; +import { IWorkspaceCommentThreadsEvent, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IStorageService } from 'vs/platform/storage/common/storage'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { CommentsList, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { CommentsList, COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; -export class CommentsPanel extends Panel { +export class CommentsPanel extends ViewPane { private treeLabels!: ResourceLabels; private tree!: CommentsList; private treeContainer!: HTMLElement; @@ -35,43 +38,50 @@ export class CommentsPanel extends Panel { private commentsModel!: CommentsModel; private collapseAllAction?: IAction; + readonly onDidChangeVisibility = this.onDidChangeBodyVisibility; + constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ICommentService private readonly commentService: ICommentService, + options: IViewPaneOptions, + @IInstantiationService readonly instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IEditorService private readonly editorService: IEditorService, - @ITelemetryService telemetryService: ITelemetryService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService + @ICommentService private readonly commentService: ICommentService ) { - super(COMMENTS_PANEL_ID, telemetryService, themeService, storageService); + super({ ...(options as IViewPaneOptions), id: COMMENTS_VIEW_ID, ariaHeaderLabel: COMMENTS_VIEW_TITLE }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); } - public create(parent: HTMLElement): void { - super.create(parent); + public renderBody(container: HTMLElement): void { + super.renderBody(container); - dom.addClass(parent, 'comments-panel'); + dom.addClass(container, 'comments-panel'); - let container = dom.append(parent, dom.$('.comments-panel-container')); - this.treeContainer = dom.append(container, dom.$('.tree-container')); + let domContainer = dom.append(container, dom.$('.comments-panel-container')); + this.treeContainer = dom.append(domContainer, dom.$('.tree-container')); this.commentsModel = new CommentsModel(); this.createTree(); - this.createMessageBox(container); + this.createMessageBox(domContainer); this._register(this.commentService.onDidSetAllCommentThreads(this.onAllCommentsChanged, this)); this._register(this.commentService.onDidUpdateCommentThreads(this.onCommentsUpdated, this)); - const styleElement = dom.createStyleSheet(parent); + const styleElement = dom.createStyleSheet(container); this.applyStyles(styleElement); this._register(this.themeService.onThemeChange(_ => this.applyStyles(styleElement))); - this._register(this.onDidChangeVisibility(visible => { + this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { this.refresh(); } })); - this.render(); + this.renderComments(); } private applyStyles(styleElement: HTMLStyleElement) { @@ -101,7 +111,7 @@ export class CommentsPanel extends Panel { styleElement.innerHTML = content.join('\n'); } - private async render(): Promise { + private async renderComments(): Promise { dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads()); await this.tree.setInput(this.commentsModel); this.renderMessage(); @@ -116,12 +126,12 @@ export class CommentsPanel extends Panel { return [this.collapseAllAction]; } - public layout(dimensions: dom.Dimension): void { - this.tree.layout(dimensions.height, dimensions.width); + public layoutBody(height: number, width: number): void { + this.tree.layout(height, width); } public getTitle(): string { - return COMMENTS_PANEL_TITLE; + return COMMENTS_VIEW_TITLE; } private createMessageBox(parent: HTMLElement): void { @@ -224,10 +234,7 @@ export class CommentsPanel extends Panel { CommandsRegistry.registerCommand({ id: 'workbench.action.focusCommentsPanel', handler: async (accessor) => { - const panelService = accessor.get(IPanelService); - const panels = panelService.getPanels(); - if (panels.some(panelIdentifier => panelIdentifier.id === COMMENTS_PANEL_ID)) { - await panelService.openPanel(COMMENTS_PANEL_ID, true); - } + const viewsService = accessor.get(IViewsService); + viewsService.openView(COMMENTS_VIEW_ID, true); } }); diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index ba8510c41f4..f047707083b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -22,8 +22,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; -export const COMMENTS_PANEL_TITLE = 'Comments'; +export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; +export const COMMENTS_VIEW_TITLE = 'Comments'; export class CommentsAsyncDataSource implements IAsyncDataSource { hasChildren(element: any): boolean { @@ -176,7 +176,7 @@ export class CommentsList extends WorkbenchAsyncDataTree { renderers, dataSource, { - ariaLabel: COMMENTS_PANEL_TITLE, + ariaLabel: COMMENTS_VIEW_TITLE, keyboardSupport: true, identityProvider: { getId: (element: any) => { From 1b18d1efca075fc7a3b903624ce2ee49e7a246da Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Feb 2020 10:52:55 -0800 Subject: [PATCH 014/192] rename commentsPanel.ts --- src/vs/workbench/api/browser/mainThreadComments.ts | 2 +- .../comments/browser/{commentsPanel.ts => commentsView.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/vs/workbench/contrib/comments/browser/{commentsPanel.ts => commentsView.ts} (100%) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 3a3afc745fd..bc2a5d3e881 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -15,7 +15,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; -import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel'; +import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol'; import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts similarity index 100% rename from src/vs/workbench/contrib/comments/browser/commentsPanel.ts rename to src/vs/workbench/contrib/comments/browser/commentsView.ts From c826b5312ae8accbe7b415919afe3f97074cae80 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 12 Feb 2020 11:04:14 -0800 Subject: [PATCH 015/192] fixes #90545 --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 30373ccb986..acb6715b8d3 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -191,7 +191,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio 'type': 'string', 'enum': ['left', 'bottom', 'right'], 'default': 'bottom', - 'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom or on the right of the workbench.") + 'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom, right, or left of the workbench.") }, 'workbench.statusBar.visible': { 'type': 'boolean', From d22fd6d8aee60757e8777cab18d1aee0d9d42df4 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Feb 2020 11:56:34 -0800 Subject: [PATCH 016/192] Add .pm as perl. Fixes #90550. --- .../syntaxes/generateTMLanguage.js | 1 + .../syntaxes/searchResult.tmLanguage.json | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index ce2acc34ff6..7f0de430869 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -40,6 +40,7 @@ const mappings = [ ['perl', 'source.perl'], ['php', 'source.php'], ['pl', 'source.perl'], + ['pm', 'source.perl'], ['ps1', 'source.powershell'], ['pug', 'text.pug'], ['py', 'source.python'], diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index ea4e3efb154..8edb4a7f89a 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -189,6 +189,9 @@ { "include": "#pl" }, + { + "include": "#pm" + }, { "include": "#ps1" }, @@ -3457,6 +3460,92 @@ } ] }, + "pm": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pm)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ (?:((\\d+)(:))|((\\d+) ))", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + } + ] + }, "ps1": { "name": "meta.resultBlock.search", "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ps1)(:)$", From a670b0a578e20f4d418dfbc60872499d494e826a Mon Sep 17 00:00:00 2001 From: Michael Scovetta Date: Wed, 12 Feb 2020 12:18:52 -0800 Subject: [PATCH 017/192] Fix suspected bug, replacing string with itself The original code did a string.replace('|', '\|'), which is a no-op. I think it was meant to escape pipes, in which case, you need another slash in there. I did not test this, and have no idea of the context for this code. --- src/vs/platform/markers/common/markers.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 0d852a63de8..e89a52153d2 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -135,15 +135,15 @@ export namespace IMarkerData { export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string { let result: string[] = [emptyString]; if (markerData.source) { - result.push(markerData.source.replace('¦', '\¦')); + result.push(markerData.source.replace('¦', '\\¦')); } else { result.push(emptyString); } if (markerData.code) { if (typeof markerData.code === 'string') { - result.push(markerData.code.replace('¦', '\¦')); + result.push(markerData.code.replace('¦', '\\¦')); } else { - result.push(markerData.code.value.replace('¦', '\¦')); + result.push(markerData.code.value.replace('¦', '\\¦')); } } else { result.push(emptyString); @@ -157,7 +157,7 @@ export namespace IMarkerData { // Modifed to not include the message as part of the marker key to work around // https://github.com/microsoft/vscode/issues/77475 if (markerData.message && useMessage) { - result.push(markerData.message.replace('¦', '\¦')); + result.push(markerData.message.replace('¦', '\\¦')); } else { result.push(emptyString); } From 6338aed25d6b07324883e1dea49b7b06837147c6 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 12 Feb 2020 12:30:20 -0800 Subject: [PATCH 018/192] Fix #90552 --- .../editor/contrib/suggest/media/suggest.css | 19 +++++++++++++++---- .../suggest/media/suggestStatusBar.css | 2 +- .../editor/contrib/suggest/suggestWidget.ts | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 2176471c248..fe26944adf8 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -177,6 +177,11 @@ opacity: 0.7; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label { + overflow: auto; + text-overflow: ellipsis; +} + .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label { margin-left: 4px; opacity: 0.4; @@ -213,8 +218,8 @@ /** Details: if using CompletionItemLabel#details, always show **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right.always-show-details > .details-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right.always-show-details > .details-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .right > .details-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label) > .contents > .main > .right > .details-label { display: inline; } @@ -228,6 +233,12 @@ overflow: hidden; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label { + flex-shrink: 0; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label { + max-width: 80%; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label { flex-shrink: 1; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right { @@ -253,11 +264,11 @@ } /** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right:not(.always-show-details) > .readMore { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .right > .readMore { display: none; } /** Focused item can show ReadMore, but can't when docs is side/below **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore { display: inline-block; } diff --git a/src/vs/editor/contrib/suggest/media/suggestStatusBar.css b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css index 1b0014ec452..f645f3c0240 100644 --- a/src/vs/editor/contrib/suggest/media/suggestStatusBar.css +++ b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css @@ -26,7 +26,7 @@ } .monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row > .contents > .main > .right > .readMore, -.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore { +.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore { display: none; } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 47362e74e7c..5baa237e29a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -244,12 +244,12 @@ class ItemRenderer implements IListRenderer Date: Wed, 12 Feb 2020 12:34:56 -0800 Subject: [PATCH 019/192] Fix #90555 (resetLocal() doesn't do anything) Add a call to the underlying synchroniser's resetLocal() method. --- src/vs/platform/userDataSync/common/userDataSyncService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index e10ef737943..afd696492d3 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -189,6 +189,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ await this.checkEnablement(); for (const synchroniser of this.synchronisers) { try { + synchroniser.resetLocal(); } catch (e) { this.logService.error(`${synchroniser.source}: ${toErrorMessage(e)}`); this.logService.error(e); From 7210d0c8c1c2b78fe32d9f878864dd7190eeb663 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 12 Feb 2020 12:58:58 -0800 Subject: [PATCH 020/192] Remove suggest status bar box-shadow --- src/vs/editor/contrib/suggest/media/suggest.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index fe26944adf8..821cb7d2623 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -137,8 +137,6 @@ border-bottom-style: solid; padding: 0 8px 0 4px; - - box-shadow: 0 -.5px 3px #ddd; } .monaco-editor .suggest-widget.list-right.docs-side > .suggest-status-bar { From 2ebc730d72f62493e78e8ff39bb591fb69cd544c Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Feb 2020 14:32:36 -0800 Subject: [PATCH 021/192] eslint code-no-unexternalized-strings --- .../syntaxes/generateTMLanguage.js | 94 +++++++++---------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js index 7f0de430869..7a9eb9204f6 100644 --- a/extensions/search-result/syntaxes/generateTMLanguage.js +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -1,6 +1,4 @@ // @ts-check -// todo@jackson -/* eslint code-no-unexternalized-strings: 0 */ const mappings = [ ['bat', 'source.batchfile'], @@ -105,43 +103,43 @@ mappings.forEach(([ext, scope, regexp]) => repository[ext] = { name: scopes.resultBlock.meta, begin: `^(?!\\s)(.*?)([^\\\\\\/\\n]*${regexp || `\\.${ext}`})(:)$`, - end: "^(?!\\s)", + end: '^(?!\\s)', beginCaptures: { - "0": { name: scopes.resultBlock.path.meta }, - "1": { name: scopes.resultBlock.path.dirname }, - "2": { name: scopes.resultBlock.path.basename }, - "3": { name: scopes.resultBlock.path.colon }, + '0': { name: scopes.resultBlock.path.meta }, + '1': { name: scopes.resultBlock.path.dirname }, + '2': { name: scopes.resultBlock.path.basename }, + '3': { name: scopes.resultBlock.path.colon }, }, patterns: [ { name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '), - begin: "^ ((\\d+) )", - while: "^ (?:((\\d+)(:))|((\\d+) ))", + begin: '^ ((\\d+) )', + while: '^ (?:((\\d+)(:))|((\\d+) ))', beginCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaContext }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaContext }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, }, whileCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaMatch }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaMatch }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, - "4": { name: scopes.resultBlock.result.prefix.metaContext }, - "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + '4': { name: scopes.resultBlock.result.prefix.metaContext }, + '5': { name: scopes.resultBlock.result.prefix.lineNumber }, }, patterns: [{ include: scope }] }, { - begin: "^ ((\\d+)(:))", - while: "(?=not)possible", + begin: '^ ((\\d+)(:))', + while: '(?=not)possible', name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaSingleLine].join(' '), beginCaptures: { - "0": { name: scopes.resultBlock.result.prefix.meta }, - "1": { name: scopes.resultBlock.result.prefix.metaMatch }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '0': { name: scopes.resultBlock.result.prefix.meta }, + '1': { name: scopes.resultBlock.result.prefix.metaMatch }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, }, patterns: [{ include: scope }] } @@ -150,10 +148,10 @@ mappings.forEach(([ext, scope, regexp]) => const header = [ { - begin: "^(# Query): ", - end: "\n", + begin: '^(# Query): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '(\\\\n)|(\\\\\\\\)', @@ -170,10 +168,10 @@ const header = [ ] }, { - begin: "^(# Flags): ", - end: "\n", + begin: '^(# Flags): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)', @@ -183,10 +181,10 @@ const header = [ ] }, { - begin: "^(# ContextLines): ", - end: "\n", + begin: '^(# ContextLines): ', + end: '\n', name: scopes.header.meta, - beginCaptures: { "1": { name: scopes.header.key }, }, + beginCaptures: { '1': { name: scopes.header.key }, }, patterns: [ { match: '\\d', @@ -196,42 +194,42 @@ const header = [ ] }, { - match: "^(# (?:Including|Excluding)): (.*)$", + match: '^(# (?:Including|Excluding)): (.*)$', name: scopes.header.meta, captures: { - "1": { name: scopes.header.key }, - "2": { name: scopes.header.value } + '1': { name: scopes.header.key }, + '2': { name: scopes.header.value } } }, ]; const plainText = [ { - match: "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$", + match: '^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$', name: [scopes.resultBlock.meta, scopes.resultBlock.path.meta].join(' '), captures: { - "1": { name: scopes.resultBlock.path.dirname }, - "2": { name: scopes.resultBlock.path.basename }, - "3": { name: scopes.resultBlock.path.colon } + '1': { name: scopes.resultBlock.path.dirname }, + '2': { name: scopes.resultBlock.path.basename }, + '3': { name: scopes.resultBlock.path.colon } } }, { - match: "^ (?:((\\d+)(:))|((\\d+)( ))(.*))", + match: '^ (?:((\\d+)(:))|((\\d+)( ))(.*))', name: [scopes.resultBlock.meta, scopes.resultBlock.result.meta].join(' '), captures: { - "1": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') }, - "2": { name: scopes.resultBlock.result.prefix.lineNumber }, - "3": { name: scopes.resultBlock.result.prefix.colon }, + '1': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') }, + '2': { name: scopes.resultBlock.result.prefix.lineNumber }, + '3': { name: scopes.resultBlock.result.prefix.colon }, - "4": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, - "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + '4': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, + '5': { name: scopes.resultBlock.result.prefix.lineNumber }, } } ]; const tmLanguage = { - "information_for_contributors": "This file is generated from ./generateTMLanguage.js.", - name: "Search Results", + 'information_for_contributors': 'This file is generated from ./generateTMLanguage.js.', + name: 'Search Results', scopeName: scopes.root, patterns: [ ...header, From 88a1564e9c823c161d3ea5dfa52e1596aa364909 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 12 Feb 2020 14:44:03 -0800 Subject: [PATCH 022/192] Fix duplicate code --- .../server/src/htmlServerMain.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 7f0045fef14..9ed3ce0f0a0 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -515,22 +515,6 @@ connection.onRequest(MatchingTagPositionRequest.type, (params, token) => { }, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token); }); -connection.onRequest(MatchingTagPositionRequest.type, (params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const pos = params.position; - if (pos.character > 0) { - const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); - if (mode && mode.findMatchingTagPosition) { - return mode.findMatchingTagPosition(document, pos); - } - } - } - return null; - }, null, `Error while computing matching tag position for ${params.textDocument.uri}`, token); -}); - let semanticTokensProvider: SemanticTokenProvider | undefined; function getSemanticTokenProvider() { if (!semanticTokensProvider) { From f2c7dcf8d372772a9dbe2b6a9118f98edde6838b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 12 Feb 2020 15:17:47 -0800 Subject: [PATCH 023/192] Move the ext host logs path to a common location And don't hardcode the exthost.log file name --- .../logs/electron-browser/logsActions.ts | 19 +++++++++++-------- .../electron-browser/desktop.main.ts | 3 ++- .../electronEnvironmentService.ts | 12 +++++++++++- .../electron-browser/extensionService.ts | 6 +----- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts index 50823a6f793..9834a34dc62 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logsActions.ts @@ -3,13 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { join } from 'vs/base/common/path'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; +import * as nls from 'vs/nls'; import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { IFileService } from 'vs/platform/files/common/files'; export class OpenLogsFolderAction extends Action { @@ -34,15 +35,17 @@ export class OpenExtensionLogsFolderAction extends Action { static readonly LABEL = nls.localize('openExtensionLogsFolder', "Open Extension Logs Folder"); constructor(id: string, label: string, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IElectronService private readonly electronService: IElectronService, - @IElectronEnvironmentService private readonly electronEnvironmentService: IElectronEnvironmentService, + @IElectronEnvironmentService private readonly electronEnvironmentSerice: IElectronEnvironmentService, + @IFileService private readonly fileService: IFileService, + @IElectronService private readonly electronService: IElectronService ) { super(id, label); } - run(): Promise { - const extensionLogsPath = URI.file(join(this.environmentService.logsPath, `exthost${this.electronEnvironmentService.windowId}`, 'exthost.log')).fsPath; - return this.electronService.showItemInFolder(extensionLogsPath); + async run(): Promise { + const folderStat = await this.fileService.resolve(this.electronEnvironmentSerice.extHostLogsPath); + if (folderStat.children && folderStat.children[0]) { + return this.electronService.showItemInFolder(folderStat.children[0].resource.fsPath); + } } } diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 9b4db895695..e9da83b049d 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -182,7 +182,8 @@ class DesktopMain extends Disposable { serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService); serviceCollection.set(IElectronEnvironmentService, new ElectronEnvironmentService( this.configuration.windowId, - this.environmentService.sharedIPCHandle + this.environmentService.sharedIPCHandle, + this.environmentService )); // Product diff --git a/src/vs/workbench/services/electron/electron-browser/electronEnvironmentService.ts b/src/vs/workbench/services/electron/electron-browser/electronEnvironmentService.ts index ead4c15e1ac..9902c63e3ec 100644 --- a/src/vs/workbench/services/electron/electron-browser/electronEnvironmentService.ts +++ b/src/vs/workbench/services/electron/electron-browser/electronEnvironmentService.ts @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; +import { memoize } from 'vs/base/common/decorators'; +import { join } from 'vs/base/common/path'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export const IElectronEnvironmentService = createDecorator('electronEnvironmentService'); @@ -14,6 +18,8 @@ export interface IElectronEnvironmentService { readonly windowId: number; readonly sharedIPCHandle: string; + + readonly extHostLogsPath: URI; } export class ElectronEnvironmentService implements IElectronEnvironmentService { @@ -22,6 +28,10 @@ export class ElectronEnvironmentService implements IElectronEnvironmentService { constructor( public readonly windowId: number, - public readonly sharedIPCHandle: string + public readonly sharedIPCHandle: string, + private readonly environmentService: IEnvironmentService ) { } + + @memoize + get extHostLogsPath(): URI { return URI.file(join(this.environmentService.logsPath, `exthost${this.windowId}`)); } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 8e2bf659aeb..a3a45ed8ec4 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -9,9 +9,7 @@ import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electro import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; -import * as path from 'vs/base/common/path'; import { runWhenIdle } from 'vs/base/common/async'; -import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -55,7 +53,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten private readonly _remoteExtensionsEnvironmentData: Map; - private readonly _extensionHostLogsLocation: URI; private readonly _extensionScanner: CachedExtensionScanner; private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; @@ -99,7 +96,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._remoteExtensionsEnvironmentData = new Map(); - this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._electronEnvironmentService.windowId}`)); this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); this._deltaExtensionsQueue = []; @@ -363,7 +359,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const result: ExtensionHostProcessManager[] = []; - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._electronEnvironmentService.extHostLogsPath); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); result.push(extHostProcessManager); From 43e268ad720d93ade207682a3c52da8b3100ee41 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 12 Feb 2020 15:45:59 -0800 Subject: [PATCH 024/192] :lipstick: --- .../src/features/documentSymbol.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index 866a86bfebc..02c1be917d3 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -32,6 +32,7 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { }; class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider { + public constructor( private readonly client: ITypeScriptServiceClient, private cachedResponse: CachedResponse, @@ -45,23 +46,27 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider const args: Proto.FileRequestArgs = { file }; const response = await this.cachedResponse.execute(document, () => this.client.execute('navtree', args, token)); - if (response.type !== 'response' || !response.body) { + if (response.type !== 'response' || !response.body?.childItems) { return undefined; } - let tree = response.body; - if (tree && tree.childItems) { - // The root represents the file. Ignore this when showing in the UI - const result: vscode.DocumentSymbol[] = []; - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item)); - return result; + // The root represents the file. Ignore this when showing in the UI + const result: vscode.DocumentSymbol[] = []; + for (const item of response.body.childItems) { + TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item); } - - return undefined; + return result; } - private static convertNavTree(resource: vscode.Uri, bucket: vscode.DocumentSymbol[], item: Proto.NavigationTree): boolean { + private static convertNavTree( + resource: vscode.Uri, + output: vscode.DocumentSymbol[], + item: Proto.NavigationTree, + ): boolean { let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item); + if (!shouldInclude && !item.childItems?.length) { + return false; + } const children = new Set(item.childItems || []); for (const span of item.spans) { @@ -83,7 +88,7 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } if (shouldInclude) { - bucket.push(symbolInfo); + output.push(symbolInfo); } } @@ -98,7 +103,6 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } } - export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, From 4619849662952df35816854484768c29578ea13c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 12 Feb 2020 15:48:32 -0800 Subject: [PATCH 025/192] Don't allow parameter hint controls to shrink to smaller than the content size Fixes #89635 --- src/vs/editor/contrib/parameterHints/parameterHints.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css index 89bf6462e7f..03c4e2640ee 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/parameterHints.css @@ -33,6 +33,7 @@ .monaco-editor .parameter-hints-widget .monaco-scrollable-element, .monaco-editor .parameter-hints-widget .body { display: flex; + flex: 1; flex-direction: column; min-height: 100%; } From e4c77cdd6e98849e17763a5eb73ea1834e739e6c Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 12 Feb 2020 15:58:00 -0800 Subject: [PATCH 026/192] Update debug icon with exploration version --- .../ui/codiconLabel/codicon/codicon.css | 5 +++-- .../ui/codiconLabel/codicon/codicon.ttf | Bin 56484 -> 56736 bytes .../debug/browser/debug.contribution.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index c017682c7f6..e60151c5aa3 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?be537a78617db0869caa4b4cc683a24a") format("truetype"); + src: url("./codicon.ttf?6caeeccc06315e827f3bff83885456fb") format("truetype"); } .codicon[class*='codicon-'] { @@ -413,4 +413,5 @@ .codicon-feedback:before { content: "\eb96" } .codicon-group-by-ref-type:before { content: "\eb97" } .codicon-ungroup-by-ref-type:before { content: "\eb98" } -.codicon-debug-alt:before { content: "\f101" } +.codicon-debug-alt-2:before { content: "\f101" } +.codicon-debug-alt:before { content: "\f102" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index 90ace76ff74d8fefeaca496859d954dea38f0cc9..a62bc6bd7440ca0455aeb2be4ddfec928d4987a3 100644 GIT binary patch delta 4677 zcmYM&dtB6I76$O=9TX6hI}FI387=}Mauw9!A|N0lC}NqZC@L8!C7PBwYG!6sCZ=v{ zZQHt=nOhcCcG+^>a?{MnTr0QC%GBPMO3f~7L_I&xAFH45@5~R8%glSud0^+u9*@83 zvB}|n4^W)|8t1k(wL619-v)T@1%ehXXk9V?)UnPDK;$~$+1{3UP0jW*hYI<=XDF|Q zZ}9qXpq-CjBWFun$MUD^_z3DOFyLtG;<-(4t-rJ%h=>AgEp1K9+ikTt#D5RxJH;(( zYMU4T)X5aM3zd! z*Z2JMMiq|-I8Ol+CN=(Eg)S(`eF$Gi6h-tVE*JC~6w7cMmSY8O$4cCZReT>eR$~qB!aA(S z2HcB{=)?nf5S#E2y08TgV=K0~u^oTFBlshB;4%COPvEck8=k~2?8ejBgTLb$?8Wow z#y-4&{dgI#;8pwsuj2sziG%nT-sH>vZ@i7eID&WZE{@?n^x%CQ#|JopllTxH;}kx@ zr#Owzc)UKx7x)rq@fFVDJlq$cVbF_ja1odAJ+9yf{DhzJ3;OUY`jt{P<)H>DFEvQ{ zsv#;^g{V*!rtB(QMW{#>rJ_}gidAtcK{=FDC8{KqtWwl4m9B=X3^hV!s*x%SkK@ny z3wELi#i+&v)L@+QLsb}RF&=h=<5|qZO}J0RqX3274-6Wj+^em_#Fa|VDpCzd1LHo-aRQe2B3Ed4g_fH*(Qz# z=-q-t0{Urz((VAiZHn^(x<_zwK))|IL!gffP7~-41m_C$3Bd^iZM_V{Sp$7iaOyyR zC^&zht@BHqM9?1#&LrqlfZMuoJV9Fro;awWKM@>R(4PtpFX+>PV+{H;!GQ*SMsT!2 ze=a!WpuZ3tchFx7%w|0+I0D_!U-5=G4WZ8o&PC|+f)f(@g5a!#wwi)C5uvr<{Dd}w zlN7pFaHc|kBRE~5t;QkFS?KR9JhdFT&=&=VFZ3nBF${fKa3Di}FF2Z^uLurl=pO{f zHS~{ygB$uM!4VGqv*0j?whk?Etg~yb@O(7%Xl-&r3Y6Q3aHUjsYuaU~GaL1I9yecfbq~ZmtoSfr1MK##3;$z<3ER85nQDbpta)#2t3KB*9;ZhkuZ^h+Y%;9aBspy3vN)D7{Of%6Dzn? zVd4b$D@?rLriDom+_^9g!R-s zahNoL1FY$SiydaT;EIRI5M1_fn-RPr?tPd{VFf=kQZNg^WC^AMm~6p(0Fxt_6ku`% zGXqSXV0wVb7t9ebqXZKLOu;pL{(JbKP%vh|j1~+WFk=Lx2h3Q(5CY>8j3Y2bg24o) zSTLf%ln90um{P&m0y9o9z`&GQuYVc8`_ruDf~f|kLf{PRc)_FtQz@8vV5$Vu4@|XS z4uY8=n22C%1hWy$M8T8) zx?rXW<}aA*1d|xd^@5oUX1ewI^TK!rbAw<|gP9>1*E>tw@Ekebx-YQfq>k^^jSUZGDU|lMdgLRouPS)Fm zN@QIwR1)h7p^{nMxAQ_Oh4l`hhOw>`%Ie`eg&NMfN+_#``Ndn5)xxWV%4A(5)JWE~ zf{7XCE`i5c*9kXcG|b(Cff{DLV6=w0M=)f=Y!E1By;m@J!)z3c;4t?IhH;oqfpM(& z3kGzU2L!C{c@V+Y1q|&ln*_#p?Fx5Hc$Rk$3kG?Zt%8vrW}ATZ>9z~Td|Xn3fghJ_ z0)>o|-VsaPHoxV5d;L!OUG%s4hxjM>7y4KGxBGA8kC0&j%>g?CUJU3B^bAZ5bOo*o z+!c5tC@d&F=wQ&#!Oq~d!8?PG1z!nC2q_C$81h8O$&migjL?~(ouPeU>%$J%J?#ta z`@>VhH-sOGh>Lh6LJxHfT|e}4WJcti$aPUcQF&3*qgJ}3jz{}NmqouG(;9Oi)+csr zTt?if_?Y;U2|FAaj%ALGjuVbPXQ^|K^V`I@#6^iGl6;cpB)y&-lsr3mOLA|@Gbv|> zH4b|-)tTCox;YJLQE6k+degnrGt*nrPYho_{6a=dMs-Ge#v>WWMwE`2F`{$C@yzhd z>6u$Hk7V|Z%o^#wedO+}QCXX^w`BL^6zA;BIg@iGH#N5-cVBK#9`c-dyYhPTSLgSR zY8-W>z^5RqU~|E~f`f%yMhA=@HTuAqpfR0eu8yr6yJzefSCng=>)WF1iZ&O$R@7Hq zUEEQ;r&yOXlx#2cE?r-GeBAVLJI0+Zt1jDG9#&phKBJ_AU zTvoZi3RNjpGpg2A9jx9^eWxngKPXHLuj1o9LXlYvQ@u%-ZF(M{2K3Dx0)y z(&kB5>Kf{O>TBv}*004LKe=M^W0Q|hNtv>@!PYRUVP(VHQ-@A1p4vQh=hTlH zr#G&g)-Y|?v>)YH3?6$tx>rP|M!MhOFC-F?!2?pxw($zh4t02X*}alIy*wQWNy%Qx z`ANx+-0a-se7j3!XT>Oc{=n?qoTLQLXk~YVhQz2Wml~_`6LXbUV3s{l4GgozxLl=P z-u3C_(FsoHu!@MPv^t-GB`#N5sb_7z)0J#1=|BHyMp63VzWB)nWd-p)>c(G>=Q|zh zQ2zx#wa-;olonh!qC74r*co4*Rv#1Rsw+(|&98EdPAHw_7oV9C8^jM*nVMRe=8FBa zP%Vz`U+JSJ^!L2LnS|~gd7)cXcK7o~n{75*PIuUQ%WQ*vo9E40x?p%yYsc`c?#}na Iec24Z1D-hpZU6uP delta 4481 zcmYM&dsvlK76$Nl10pvMSGkKQ7ZDT?Z>ZcvLPSJCL^HEcL^4rSG&S>(nUYzO7&eW= zRMS|RV-}6swJJ5IA^c5UVZFk&nI8< zY)x<<1I%Us)pHwa8c%kV9R|Gj03oyHFI-i(WI}BU5Yq@e8&*HBrZ!^N)YJUhE*eqK z5A^(?M>roJq{RA$rj<|C^AXHh!0Yvei|5w7n0Q|R5ak1S)HT$sZ1k9f*ZBEJeo_3Q znudA(c14f98+cs^0Q!^ZwTy(8+iDZz?_Tl_w06_ zG8oBm&ayW;ts=Qq6ffrs-^{^8#-S#5FVIb%AHo6}xx`@BQ@bYK5=@c0~_*B=dH z^#k}Vy*&1}b$Le^({|Z=qG+8Hgpg74P9?{1y966gFcu)?*lc!yFTc%h+yu;1|==xQwsy#0Fe5{`?6%;e{UX zMo<3WzWho25daqggAjyZjK&z`AQuG~+jgkuIQKXd;RcLH8OkvkQ&53QRACybF- zBWB_z%)-sM1vQw9d8or}Sb*EH5Di#_#c0G*EJG8PV+HQOO02@2ScALxHEZEUGuGiA z+>4E9!F{+N58y#;K`XYR4cqWA+TGZWNANp5ir?c8=)mK65`V@{JcV7@jc2e2f5EeO z4$q?#FW^NSz)ScWkH+6|7_Z7{D2c=*6e9wW*o#`!W0Q%;C>}EJw#R+1wC(ftb@QkLt|sTo zj}Haxt;#u+wnkwG>m0>X20K^ryusEgo;=uj3U9L3DV|2y`HJTfmhI-?35C5a%(?Jp zAL{~zi*4!t5$;spH7Fio*hPwm8FsPav4&-zJ9xlhmna@}*rkex9(I}H@rUKF>R=~;`*cA$Ctakuz=fXaLU8&eHu&We$v94C^BG@|>`w4c9VrRkLrPyPzYZbc<)~(og zu+0klS)JFA*o$u1b-W?ACG0(l4GOznu~lL3Rcu<=4T|jxyHUY;(^?c;8g`RnbHm=R z*dnmba}XOIc8g;MpZFI(XjP^stMihPaj|Yy#+S8CaYn#yQ=AsC4=Y?_ZCA#>ExOm? z+|j&yN^xMoI&T1RbinRX93rs06$)6NQ5f49?%HlRSzwPV&KTHt6sHaB3B|bs`>x^y zf_2^o;w*xFUvVnIo>ZJqu+EVtPAb?_iZcuLL&fO@%NB4u7Y;Jmj}=E6>}kc}2K$NP zn1el|IPhS(D;yksuw9Bn5cV_0aR~dl;$VdRLUBaGo>d%{JRe{3hB!B2zfzo_uwN_A zQrK?AsS5jz;_QUAijx*r6lX5%w~Esj*6A+d9ELroIFVt$cewe9Mm{*NIHqBLP#oB> z7ZgV~?2n2=9QG&0aSnS?aj?T)QXKKHKPwJ<*vpDzAJ#dK#09_(y21-_7r_2kUH{Fl z@-cBg!2YWEV!=9xkkHKfo8tE1Xja@K5Tm$JARdal1>&i=WguS4&4mN$p}2Y=-ik{I z(o=CAL3|V!6U0|>MM3-&mleccacw~Y6c-qzm*OgexD?u11NkfQ0;hK+NO93Yf)!UD zBt&ufK|&SRAS6t2Awqg9u0}|>;*x|!D6UILq~hX)L@Dg#ODfTd+Y}O`xK|;uiW?Tv zM{(Ce;uNa$GoAOJ7X~@VO^T5YGD|VsL2g!zd63x(zp~z< z4F9^^stj+WMi~#*Im&pk&Q->nwN@D)r~l{ihKwI;oiYKe^OXr*_q`DM!VWo=TXAM0{u z`m?T3CV}-1WfECeDwD*zN||I<_iA3q3}C%enH1JF$~gUemokG`*DB-mGk|*V|;M0V)TY=Pz>Rajf!y`(xMp5A@?apbjT*funxIj zF}6cCD-^Ompcv&L4=R+fZb7JX0pmTSRiU_TZ)8I5Ufw;d82KUXis2u!UBP+kN0f>L5~I<3OW%S z5}XrUAG|yGm5}O?%^?Ru!$Y$}=Y+O|?hic`dM#{3*n+UFVMoF)^p5Y{8Xg^975-@W zwTQBagORb3Gb3M!N{?C_bs{=CdPDS)=u0t?F*{@4h&dM<6kF$xeIoW$pUODIRmFAm zP3?Omeo_4M{TB4k?0+P|FCi|WKA|23}1~PTiKeFZFm@bJ~uySJSQzsvjIZc+KGF2cJpzN>5LplYTP&d`5gm zUB;e_bD1fbvol*WUmM~Y;?5g#G%Imv%Fx=O?+wcy)-_icW1 z{*?UI{4)hU1qB6b3%bV+8oQ$~y0EVB+_=nfOUIolN-f%5bgbxnF^cnwXBKa87w<0a z8lO78tHiIQtYlls!IJY6A}3Ur#+J@3Z7n@L(P!c#6Hk_vm#r>4Fezu!>PfGb2b9k) zZz(@D*>7_49_uJz5o1HLGfG)q7Q! zr)5uDIPF|@P<8k8oasCe`kxS(ViewExtensions.ViewCo id: VIEWLET_ID, name: nls.localize('run', "Run"), ctorDescriptor: new SyncDescriptor(DebugViewPaneContainer), - icon: 'codicon-debug-alt', + icon: 'codicon-debug-alt-2', order: 3 }, ViewContainerLocation.Sidebar); From 43fd87c4cb090f5cc549e92d42cd8320ace70119 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 12 Feb 2020 16:44:33 -0800 Subject: [PATCH 027/192] Fix #90559 --- src/vs/workbench/contrib/search/browser/searchWidget.ts | 7 +++---- .../contrib/searchEditor/browser/searchEditorActions.ts | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 6de22f21f68..3f8d572d78a 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -448,8 +448,9 @@ export class SearchWidget extends Widget { } setValue(value: string, skipSearchOnChange: boolean) { - this.temporarilySkipSearchOnChange = skipSearchOnChange || this.temporarilySkipSearchOnChange; + this.temporarilySkipSearchOnChange = skipSearchOnChange; this.searchInput.setValue(value); + this.temporarilySkipSearchOnChange = false; } setReplaceAllActionState(enabled: boolean): void { @@ -491,9 +492,7 @@ export class SearchWidget extends Widget { this.setReplaceAllActionState(false); if (this.searchConfiguration.searchOnType) { - if (this.temporarilySkipSearchOnChange) { - this.temporarilySkipSearchOnChange = false; - } else { + if (!this.temporarilySkipSearchOnChange) { this._onSearchCancel.fire({ focus: false }); if (this.searchInput.getRegex()) { try { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index d348146e3dc..cabfbd9a6ca 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -136,6 +136,7 @@ const openNewSearchEditor = const editorService = accessor.get(IEditorService); const telemetryService = accessor.get(ITelemetryService); const instantiationService = accessor.get(IInstantiationService); + const configurationService = accessor.get(IConfigurationService); const activeEditor = editorService.activeTextEditorWidget; let activeModel: ICodeEditor | undefined; @@ -162,7 +163,11 @@ const openNewSearchEditor = telemetryService.publicLog2('searchEditor/openNewSearchEditor'); const input = instantiationService.invokeFunction(getOrMakeSearchEditorInput, { config: { query: selected } }); - await editorService.openEditor(input, { pinned: true }); + const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor; + + if (selected && configurationService.getValue('search').searchOnType) { + editor.runSearch(true, true); + } }; export const createEditorFromSearchResult = From ea0880611f706e0529f89aa25d1989363ab53b11 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 12 Feb 2020 17:12:00 -0800 Subject: [PATCH 028/192] Use non-deprecated version of registerTasksProvider For #88391 --- extensions/grunt/src/main.ts | 2 +- extensions/gulp/src/main.ts | 2 +- extensions/jake/src/main.ts | 2 +- extensions/npm/src/main.ts | 2 +- extensions/typescript-language-features/src/extension.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index d8ef8a82673..597aa909701 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -295,7 +295,7 @@ class TaskDetector { private updateProvider(): void { if (!this.taskProvider && this.detectors.size > 0) { const thisCapture = this; - this.taskProvider = vscode.workspace.registerTaskProvider('grunt', { + this.taskProvider = vscode.tasks.registerTaskProvider('grunt', { provideTasks: (): Promise => { return thisCapture.getTasks(); }, diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index 20d824c25ed..534756ac81d 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -277,7 +277,7 @@ class TaskDetector { private updateProvider(): void { if (!this.taskProvider && this.detectors.size > 0) { const thisCapture = this; - this.taskProvider = vscode.workspace.registerTaskProvider('gulp', { + this.taskProvider = vscode.tasks.registerTaskProvider('gulp', { provideTasks(): Promise { return thisCapture.getTasks(); }, diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index b8c6d5e7344..93fa1aa285b 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -269,7 +269,7 @@ class TaskDetector { private updateProvider(): void { if (!this.taskProvider && this.detectors.size > 0) { const thisCapture = this; - this.taskProvider = vscode.workspace.registerTaskProvider('jake', { + this.taskProvider = vscode.tasks.registerTaskProvider('jake', { provideTasks(): Promise { return thisCapture.getTasks(); }, diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 3777f393757..4092e27e662 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -70,7 +70,7 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab context.subscriptions.push(workspaceWatcher); let provider: vscode.TaskProvider = new NpmTaskProvider(); - let disposable = vscode.workspace.registerTaskProvider('npm', provider); + let disposable = vscode.tasks.registerTaskProvider('npm', provider); context.subscriptions.push(disposable); return disposable; } diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 7416c5efdc3..d9f8284fd24 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -38,7 +38,7 @@ export function activate( }); registerCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); + context.subscriptions.push(vscode.tasks.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); context.subscriptions.push(new LanguageConfigurationManager()); import('./features/tsconfig').then(module => { From b4c19fd9265fdd2b99b1fe21478909626af32297 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Feb 2020 16:53:11 -0800 Subject: [PATCH 029/192] xterm@4.5.0-beta.4 https://github.com/xtermjs/xterm.js/compare/a840db3...c5593bd --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 3465c230a00..4d03b1f121c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "vscode-ripgrep": "^1.5.8", "vscode-sqlite3": "4.0.9", "vscode-textmate": "4.4.0", - "xterm": "4.4.0", + "xterm": "4.5.0-beta.4", "xterm-addon-search": "0.5.0", "xterm-addon-unicode11": "0.1.1", "xterm-addon-web-links": "0.2.1", diff --git a/remote/package.json b/remote/package.json index 14b25d4666b..7d3a3755dd3 100644 --- a/remote/package.json +++ b/remote/package.json @@ -20,7 +20,7 @@ "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.8", "vscode-textmate": "4.4.0", - "xterm": "4.4.0", + "xterm": "4.5.0-beta.4", "xterm-addon-search": "0.5.0", "xterm-addon-unicode11": "0.1.1", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/package.json b/remote/web/package.json index cdb1fd8db2e..ffcd7add280 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,7 +5,7 @@ "onigasm-umd": "2.2.5", "semver-umd": "^5.5.5", "vscode-textmate": "4.4.0", - "xterm": "4.4.0", + "xterm": "4.5.0-beta.4", "xterm-addon-search": "0.5.0", "xterm-addon-unicode11": "0.1.1", "xterm-addon-web-links": "0.2.1", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 0b1d4fb601b..c9f5c9ebb95 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -51,7 +51,7 @@ xterm-addon-webgl@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e" integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA== -xterm@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589" - integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig== +xterm@4.5.0-beta.4: + version "4.5.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92" + integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew== diff --git a/remote/yarn.lock b/remote/yarn.lock index 3fa3b053485..d576786f982 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -433,10 +433,10 @@ xterm-addon-webgl@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e" integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA== -xterm@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589" - integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig== +xterm@4.5.0-beta.4: + version "4.5.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92" + integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew== yauzl@^2.9.2: version "2.10.0" diff --git a/yarn.lock b/yarn.lock index 4d25e3f82a9..e55bdc53b9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9908,10 +9908,10 @@ xterm-addon-webgl@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e" integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA== -xterm@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589" - integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig== +xterm@4.5.0-beta.4: + version "4.5.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92" + integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew== y18n@^3.2.1: version "3.2.1" From 2b63a583e470bc1a3cafff819ec5a4b36c7a660b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Feb 2020 17:34:29 -0800 Subject: [PATCH 030/192] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d03b1f121c..352e42b5ad5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "50679cbf1e8bf849cb5785226be6593d182153aa", + "distro": "6bf87915eae2f87dc4194c489df2ef1505c96b9c", "author": { "name": "Microsoft Corporation" }, From 1fbacccbc900bb59ba8a8f26a4128d48a1c97842 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 12 Feb 2020 17:42:24 -0800 Subject: [PATCH 031/192] Remove 'rootPath' reference #90562 --- .../php-language-features/src/features/validationProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/php-language-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index 122c108ac37..5c9d34afb59 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -247,7 +247,7 @@ export default class PHPValidationProvider { } }; - let options = vscode.workspace.rootPath ? { cwd: vscode.workspace.rootPath } : undefined; + let options = (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]) ? { cwd: vscode.workspace.workspaceFolders[0].uri.fsPath } : undefined; let args: string[]; if (this.trigger === RunTrigger.onSave) { args = PHPValidationProvider.FileArgs.slice(0); From 1d4f6e641f7592b5a58e4f6dbc5823ebdd2b5117 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 07:15:57 +0100 Subject: [PATCH 032/192] Option to disable setting language of untitled file on paste (fix #90492) --- .../browser/parts/editor/editor.contribution.ts | 15 ++++++++++++++- .../browser/parts/editor/textResourceEditor.ts | 4 ++++ .../untitled/common/untitledTextEditorModel.ts | 12 ++++++++++++ .../test/browser/untitledTextEditor.test.ts | 2 ++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index da914ba0b90..2012c00b543 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -55,6 +55,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; 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'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -134,9 +135,21 @@ class UntitledTextEditorInputFactory implements IEditorInputFactory { resource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority); // untitled with associated file path use the local schema } + // Mode: only remember mode if it is either specific (not text) + // or if the mode was explicitly set by the user. We want to preserve + // this information across restarts and not set the mode unless + // this is the case. + let modeId: string | undefined; + const modeIdCandidate = untitledTextEditorInput.getMode(); + if (modeIdCandidate !== PLAINTEXT_MODE_ID) { + modeId = modeIdCandidate; + } else if (untitledTextEditorInput.model.hasModeSetExplicitly) { + modeId = modeIdCandidate; + } + const serialized: ISerializedUntitledTextEditorInput = { resourceJSON: resource.toJSON(), - modeId: untitledTextEditorInput.getMode(), + modeId, encoding: untitledTextEditorInput.getEncoding() }; diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index a04814d2019..3909f3aa753 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -214,6 +214,10 @@ export class TextResourceEditor extends AbstractTextResourceEditor { } private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void { + if (this.input instanceof UntitledTextEditorInput && this.input.model.hasModeSetExplicitly) { + return; // do not override mode if it was set explicitly + } + if (e.range.startLineNumber !== 1 || e.range.startColumn !== 1) { return; // only when pasting into first line, first column (= empty document) } diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index e9592bbbbe2..05e918a797d 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -44,6 +44,11 @@ export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport */ readonly hasAssociatedFilePath: boolean; + /** + * Wether this model has an explicit language mode or not. + */ + readonly hasModeSetExplicitly: boolean; + /** * Sets the encoding to use for this untitled model. */ @@ -150,7 +155,14 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt return this.versionId; } + private _hasModeSetExplicitly: boolean = false; + get hasModeSetExplicitly(): boolean { return this._hasModeSetExplicitly; } + setMode(mode: string): void { + + // Remember that an explicit mode was set + this._hasModeSetExplicitly = true; + let actualMode: string | undefined = undefined; if (mode === '${activeEditorLanguage}') { // support the special '${activeEditorLanguage}' mode by diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 62938a00d2d..2b3c0d3adc3 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -292,7 +292,9 @@ suite('Untitled text editors', () => { const model = await input.resolve(); assert.equal(model.getMode(), mode); + assert.ok(!input.model.hasModeSetExplicitly); input.setMode('text'); + assert.ok(input.model.hasModeSetExplicitly); assert.equal(input.getMode(), PLAINTEXT_MODE_ID); From fb6b2843069f720425335e918eed50b46b890a1f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 07:21:24 +0100 Subject: [PATCH 033/192] untitled - fix tests --- .../test/browser/untitledTextEditor.test.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 2b3c0d3adc3..011bbabbcd8 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -287,13 +287,33 @@ suite('Untitled text editors', () => { const service = accessor.untitledTextEditorService; const input = instantiationService.createInstance(UntitledTextEditorInput, service.create({ mode })); + assert.ok(input.model.hasModeSetExplicitly); assert.equal(input.getMode(), mode); const model = await input.resolve(); assert.equal(model.getMode(), mode); + input.setMode('plaintext'); + + assert.equal(input.getMode(), PLAINTEXT_MODE_ID); + + input.dispose(); + model.dispose(); + }); + + test('remembers that mode was set explicitly', async () => { + const mode = 'untitled-input-test'; + + ModesRegistry.registerLanguage({ + id: mode, + }); + + const service = accessor.untitledTextEditorService; + const model = service.create(); + const input = instantiationService.createInstance(UntitledTextEditorInput, model); + assert.ok(!input.model.hasModeSetExplicitly); - input.setMode('text'); + input.setMode('plaintext'); assert.ok(input.model.hasModeSetExplicitly); assert.equal(input.getMode(), PLAINTEXT_MODE_ID); From c366e628032addb87a90345edbbc4f0207beff0a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 07:45:03 +0100 Subject: [PATCH 034/192] Web API: registerCommand() (fix #90569) --- src/vs/workbench/browser/web.main.ts | 12 ++---- src/vs/workbench/workbench.web.api.ts | 53 +++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 96de0d2a04d..11df61ac725 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -93,20 +93,16 @@ class BrowserMain extends Disposable { // Layout const viewport = platform.isIOS && (window).visualViewport ? (window).visualViewport /** Visual viewport */ : window /** Layout viewport */; - this._register(addDisposableListener(viewport, EventType.RESIZE, () => { - workbench.layout(); - })); + this._register(addDisposableListener(viewport, EventType.RESIZE, () => workbench.layout())); // Prevent the back/forward gestures in macOS - this._register(addDisposableListener(this.domElement, EventType.WHEEL, (e) => { - e.preventDefault(); - }, { passive: false })); + this._register(addDisposableListener(this.domElement, EventType.WHEEL, e => e.preventDefault(), { passive: false })); // Prevent native context menus in web - this._register(addDisposableListener(this.domElement, EventType.CONTEXT_MENU, (e) => EventHelper.stop(e, true))); + this._register(addDisposableListener(this.domElement, EventType.CONTEXT_MENU, e => EventHelper.stop(e, true))); // Prevent default navigation on drop - this._register(addDisposableListener(this.domElement, EventType.DROP, (e) => EventHelper.stop(e, true))); + this._register(addDisposableListener(this.domElement, EventType.DROP, e => EventHelper.stop(e, true))); // Workbench Lifecycle this._register(workbench.onBeforeShutdown(event => { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 8703e1a79fd..4cd646930bb 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -16,6 +16,7 @@ import { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/browser/u import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; interface IResourceUriProvider { (uri: URI): URI; @@ -36,18 +37,23 @@ interface IExternalUriResolver { interface TunnelOptions { remoteAddress: { port: number, host: string }; - // The desired local port. If this port can't be used, then another will be chosen. + /** + * The desired local port. If this port can't be used, then another will be chosen. + */ localAddressPort?: number; label?: string; } -interface Tunnel { +interface Tunnel extends IDisposable { remoteAddress: { port: number, host: string }; - //The complete local address(ex. localhost:1234) + /** + * The complete local address(ex. localhost:1234) + */ localAddress: string; - // Implementers of Tunnel should fire onDidDispose when dispose is called. + /** + * Implementers of Tunnel should fire onDidDispose when dispose is called. + */ onDidDispose: Event; - dispose(): void; } interface ITunnelFactory { @@ -186,14 +192,43 @@ interface IWorkbenchConstructionOptions { readonly driver?: boolean; } +interface ICommandHandler { + (...args: any[]): void; +} + +interface IWorkbench { + + /** + * Register a command with the provided identifier and handler with + * the workbench. The command can be called from extensions using the + * `vscode.commands.executeCommand` API. + */ + registerCommand(id: string, command: ICommandHandler): IDisposable; +} + /** * Creates the workbench with the provided options in the provided container. * * @param domElement the container to create the workbench in * @param options for setting up the workbench + * + * @returns the workbench facade with additional methods to call on. */ -function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { - return main(domElement, options); +async function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { + + // Startup workbench + await main(domElement, options); + + // Return facade + return { + registerCommand: (id: string, command: ICommandHandler): IDisposable => { + return CommandsRegistry.registerCommand(id, (accessor, ...args: any[]) => { + // we currently only pass on the arguments but not the accessor + // to the command to reduce our exposure of internal API. + command(...args); + }); + } + }; } export { @@ -202,6 +237,10 @@ export { create, IWorkbenchConstructionOptions, + // Workbench Facade + IWorkbench, + ICommandHandler, + // Basic Types URI, UriComponents, From 89a1183946e430adb5500aeaecba315dec1f6776 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 09:47:55 +0100 Subject: [PATCH 035/192] fixes #90478 --- .../workbench/contrib/debug/browser/debugTaskRunner.ts | 10 +++++----- src/vs/workbench/contrib/debug/common/debug.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts index a0aea70ae0e..231ac807506 100644 --- a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts +++ b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts @@ -72,7 +72,7 @@ export class DebugTaskRunner { await this.viewsService.openView(Constants.MARKERS_VIEW_ID); return Promise.resolve(TaskRunResult.Failure); } - if (onTaskErrors === 'cancel') { + if (onTaskErrors === 'abort') { return Promise.resolve(TaskRunResult.Failure); } @@ -85,7 +85,7 @@ export class DebugTaskRunner { ? nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary.exitCode) : nls.localize('preLaunchTaskTerminated', "The preLaunchTask '{0}' terminated.", taskLabel); - const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], { + const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('abort', "Abort")], { checkbox: { label: nls.localize('remember', "Remember my choice in user settings"), }, @@ -94,12 +94,12 @@ export class DebugTaskRunner { const debugAnyway = result.choice === 0; - const cancel = result.choice = 2; + const abort = result.choice === 2; if (result.checkboxChecked) { - this.configurationService.updateValue('debug.onTaskErrors', result.choice === 0 ? 'debugAnyway' : cancel ? 'cancel' : 'showErrors'); + this.configurationService.updateValue('debug.onTaskErrors', result.choice === 0 ? 'debugAnyway' : abort ? 'abort' : 'showErrors'); } - if (cancel) { + if (abort) { return Promise.resolve(TaskRunResult.Failure); } if (debugAnyway) { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 6d540653dbf..56424879c1c 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -467,7 +467,7 @@ export interface IDebugConfiguration { closeOnEnd: boolean; }; focusWindowOnBreak: boolean; - onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt' | 'cancel'; + onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt' | 'abort'; showBreakpointsInOverviewRuler: boolean; showInlineBreakpointCandidates: boolean; } From a56618ad8fba77ab9da5c1e48deb7689a849997a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 10:20:32 +0100 Subject: [PATCH 036/192] web - fix warnings --- scripts/code-web.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/code-web.js b/scripts/code-web.js index 26fb980e3d5..4556e7d7e6d 100755 --- a/scripts/code-web.js +++ b/scripts/code-web.js @@ -66,11 +66,11 @@ const server = http.createServer((req, res) => { // manifest res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ - "name": "Code Web - OSS", - "short_name": "Code Web - OSS", - "start_url": "/", - "lang": "en-US", - "display": "standalone" + 'name': 'Code Web - OSS', + 'short_name': 'Code Web - OSS', + 'start_url': '/', + 'lang': 'en-US', + 'display': 'standalone' })); } if (/^\/static\//.test(pathname)) { From 32a1dfbf2f3bdfb5801ddd52cbfb4a6a34835b91 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 11:07:54 +0100 Subject: [PATCH 037/192] debug: only count sessions which do not have parent sessions #90518 --- src/vs/workbench/contrib/debug/browser/debugService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index f6a49d3eeeb..30a87cca33c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -165,7 +165,7 @@ export class DebugService implements IDebugService { this.debugUx.set(!!(this.state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple'); })); this.toDispose.push(this.model.onDidChangeCallStack(() => { - const numberOfSessions = this.model.getSessions().length; + const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length; if (this.activity) { this.activity.dispose(); } From 1b0f560af04fe273a488d743e06f7dd5aee81958 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 11:24:43 +0100 Subject: [PATCH 038/192] openEditorsView: enable to move between containers #89729 --- .../contrib/files/browser/views/openEditorsView.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index c4f4a7ff572..004e956f796 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -42,7 +42,6 @@ import { URI } from 'vs/base/common/uri'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { isWeb } from 'vs/base/common/platform'; import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -225,7 +224,7 @@ export class OpenEditorsView extends ViewPane { identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() }, dnd: new OpenEditorsDragAndDrop(this.instantiationService, this.editorGroupService), overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND + listBackground: this.getBackgroundColor() } }); this._register(this.list); @@ -303,6 +302,12 @@ export class OpenEditorsView extends ViewPane { this.listRefreshScheduler.schedule(0); } })); + + this._register(this.viewDescriptorService.onDidChangeLocation(({ views }) => { + if (views.some(v => v.id === this.id)) { + this.list.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); + } + })); } getActions(): IAction[] { From 3cb35b0d5e857cc0f7246337916603e1fccae2ed Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Feb 2020 11:26:19 +0100 Subject: [PATCH 039/192] fix #90515 --- .../contrib/suggest/test/wordDistance.test.ts | 2 -- src/vs/editor/contrib/suggest/wordDistance.ts | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts index cc83318ce12..498318c7128 100644 --- a/src/vs/editor/contrib/suggest/test/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/wordDistance.test.ts @@ -105,8 +105,6 @@ suite('suggest, word distance', function () { } test('Suggest locality bonus can boost current word #90515', function () { - this.skip(); - const pos = { lineNumber: 2, column: 2 }; const d1 = distance.distance(pos, createSuggestItem('a', 1, pos).completion); const d2 = distance.distance(pos, createSuggestItem('aa', 1, pos).completion); diff --git a/src/vs/editor/contrib/suggest/wordDistance.ts b/src/vs/editor/contrib/suggest/wordDistance.ts index 3ae8097c51a..85e98536f2b 100644 --- a/src/vs/editor/contrib/suggest/wordDistance.ts +++ b/src/vs/editor/contrib/suggest/wordDistance.ts @@ -35,14 +35,23 @@ export abstract class WordDistance { return WordDistance.None; } - const ranges = await new BracketSelectionRangeProvider().provideSelectionRanges(model, [position]); - if (!ranges || ranges.length === 0 || ranges[0].length === 0) { + const [ranges] = await new BracketSelectionRangeProvider().provideSelectionRanges(model, [position]); + if (ranges.length === 0) { return WordDistance.None; } - const wordRanges = await service.computeWordRanges(model.uri, ranges[0][0].range); + + const wordRanges = await service.computeWordRanges(model.uri, ranges[0].range); + if (!wordRanges) { + return WordDistance.None; + } + + // remove current word + const wordUntilPos = model.getWordUntilPosition(position); + delete wordRanges[wordUntilPos.word]; + return new class extends WordDistance { distance(anchor: IPosition, suggestion: CompletionItem) { - if (!wordRanges || !position.equals(editor.getPosition())) { + if (!position.equals(editor.getPosition())) { return 0; } if (suggestion.kind === CompletionItemKind.Keyword) { @@ -56,7 +65,7 @@ export abstract class WordDistance { let idx = binarySearch(wordLines, Range.fromPositions(anchor), Range.compareRangesUsingStarts); let bestWordRange = idx >= 0 ? wordLines[idx] : wordLines[Math.max(0, ~idx - 1)]; let blockDistance = ranges.length; - for (const range of ranges[0]) { + for (const range of ranges) { if (!Range.containsRange(range.range, bestWordRange)) { break; } From 54d57f552fdcbaf1c596c0365cbffd958f215b33 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 11:31:52 +0100 Subject: [PATCH 040/192] Integration tests: terminal throws exceptions (fix #90046) --- .../api/common/extHostTerminalService.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 16000c4f0c9..9d81c5b30cb 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -13,6 +13,7 @@ import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY } f import { timeout } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { @@ -290,6 +291,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _activeTerminal: ExtHostTerminal | undefined; protected _terminals: ExtHostTerminal[] = []; protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; + protected _terminalProcessDisposables: { [id: number]: IDisposable } = {}; protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; @@ -332,7 +334,10 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options, options.name); const p = new ExtHostPseudoterminal(options.pty); - terminal.createExtensionTerminal().then(id => this._setupExtHostProcessListeners(id, p)); + terminal.createExtensionTerminal().then(id => { + const disposable = this._setupExtHostProcessListeners(id, p); + this._terminalProcessDisposables[id] = disposable; + }); this._terminals.push(terminal); return terminal; } @@ -343,7 +348,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ throw new Error(`Cannot resolve terminal with id ${id} for virtual process`); } const p = new ExtHostPseudoterminal(pty); - this._setupExtHostProcessListeners(id, p); + const disposable = this._setupExtHostProcessListeners(id, p); + this._terminalProcessDisposables[id] = disposable; } public async $acceptActiveTerminalChanged(id: number | null): Promise { @@ -474,16 +480,18 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } - protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): void { - p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd)); - p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); + protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): IDisposable { + const disposables = new DisposableStore(); + + disposables.add(p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd))); + disposables.add(p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title))); // Buffer data events to reduce the amount of messages going to the renderer this._bufferer.startBuffering(id, p.onProcessData); - p.onProcessExit(exitCode => this._onProcessExit(id, exitCode)); + disposables.add(p.onProcessExit(exitCode => this._onProcessExit(id, exitCode))); if (p.onProcessOverrideDimensions) { - p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e)); + disposables.add(p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e))); } this._terminalProcesses[id] = p; @@ -492,6 +500,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ p.startSendingEvents(awaitingStart.initialDimensions); delete this._extensionTerminalAwaitingStart[id]; } + + return disposables; } public $acceptProcessInput(id: number, data: string): void { @@ -532,6 +542,13 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ delete this._terminalProcesses[id]; delete this._extensionTerminalAwaitingStart[id]; + // Clean up process disposables + const processDiposable = this._terminalProcessDisposables[id]; + if (processDiposable) { + processDiposable.dispose(); + delete this._terminalProcessDisposables[id]; + } + // Send exit event to main side this._proxy.$sendProcessExit(id, exitCode); } From ec19366e193478af75be7ca0c4a5e269d90a1079 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Feb 2020 11:40:18 +0100 Subject: [PATCH 041/192] :lipstick: --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 0d0a92fbccc..a782d93c1a6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1430,7 +1430,7 @@ declare module 'vscode' { label: string; /** - * A human-readable string which is rendered less prominent in the same line. + * A human-readable string which is rendered less prominent on the same line. */ description?: string; From e5ca30b59691f62d2618fb812cd7f81cc40c4ffb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 12:02:15 +0100 Subject: [PATCH 042/192] docs integration test scripts --- scripts/test-integration.bat | 2 ++ scripts/test-integration.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index b8d673f70d4..feb03625fcc 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -15,6 +15,8 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( echo Running integration tests out of sources. ) else ( :: Run from a built: need to compile all test extensions + :: because we run extension tests from their source folders + :: and the build bundles extensions into .build webpacked call yarn gulp compile-extension:vscode-api-tests call yarn gulp compile-extension:vscode-colorize-tests call yarn gulp compile-extension:markdown-language-features diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 9aff685b7c6..df57fec23e6 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -21,6 +21,8 @@ then echo "Running integration tests out of sources." else # Run from a built: need to compile all test extensions + # because we run extension tests from their source folders + # and the build bundles extensions into .build webpacked yarn gulp compile-extension:vscode-api-tests yarn gulp compile-extension:vscode-colorize-tests yarn gulp compile-extension:markdown-language-features From 60294bf9d95f5a13bcec96c7c6584278a6f5896f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 12:03:12 +0100 Subject: [PATCH 043/192] Leave a gap at the end when scrolling beyond last line --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index f8b400f8161..54cd002d9b1 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -639,9 +639,13 @@ export class Minimap extends ViewPart implements IMinimapModel { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); const pixelRatio = options.get(EditorOption.pixelRatio); - - const minimapLineCount = Math.round(pixelRatio * layoutInfo.height); + const lineHeight = options.get(EditorOption.lineHeight); + const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); const modelLineCount = this._context.model.getLineCount(); + + const extraLinesBeyondLastLine = scrollBeyondLastLine ? (layoutInfo.height / lineHeight - 1) : 0; + const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); + const minimapLineCount = Math.floor(modelLineCount / desiredRatio); const ratio = modelLineCount / minimapLineCount; let result: number[] = []; From 59171e630741d866b39f7b1fa5540ade3c196637 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 12:24:11 +0100 Subject: [PATCH 044/192] empty views: link API to registry to view pane container --- .../browser/parts/views/viewPaneContainer.ts | 83 ++++++++++++++++--- src/vs/workbench/common/views.ts | 6 ++ .../common/documentationContribution.ts | 34 ++++++-- 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index f50f32cc0ae..1ed47c4dfbc 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -25,7 +25,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndControlle import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry } from 'vs/workbench/common/views'; +import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor } from 'vs/workbench/common/views'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { assertIsDefined } from 'vs/base/common/types'; @@ -60,6 +60,67 @@ export interface IViewPaneOptions extends IPaneOptions { const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); +interface IEmptyViewItem { + readonly view: IViewContentDescriptor; + visible: boolean; +} + +class EmptyViewsController { + + private _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + private viewItems: IEmptyViewItem[] = []; + get views(): IViewContentDescriptor[] { return this.viewItems.map(v => v.view); } + + private disposables = new DisposableStore(); + + constructor( + private id: string, + @IContextKeyService private contextKeyService: IContextKeyService, + ) { + contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); + Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id)(this.onDidChangeEmptyViewContent, this, this.disposables); + this.onDidChangeEmptyViewContent(); + } + + private onDidChangeEmptyViewContent(): void { + this.viewItems = viewsRegistry.getEmptyViewContent(this.id).map(view => ({ + view, + visible: view.when ? this.contextKeyService.contextMatchesRules(view.when) : true + })); + + this._onDidChange.fire(); + } + + private onDidChangeContext(): void { + let didChange = false; + + for (const item of this.viewItems) { + if (!item.view.when) { + continue; + } + + const visible = this.contextKeyService.contextMatchesRules(item.view.when); + + if (item.visible === visible) { + continue; + } + + item.visible = visible; + didChange = true; + } + + if (didChange) { + this._onDidChange.fire(); + } + } + + dispose(): void { + this.disposables.dispose(); + } +} + export abstract class ViewPane extends Pane implements IView { private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; @@ -97,6 +158,7 @@ export abstract class ViewPane extends Pane implements IView { private bodyContainer!: HTMLElement; private emptyViewContainer!: HTMLElement; private emptyViewDisposable: IDisposable = Disposable.None; + private emptyViewsController: EmptyViewsController; constructor( options: IViewPaneOptions, @@ -119,6 +181,8 @@ export abstract class ViewPane extends Pane implements IView { this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext)); this._register(this.menuActions.onDidChangeTitle(() => this.updateActions())); + + this.emptyViewsController = new EmptyViewsController(this.id, contextKeyService); } setVisible(visible: boolean): void { @@ -208,16 +272,9 @@ export abstract class ViewPane extends Pane implements IView { this.bodyContainer = container; this.emptyViewContainer = append(container, $('.empty-view', { tabIndex: 0 })); - // we should update our empty state whenever - const onEmptyViewContentChange = Event.any( - // the registry changes - Event.map(Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id), () => this.isEmpty()), - // or the view's empty state changes - Event.latch(Event.map(this.onDidChangeEmptyState, () => this.isEmpty())) - ); - - this._register(onEmptyViewContentChange(this.updateEmptyState, this)); - this.updateEmptyState(this.isEmpty()); + const onEmptyViewContentChange = Event.any(this.emptyViewsController.onDidChange, this.onDidChangeEmptyState); + this._register(onEmptyViewContentChange(this.updateEmptyContents, this)); + this.updateEmptyContents(); } protected getProgressLocation(): string { @@ -286,10 +343,10 @@ export abstract class ViewPane extends Pane implements IView { // Subclasses to implement for saving state } - private updateEmptyState(isEmpty: boolean): void { + private updateEmptyContents(): void { this.emptyViewDisposable.dispose(); - if (!isEmpty) { + if (!this.isEmpty()) { removeClass(this.bodyContainer, 'empty'); this.emptyViewContainer.innerHTML = ''; return; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 0a6073ca70b..0b1f83c1a93 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -213,6 +213,7 @@ export interface IViewDescriptorCollection extends IDisposable { export interface IViewContentDescriptor { readonly content: string; + readonly when?: ContextKeyExpr; } export interface IViewsRegistry { @@ -240,6 +241,10 @@ export interface IViewsRegistry { getEmptyViewContent(id: string): IViewContentDescriptor[]; } +function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number { + return a.content < b.content ? -1 : 1; +} + class ViewsRegistry extends Disposable implements IViewsRegistry { private readonly _onViewsRegistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>()); @@ -318,6 +323,7 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { getEmptyViewContent(id: string): IViewContentDescriptor[] { const result: IViewContentDescriptor[] = []; + result.sort(compareViewContentDescriptors); this._emptyViewContents.forEach(id, descriptor => result.push(descriptor)); return result; } diff --git a/src/vs/workbench/contrib/documentation/common/documentationContribution.ts b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts index 97a97463f01..eb54ebe45f8 100644 --- a/src/vs/workbench/contrib/documentation/common/documentationContribution.ts +++ b/src/vs/workbench/contrib/documentation/common/documentationContribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; @@ -13,7 +13,9 @@ import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { DocumentationExtensionPoint } from './documentationExtensionPoint'; +import { DocumentationExtensionPoint, ViewDocumentationExtensionPoint } from './documentationExtensionPoint'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; interface ICodeActionContribution { title: string; @@ -21,11 +23,15 @@ interface ICodeActionContribution { command: string; } +const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); + export class DocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { private codeActionContributions: ICodeActionContribution[] = []; private readonly emptyCodeActionsList = { actions: [], dispose: () => { } }; + private emptyViewContents = new Map(); + constructor( extensionPoint: IExtensionPoint, @IContextKeyService private readonly contextKeyService: IContextKeyService, @@ -34,8 +40,7 @@ export class DocumentationContribution extends Disposable implements IWorkbenchC this._register(modes.CodeActionProviderRegistry.register('*', this)); - extensionPoint.setHandler(points => { - this.codeActionContributions = []; + extensionPoint.setHandler((points, { added, removed }) => { for (const documentation of points) { if (documentation.value.refactoring) { for (const contribution of documentation.value.refactoring) { @@ -51,12 +56,29 @@ export class DocumentationContribution extends Disposable implements IWorkbenchC }); } } + } + for (const documentation of removed) { if (documentation.value.view) { for (const contribution of documentation.value.view) { - const precondition = ContextKeyExpr.deserialize(contribution.when); + const disposable = this.emptyViewContents.get(contribution); - // TODO + if (disposable) { + disposable.dispose(); + } + } + } + } + + for (const documentation of added) { + if (documentation.value.view) { + for (const contribution of documentation.value.view) { + const disposable = viewsRegistry.registerEmptyViewContent(contribution.view, { + content: contribution.contents, + when: ContextKeyExpr.deserialize(contribution.when) + }); + + this.emptyViewContents.set(contribution, disposable); } } } From 6f34d1a7d826021cad87075dd9ed1f7c31f53e7a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 12:26:32 +0100 Subject: [PATCH 045/192] integration - prevent excessive gulp usage --- scripts/test-integration.bat | 16 ++++++++-------- scripts/test-integration.sh | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index feb03625fcc..cb7271a9152 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -17,14 +17,14 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests - call yarn gulp compile-extension:vscode-colorize-tests - call yarn gulp compile-extension:markdown-language-features - call yarn gulp compile-extension:emmet - call yarn gulp compile-extension:css-language-features-server - call yarn gulp compile-extension:html-language-features-server - call yarn gulp compile-extension:json-language-features-server - call yarn gulp compile-extension:git + call yarn gulp compile-extension:vscode-api-tests^ + compile-extension:vscode-colorize-tests^ + compile-extension:markdown-language-features^ + compile-extension:emmet^ + compile-extension:css-language-features-server^ + compile-extension:html-language-features-server^ + compile-extension:json-language-features-server^ + compile-extension:git :: Configuration for more verbose output set VSCODE_CLI=1 diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index df57fec23e6..6659549f001 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -23,14 +23,14 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests - yarn gulp compile-extension:vscode-colorize-tests - yarn gulp compile-extension:markdown-language-features - yarn gulp compile-extension:emmet - yarn gulp compile-extension:css-language-features-server - yarn gulp compile-extension:html-language-features-server - yarn gulp compile-extension:json-language-features-server - yarn gulp compile-extension:git + yarn gulp compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:markdown-language-features \ + compile-extension:emmet \ + compile-extension:css-language-features-server \ + compile-extension:html-language-features-server \ + compile-extension:json-language-features-server \ + compile-extension:git # Configuration for more verbose output export VSCODE_CLI=1 From 30749362bc24836c258be2d9d61fd46d4a716aaa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 12:33:52 +0100 Subject: [PATCH 046/192] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 352e42b5ad5..77e8caf8327 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "6bf87915eae2f87dc4194c489df2ef1505c96b9c", + "distro": "7e99e53af46116b121bcadc01b54f84229ae6e25", "author": { "name": "Microsoft Corporation" }, From af69a32dba0e64ba927f1eee516dbb40b9f3e5e0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 12:41:00 +0100 Subject: [PATCH 047/192] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77e8caf8327..531ea49e514 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "7e99e53af46116b121bcadc01b54f84229ae6e25", + "distro": "a87981a5cc52fed24735d447e954adccb9c9f914", "author": { "name": "Microsoft Corporation" }, From 4e268350e49eab90ec1e8c58dde3d00eae989129 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 12:45:43 +0100 Subject: [PATCH 048/192] Adjust sampled lines with model events --- .../browser/viewParts/minimap/minimap.ts | 83 +++++++++++++++---- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 54cd002d9b1..2b35d39d44a 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -399,14 +399,14 @@ class RenderData { }; } - public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { - return this._renderedLines.onLinesChanged(e.fromLineNumber, e.toLineNumber); + public onLinesChanged(changeFromLineNumber: number, changeToLineNumber: number): boolean { + return this._renderedLines.onLinesChanged(changeFromLineNumber, changeToLineNumber); } - public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): void { - this._renderedLines.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): void { + this._renderedLines.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); } - public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { - this._renderedLines.onLinesInserted(e.fromLineNumber, e.toLineNumber); + public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): void { + this._renderedLines.onLinesInserted(insertFromLineNumber, insertToLineNumber); } public onTokensChanged(ranges: { fromLineNumber: number; toLineNumber: number; }[]): boolean { return this._renderedLines.onTokensChanged(ranges); @@ -561,13 +561,61 @@ export class Minimap extends ViewPart implements IMinimapModel { return this._actual.onFlushed(); } public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { - return this._actual.onLinesChanged(e); // TODO + if (this._isSampling) { + let fromLineIndex = this._modelLineToMinimapLine(e.fromLineNumber) - 1; + while (fromLineIndex > 0 && this._minimapLines[fromLineIndex - 1] >= e.fromLineNumber) { + fromLineIndex--; + } + let toLineIndex = this._modelLineToMinimapLine(e.toLineNumber) - 1; + while (toLineIndex + 1 < this._minimapLines.length && this._minimapLines[toLineIndex + 1] <= e.toLineNumber) { + toLineIndex++; + } + return this._actual.onLinesChanged(fromLineIndex + 1, toLineIndex + 1); + } else { + return this._actual.onLinesChanged(e.fromLineNumber, e.toLineNumber); + } } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { - return this._actual.onLinesDeleted(e); // TODO + if (this._isSampling) { + // have the mapping be sticky + const deletedLineCount = e.toLineNumber - e.fromLineNumber + 1; + let changeStartIndex = this._minimapLines.length; + let changeEndIndex = 0; + for (let i = this._minimapLines.length - 1; i >= 0; i--) { + if (this._minimapLines[i] < e.fromLineNumber) { + break; + } + if (this._minimapLines[i] <= e.toLineNumber) { + // this line got deleted => move to previous available + this._minimapLines[i] = Math.max(1, e.fromLineNumber - 1); + changeStartIndex = Math.min(changeStartIndex, i); + changeEndIndex = Math.max(changeEndIndex, i); + } else { + this._minimapLines[i] -= deletedLineCount; + } + } + if (changeStartIndex <= changeEndIndex) { + this._actual.onLinesChanged(changeStartIndex + 1, changeEndIndex + 1); + } + return this._checkMapping(); + } else { + return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber); + } } public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { - return this._actual.onLinesInserted(e); // TODO + if (this._isSampling) { + // have the mapping be sticky + const insertedLineCount = e.toLineNumber - e.fromLineNumber + 1; + for (let i = this._minimapLines.length - 1; i >= 0; i--) { + if (this._minimapLines[i] < e.fromLineNumber) { + break; + } + this._minimapLines[i] += insertedLineCount; + } + return this._checkMapping(); + } else { + return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber); + } } public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { return this._actual.onScrollChanged(); @@ -635,6 +683,11 @@ export class Minimap extends ViewPart implements IMinimapModel { //#region IMinimapModel + private _checkMapping(): boolean { + // console.log(`TODO: I SHOULD ADJUST THE MAPPING!!!`, this._minimapLines); + return false; + } + private _recreateLineMapping(): void { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); @@ -1010,21 +1063,21 @@ class InnerMinimap extends Disposable { this._lastRenderData = null; return true; } - public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + public onLinesChanged(changeFromLineNumber: number, changeToLineNumber: number): boolean { if (this._lastRenderData) { - return this._lastRenderData.onLinesChanged(e); + return this._lastRenderData.onLinesChanged(changeFromLineNumber, changeToLineNumber); } return false; } - public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): boolean { if (this._lastRenderData) { - this._lastRenderData.onLinesDeleted(e); + this._lastRenderData.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); } return true; } - public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): boolean { if (this._lastRenderData) { - this._lastRenderData.onLinesInserted(e); + this._lastRenderData.onLinesInserted(insertFromLineNumber, insertToLineNumber); } return true; } From f704c339dc459f0add0325b8d74367da6ac72307 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Feb 2020 12:34:53 +0100 Subject: [PATCH 049/192] re-enable active editor test for electron-runner, https://github.com/microsoft/vscode/issues/90470 --- .../src/singlefolder-tests/window.test.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 175804c6095..a8fef8ec2ad 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind } from 'vscode'; +import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, env, UIKind } from 'vscode'; import { join } from 'path'; import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; @@ -145,14 +145,13 @@ suite('window namespace tests', () => { }); }); - // TODO this randomly fails when running against web and visually - // what seems to happen is that the second editor opens and both - // left and right editor show a blinking cursor. Since active editor - // tracking relies on editor focus to function properly, this - // seems to be the root cause of the failure. - // https://github.com/microsoft/vscode/issues/90470 - test.skip('active editor not always correct... #49125', async function () { - + test('active editor not always correct... #49125', async function () { + if (env.uiKind === UIKind.Web) { + // https://github.com/microsoft/vscode/issues/90470 + // https://github.com/microsoft/playwright/issues/979 + this.skip(); + return; + } const randomFile1 = await createRandomFile(); const randomFile2 = await createRandomFile(); From 5166060e710e900ff14bbbdb3263f3082b6d4630 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Feb 2020 12:47:48 +0100 Subject: [PATCH 050/192] test: don't assert in event handler --- .../src/singlefolder-tests/workspace.test.ts | 100 ++++++++---------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index a572fd95df8..0c0e8256ef2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -248,70 +248,64 @@ suite('workspace-namespace', () => { }); }); - test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', () => { - return createRandomFile().then(file => { - let disposables: vscode.Disposable[] = []; + test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', async () => { + const file = await createRandomFile(); + let disposables: vscode.Disposable[] = []; - let onDidOpenTextDocument = false; - disposables.push(vscode.workspace.onDidOpenTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidOpenTextDocument = true; - })); + let pendingAsserts: Function[] = []; + let onDidOpenTextDocument = false; + disposables.push(vscode.workspace.onDidOpenTextDocument(e => { + pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + onDidOpenTextDocument = true; + })); - let onDidChangeTextDocument = false; - disposables.push(vscode.workspace.onDidChangeTextDocument(e => { - assert.ok(pathEquals(e.document.uri.fsPath, file.fsPath)); - onDidChangeTextDocument = true; - })); + let onDidChangeTextDocument = false; + disposables.push(vscode.workspace.onDidChangeTextDocument(e => { + pendingAsserts.push(() => assert.ok(pathEquals(e.document.uri.fsPath, file.fsPath))); + onDidChangeTextDocument = true; + })); - let onDidSaveTextDocument = false; - disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidSaveTextDocument = true; - })); + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + onDidSaveTextDocument = true; + })); - return vscode.workspace.openTextDocument(file).then(doc => { - return vscode.window.showTextDocument(doc).then((editor) => { - return editor.edit((builder) => { - builder.insert(new vscode.Position(0, 0), 'Hello World'); - }).then(_applied => { - return doc.save().then(_saved => { - assert.ok(onDidOpenTextDocument); - assert.ok(onDidChangeTextDocument); - assert.ok(onDidSaveTextDocument); + const doc = await vscode.workspace.openTextDocument(file); + const editor = await vscode.window.showTextDocument(doc); - disposeAll(disposables); - - return deleteFile(file); - }); - }); - }); - }); + await editor.edit((builder) => { + builder.insert(new vscode.Position(0, 0), 'Hello World'); }); + await doc.save(); + + assert.ok(onDidOpenTextDocument); + assert.ok(onDidChangeTextDocument); + assert.ok(onDidSaveTextDocument); + pendingAsserts.forEach(assert => assert()); + disposeAll(disposables); + return deleteFile(file); }); - test('events: onDidSaveTextDocument fires even for non dirty file when saved', () => { - return createRandomFile().then(file => { - let disposables: vscode.Disposable[] = []; + test('events: onDidSaveTextDocument fires even for non dirty file when saved', async () => { + const file = await createRandomFile(); + let disposables: vscode.Disposable[] = []; + let pendingAsserts: Function[] = []; - let onDidSaveTextDocument = false; - disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); - onDidSaveTextDocument = true; - })); + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + onDidSaveTextDocument = true; + })); - return vscode.workspace.openTextDocument(file).then(doc => { - return vscode.window.showTextDocument(doc).then(() => { - return vscode.commands.executeCommand('workbench.action.files.save').then(() => { - assert.ok(onDidSaveTextDocument); + const doc = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(doc); + await vscode.commands.executeCommand('workbench.action.files.save'); - disposeAll(disposables); - - return deleteFile(file); - }); - }); - }); - }); + assert.ok(onDidSaveTextDocument); + pendingAsserts.forEach(fn => fn()); + disposeAll(disposables); + return deleteFile(file); }); test('openTextDocument, with selection', function () { From 2b3fed13e16ca13db3a8504866b215d7098b3de5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 13 Feb 2020 12:54:05 +0100 Subject: [PATCH 051/192] some :lipstick: to prep for https://github.com/microsoft/vscode/issues/90359 --- .../src/singlefolder-tests/workspace.test.ts | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 0c0e8256ef2..25b34c5827b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -214,11 +214,12 @@ suite('workspace-namespace', () => { }); }); - test('eol, change via onWillSave', function () { + test('eol, change via onWillSave', async function () { if (vscode.env.uiKind === vscode.UIKind.Web) { // TODO@Jo Test seems to fail when running in web due to // onWillSaveTextDocument not getting called - return this.skip(); + this.skip(); + return; } let called = false; @@ -227,25 +228,21 @@ suite('workspace-namespace', () => { e.waitUntil(Promise.resolve([vscode.TextEdit.setEndOfLine(vscode.EndOfLine.LF)])); }); - return createRandomFile('foo\r\nbar\r\nbar').then(file => { - return vscode.workspace.openTextDocument(file).then(doc => { - assert.equal(doc.eol, vscode.EndOfLine.CRLF); - const edit = new vscode.WorkspaceEdit(); - edit.set(file, [vscode.TextEdit.insert(new vscode.Position(0, 0), '-changes-')]); + const file = await createRandomFile('foo\r\nbar\r\nbar'); + const doc = await vscode.workspace.openTextDocument(file); + assert.equal(doc.eol, vscode.EndOfLine.CRLF); - return vscode.workspace.applyEdit(edit).then(success => { - assert.ok(success); - return doc.save(); + const edit = new vscode.WorkspaceEdit(); + edit.set(file, [vscode.TextEdit.insert(new vscode.Position(0, 0), '-changes-')]); + const successEdit = await vscode.workspace.applyEdit(edit); + assert.ok(successEdit); - }).then(success => { - assert.ok(success); - assert.ok(called); - assert.ok(!doc.isDirty); - assert.equal(doc.eol, vscode.EndOfLine.LF); - sub.dispose(); - }); - }); - }); + const successSave = await doc.save(); + assert.ok(successSave); + assert.ok(called); + assert.ok(!doc.isDirty); + assert.equal(doc.eol, vscode.EndOfLine.LF); + sub.dispose(); }); test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', async () => { From 3f315eda04964ebde47398bb13d8c2ef423c705a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 12:59:58 +0100 Subject: [PATCH 052/192] Deliver events only if sampled lines are affected --- .../browser/viewParts/minimap/minimap.ts | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 2b35d39d44a..a1613f550bc 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -562,15 +562,12 @@ export class Minimap extends ViewPart implements IMinimapModel { } public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { if (this._isSampling) { - let fromLineIndex = this._modelLineToMinimapLine(e.fromLineNumber) - 1; - while (fromLineIndex > 0 && this._minimapLines[fromLineIndex - 1] >= e.fromLineNumber) { - fromLineIndex--; + const minimapLineRange = this._modelLineRangeToMinimapLineRange(e.fromLineNumber, e.toLineNumber); + if (minimapLineRange) { + return this._actual.onLinesChanged(minimapLineRange[0], minimapLineRange[1]); + } else { + return false; } - let toLineIndex = this._modelLineToMinimapLine(e.toLineNumber) - 1; - while (toLineIndex + 1 < this._minimapLines.length && this._minimapLines[toLineIndex + 1] <= e.toLineNumber) { - toLineIndex++; - } - return this._actual.onLinesChanged(fromLineIndex + 1, toLineIndex + 1); } else { return this._actual.onLinesChanged(e.fromLineNumber, e.toLineNumber); } @@ -630,12 +627,16 @@ export class Minimap extends ViewPart implements IMinimapModel { if (this._isSampling) { let ranges: { fromLineNumber: number; toLineNumber: number; }[] = []; for (const range of e.ranges) { - ranges.push({ - fromLineNumber: this._modelLineToMinimapLine(range.fromLineNumber), - toLineNumber: this._modelLineToMinimapLine(range.toLineNumber) - }); + const minimapLineRange = this._modelLineRangeToMinimapLineRange(range.fromLineNumber, range.toLineNumber); + if (minimapLineRange) { + ranges.push({ fromLineNumber: minimapLineRange[0], toLineNumber: minimapLineRange[1] }); + } + } + if (ranges.length) { + return this._actual.onTokensChanged(ranges); + } else { + return false; } - return this._actual.onTokensChanged(ranges); } else { return this._actual.onTokensChanged(e.ranges); } @@ -715,6 +716,28 @@ export class Minimap extends ViewPart implements IMinimapModel { return Math.min(this._minimapLines.length, Math.max(1, Math.round(lineNumber / this._samplingRatio))); } + /** + * Will return null if the model line ranges are not intersecting with a sampled model line. + */ + private _modelLineRangeToMinimapLineRange(fromLineNumber: number, toLineNumber: number): [number, number] | null { + let fromLineIndex = this._modelLineToMinimapLine(fromLineNumber) - 1; + while (fromLineIndex > 0 && this._minimapLines[fromLineIndex - 1] >= fromLineNumber) { + fromLineIndex--; + } + let toLineIndex = this._modelLineToMinimapLine(toLineNumber) - 1; + while (toLineIndex + 1 < this._minimapLines.length && this._minimapLines[toLineIndex + 1] <= toLineNumber) { + toLineIndex++; + } + if (fromLineIndex === toLineIndex) { + const sampledLineNumber = this._minimapLines[fromLineIndex]; + if (sampledLineNumber < fromLineNumber || sampledLineNumber > toLineNumber) { + // This line is not part of the sampled lines ==> nothing to do + return null; + } + } + return [fromLineIndex + 1, toLineIndex + 1]; + } + private _recomputeMinimapSelections(): void { this._minimapSelections = []; for (const selection of this._selections) { From b4ca4e42fffc17d112fb0371f190dd4c46b1100e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 13:05:45 +0100 Subject: [PATCH 053/192] Reduce diff --- .../editor/browser/viewParts/minimap/minimap.ts | 2 +- .../common/viewModel/splitLinesCollection.ts | 2 +- src/vs/editor/common/viewModel/viewModel.ts | 15 ++++++++++++++- src/vs/editor/common/viewModel/viewModelImpl.ts | 10 +++++++--- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index a1613f550bc..8e1846c8641 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -789,7 +789,7 @@ export class Minimap extends ViewPart implements IMinimapModel { } return result; } - return this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); + return this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed).data; } public getSelections(): Selection[] { diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 07c22199f39..3a3eab70a1c 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -129,7 +129,7 @@ export interface IViewModelLinesCollection extends IDisposable { getViewLineMinColumn(viewLineNumber: number): number; getViewLineMaxColumn(viewLineNumber: number): number; getViewLineData(viewLineNumber: number): ViewLineData; - getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; + getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array; getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations; getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[]; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 82051bbeb9b..529f386a135 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -113,7 +113,7 @@ export interface IViewModel { getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; getViewLineData(lineNumber: number): ViewLineData; - getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; + getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData; getCompletelyVisibleViewRange(): Range; getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; @@ -142,6 +142,19 @@ export interface IViewModel { getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null; } +export class MinimapLinesRenderingData { + public readonly tabSize: number; + public readonly data: Array; + + constructor( + tabSize: number, + data: Array + ) { + this.tabSize = tabSize; + this.data = data; + } +} + export class ViewLineData { _viewLineDataBrand: void; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index ebd64856397..ed3b3382535 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -19,7 +19,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection'; -import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as platform from 'vs/base/common/platform'; @@ -587,8 +587,12 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.lines.getViewLineData(lineNumber); } - public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[] { - return this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); + public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData { + let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed); + return new MinimapLinesRenderingData( + this.getTabSize(), + result + ); } public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations { From b08cb07f96c76fa4092d356daafd68dea39b85a0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 13:55:47 +0100 Subject: [PATCH 054/192] scm: use empty views --- .../browser/parts/views/viewPaneContainer.ts | 30 +++++-- .../contrib/scm/browser/media/scmViewlet.css | 11 --- .../contrib/scm/browser/scmViewlet.ts | 86 +++++++++++++------ 3 files changed, 82 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 1ed47c4dfbc..54844f6ead4 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -70,22 +70,36 @@ class EmptyViewsController { private _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - private viewItems: IEmptyViewItem[] = []; - get views(): IViewContentDescriptor[] { return this.viewItems.map(v => v.view); } + private items: IEmptyViewItem[] = []; + get viewContentDescriptors(): IViewContentDescriptor[] { + return this.items + .filter(v => v.visible) + .map(v => v.view); + } + private contextKeyService: IContextKeyService; + private countKey: IContextKey; private disposables = new DisposableStore(); constructor( private id: string, - @IContextKeyService private contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, ) { + this.contextKeyService = contextKeyService.createScoped(); + this.disposables.add(this.contextKeyService); + + this.countKey = this.contextKeyService.createKey('view.emptyViewContentCount', 0); + contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id)(this.onDidChangeEmptyViewContent, this, this.disposables); this.onDidChangeEmptyViewContent(); } private onDidChangeEmptyViewContent(): void { - this.viewItems = viewsRegistry.getEmptyViewContent(this.id).map(view => ({ + const contentDescriptors = viewsRegistry.getEmptyViewContent(this.id); + this.countKey.set(contentDescriptors.length); + + this.items = contentDescriptors.map(view => ({ view, visible: view.when ? this.contextKeyService.contextMatchesRules(view.when) : true })); @@ -96,7 +110,7 @@ class EmptyViewsController { private onDidChangeContext(): void { let didChange = false; - for (const item of this.viewItems) { + for (const item of this.items) { if (!item.view.when) { continue; } @@ -277,6 +291,10 @@ export abstract class ViewPane extends Pane implements IView { this.updateEmptyContents(); } + protected layoutBody(height: number, width: number): void { + // noop + } + protected getProgressLocation(): string { return this.viewDescriptorService.getViewContainer(this.id)!.id; } @@ -352,7 +370,7 @@ export abstract class ViewPane extends Pane implements IView { return; } - const contents = viewsRegistry.getEmptyViewContent(this.id); + const contents = this.emptyViewsController.viewContentDescriptors; if (contents.length === 0) { removeClass(this.bodyContainer, 'empty'); diff --git a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css index 482b66fb18d..9c94a5c49eb 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css +++ b/src/vs/workbench/contrib/scm/browser/media/scmViewlet.css @@ -8,17 +8,6 @@ flex: 1; } -.scm-viewlet .empty-message { - box-sizing: border-box; - height: 100%; - padding: 10px 22px 0 22px; -} - -.scm-viewlet:not(.empty) .empty-message, -.scm-viewlet.empty .monaco-pane-view { - display: none; -} - .scm-viewlet .scm-status { height: 100%; position: relative; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 128c0029152..7206af16036 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -6,12 +6,11 @@ import 'vs/css!./media/scmViewlet'; import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { append, $, toggleClass, addClasses } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -25,13 +24,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views'; +import { IViewsRegistry, Extensions, IViewDescriptorService, IViewDescriptor } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane'; import { MainPaneDescriptor, MainPane, IViewModel } from 'vs/workbench/contrib/scm/browser/mainPane'; -import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { ViewPaneContainer, IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import type { IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; import { debounce } from 'vs/base/common/decorators'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { addClass } from 'vs/base/browser/dom'; export interface ISpliceEvent { index: number; @@ -39,12 +41,45 @@ export interface ISpliceEvent { elements: T[]; } +export class EmptyPane extends ViewPane { + + static readonly ID = 'scm'; + static readonly TITLE = localize('scm providers', "Source Control Providers"); + + constructor( + options: IViewPaneOptions, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IInstantiationService instantiationService: IInstantiationService, + @IOpenerService openerService: IOpenerService, + @IThemeService themeService: IThemeService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + } + + isEmpty(): boolean { + return true; + } +} + +export class EmptyPaneDescriptor implements IViewDescriptor { + readonly id = EmptyPane.ID; + readonly name = EmptyPane.TITLE; + readonly ctorDescriptor = new SyncDescriptor(EmptyPane); + readonly canToggleVisibility = true; + readonly hideByDefault = false; + readonly order = -1000; + readonly workspace = true; + readonly when = ContextKeyExpr.equals('scm.providerCount', 0); +} + export class SCMViewPaneContainer extends ViewPaneContainer implements IViewModel { private static readonly STATE_KEY = 'workbench.scm.views.state'; - private el!: HTMLElement; - private message: HTMLElement; private menus: SCMMenus; private _repositories: ISCMRepository[] = []; @@ -94,9 +129,14 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode this.menus = instantiationService.createInstance(SCMMenus, undefined); this._register(this.menus.onDidChangeTitle(this.updateTitleArea, this)); - this.message = $('.empty-message', { tabIndex: 0 }, localize('no open repo', "No source control providers registered.")); - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + + viewsRegistry.registerEmptyViewContent(EmptyPane.ID, { + content: localize('no open repo', "No source control providers registered."), + when: ContextKeyExpr.equals('view.emptyViewContentCount', 1) + }); + + viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer); viewsRegistry.registerViews([new MainPaneDescriptor(this)], this.viewContainer); this._register(configurationService.onDidChangeConfiguration(e => { @@ -113,11 +153,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode create(parent: HTMLElement): void { super.create(parent); - - this.el = parent; - addClasses(parent, 'scm-viewlet', 'empty'); - append(parent, this.message); - + addClass(parent, 'scm-viewlet'); this._register(this.scmService.onDidAddRepository(this.onDidAddRepository, this)); this._register(this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this)); this.scmService.repositories.forEach(r => this.onDidAddRepository(r)); @@ -156,9 +192,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode } private onDidChangeRepositories(): void { - const repositoryCount = this.repositories.length; - toggleClass(this.el, 'empty', repositoryCount === 0); - this.repositoryCountKey.set(repositoryCount); + this.repositoryCountKey.set(this.repositories.length); } private onDidShowView(e: IAddedViewDescriptorRef[]): void { @@ -187,23 +221,19 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode } focus(): void { - if (this.repositoryCountKey.get()! === 0) { - this.message.focus(); - } else { - const repository = this.visibleRepositories[0]; + const repository = this.visibleRepositories[0]; - if (repository) { - const pane = this.panes - .filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined; + if (repository) { + const pane = this.panes + .filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined; - if (pane) { - pane.focus(); - } else { - super.focus(); - } + if (pane) { + pane.focus(); } else { super.focus(); } + } else { + super.focus(); } } From 55d1af3e68d4fe8ac49c22b96dc504a702c46687 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 15:10:53 +0100 Subject: [PATCH 055/192] better explorer aria label #90446 --- src/vs/workbench/contrib/files/browser/views/emptyView.ts | 2 +- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 66fd1ce815b..c4f45d6d313 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -50,7 +50,7 @@ export class EmptyView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: No Folder Opened") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.setLabels())); this._register(this.labelService.onDidChangeFormatters(() => this.setLabels())); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 4f5ec72cb90..806bbb4fda1 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -172,7 +172,7 @@ export class ExplorerView extends ViewPane { @IFileService private readonly fileService: IFileService, @IOpenerService openerService: IOpenerService, ) { - super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: {0}", labelService.getWorkspaceLabel(contextService.getWorkspace())) }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); this.resourceContext = instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); @@ -229,6 +229,7 @@ export class ExplorerView extends ViewPane { const title = workspace.folders.map(folder => folder.name).join(); titleElement.textContent = this.name; titleElement.title = title; + titleElement.setAttribute('aria-label', nls.localize('explorerSection', "Explorer Section: {0}", this.name)); }; this._register(this.contextService.onDidChangeWorkspaceName(setHeader)); From f7884f569883bceda14898abcdff59b23946abfc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 15:32:02 +0100 Subject: [PATCH 056/192] integration - mention how to debug --- test/integration/browser/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration/browser/README.md b/test/integration/browser/README.md index 6140aab9acf..10a55f7de17 100644 --- a/test/integration/browser/README.md +++ b/test/integration/browser/README.md @@ -19,3 +19,7 @@ All integration tests run in an Electron instance. You can specify to run the te All integration tests run in a browser instance as specified by the command line arguments. Add the `--debug` flag to see a browser window with the tests running. + +## Debug + +All integration tests can be run and debugged from within VSCode (both Electron and Web) simply by selecting the related launch configuration and running them. From 52d3df6bf2416ed8b89f50944b05719d3e9adce8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 15:32:45 +0100 Subject: [PATCH 057/192] empty view: placeholder when --- extensions/git/package.json | 9 +++++ .../browser/parts/views/viewPaneContainer.ts | 39 +++++++++++-------- src/vs/workbench/common/views.ts | 2 +- .../contrib/scm/browser/scmViewlet.ts | 4 +- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c1cd8b5135c..c45a83e1037 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1761,6 +1761,15 @@ 72 ] } + }, + "documentation": { + "view": [ + { + "view": "workbench.scm", + "contents": "Git is installed, but you currently don't have a folder open.\n[Open Folder](command:vscode.openFolder)", + "when": "workbenchState == empty" + } + ] } }, "dependencies": { diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 54844f6ead4..d380b02c9e8 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -60,8 +60,8 @@ export interface IViewPaneOptions extends IPaneOptions { const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); -interface IEmptyViewItem { - readonly view: IViewContentDescriptor; +interface IItem { + readonly descriptor: IViewContentDescriptor; visible: boolean; } @@ -70,15 +70,19 @@ class EmptyViewsController { private _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - private items: IEmptyViewItem[] = []; + private placeholderItem: IItem | undefined; + private items: IItem[] = []; get viewContentDescriptors(): IViewContentDescriptor[] { - return this.items - .filter(v => v.visible) - .map(v => v.view); + const visibleItems = this.items.filter(v => v.visible); + + if (visibleItems.length === 0 && this.placeholderItem) { + return [this.placeholderItem.descriptor]; + } + + return visibleItems.map(v => v.descriptor); } private contextKeyService: IContextKeyService; - private countKey: IContextKey; private disposables = new DisposableStore(); constructor( @@ -88,8 +92,6 @@ class EmptyViewsController { this.contextKeyService = contextKeyService.createScoped(); this.disposables.add(this.contextKeyService); - this.countKey = this.contextKeyService.createKey('view.emptyViewContentCount', 0); - contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id)(this.onDidChangeEmptyViewContent, this, this.disposables); this.onDidChangeEmptyViewContent(); @@ -97,12 +99,17 @@ class EmptyViewsController { private onDidChangeEmptyViewContent(): void { const contentDescriptors = viewsRegistry.getEmptyViewContent(this.id); - this.countKey.set(contentDescriptors.length); - this.items = contentDescriptors.map(view => ({ - view, - visible: view.when ? this.contextKeyService.contextMatchesRules(view.when) : true - })); + this.items = []; + + for (const descriptor of contentDescriptors) { + if (descriptor.when === 'placeholder') { + this.placeholderItem = { descriptor, visible: true }; + } else { + const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true; + this.items.push({ descriptor, visible }); + } + } this._onDidChange.fire(); } @@ -111,11 +118,11 @@ class EmptyViewsController { let didChange = false; for (const item of this.items) { - if (!item.view.when) { + if (!item.descriptor.when || item.descriptor.when === 'placeholder') { continue; } - const visible = this.contextKeyService.contextMatchesRules(item.view.when); + const visible = this.contextKeyService.contextMatchesRules(item.descriptor.when); if (item.visible === visible) { continue; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 0b1f83c1a93..d1c4eba4294 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -213,7 +213,7 @@ export interface IViewDescriptorCollection extends IDisposable { export interface IViewContentDescriptor { readonly content: string; - readonly when?: ContextKeyExpr; + readonly when?: ContextKeyExpr | 'placeholder'; } export interface IViewsRegistry { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 7206af16036..26a7fd69aab 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -43,7 +43,7 @@ export interface ISpliceEvent { export class EmptyPane extends ViewPane { - static readonly ID = 'scm'; + static readonly ID = 'workbench.scm'; static readonly TITLE = localize('scm providers', "Source Control Providers"); constructor( @@ -133,7 +133,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode viewsRegistry.registerEmptyViewContent(EmptyPane.ID, { content: localize('no open repo', "No source control providers registered."), - when: ContextKeyExpr.equals('view.emptyViewContentCount', 1) + when: 'placeholder' }); viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer); From 086aa9d8c50fafc4f10fb4a5795a25ed2a8b3855 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 15:35:47 +0100 Subject: [PATCH 058/192] notifications - add "Focus Notification Toast" to command palette if toast visible --- .../browser/parts/notifications/notificationsCommands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index dbf6f3adbe5..133bb8ff3ab 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -222,4 +222,5 @@ export function registerNotificationCommands(center: INotificationsCenterControl MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: SHOW_NOTIFICATIONS_CENTER, title: { value: localize('showNotifications', "Show Notifications"), original: 'Show Notifications' }, category }, when: NotificationsCenterVisibleContext.toNegated() }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: HIDE_NOTIFICATIONS_CENTER, title: { value: localize('hideNotifications', "Hide Notifications"), original: 'Hide Notifications' }, category }, when: NotificationsCenterVisibleContext }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: CLEAR_ALL_NOTIFICATIONS, title: { value: localize('clearAllNotifications', "Clear All Notifications"), original: 'Clear All Notifications' }, category } }); -} \ No newline at end of file + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: FOCUS_NOTIFICATION_TOAST, title: { value: localize('focusNotificationToasts', "Focus Notification Toast"), original: 'Focus Notification Toast' }, category }, when: NotificationsToastsVisibleContext }); +} From 3e8bb6cfd452f6f0082e8c2757c03d9f73618c42 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 16:03:18 +0100 Subject: [PATCH 059/192] git: more empty view documentation --- extensions/git/package.json | 14 ++++++++++++-- extensions/git/package.nls.json | 5 ++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c45a83e1037..c1ad297254f 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1766,8 +1766,18 @@ "view": [ { "view": "workbench.scm", - "contents": "Git is installed, but you currently don't have a folder open.\n[Open Folder](command:vscode.openFolder)", - "when": "workbenchState == empty" + "contents": "%view.workbench.scm.empty%", + "when": "config.git.enabled && workbenchState == empty" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.folder%", + "when": "config.git.enabled && workbenchState == folder" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.workspace%", + "when": "config.git.enabled && workbenchState == workspace" } ] } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index f3d75519653..2b787fe82ae 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -146,5 +146,8 @@ "colors.untracked": "Color for untracked resources.", "colors.ignored": "Color for ignored resources.", "colors.conflict": "Color for resources with conflicts.", - "colors.submodule": "Color for submodule resources." + "colors.submodule": "Color for submodule resources.", + "view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", + "view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", + "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)." } From 429fee578d2e02246cf2363965fec17a14846b4e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 16:09:03 +0100 Subject: [PATCH 060/192] Recreate sampling incrementally --- .../browser/viewParts/minimap/minimap.ts | 172 +++++++++++++++--- .../viewParts/minimap/minimapCharRenderer.ts | 6 +- .../browser/view/minimapCharRenderer.test.ts | 4 +- 3 files changed, 156 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 8e1846c8641..3d2ffdd9a5a 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -234,7 +234,6 @@ class MinimapLayout { scrollHeight: number, previousLayout: MinimapLayout | null ): MinimapLayout { - // console.log(`create - ${viewportStartLineNumber}, ${viewportEndLineNumber}, ${viewportHeight}, ${viewportContainsWhitespaceGaps}, ${lineCount}, ${scrollTop}, ${scrollHeight}`); const pixelRatio = options.pixelRatio; const minimapLineHeight = options.minimapLineHeight; const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); @@ -502,9 +501,10 @@ export class Minimap extends ViewPart implements IMinimapModel { private _selections: Selection[]; private _minimapSelections: Selection[]; - private _samplingRatio!: number; - private _minimapLines!: number[]; - private _isSampling = false;//true; + private _samplingRatio: number; + private _minimapLines: number[]; + private _isSampling = false; + private _shouldCheckSampling: boolean; public readonly tokensColorTracker: MinimapTokensColorTracker; public options: MinimapOptions; @@ -517,7 +517,10 @@ export class Minimap extends ViewPart implements IMinimapModel { this._selections = []; this._minimapSelections = []; - this._recreateLineMapping(); + this._samplingRatio = 1; + this._minimapLines = []; + this._shouldCheckSampling = false; + this._recreateLineSampling(null); this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); @@ -594,7 +597,8 @@ export class Minimap extends ViewPart implements IMinimapModel { if (changeStartIndex <= changeEndIndex) { this._actual.onLinesChanged(changeStartIndex + 1, changeEndIndex + 1); } - return this._checkMapping(); + this._shouldCheckSampling = true; + return true; } else { return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber); } @@ -609,7 +613,8 @@ export class Minimap extends ViewPart implements IMinimapModel { } this._minimapLines[i] += insertedLineCount; } - return this._checkMapping(); + this._shouldCheckSampling = true; + return true; } else { return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber); } @@ -651,7 +656,10 @@ export class Minimap extends ViewPart implements IMinimapModel { // --- end event handlers public prepareRender(ctx: RenderingContext): void { - // nothing to read + if (this._shouldCheckSampling) { + this._shouldCheckSampling = false; + this._recreateLineSampling(this._minimapLines); + } } public render(ctx: RestrictedRenderingContext): void { @@ -684,12 +692,23 @@ export class Minimap extends ViewPart implements IMinimapModel { //#region IMinimapModel - private _checkMapping(): boolean { - // console.log(`TODO: I SHOULD ADJUST THE MAPPING!!!`, this._minimapLines); - return false; + private _createLineSampling(minimapLineCount: number, modelLineCount: number, ratio: number): number[] { + let result: number[] = []; + result[0] = 1; + if (minimapLineCount > 1) { + const halfRatio = ratio / 2; + for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) { + result[i] = Math.round(i * ratio + halfRatio); + } + result[minimapLineCount - 1] = modelLineCount; + } + return result; } - private _recreateLineMapping(): void { + private _recreateLineSampling(oldMinimapLines: number[] | null): void { + // generate at most 10 events, if there are more than 10 changes, just flush all previous data + const MAX_EVENT_COUNT = 10; + const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); const pixelRatio = options.get(EditorOption.pixelRatio); @@ -702,10 +721,113 @@ export class Minimap extends ViewPart implements IMinimapModel { const minimapLineCount = Math.floor(modelLineCount / desiredRatio); const ratio = modelLineCount / minimapLineCount; + if (!oldMinimapLines) { + this._minimapLines = this._createLineSampling(minimapLineCount, modelLineCount, ratio); + this._samplingRatio = ratio; + return; + } + + const halfRatio = ratio / 2; + const oldLength = oldMinimapLines.length; let result: number[] = []; + let oldIndex = 0; + let oldDeltaLineCount = 0; + let minModelLineNumber = 1; + let eventCount = 0; + let currentDeleteStart = 0, currentDeleteEnd = 0; + let currentInsertStart = 0, currentInsertEnd = 0; for (let i = 0; i < minimapLineCount; i++) { - const desiredLine = Math.min(modelLineCount, Math.round((i + 1) * ratio)); - result[i] = desiredLine; + const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio)); + const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio)); + + while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) { + if (eventCount < MAX_EVENT_COUNT) { + if (currentInsertEnd !== 0) { + // must deliver previous insert event + this._actual.onLinesInserted(currentInsertStart, currentInsertEnd); + oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); + currentInsertStart = 0; + currentInsertEnd = 0; + eventCount++; + } + + if (currentDeleteStart === 0) { + currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; + currentDeleteEnd = currentDeleteStart; + } else { + currentDeleteEnd++; + } + } + oldIndex++; + } + + let selectedModelLineNumber: number; + if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) { + // reuse the old sampled line + selectedModelLineNumber = oldMinimapLines[oldIndex]; + oldIndex++; + } else { + if (i === 0) { + selectedModelLineNumber = 1; + } else if (i + 1 === minimapLineCount) { + selectedModelLineNumber = modelLineCount; + } else { + selectedModelLineNumber = Math.round(i * ratio + halfRatio); + } + if (eventCount < MAX_EVENT_COUNT) { + if (currentDeleteEnd !== 0) { + // must deliver previous delete event + this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd); + oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); + currentDeleteStart = 0; + currentDeleteEnd = 0; + eventCount++; + } + + if (currentInsertStart === 0) { + currentInsertStart = oldIndex + 1 + oldDeltaLineCount; + currentInsertEnd = currentInsertStart; + } else { + currentInsertEnd++; + } + } + } + + result[i] = selectedModelLineNumber; + if (selectedModelLineNumber === modelLineCount) { + minModelLineNumber = selectedModelLineNumber; + } else { + minModelLineNumber = selectedModelLineNumber + 1; + } + } + + if (eventCount < MAX_EVENT_COUNT) { + if (currentInsertEnd !== 0) { + // must deliver previous insert event + this._actual.onLinesInserted(currentInsertStart, currentInsertEnd); + oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); + currentInsertStart = 0; + currentInsertEnd = 0; + } + while (oldIndex < oldLength) { + if (currentDeleteStart === 0) { + currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; + currentDeleteEnd = currentDeleteStart; + } else { + currentDeleteEnd++; + } + oldIndex++; + } + if (currentDeleteEnd !== 0) { + // must deliver previous delete event + this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd); + oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); + currentDeleteStart = 0; + currentDeleteEnd = 0; + } + } else { + // too many events, just give up + this._actual.onFlushed(); } this._samplingRatio = ratio; @@ -1288,8 +1410,6 @@ class InnerMinimap extends Disposable { } private renderLines(layout: MinimapLayout): RenderData | null { - const renderMinimap = this._model.options.renderMinimap; - const charRenderer = this._model.options.charRenderer(); const startLineNumber = layout.startLineNumber; const endLineNumber = layout.endLineNumber; const minimapLineHeight = this._model.options.minimapLineHeight; @@ -1322,7 +1442,13 @@ class InnerMinimap extends Disposable { const lineInfo = this._model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed); const tabSize = this._model.getOptions().tabSize; const background = this._model.options.backgroundColor; - const useLighterFont = this._model.tokensColorTracker.backgroundIsLight(); + const tokensColorTracker = this._model.tokensColorTracker; + const useLighterFont = tokensColorTracker.backgroundIsLight(); + const renderMinimap = this._model.options.renderMinimap; + const charRenderer = this._model.options.charRenderer(); + const isSampling = this._model.options.isSampling; + const fontScale = this._model.options.fontScale; + const minimapCharWidth = this._model.options.minimapCharWidth; // Render the rest of lines let dy = 0; @@ -1334,13 +1460,14 @@ class InnerMinimap extends Disposable { background, useLighterFont, renderMinimap, - this._model.options.minimapCharWidth, - this._model.tokensColorTracker, + minimapCharWidth, + tokensColorTracker, charRenderer, dy, tabSize, lineInfo[lineIndex]!, - this._model.options.fontScale + fontScale, + isSampling ); } renderedLines[lineIndex] = new MinimapLine(dy); @@ -1466,7 +1593,8 @@ class InnerMinimap extends Disposable { dy: number, tabSize: number, lineData: ViewLineData, - fontScale: number + fontScale: number, + isSampling: boolean ): void { const content = lineData.content; const tokens = lineData.tokens; @@ -1504,7 +1632,7 @@ class InnerMinimap extends Disposable { if (renderMinimap === RenderMinimap.Blocks) { minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont); + minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, isSampling); } dx += charWidth; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index 6feeecc03ac..483abbaac3b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -34,7 +34,8 @@ export class MinimapCharRenderer { color: RGBA8, backgroundColor: RGBA8, fontScale: number, - useLighterFont: boolean + useLighterFont: boolean, + force1pxHeight: boolean ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; @@ -60,7 +61,8 @@ export class MinimapCharRenderer { let sourceOffset = charIndex * charWidth * charHeight; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; - for (let y = 0; y < charHeight; y++) { + const renderHeight = (force1pxHeight ? 1 : charHeight); + for (let y = 0; y < renderHeight; y++) { let column = row; for (let x = 0; x < charWidth; x++) { const c = charData[sourceOffset++] / 255; diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts index b0b0762997c..cde86790141 100644 --- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts @@ -78,7 +78,7 @@ suite('MinimapCharRenderer', () => { imageData.data[4 * i + 2] = background.b; imageData.data[4 * i + 3] = 255; } - renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false); + renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false, false); let actual: number[] = []; for (let i = 0; i < imageData.data.length; i++) { @@ -108,7 +108,7 @@ suite('MinimapCharRenderer', () => { imageData.data[4 * i + 3] = 255; } - renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false); + renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false, false); let actual: number[] = []; for (let i = 0; i < imageData.data.length; i++) { From d946716fe2830378769c3c62fc02fdc63cae8167 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 16:13:10 +0100 Subject: [PATCH 061/192] improve git empty view docs --- extensions/git/package.json | 5 +++++ extensions/git/package.nls.json | 1 + 2 files changed, 6 insertions(+) diff --git a/extensions/git/package.json b/extensions/git/package.json index c1ad297254f..f450b7b4f70 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1764,6 +1764,11 @@ }, "documentation": { "view": [ + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.disabled%", + "when": "!config.git.enabled" + }, { "view": "workbench.scm", "contents": "%view.workbench.scm.empty%", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 2b787fe82ae..62b2b21357d 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -147,6 +147,7 @@ "colors.ignored": "Color for ignored resources.", "colors.conflict": "Color for resources with conflicts.", "colors.submodule": "Color for submodule resources.", + "view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)." From df5969701ee6fe14fa6cae5adeaa89562e30527d Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 16:31:02 +0100 Subject: [PATCH 062/192] fixes #90593 --- src/vs/workbench/contrib/debug/common/debugSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index 77740aa032f..ca090ad6399 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -73,7 +73,7 @@ export class Source { openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { return !this.available ? Promise.resolve(undefined) : editorService.openEditor({ - resource: this.uri.with({ query: null }), + resource: this.uri, description: this.origin, options: { preserveFocus, From b989a6ab0568196e6520c9333287bf30ac4690d1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 16:45:49 +0100 Subject: [PATCH 063/192] Investigate to cancel a running save participant when saving again (#90590) * Investigate to cancel a running save participant when saving again (fix #90509) * keep token source --- .../api/browser/mainThreadSaveParticipant.ts | 6 ++-- .../textfile/common/saveSequenzializer.ts | 9 ++++-- .../textfile/common/textFileEditorModel.ts | 13 ++++++-- .../services/textfile/common/textfiles.ts | 3 +- .../test/browser/textFileEditorModel.test.ts | 30 +++++++++++++++++++ .../test/common/saveSequenzializer.test.ts | 10 +++++++ 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 3cd807c4cce..288a349b183 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -365,9 +365,9 @@ export class SaveParticipant implements ISaveParticipant { this._saveParticipants.dispose(); } - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { + async participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise { - const cts = new CancellationTokenSource(); + const cts = new CancellationTokenSource(token); return this._progressService.withProgress({ title: localize('saveParticipants', "Running Save Participants for '{0}'", this._labelService.getUriLabel(model.resource, { relative: true })), @@ -383,7 +383,7 @@ export class SaveParticipant implements ISaveParticipant { break; } try { - const promise = p.participate(model, env, progress, cts.token); + const promise = p.participate(model, context, progress, cts.token); await raceCancellation(promise, cts.token); } catch (err) { this._logService.warn(err); diff --git a/src/vs/workbench/services/textfile/common/saveSequenzializer.ts b/src/vs/workbench/services/textfile/common/saveSequenzializer.ts index 2b7cc0c8282..76513ef1e16 100644 --- a/src/vs/workbench/services/textfile/common/saveSequenzializer.ts +++ b/src/vs/workbench/services/textfile/common/saveSequenzializer.ts @@ -5,6 +5,7 @@ interface IPendingSave { versionId: number; + cancel: () => void; promise: Promise; } @@ -35,8 +36,12 @@ export class SaveSequentializer { return this._pendingSave ? this._pendingSave.promise : undefined; } - setPending(versionId: number, promise: Promise): Promise { - this._pendingSave = { versionId, promise }; + cancelPending(): void { + this._pendingSave?.cancel(); + } + + setPending(versionId: number, promise: Promise, onCancel?: () => void, ): Promise { + this._pendingSave = { versionId, cancel: () => onCancel?.(), promise }; promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 52ef8b8e949..40b3b72bbd5 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -24,6 +24,7 @@ import { IWorkingCopyService, IWorkingCopyBackup } from 'vs/workbench/services/w import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { SaveSequentializer } from 'vs/workbench/services/textfile/common/saveSequenzializer'; import { ILabelService } from 'vs/platform/label/common/label'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; interface IBackupMetaData { mtime: number; @@ -601,6 +602,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.saveSequentializer.hasPendingSave()) { this.logService.trace(`[text file model] doSave(${versionId}) - exit - because busy saving`, this.resource.toString()); + // Indicate to the save sequentializer that we want to + // cancel the pending operation so that ours can run + // before the pending one finishes. + // Currently this will try to cancel pending save + // participants but never a pending save. + this.saveSequentializer.cancelPending(); + // Register this as the next upcoming save and return return this.saveSequentializer.setNext(() => this.doSave(options)); } @@ -616,6 +624,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // In addition we update our version right after in case it changed because of a model change // // Save participants can also be skipped through API. + const saveParticipantCancellation = new CancellationTokenSource(); let saveParticipantPromise: Promise = Promise.resolve(versionId); if (this.isResolved() && this.textFileService.saveParticipant && !options.skipSaveParticipants) { const onCompleteOrError = () => { @@ -625,7 +634,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil }; this.ignoreDirtyOnModelContentChange = true; - saveParticipantPromise = this.textFileService.saveParticipant.participate(this, { reason: options.reason }).then(onCompleteOrError, onCompleteOrError); + saveParticipantPromise = this.textFileService.saveParticipant.participate(this, { reason: options.reason }, saveParticipantCancellation.token).then(onCompleteOrError, onCompleteOrError); } // mark the save participant as current pending save operation @@ -678,7 +687,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, this.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => this.handleSaveSuccess(stat, versionId, options), error => this.handleSaveError(error, versionId, options))); - })); + }), () => saveParticipantCancellation.cancel()); } private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveOptions): void { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index f211c5939fd..c3c1f68d02c 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -16,6 +16,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { isNative } from 'vs/base/common/platform'; import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IUntitledTextEditorModelManager } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const ITextFileService = createDecorator('textFileService'); @@ -230,7 +231,7 @@ export interface ISaveParticipant { /** * Participate in a save of a model. Allows to change the model before it is being saved to disk. */ - participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise; + participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason }, token: CancellationToken): Promise; } /** diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index d6bc866bf8f..61bddf39309 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -556,4 +556,34 @@ suite('Files - TextFileEditorModel', () => { await model.save(); model.dispose(); }); + + test('Save Participant, participant cancelled when saved again', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + let participations: boolean[] = []; + + accessor.textFileService.saveParticipant = { + participate: (model) => { + return timeout(10).then(() => { + participations.push(true); + }); + } + }; + + await model.load(); + + model.textEditorModel!.setValue('foo'); + const p1 = model.save(); + + model.textEditorModel!.setValue('foo 1'); + const p2 = model.save(); + + model.textEditorModel!.setValue('foo 2'); + await model.save(); + + await p1; + await p2; + assert.equal(participations.length, 1); + model.dispose(); + }); }); diff --git a/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts b/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts index c85a38bd8b3..097fea645ab 100644 --- a/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts +++ b/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts @@ -87,4 +87,14 @@ suite('Files - SaveSequentializer', () => { assert.ok(!secondDone); assert.ok(thirdDone); }); + + test('SaveSequentializer - cancel pending', async function () { + const sequentializer = new SaveSequentializer(); + + let pendingCancelled = false; + sequentializer.setPending(1, timeout(1), () => pendingCancelled = true); + sequentializer.cancelPending(); + + assert.ok(pendingCancelled); + }); }); From ca9be9fc65be63d5a46cd92db7f01926c182bedc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 16:52:51 +0100 Subject: [PATCH 064/192] command: workbench.extensions.search --- .../browser/extensions.contribution.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 3228cb72d0c..49ffa58bb98 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 { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } fro 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 } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, @@ -47,6 +47,7 @@ import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewConta import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -290,6 +291,30 @@ CommandsRegistry.registerCommand({ } }); +CommandsRegistry.registerCommand({ + id: 'workbench.extensions.search', + description: { + description: localize('workbench.extensions.search.description', "Search for a specific extension"), + args: [ + { + name: localize('workbench.extensions.search.arg.name', "Query to use in search"), + schema: { 'type': 'string' } + } + ] + }, + handler: async (accessor, query: string = '') => { + const viewletService = accessor.get(IViewletService); + const viewlet = await viewletService.openViewlet(VIEWLET_ID, true); + + if (!viewlet) { + return; + } + + (viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query); + viewlet.focus(); + } +}); + // File menu registration MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { From eab81b4a31148ff4fe69fc8efd3076feb82154c3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 13 Feb 2020 16:53:08 +0100 Subject: [PATCH 065/192] git missing docs --- extensions/git/package.json | 11 ++++++++--- extensions/git/package.nls.json | 1 + extensions/git/src/main.ts | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index f450b7b4f70..922a16d0bf0 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1769,20 +1769,25 @@ "contents": "%view.workbench.scm.disabled%", "when": "!config.git.enabled" }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.missing%", + "when": "config.git.enabled && git.missing" + }, { "view": "workbench.scm", "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && workbenchState == empty" + "when": "config.git.enabled && !git.missing && workbenchState == empty" }, { "view": "workbench.scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && workbenchState == folder" + "when": "config.git.enabled && !git.missing && workbenchState == folder" }, { "view": "workbench.scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && workbenchState == workspace" + "when": "config.git.enabled && !git.missing && workbenchState == workspace" } ] } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 62b2b21357d..55b1b423f20 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -147,6 +147,7 @@ "colors.ignored": "Color for ignored resources.", "colors.conflict": "Color for resources with conflicts.", "colors.submodule": "Color for submodule resources.", + "view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use Git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.", "view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 7bb5081d12f..ddf16d76aac 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -175,6 +175,7 @@ export async function activate(context: ExtensionContext): Promise console.warn(err.message); outputChannel.appendLine(err.message); + commands.executeCommand('setContext', 'git.missing', true); warnAboutMissingGit(); return new GitExtensionImpl(); From d05b78490f04d66d05a856f57169f724d7679049 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 13 Feb 2020 07:57:01 -0800 Subject: [PATCH 066/192] timeline is movable --- .../contrib/timeline/browser/timeline.contribution.ts | 2 ++ src/vs/workbench/contrib/timeline/browser/timelinePane.ts | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index ab922d819ae..142fb5cb596 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -26,6 +26,8 @@ export class TimelinePaneDescriptor implements IViewDescriptor { readonly collapsed = true; readonly canToggleVisibility = true; readonly hideByDefault = false; + readonly canMoveView = true; + focusCommand = { id: 'timeline.focus' }; } diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 393d822695f..241ee3dabc0 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -294,7 +294,10 @@ export class TimelinePane extends ViewPane { const renderer = this.instantiationService.createInstance(TimelineTreeRenderer); this._tree = >this.instantiationService.createInstance(WorkbenchObjectTree, 'TimelinePane', this._treeElement, new TimelineListVirtualDelegate(), [renderer], { identityProvider: new TimelineIdentityProvider(), - keyboardNavigationLabelProvider: new TimelineKeyboardNavigationLabelProvider() + keyboardNavigationLabelProvider: new TimelineKeyboardNavigationLabelProvider(), + overrideStyles: { + listBackground: this.getBackgroundColor() + } }); const customTreeNavigator = new TreeResourceNavigator(this._tree, { openOnFocus: false, openOnSelection: false }); From 1174fe54f7db9775834c9ac039783566989faacd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 17:01:00 +0100 Subject: [PATCH 067/192] async - make save sequentializer a reusable thing --- src/vs/base/common/async.ts | 104 ++++++++++++++++++ src/vs/base/test/common/async.test.ts | 89 +++++++++++++++ .../textfile/common/saveSequenzializer.ts | 100 ----------------- .../textfile/common/textFileEditorModel.ts | 15 ++- .../test/common/saveSequenzializer.test.ts | 100 ----------------- 5 files changed, 200 insertions(+), 208 deletions(-) delete mode 100644 src/vs/workbench/services/textfile/common/saveSequenzializer.ts delete mode 100644 src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 81c29e6731b..3a6e4124acb 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -779,3 +779,107 @@ export async function retry(task: ITask>, delay: number, retries: throw lastError; } + +//#region Task Sequentializer + +interface IPendingTask { + versionId: number; + cancel: () => void; + promise: Promise; +} + +interface ISequentialTask { + promise: Promise; + promiseResolve: () => void; + promiseReject: (error: Error) => void; + run: () => Promise; +} + +export interface ITaskSequentializerWithPendingTask { + readonly pending: Promise; +} + +export class TaskSequentializer { + private _pending?: IPendingTask; + private _next?: ISequentialTask; + + hasPending(versionId?: number): this is ITaskSequentializerWithPendingTask { + if (!this._pending) { + return false; + } + + if (typeof versionId === 'number') { + return this._pending.versionId === versionId; + } + + return !!this._pending; + } + + get pending(): Promise | undefined { + return this._pending ? this._pending.promise : undefined; + } + + cancelPending(): void { + this._pending?.cancel(); + } + + setPending(versionId: number, promise: Promise, onCancel?: () => void, ): Promise { + this._pending = { versionId, cancel: () => onCancel?.(), promise }; + + promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); + + return promise; + } + + private donePending(versionId: number): void { + if (this._pending && versionId === this._pending.versionId) { + + // only set pending to done if the promise finished that is associated with that versionId + this._pending = undefined; + + // schedule the next task now that we are free if we have any + this.triggerNext(); + } + } + + private triggerNext(): void { + if (this._next) { + const next = this._next; + this._next = undefined; + + // Run next task and complete on the associated promise + next.run().then(next.promiseResolve, next.promiseReject); + } + } + + setNext(run: () => Promise): Promise { + + // this is our first next task, so we create associated promise with it + // so that we can return a promise that completes when the task has + // completed. + if (!this._next) { + let promiseResolve: () => void; + let promiseReject: (error: Error) => void; + const promise = new Promise((resolve, reject) => { + promiseResolve = resolve; + promiseReject = reject; + }); + + this._next = { + run, + promise, + promiseResolve: promiseResolve!, + promiseReject: promiseReject! + }; + } + + // we have a previous next task, just overwrite it + else { + this._next.run = run; + } + + return this._next.promise; + } +} + +//#endregion diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 586c0fa8a7f..dda98ab6e1c 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -557,4 +557,93 @@ suite('Async', () => { assert.equal(error, error); } }); + + test('TaskSequentializer - pending basics', async function () { + const sequentializer = new async.TaskSequentializer(); + + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(2323)); + assert.ok(!sequentializer.pending); + + // pending removes itself after done + await sequentializer.setPending(1, Promise.resolve()); + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(1)); + assert.ok(!sequentializer.pending); + + // pending removes itself after done (use async.timeout) + sequentializer.setPending(2, async.timeout(1)); + assert.ok(sequentializer.hasPending()); + assert.ok(sequentializer.hasPending(2)); + assert.ok(!sequentializer.hasPending(1)); + assert.ok(sequentializer.pending); + + await async.timeout(2); + assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.hasPending(2)); + assert.ok(!sequentializer.pending); + }); + + test('TaskSequentializer - pending and next (finishes instantly)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes instantly + let nextDone = false; + const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('TaskSequentializer - pending and next (finishes after timeout)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after async.timeout + let nextDone = false; + const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); + + await res; + assert.ok(pendingDone); + assert.ok(nextDone); + }); + + test('TaskSequentializer - pending and multiple next (last one wins)', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingDone = false; + sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + + // next finishes after async.timeout + let firstDone = false; + let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); + + let secondDone = false; + let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); + + let thirdDone = false; + let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); + + await Promise.all([firstRes, secondRes, thirdRes]); + assert.ok(pendingDone); + assert.ok(!firstDone); + assert.ok(!secondDone); + assert.ok(thirdDone); + }); + + test('TaskSequentializer - cancel pending', async function () { + const sequentializer = new async.TaskSequentializer(); + + let pendingCancelled = false; + sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true); + sequentializer.cancelPending(); + + assert.ok(pendingCancelled); + }); }); diff --git a/src/vs/workbench/services/textfile/common/saveSequenzializer.ts b/src/vs/workbench/services/textfile/common/saveSequenzializer.ts deleted file mode 100644 index 76513ef1e16..00000000000 --- a/src/vs/workbench/services/textfile/common/saveSequenzializer.ts +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface IPendingSave { - versionId: number; - cancel: () => void; - promise: Promise; -} - -interface ISaveOperation { - promise: Promise; - promiseResolve: () => void; - promiseReject: (error: Error) => void; - run: () => Promise; -} - -export class SaveSequentializer { - private _pendingSave?: IPendingSave; - private _nextSave?: ISaveOperation; - - hasPendingSave(versionId?: number): boolean { - if (!this._pendingSave) { - return false; - } - - if (typeof versionId === 'number') { - return this._pendingSave.versionId === versionId; - } - - return !!this._pendingSave; - } - - get pendingSave(): Promise | undefined { - return this._pendingSave ? this._pendingSave.promise : undefined; - } - - cancelPending(): void { - this._pendingSave?.cancel(); - } - - setPending(versionId: number, promise: Promise, onCancel?: () => void, ): Promise { - this._pendingSave = { versionId, cancel: () => onCancel?.(), promise }; - - promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); - - return promise; - } - - private donePending(versionId: number): void { - if (this._pendingSave && versionId === this._pendingSave.versionId) { - - // only set pending to done if the promise finished that is associated with that versionId - this._pendingSave = undefined; - - // schedule the next save now that we are free if we have any - this.triggerNextSave(); - } - } - - private triggerNextSave(): void { - if (this._nextSave) { - const saveOperation = this._nextSave; - this._nextSave = undefined; - - // Run next save and complete on the associated promise - saveOperation.run().then(saveOperation.promiseResolve, saveOperation.promiseReject); - } - } - - setNext(run: () => Promise): Promise { - - // this is our first next save, so we create associated promise with it - // so that we can return a promise that completes when the save operation - // has completed. - if (!this._nextSave) { - let promiseResolve: () => void; - let promiseReject: (error: Error) => void; - const promise = new Promise((resolve, reject) => { - promiseResolve = resolve; - promiseReject = reject; - }); - - this._nextSave = { - run, - promise, - promiseResolve: promiseResolve!, - promiseReject: promiseReject! - }; - } - - // we have a previous next save, just overwrite it - else { - this._nextSave.run = run; - } - - return this._nextSave.promise; - } -} diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 40b3b72bbd5..fb425e6ac9f 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -14,7 +14,7 @@ import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backu import { IFileService, FileOperationError, FileOperationResult, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { timeout } from 'vs/base/common/async'; +import { timeout, TaskSequentializer } from 'vs/base/common/async'; import { ITextBufferFactory, ITextModel } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ILogService } from 'vs/platform/log/common/log'; @@ -22,7 +22,6 @@ import { basename } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IWorkingCopyService, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { SaveSequentializer } from 'vs/workbench/services/textfile/common/saveSequenzializer'; import { ILabelService } from 'vs/platform/label/common/label'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -79,7 +78,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private lastResolvedFileStat: IFileStatWithMetadata | undefined; - private readonly saveSequentializer = new SaveSequentializer(); + private readonly saveSequentializer = new TaskSequentializer(); private lastSaveAttemptTime = 0; private dirty = false; @@ -256,7 +255,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // It is very important to not reload the model when the model is dirty. // We also only want to reload the model from the disk if no save is pending // to avoid data loss. - if (this.dirty || this.saveSequentializer.hasPendingSave()) { + if (this.dirty || this.saveSequentializer.hasPending()) { this.logService.trace('[text file model] load() - exit - without loading because model is dirty or being saved', this.resource.toString()); return this; @@ -576,10 +575,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario: user invoked the save action multiple times quickly for the same contents // while the save was not yet finished to disk // - if (this.saveSequentializer.hasPendingSave(versionId)) { + if (this.saveSequentializer.hasPending(versionId)) { this.logService.trace(`[text file model] doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString()); - return this.saveSequentializer.pendingSave || Promise.resolve(); + return this.saveSequentializer.pending; } // Return early if not dirty (unless forced) @@ -599,7 +598,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save // while the first save has not returned yet. // - if (this.saveSequentializer.hasPendingSave()) { + if (this.saveSequentializer.hasPending()) { this.logService.trace(`[text file model] doSave(${versionId}) - exit - because busy saving`, this.resource.toString()); // Indicate to the save sequentializer that we want to @@ -792,7 +791,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil case ModelState.ORPHAN: return this.inOrphanMode; case ModelState.PENDING_SAVE: - return this.saveSequentializer.hasPendingSave(); + return this.saveSequentializer.hasPending(); case ModelState.SAVED: return !this.dirty; } diff --git a/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts b/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts deleted file mode 100644 index 097fea645ab..00000000000 --- a/src/vs/workbench/services/textfile/test/common/saveSequenzializer.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { timeout } from 'vs/base/common/async'; -import { SaveSequentializer } from 'vs/workbench/services/textfile/common/saveSequenzializer'; - -suite('Files - SaveSequentializer', () => { - - test('SaveSequentializer - pending basics', async function () { - const sequentializer = new SaveSequentializer(); - - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(2323)); - assert.ok(!sequentializer.pendingSave); - - // pending removes itself after done - await sequentializer.setPending(1, Promise.resolve()); - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(1)); - assert.ok(!sequentializer.pendingSave); - - // pending removes itself after done (use timeout) - sequentializer.setPending(2, timeout(1)); - assert.ok(sequentializer.hasPendingSave()); - assert.ok(sequentializer.hasPendingSave(2)); - assert.ok(!sequentializer.hasPendingSave(1)); - assert.ok(sequentializer.pendingSave); - - await timeout(2); - assert.ok(!sequentializer.hasPendingSave()); - assert.ok(!sequentializer.hasPendingSave(2)); - assert.ok(!sequentializer.pendingSave); - }); - - test('SaveSequentializer - pending and next (finishes instantly)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes instantly - let nextDone = false; - const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); - - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); - - test('SaveSequentializer - pending and next (finishes after timeout)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes after timeout - let nextDone = false; - const res = sequentializer.setNext(() => timeout(1).then(() => { nextDone = true; return; })); - - await res; - assert.ok(pendingDone); - assert.ok(nextDone); - }); - - test('SaveSequentializer - pending and multiple next (last one wins)', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingDone = false; - sequentializer.setPending(1, timeout(1).then(() => { pendingDone = true; return; })); - - // next finishes after timeout - let firstDone = false; - let firstRes = sequentializer.setNext(() => timeout(2).then(() => { firstDone = true; return; })); - - let secondDone = false; - let secondRes = sequentializer.setNext(() => timeout(3).then(() => { secondDone = true; return; })); - - let thirdDone = false; - let thirdRes = sequentializer.setNext(() => timeout(4).then(() => { thirdDone = true; return; })); - - await Promise.all([firstRes, secondRes, thirdRes]); - assert.ok(pendingDone); - assert.ok(!firstDone); - assert.ok(!secondDone); - assert.ok(thirdDone); - }); - - test('SaveSequentializer - cancel pending', async function () { - const sequentializer = new SaveSequentializer(); - - let pendingCancelled = false; - sequentializer.setPending(1, timeout(1), () => pendingCancelled = true); - sequentializer.cancelPending(); - - assert.ok(pendingCancelled); - }); -}); From 97eb6186e7ac1a51b5326c7fb51a6d5834ed4181 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 13 Feb 2020 17:04:48 +0100 Subject: [PATCH 068/192] async - versionId => taskId --- src/vs/base/common/async.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 3a6e4124acb..ceb38bd5b18 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -783,7 +783,7 @@ export async function retry(task: ITask>, delay: number, retries: //#region Task Sequentializer interface IPendingTask { - versionId: number; + taskId: number; cancel: () => void; promise: Promise; } @@ -803,13 +803,13 @@ export class TaskSequentializer { private _pending?: IPendingTask; private _next?: ISequentialTask; - hasPending(versionId?: number): this is ITaskSequentializerWithPendingTask { + hasPending(taskId?: number): this is ITaskSequentializerWithPendingTask { if (!this._pending) { return false; } - if (typeof versionId === 'number') { - return this._pending.versionId === versionId; + if (typeof taskId === 'number') { + return this._pending.taskId === taskId; } return !!this._pending; @@ -823,18 +823,18 @@ export class TaskSequentializer { this._pending?.cancel(); } - setPending(versionId: number, promise: Promise, onCancel?: () => void, ): Promise { - this._pending = { versionId, cancel: () => onCancel?.(), promise }; + setPending(taskId: number, promise: Promise, onCancel?: () => void, ): Promise { + this._pending = { taskId: taskId, cancel: () => onCancel?.(), promise }; - promise.then(() => this.donePending(versionId), () => this.donePending(versionId)); + promise.then(() => this.donePending(taskId), () => this.donePending(taskId)); return promise; } - private donePending(versionId: number): void { - if (this._pending && versionId === this._pending.versionId) { + private donePending(taskId: number): void { + if (this._pending && taskId === this._pending.taskId) { - // only set pending to done if the promise finished that is associated with that versionId + // only set pending to done if the promise finished that is associated with that taskId this._pending = undefined; // schedule the next task now that we are free if we have any From c58e4378304fe7fe5a200a6ce554bf9c4504cb73 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 13 Feb 2020 08:14:41 -0800 Subject: [PATCH 069/192] remove unnecessary listeners for built-in views --- .../workbench/contrib/debug/browser/breakpointsView.ts | 6 ------ .../workbench/contrib/debug/browser/callStackView.ts | 6 ------ .../contrib/debug/browser/loadedScriptsView.ts | 6 ------ .../workbench/contrib/debug/browser/variablesView.ts | 5 ----- .../contrib/debug/browser/watchExpressionsView.ts | 10 +++++----- .../contrib/files/browser/views/openEditorsView.ts | 6 ------ 6 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 67cff6f0e97..921e3a59801 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -153,12 +153,6 @@ export class BreakpointsView extends ViewPane { this.onBreakpointsChange(); } })); - - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.list.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); } public focus(): void { diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 4358be1522e..8c2ec325030 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -298,12 +298,6 @@ export class CallStackView extends ViewPane { this.parentSessionToExpand.add(s.parentSession); } })); - - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); } layoutBody(height: number, width: number): void { diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 82460c27fa4..8cd236dcc3d 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -573,12 +573,6 @@ export class LoadedScriptsView extends ViewPane { } })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); - // feature: expand all nodes when filtering (not when finding) let viewState: IViewState | undefined; this._register(this.tree.onDidChangeTypeFilterPattern(pattern => { diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 30fb675b178..278ea98378a 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -143,11 +143,6 @@ export class VariablesView extends ViewPane { this.tree.rerender(e); } })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); } getActions(): IAction[] { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 0fb5dfb061b..190d2a2b435 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -138,11 +138,11 @@ export class WatchExpressionsView extends ViewPane { this.tree.rerender(e); } })); - this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - if (views.some(v => v.id === this.id)) { - this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); + // this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { + // if (views.some(v => v.id === this.id)) { + // this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); + // } + // })); } layoutBody(height: number, width: number): void { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 004e956f796..d046727ff64 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -302,12 +302,6 @@ export class OpenEditorsView extends ViewPane { this.listRefreshScheduler.schedule(0); } })); - - this._register(this.viewDescriptorService.onDidChangeLocation(({ views }) => { - if (views.some(v => v.id === this.id)) { - this.list.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - } - })); } getActions(): IAction[] { From a28e0a93b170ead69645a3d64f00e7d3a06af2d0 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 13 Feb 2020 08:18:10 -0800 Subject: [PATCH 070/192] set flag on open editors view to move --- src/vs/workbench/contrib/files/browser/explorerViewlet.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index cc45efefec9..2850d3ea76b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -108,6 +108,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor order: 0, when: OpenEditorsVisibleContext, canToggleVisibility: true, + canMoveView: true, focusCommand: { id: 'workbench.files.action.focusOpenEditorsView', keybindings: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_E) } From 8709f1b4905a17a1e42cfa3e575841b4513113bc Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Feb 2020 17:30:44 +0100 Subject: [PATCH 071/192] open editors: fix css to work in panel --- .../files/browser/media/explorerviewlet.css | 67 +-------------- .../files/browser/views/media/openeditors.css | 83 +++++++++++++++++++ .../files/browser/views/openEditorsView.ts | 3 +- 3 files changed, 86 insertions(+), 67 deletions(-) create mode 100644 src/vs/workbench/contrib/files/browser/views/media/openeditors.css diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 8f25376b925..ce694ddae30 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -19,7 +19,6 @@ } .explorer-viewlet .explorer-item, -.explorer-viewlet .open-editor, .explorer-viewlet .editor-group { height: 22px; line-height: 22px; @@ -31,7 +30,6 @@ } .explorer-viewlet .explorer-item > a, -.explorer-viewlet .open-editor > a, .explorer-viewlet .editor-group { text-overflow: ellipsis; overflow: hidden; @@ -50,16 +48,6 @@ flex: 0; /* do not steal space when label is hidden because we are in edit mode */ } -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row { - padding-left: 22px; - display: flex; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar { - visibility: hidden; - display: flex; - align-items: center; -} .explorer-viewlet .pane-header .count { min-width: fit-content; @@ -72,42 +60,6 @@ display: none; } -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row:hover > .monaco-action-bar, -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.focused > .monaco-action-bar, -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty > .monaco-action-bar { - visibility: visible; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar .action-label { - display: block; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar .codicon { - color: inherit; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar .codicon-close { - width: 8px; - height: 22px; - display: flex; - align-items: center; - justify-content: center; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar .action-close-all-files, -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .monaco-action-bar .save-all { - width: 23px; - height: 22px; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .open-editor { - flex: 1; -} - -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row > .editor-group { - flex: 1; -} - .explorer-viewlet .monaco-count-badge { padding: 1px 6px 2px; margin-left: 6px; @@ -155,24 +107,7 @@ height: 20px; } -.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row .editor-group { - font-size: 11px; - font-weight: bold; - text-transform: uppercase; - cursor: default; -} - -/* Bold font style does not go well with CJK fonts */ -.explorer-viewlet:lang(zh-Hans) .explorer-open-editors .monaco-list .monaco-list-row .editor-group, -.explorer-viewlet:lang(zh-Hant) .explorer-open-editors .monaco-list .monaco-list-row .editor-group, -.explorer-viewlet:lang(ja) .explorer-open-editors .monaco-list .monaco-list-row .editor-group, -.explorer-viewlet:lang(ko) .explorer-open-editors .monaco-list .monaco-list-row .editor-group { - font-weight: normal; -} - /* High Contrast Theming */ -.hc-black .monaco-workbench .explorer-viewlet .explorer-item, -.hc-black .monaco-workbench .explorer-viewlet .open-editor, -.hc-black .monaco-workbench .explorer-viewlet .editor-group { +.hc-black .monaco-workbench .explorer-viewlet .explorer-item { line-height: 20px; } diff --git a/src/vs/workbench/contrib/files/browser/views/media/openeditors.css b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css new file mode 100644 index 00000000000..a64e503df77 --- /dev/null +++ b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.open-editors .monaco-list .monaco-list-row:hover > .monaco-action-bar, +.open-editors .monaco-list .monaco-list-row.focused > .monaco-action-bar, +.open-editors .monaco-list .monaco-list-row.dirty > .monaco-action-bar { + visibility: visible; +} + +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .action-label { + display: block; +} + +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .codicon { + color: inherit; +} + +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .codicon-close { + width: 8px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; +} + +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .action-close-all-files, +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar .save-all { + width: 23px; + height: 22px; +} + +.open-editors .monaco-list .monaco-list-row > .open-editor { + flex: 1; +} + +.open-editors .monaco-list .monaco-list-row > .editor-group { + flex: 1; +} + +.open-editors .monaco-list .monaco-list-row { + padding-left: 22px; + display: flex; +} + +.open-editors .monaco-list .monaco-list-row > .monaco-action-bar { + visibility: hidden; + display: flex; + align-items: center; +} + +.open-editors .monaco-list .monaco-list-row .editor-group { + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + cursor: default; +} + +/* Bold font style does not go well with CJK fonts */ +.composite:lang(zh-Hans) .open-editors .monaco-list .monaco-list-row .editor-group, +.composite:lang(zh-Hant) .open-editors .monaco-list .monaco-list-row .editor-group, +.composite:lang(ja) .open-editors .monaco-list .monaco-list-row .editor-group, +.composite:lang(ko) .open-editors .monaco-list .monaco-list-row .editor-group { + font-weight: normal; +} + +.open-editors .open-editor, +.open-editors .editor-group { + height: 22px; + line-height: 22px; +} + +.open-editors .open-editor > a, +.open-editors .editor-group { + text-overflow: ellipsis; + overflow: hidden; +} + +.hc-black .monaco-workbench .open-editors .open-editor, +.hc-black .monaco-workbench .open-editors .editor-group { + line-height: 20px; +} diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index d046727ff64..7e53ad1f395 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/openeditors'; import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IAction, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; @@ -205,7 +206,7 @@ export class OpenEditorsView extends ViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); - dom.addClass(container, 'explorer-open-editors'); + dom.addClass(container, 'open-editors'); dom.addClass(container, 'show-file-icons'); const delegate = new OpenEditorsDelegate(); From db2093d705b133ee9c4bbcb687e7f425c72d5b90 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt-Krulig Date: Thu, 13 Feb 2020 17:34:44 +0100 Subject: [PATCH 072/192] Add entireDocument minimap option --- src/vs/editor/common/config/editorOptions.ts | 11 +++++++++++ src/vs/monaco.d.ts | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 50d6dd6c3e0..29e137cf5d8 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1995,6 +1995,10 @@ export interface IEditorMinimapOptions { * Relative size of the font in the minimap. Defaults to 1. */ scale?: number; + /** + * Minimap covers entire document. + */ + entireDocument?: boolean; } export type EditorMinimapOptions = Readonly>; @@ -2009,6 +2013,7 @@ class EditorMinimap extends BaseEditorOption>; From 29d7bdf5c7bd9f0e8039513924f636bc13a31b9d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 17:35:30 +0100 Subject: [PATCH 073/192] Read and honor entireDocument --- .../browser/viewParts/minimap/minimap.ts | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 3d2ffdd9a5a..4cf1bdc1571 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -501,9 +501,9 @@ export class Minimap extends ViewPart implements IMinimapModel { private _selections: Selection[]; private _minimapSelections: Selection[]; + private _isSampling: boolean; private _samplingRatio: number; private _minimapLines: number[]; - private _isSampling = false; private _shouldCheckSampling: boolean; public readonly tokensColorTracker: MinimapTokensColorTracker; @@ -517,10 +517,11 @@ export class Minimap extends ViewPart implements IMinimapModel { this._selections = []; this._minimapSelections = []; + this._isSampling = false; this._samplingRatio = 1; this._minimapLines = []; this._shouldCheckSampling = false; - this._recreateLineSampling(null); + this._recreateLineSampling(true); this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); @@ -550,7 +551,10 @@ export class Minimap extends ViewPart implements IMinimapModel { // ---- begin view event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - return this._onOptionsMaybeChanged(); + let shouldRender = false; + shouldRender = this._recreateLineSampling(false) || shouldRender; + shouldRender = this._onOptionsMaybeChanged() || shouldRender; + return shouldRender; } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { this._selections = e.selections; @@ -658,7 +662,7 @@ export class Minimap extends ViewPart implements IMinimapModel { public prepareRender(ctx: RenderingContext): void { if (this._shouldCheckSampling) { this._shouldCheckSampling = false; - this._recreateLineSampling(this._minimapLines); + this._recreateLineSampling(false); } } @@ -705,11 +709,22 @@ export class Minimap extends ViewPart implements IMinimapModel { return result; } - private _recreateLineSampling(oldMinimapLines: number[] | null): void { - // generate at most 10 events, if there are more than 10 changes, just flush all previous data - const MAX_EVENT_COUNT = 10; - + private _recreateLineSampling(ctor: boolean): boolean { + const wasSampling = this._isSampling; const options = this._context.configuration.options; + const minimapOpts = options.get(EditorOption.minimap); + if (!minimapOpts.entireDocument) { + // no sampling! + this._isSampling = false; + this._minimapLines = []; + this._samplingRatio = 1; + if (!ctor && wasSampling) { + // was sampling => sampling stopped + return this._onOptionsMaybeChanged(); + } + return false; + } + const layoutInfo = options.get(EditorOption.layoutInfo); const pixelRatio = options.get(EditorOption.pixelRatio); const lineHeight = options.get(EditorOption.lineHeight); @@ -721,18 +736,25 @@ export class Minimap extends ViewPart implements IMinimapModel { const minimapLineCount = Math.floor(modelLineCount / desiredRatio); const ratio = modelLineCount / minimapLineCount; - if (!oldMinimapLines) { + if (!this._minimapLines || this._minimapLines.length === 0) { + this._isSampling = true; this._minimapLines = this._createLineSampling(minimapLineCount, modelLineCount, ratio); this._samplingRatio = ratio; - return; + if (!ctor && !wasSampling) { + // was not sampling => now is sampling + return this._onOptionsMaybeChanged(); + } + return false; } const halfRatio = ratio / 2; + const oldMinimapLines = this._minimapLines; const oldLength = oldMinimapLines.length; let result: number[] = []; let oldIndex = 0; let oldDeltaLineCount = 0; let minModelLineNumber = 1; + const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data let eventCount = 0; let currentDeleteStart = 0, currentDeleteEnd = 0; let currentInsertStart = 0, currentInsertEnd = 0; @@ -830,8 +852,10 @@ export class Minimap extends ViewPart implements IMinimapModel { this._actual.onFlushed(); } + this._isSampling = true; this._samplingRatio = ratio; this._minimapLines = result; + return false; } private _modelLineToMinimapLine(lineNumber: number): number { From 5def21f51c58d6d3fc0bd9d4cbf85e6b3cc723f8 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 13 Feb 2020 09:03:17 -0800 Subject: [PATCH 074/192] update css for debug viewlet --- .../contrib/debug/browser/baseDebugView.ts | 9 ++ .../contrib/debug/browser/breakpointsView.ts | 5 +- .../contrib/debug/browser/callStackView.ts | 6 +- .../debug/browser/loadedScriptsView.ts | 6 +- .../debug/browser/media/debugViewlet.css | 130 +++++++++--------- .../contrib/debug/browser/variablesView.ts | 6 +- .../debug/browser/watchExpressionsView.ts | 11 +- 7 files changed, 89 insertions(+), 84 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 5d4be69a867..3b75c468a51 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -19,6 +19,7 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; import { once } from 'vs/base/common/functional'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export const twistiePixels = 20; @@ -232,3 +233,11 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer; private needsRefresh = false; diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 8c2ec325030..805bac9c916 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -13,12 +13,12 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction, Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ILabelService } from 'vs/platform/label/common/label'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -74,7 +74,7 @@ export function getContextForContributedActions(element: CallStackItem | null): return ''; } -export class CallStackView extends ViewPane { +export class CallStackView extends BaseDebugViewPane { private pauseMessage!: HTMLSpanElement; private pauseMessageLabel!: HTMLSpanElement; private onCallStackChangeScheduler: RunOnceScheduler; diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 8cd236dcc3d..bf56471c4a4 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -7,12 +7,12 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { normalize, isAbsolute, posix } from 'vs/base/common/path'; -import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -402,7 +402,7 @@ function asTreeElement(item: BaseTreeItem, viewState?: IViewState): ITreeElement }; } -export class LoadedScriptsView extends ViewPane { +export class LoadedScriptsView extends BaseDebugViewPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index f14030847df..2408b52df02 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -5,7 +5,7 @@ /* Debug viewlet */ -.debug-viewlet { +.debug-pane { height: 100%; } @@ -13,27 +13,27 @@ height: 100%; } -.debug-viewlet .debug-start-view { +.debug-pane .debug-start-view { padding: 0 20px 0 20px; } -.debug-viewlet .debug-start-view .monaco-button, -.debug-viewlet .debug-start-view .section { +.debug-pane .debug-start-view .monaco-button, +.debug-pane .debug-start-view .section { margin-top: 20px; } -.debug-viewlet .debug-start-view .top-section { +.debug-pane .debug-start-view .top-section { margin-top: 10px; } -.debug-viewlet .debug-start-view .monaco-button { +.debug-pane .debug-start-view .monaco-button { max-width: 260px; margin-left: auto; margin-right: auto; display: block; } -.debug-viewlet .debug-start-view .click { +.debug-pane .debug-start-view .click { cursor: pointer; color: #007ACC; } @@ -87,7 +87,7 @@ /* Debug viewlet trees */ -.debug-viewlet .line-number { +.debug-pane .line-number { background: rgba(136, 136, 136, 0.3); border-radius: 2px; font-size: 0.9em; @@ -95,29 +95,29 @@ line-height: 20px; } -.debug-viewlet .monaco-list-row.selected .line-number, -.debug-viewlet .monaco-list-row.selected .thread > .state > .label, -.debug-viewlet .monaco-list-row.selected .session > .state > .label { +.debug-pane .monaco-list-row.selected .line-number, +.debug-pane .monaco-list-row.selected .thread > .state > .label, +.debug-pane .monaco-list-row.selected .session > .state > .label { background-color: #ffffff; color: #666; } -.debug-viewlet .monaco-list:focus .monaco-list-row.selected.focused .codicon { +.debug-pane .monaco-list:focus .monaco-list-row.selected.focused .codicon { color: inherit !important; } -.debug-viewlet .disabled { +.debug-pane .disabled { opacity: 0.65; } /* Call stack */ -.debug-viewlet .debug-call-stack-title { +.debug-pane .debug-call-stack-title { display: flex; width: 100%; } -.debug-viewlet .debug-call-stack-title > .pause-message { +.debug-pane .debug-call-stack-title > .pause-message { flex: 1; text-align: right; text-overflow: ellipsis; @@ -126,41 +126,41 @@ margin: 0px 10px; } -.debug-viewlet .debug-call-stack-title > .pause-message > .label { +.debug-pane .debug-call-stack-title > .pause-message > .label { border-radius: 3px; padding: 1px 2px; font-size: 9px; } -.debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { +.debug-pane .debug-call-stack-title > .pause-message > .label.exception { background-color: #A31515; color: rgb(255, 255, 255); } -.vs-dark .debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { +.vs-dark .debug-pane .debug-call-stack-title > .pause-message > .label.exception { background-color: #6C2022; color: inherit; } -.hc-black .debug-viewlet .debug-call-stack-title > .pause-message > .label.exception { +.hc-black .debug-pane .debug-call-stack-title > .pause-message > .label.exception { background-color: #6C2022; color: inherit; } -.debug-viewlet .debug-call-stack .thread, -.debug-viewlet .debug-call-stack .session { +.debug-pane .debug-call-stack .thread, +.debug-pane .debug-call-stack .session { display: flex; } -.debug-viewlet .debug-call-stack .thread > .name, -.debug-viewlet .debug-call-stack .session > .name { +.debug-pane .debug-call-stack .thread > .name, +.debug-pane .debug-call-stack .session > .name { flex: 1; overflow: hidden; text-overflow: ellipsis; } -.debug-viewlet .debug-call-stack .thread > .state, -.debug-viewlet .debug-call-stack .session > .state { +.debug-pane .debug-call-stack .thread > .state, +.debug-pane .debug-call-stack .session > .state { text-align: right; overflow: hidden; text-overflow: ellipsis; @@ -168,116 +168,116 @@ text-transform: uppercase; } -.debug-viewlet .debug-call-stack .monaco-list-row:hover .state { +.debug-pane .debug-call-stack .monaco-list-row:hover .state { display: none; } -.debug-viewlet .debug-call-stack .monaco-list-row:hover .stack-frame.has-actions .file .line-number { +.debug-pane .debug-call-stack .monaco-list-row:hover .stack-frame.has-actions .file .line-number { display: none; } -.debug-viewlet .debug-call-stack .monaco-list-row .monaco-action-bar { +.debug-pane .debug-call-stack .monaco-list-row .monaco-action-bar { display: none; } -.debug-viewlet .debug-call-stack .monaco-list-row:hover .monaco-action-bar { +.debug-pane .debug-call-stack .monaco-list-row:hover .monaco-action-bar { display: initial; } -.monaco-workbench .debug-viewlet .debug-call-stack .monaco-action-bar .action-item > .action-label { +.monaco-workbench .debug-pane .debug-call-stack .monaco-action-bar .action-item > .action-label { width: 16px; height: 100%; margin-right: 8px; vertical-align: text-top; } -.debug-viewlet .debug-call-stack .thread > .state > .label, -.debug-viewlet .debug-call-stack .session > .state > .label { +.debug-pane .debug-call-stack .thread > .state > .label, +.debug-pane .debug-call-stack .session > .state > .label { background: rgba(136, 136, 136, 0.3); border-radius: 2px; font-size: 0.8em; padding: 0 3px; } -.debug-viewlet .debug-call-stack .stack-frame { +.debug-pane .debug-call-stack .stack-frame { overflow: hidden; text-overflow: ellipsis; padding-right: 0.8em; display: flex; } -.debug-viewlet .debug-call-stack .stack-frame.label { +.debug-pane .debug-call-stack .stack-frame.label { text-align: center; font-style: italic; } -.debug-viewlet .debug-call-stack .stack-frame .label { +.debug-pane .debug-call-stack .stack-frame .label { flex: 1; flex-shrink: 0; min-width: fit-content; min-width: -moz-fit-content; } -.debug-viewlet .debug-call-stack .stack-frame.subtle { +.debug-pane .debug-call-stack .stack-frame.subtle { font-style: italic; } -.debug-viewlet .debug-call-stack .stack-frame.label > .file { +.debug-pane .debug-call-stack .stack-frame.label > .file { display: none; } -.debug-viewlet .debug-call-stack .stack-frame > .file { +.debug-pane .debug-call-stack .stack-frame > .file { display: flex; overflow: hidden; flex-wrap: wrap; justify-content: flex-end; } -.debug-viewlet .debug-call-stack .stack-frame > .file > .line-number.unavailable { +.debug-pane .debug-call-stack .stack-frame > .file > .line-number.unavailable { display: none; } -.debug-viewlet .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file { +.debug-pane .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file { color: rgba(108, 108, 108, 0.8); } -.debug-viewlet .debug-call-stack .stack-frame > .file > .file-name { +.debug-pane .debug-call-stack .stack-frame > .file > .file-name { overflow: hidden; text-overflow: ellipsis; margin-right: 0.8em; } -.vs-dark .debug-viewlet .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file { +.vs-dark .debug-pane .debug-call-stack .monaco-list-row:not(.selected) .stack-frame > .file { color: rgba(204, 204, 204, 0.6); } -.debug-viewlet .debug-call-stack .stack-frame > .file:not(:first-child) { +.debug-pane .debug-call-stack .stack-frame > .file:not(:first-child) { margin-left: 0.8em; } -.debug-viewlet .debug-call-stack .load-more { +.debug-pane .debug-call-stack .load-more { font-style: italic; text-align: center; } -.debug-viewlet .debug-call-stack .show-more { +.debug-pane .debug-call-stack .show-more { font-style: italic; opacity: 0.35; } -.debug-viewlet .debug-call-stack .error { +.debug-pane .debug-call-stack .error { font-style: italic; text-overflow: ellipsis; overflow: hidden; } -.debug-viewlet .debug-call-stack .monaco-list:focus .monaco-list-row.selected .codicon { +.debug-pane .debug-call-stack .monaco-list:focus .monaco-list-row.selected .codicon { color: inherit !important; } /* Variables & Expression view */ -.debug-viewlet .scope { +.debug-pane .scope { font-weight: bold; font-size: 11px; } @@ -295,7 +295,7 @@ 100% { background-color: rgba(86, 156, 214, .2) } } -.debug-viewlet .monaco-list-row .expression .value.changed { +.debug-pane .monaco-list-row .expression .value.changed { padding: 2px; margin: 4px; border-radius: 4px; @@ -305,67 +305,67 @@ animation-fill-mode: forwards; } -.debug-viewlet .monaco-inputbox { +.debug-pane .monaco-inputbox { width: 100%; line-height: normal; } -.debug-viewlet .inputBoxContainer { +.debug-pane .inputBoxContainer { box-sizing: border-box; flex-grow: 1; } -.debug-viewlet .debug-watch .monaco-inputbox { +.debug-pane .debug-watch .monaco-inputbox { font-family: var(--monaco-monospace-font); } -.debug-viewlet .monaco-inputbox > .wrapper { +.debug-pane .monaco-inputbox > .wrapper { height: 19px; } -.debug-viewlet .monaco-inputbox > .wrapper > .input { +.debug-pane .monaco-inputbox > .wrapper > .input { padding: 0px; color: initial; } -.debug-viewlet .watch-expression { +.debug-pane .watch-expression { display: flex; } -.debug-viewlet .watch-expression .expression { +.debug-pane .watch-expression .expression { flex : 1; } -.vs-dark .debug-viewlet .monaco-list-row .expression .value.changed { +.vs-dark .debug-pane .monaco-list-row .expression .value.changed { animation-name: debugViewletValueChanged; } /* Breakpoints */ -.debug-viewlet .monaco-list-row { +.debug-pane .monaco-list-row { line-height: 22px; } -.debug-viewlet .debug-breakpoints .monaco-list-row .breakpoint { +.debug-pane .debug-breakpoints .monaco-list-row .breakpoint { padding-left: 2px; } -.debug-viewlet .debug-breakpoints .breakpoint.exception { +.debug-pane .debug-breakpoints .breakpoint.exception { padding-left: 20px; } -.debug-viewlet .debug-breakpoints .breakpoint { +.debug-pane .debug-breakpoints .breakpoint { display: flex; padding-right: 0.8em; flex: 1; align-items: center; } -.debug-viewlet .debug-breakpoints .breakpoint input { +.debug-pane .debug-breakpoints .breakpoint input { flex-shrink: 0; } -.debug-viewlet .debug-breakpoints .breakpoint > .codicon { +.debug-pane .debug-breakpoints .breakpoint > .codicon { width: 19px; height: 19px; min-width: 19px; @@ -374,7 +374,7 @@ justify-content: center; } -.debug-viewlet .debug-breakpoints .breakpoint > .file-path { +.debug-pane .debug-breakpoints .breakpoint > .file-path { opacity: 0.7; font-size: 0.9em; margin-left: 0.8em; @@ -383,7 +383,7 @@ overflow: hidden; } -.debug-viewlet .debug-breakpoints .breakpoint .name { +.debug-pane .debug-breakpoints .breakpoint .name { overflow: hidden; text-overflow: ellipsis } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 278ea98378a..8e9dd8dc2b0 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -12,12 +12,12 @@ import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IViewMod import { Variable, Scope } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction, Action } from 'vs/base/common/actions'; import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -39,7 +39,7 @@ let forgetScopes = true; export const variableSetEmitter = new Emitter(); -export class VariablesView extends ViewPane { +export class VariablesView extends BaseDebugViewPane { private onFocusStackFrameScheduler: RunOnceScheduler; private needsRefresh = false; diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 190d2a2b435..df6770a121f 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -16,9 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -38,7 +38,7 @@ const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreVariableSetEmitter = false; let useCachedEvaluation = false; -export class WatchExpressionsView extends ViewPane { +export class WatchExpressionsView extends BaseDebugViewPane { private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh = false; @@ -138,11 +138,6 @@ export class WatchExpressionsView extends ViewPane { this.tree.rerender(e); } })); - // this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { - // if (views.some(v => v.id === this.id)) { - // this.tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); - // } - // })); } layoutBody(height: number, width: number): void { From 440e910ccc62b2ddca1125c05880313b339350ad Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 13 Feb 2020 10:15:20 +0100 Subject: [PATCH 075/192] allow platform common tests use bas/parts/common --- .eslintrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.json b/.eslintrc.json index ba382f622ac..e1dc514e65e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -183,6 +183,7 @@ "sinon", "vs/nls", "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", "**/vs/platform/*/common/**", "**/vs/platform/*/test/common/**" ] From 903dea01d4b9b606e9cedc5c5269c8b521966247 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 13 Feb 2020 18:24:57 +0100 Subject: [PATCH 076/192] Fix #90598 --- .../remote/common/remote.contribution.ts | 36 +++++++++++++++++++ .../electron-browser/remote.contribution.ts | 26 -------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 7debeeb851e..dbd34cdc3d8 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -18,6 +18,8 @@ import { joinPath } from 'vs/base/common/resources'; import { Disposable } from 'vs/base/common/lifecycle'; import { TunnelFactoryContribution } from 'vs/workbench/contrib/remote/common/tunnelFactory'; import { ShowCandidateContribution } from 'vs/workbench/contrib/remote/common/showCandidate'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; export const VIEWLET_ID = 'workbench.view.remote'; @@ -90,3 +92,37 @@ workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContr workbenchContributionsRegistry.registerWorkbenchContribution(RemoteLogOutputChannels, LifecyclePhase.Restored); workbenchContributionsRegistry.registerWorkbenchContribution(TunnelFactoryContribution, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(ShowCandidateContribution, LifecyclePhase.Ready); + +const extensionKindSchema: IJSONSchema = { + type: 'string', + enum: [ + 'ui', + 'workspace' + ], + enumDescriptions: [ + localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), + localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + ], +}; + +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + id: 'remote', + title: localize('remote', "Remote"), + type: 'object', + properties: { + 'remote.extensionKind': { + type: 'object', + markdownDescription: localize('remote.extensionKind', "Override the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote. By overriding an extension's default kind using this setting, you specify if that extension should be installed and enabled locally or remotely."), + patternProperties: { + '([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$': { + oneOf: [{ type: 'array', items: extensionKindSchema }, extensionKindSchema], + default: ['ui'], + }, + }, + default: { + 'pub.name': ['ui'] + } + }, + } + }); diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 755d1d14ebc..eb03c3f76d7 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -38,7 +38,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { RemoteConnectionState, Deprecated_RemoteAuthorityContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { IDownloadService } from 'vs/platform/download/common/download'; import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; const WINDOW_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; const CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; @@ -370,37 +369,12 @@ workbenchContributionsRegistry.registerWorkbenchContribution(RemoteWindowActiveI workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteEmptyWorkbenchPresentation, LifecyclePhase.Starting); -const extensionKindSchema: IJSONSchema = { - type: 'string', - enum: [ - 'ui', - 'workspace' - ], - enumDescriptions: [ - nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), - nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") - ], -}; - Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ id: 'remote', title: nls.localize('remote', "Remote"), type: 'object', properties: { - 'remote.extensionKind': { - type: 'object', - markdownDescription: nls.localize('remote.extensionKind', "Override the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote. By overriding an extension's default kind using this setting, you specify if that extension should be installed and enabled locally or remotely."), - patternProperties: { - '([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$': { - oneOf: [{ type: 'array', items: extensionKindSchema }, extensionKindSchema], - default: ['ui'], - }, - }, - default: { - 'pub.name': ['ui'] - } - }, 'remote.downloadExtensionsLocally': { type: 'boolean', markdownDescription: nls.localize('remote.downloadExtensionsLocally', "When enabled extensions are downloaded locally and installed on remote."), From 6a951408473b2027555d195f6fff211b34598803 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 13 Feb 2020 18:38:54 +0100 Subject: [PATCH 077/192] Fix #90557 --- src/vs/platform/userDataSync/common/userDataSyncService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index afd696492d3..adede4eb82d 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -140,10 +140,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return synchroniser.accept(content); } - async hasPreviouslySynced(): Promise { + private async hasPreviouslySynced(): Promise { await this.checkEnablement(); for (const synchroniser of this.synchronisers) { if (await synchroniser.hasPreviouslySynced()) { + return true; } } return false; @@ -153,6 +154,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ await this.checkEnablement(); for (const synchroniser of this.synchronisers) { if (await synchroniser.hasLocalData()) { + return true; } } return false; From f8014d495791145f1fac93cc0283737878272129 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 13 Feb 2020 10:12:43 -0800 Subject: [PATCH 078/192] Update debug icon, make play bigger --- .../ui/codiconLabel/codicon/codicon.css | 2 +- .../ui/codiconLabel/codicon/codicon.ttf | Bin 56736 -> 56736 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index e60151c5aa3..2fa65d71923 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?6caeeccc06315e827f3bff83885456fb") format("truetype"); + src: url("./codicon.ttf?d0510f6ecacbb2788db2b3162273a3d8") format("truetype"); } .codicon[class*='codicon-'] { diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index a62bc6bd7440ca0455aeb2be4ddfec928d4987a3..f712f5cd0531a353cc21917dd937fa12d375c1dc 100644 GIT binary patch delta 226 zcmZ3mn|Z-*<_QiWGletnFfd5BFfgotlaZR3BL1lV!Nia$GCmJxs>bu%d}ZKfe!&0) zx3X1xVDx4d#?7YOhncT1s50m=STeXxK4fN7&n(EuDkiFG%E&HgY{bNAXXnJu9-!kY ztEj4?=_=`|6TrpdWM`-2$mVCRVyDjJ`0x2#J$s##e-tfUEEQdtjQ?HXV9ftpX2hY$ zINvV7PDdzE-&H|KSVhrQCqPcYF2G60$=p-fTFEJ%U%^06UdYZ)+e1ssL)(r~l+p2D WE7uV%(N|WCD`o#p+}v(1xeNelCO^Lb delta 226 zcmZ3mn|Z-*<_QiWx6bu+Ffd5BFfgotlaZR3BL3i*)x?k~GADDNOUCotd}ZKfe!&0) zx3U~|!syK`jGIllPXi@X8T1$|8C)kHGP9{SS2bm17c>$VWMma%lC!gOV&@3dag|k4 zQPFgh^wbXEVs*5$({^I>Ggq-wXL9`ae6F6o&dEQDmM)fx7Z_vzUNTovW<2)q1s~%U zy8wG_;Q)PC1tDP-MOW=WIR(1_CmknqPi1Q*r+7X^13h^mpjjSTS{~YV@^`Hm^JV{W U)o?L-|GThNo`GR=ySd~t03jJdzW@LL From 615c24ca3a921879ac4f72d3eb60d8d86ae55102 Mon Sep 17 00:00:00 2001 From: Bailey Date: Thu, 13 Feb 2020 11:40:20 -0700 Subject: [PATCH 079/192] Add explorerView check to see if element is visible --- src/vs/base/browser/ui/list/listWidget.ts | 10 ++++++++++ src/vs/base/browser/ui/tree/abstractTree.ts | 13 +++++++++++++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ++++ .../contrib/files/browser/views/explorerView.ts | 6 +++++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 026344c5e1b..339cc5af913 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1546,6 +1546,16 @@ export class List implements ISpliceable, IDisposable { return this.getFocus().map(i => this.view.element(i)); } + isElementVisible(index: number) { + const viewTop = this.view.getScrollTop(); + const viewBottom = this.view.renderHeight + viewTop; + + const elementTop = this.view.elementTop(index); + const elementBottom = this.view.elementHeight(index) + elementTop; + + return (elementTop >= viewTop && elementBottom <= viewBottom); + } + reveal(index: number, relativeTop?: number): void { if (index < 0 || index >= this.length) { throw new ListError(this.user, `Invalid index ${index}`); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9e584efcc56..1032a5a723f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1589,6 +1589,19 @@ export abstract class AbstractTree implements IDisposable this.view.open(indexes, browserEvent); } + isElementVisible(location: TRef) { + const index = this.model.getListIndex(location); + + if (index === -1) { + return false; + } + + const parentNode = this.model.getNode(this.model.getParentNodeLocation(location)); + const parentCollapsed = !parentNode.visible || parentNode.collapsed; + + return this.view.isElementVisible(index) && !parentCollapsed; + } + reveal(location: TRef, relativeTop?: number): void { this.model.expandTo(location); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index bbdc7bc6054..7edd15a2f62 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -662,6 +662,10 @@ export class AsyncDataTree implements IDisposable this.tree.open(nodes, browserEvent); } + isElementVisible(element: T) { + return this.tree.isElementVisible(this.getDataNode(element)); + } + reveal(element: T, relativeTop?: number): void { this.tree.reveal(this.getDataNode(element), relativeTop); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index f7537747649..cce6ba126a0 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -675,7 +675,11 @@ export class ExplorerView extends ViewPane { if (item.isDisposed) { return this.onSelectResource(resource, reveal, retry + 1); } - this.tree.reveal(item, 0.5); + + // Don't scroll to the item if it's already visible + if (!this.tree.isElementVisible(item)) { + this.tree.reveal(item, 0.5); + } } this.tree.setFocus([item]); From 28906d62f5edc2a5d02c12b3fc6cd862fae750fb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 21:04:20 +0100 Subject: [PATCH 080/192] Fix compilation error --- .../editor/test/common/viewLayout/editorLayoutProvider.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index cfb8396fe37..7cb925796e3 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -50,6 +50,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { maxColumn: input.minimapMaxColumn, showSlider: 'mouseover', scale: 1, + entireDocument: false }; options._write(EditorOption.minimap, minimapOptions); const scrollbarOptions: InternalEditorScrollbarOptions = { From e3dc04a266cf63333db5cf5af1b6ca0da680ca40 Mon Sep 17 00:00:00 2001 From: Bailey Date: Thu, 13 Feb 2020 13:24:22 -0700 Subject: [PATCH 081/192] Add slider color options --- .../editor/browser/viewParts/minimap/minimap.ts | 17 +++++++---------- src/vs/platform/theme/common/colorRegistry.ts | 4 ++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 5c8b974989d..09c507ee879 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -25,7 +25,7 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry'; +import { minimapSelection, scrollbarShadow, minimapBackground, minimapSliderBackground, minimapSliderHoverBackground, minimapSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import { Selection } from 'vs/editor/common/core/selection'; @@ -1158,20 +1158,17 @@ registerThemingParticipant((theme, collector) => { if (minimapBackgroundValue) { collector.addRule(`.monaco-editor .minimap > canvas { opacity: ${minimapBackgroundValue.rgba.a}; will-change: opacity; }`); } - const sliderBackground = theme.getColor(scrollbarSliderBackground); + const sliderBackground = theme.getColor(minimapSliderBackground); if (sliderBackground) { - const halfSliderBackground = sliderBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider, .monaco-editor .minimap-slider .minimap-slider-horizontal { background: ${halfSliderBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider .minimap-slider-horizontal { background: ${sliderBackground}; }`); } - const sliderHoverBackground = theme.getColor(scrollbarSliderHoverBackground); + const sliderHoverBackground = theme.getColor(minimapSliderHoverBackground); if (sliderHoverBackground) { - const halfSliderHoverBackground = sliderHoverBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider:hover, .monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: ${halfSliderHoverBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: ${sliderHoverBackground}; }`); } - const sliderActiveBackground = theme.getColor(scrollbarSliderActiveBackground); + const sliderActiveBackground = theme.getColor(minimapSliderActiveBackground); if (sliderActiveBackground) { - const halfSliderActiveBackground = sliderActiveBackground.transparent(0.5); - collector.addRule(`.monaco-editor .minimap-slider.active, .monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: ${halfSliderActiveBackground}; }`); + collector.addRule(`.monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: ${sliderActiveBackground}; }`); } const shadow = theme.getColor(scrollbarShadow); if (shadow) { diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 2b47b768fb7..3fde36a7b6f 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -426,6 +426,10 @@ export const minimapError = registerColor('minimap.errorHighlight', { dark: new export const minimapWarning = registerColor('minimap.warningHighlight', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Minimap marker color for warnings.')); export const minimapBackground = registerColor('minimap.background', { dark: null, light: null, hc: null }, nls.localize('minimapBackground', "Minimap background color.")); +export const minimapSliderBackground = registerColor('minimapSlider.background', { light: transparent(scrollbarSliderBackground, 0.5), dark: transparent(scrollbarSliderBackground, 0.5), hc: transparent(scrollbarSliderBackground, 0.5) }, nls.localize('minimapSliderBackground', "Minimap slider background color.")); +export const minimapSliderHoverBackground = registerColor('minimapSlider.hoverBackground', { light: transparent(scrollbarSliderHoverBackground, 0.5), dark: transparent(scrollbarSliderHoverBackground, 0.5), hc: transparent(scrollbarSliderHoverBackground, 0.5) }, nls.localize('minimapSliderHoverBackground', "Minimap slider background color when hovering.")); +export const minimapSliderActiveBackground = registerColor('minimapSlider.activeBackground', { light: transparent(scrollbarSliderActiveBackground, 0.5), dark: transparent(scrollbarSliderActiveBackground, 0.5), hc: transparent(scrollbarSliderActiveBackground, 0.5) }, nls.localize('minimapSliderActiveBackground', "Minimap slider background color when clicked on.")); + export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon.")); export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon.")); export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon.")); From f5013e5a2a3da1a617ce00a6f63c11e70a48e781 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 22:24:57 +0100 Subject: [PATCH 082/192] Extract sampling computation to a separate class --- .../browser/viewParts/minimap/minimap.ts | 369 ++++++++++-------- 1 file changed, 211 insertions(+), 158 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 4cf1bdc1571..04218f80d2c 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -24,7 +24,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ViewModelDecoration, IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; @@ -479,7 +479,7 @@ export interface IMinimapModel { setScrollTop(scrollTop: number): void; } -export interface IMinimapRenderingContext { +interface IMinimapRenderingContext { readonly samplingRatio: number; readonly viewportContainsWhitespaceGaps: boolean; @@ -496,6 +496,175 @@ export interface IMinimapRenderingContext { readonly viewportHeight: number; } +interface SamplingStateLinesDeletedEvent { + type: 'deleted'; + deleteFromLineNumber: number; + deleteToLineNumber: number; +} + +interface SamplingStateLinesInsertedEvent { + type: 'inserted'; + insertFromLineNumber: number; + insertToLineNumber: number; +} + +interface SamplingStateFlushEvent { + type: 'flush'; +} + +type SamplingStateEvent = SamplingStateLinesInsertedEvent | SamplingStateLinesDeletedEvent | SamplingStateFlushEvent; + +class MinimapSamplingState { + + public static compute(configuration: IConfiguration, model: IViewModel, oldMinimapLines: number[] | null): [MinimapSamplingState, SamplingStateEvent[]] { + const options = configuration.options; + const minimapOpts = options.get(EditorOption.minimap); + if (minimapOpts.enabled === false || !minimapOpts.entireDocument) { + return [new MinimapSamplingState(false, 1, []), []]; + } + + const layoutInfo = options.get(EditorOption.layoutInfo); + const pixelRatio = options.get(EditorOption.pixelRatio); + const lineHeight = options.get(EditorOption.lineHeight); + const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); + const modelLineCount = model.getLineCount(); + + const extraLinesBeyondLastLine = scrollBeyondLastLine ? (layoutInfo.height / lineHeight - 1) : 0; + const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); + const minimapLineCount = Math.floor(modelLineCount / desiredRatio); + const ratio = modelLineCount / minimapLineCount; + + if (!oldMinimapLines || oldMinimapLines.length === 0) { + let result: number[] = []; + result[0] = 1; + if (minimapLineCount > 1) { + const halfRatio = ratio / 2; + for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) { + result[i] = Math.round(i * ratio + halfRatio); + } + result[minimapLineCount - 1] = modelLineCount; + } + return [new MinimapSamplingState(true, ratio, result), []]; + } + + const halfRatio = ratio / 2; + const oldLength = oldMinimapLines.length; + let result: number[] = []; + let oldIndex = 0; + let oldDeltaLineCount = 0; + let minModelLineNumber = 1; + const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data + let events: SamplingStateEvent[] = []; + let currentDeleteStart = 0, currentDeleteEnd = 0; + let currentInsertStart = 0, currentInsertEnd = 0; + for (let i = 0; i < minimapLineCount; i++) { + const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio)); + const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio)); + + console.log(`LINE AT ${i} SHOULD FIT BTW ${fromModelLineNumber} to ${toModelLineNumber}`); + + while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) { + if (events.length < MAX_EVENT_COUNT) { + if (currentInsertEnd !== 0) { + // generate previous insert event + events.push({ type: 'inserted', insertFromLineNumber: currentInsertStart, insertToLineNumber: currentInsertEnd }); + oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); + currentInsertStart = 0; + currentInsertEnd = 0; + } + + if (currentDeleteStart === 0) { + currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; + currentDeleteEnd = currentDeleteStart; + } else { + currentDeleteEnd++; + } + } + console.log(` => deleting ${oldMinimapLines[oldIndex]}`); + oldIndex++; + } + + let selectedModelLineNumber: number; + if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) { + // reuse the old sampled line + selectedModelLineNumber = oldMinimapLines[oldIndex]; + console.log(` => keeping ${oldMinimapLines[oldIndex]}`); + oldIndex++; + } else { + if (i === 0) { + selectedModelLineNumber = 1; + } else if (i + 1 === minimapLineCount) { + selectedModelLineNumber = modelLineCount; + } else { + selectedModelLineNumber = Math.round(i * ratio + halfRatio); + } + console.log(` => inserting ${selectedModelLineNumber}`); + if (events.length < MAX_EVENT_COUNT) { + if (currentDeleteEnd !== 0) { + // generate previous delete event + events.push({ type: 'deleted', deleteFromLineNumber: currentDeleteStart, deleteToLineNumber: currentDeleteEnd }); + oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); + currentDeleteStart = 0; + currentDeleteEnd = 0; + } + + if (currentInsertStart === 0) { + currentInsertStart = oldIndex + 1 + oldDeltaLineCount; + currentInsertEnd = currentInsertStart; + } else { + currentInsertEnd++; + } + } + } + + result[i] = selectedModelLineNumber; + if (selectedModelLineNumber === modelLineCount) { + minModelLineNumber = selectedModelLineNumber; + } else { + minModelLineNumber = selectedModelLineNumber + 1; + } + } + + if (events.length < MAX_EVENT_COUNT) { + if (currentInsertEnd !== 0) { + // generate previous insert event + events.push({ type: 'inserted', insertFromLineNumber: currentInsertStart, insertToLineNumber: currentInsertEnd }); + oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); + currentInsertStart = 0; + currentInsertEnd = 0; + } + while (oldIndex < oldLength) { + if (currentDeleteStart === 0) { + currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; + currentDeleteEnd = currentDeleteStart; + } else { + currentDeleteEnd++; + } + oldIndex++; + } + if (currentDeleteEnd !== 0) { + // generate previous delete event + events.push({ type: 'deleted', deleteFromLineNumber: currentDeleteStart, deleteToLineNumber: currentDeleteEnd }); + oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); + currentDeleteStart = 0; + currentDeleteEnd = 0; + } + } else { + // too many events, just give up + events = [{ type: 'flush' }]; + } + + return [new MinimapSamplingState(true, ratio, result), events]; + } + + constructor( + public readonly isSampling: boolean, + public readonly samplingRatio: number, + public readonly minimapLines: number[] + ) { + } +} + export class Minimap extends ViewPart implements IMinimapModel { private _selections: Selection[]; @@ -517,11 +686,11 @@ export class Minimap extends ViewPart implements IMinimapModel { this._selections = []; this._minimapSelections = []; - this._isSampling = false; - this._samplingRatio = 1; - this._minimapLines = []; + const [samplingState,] = MinimapSamplingState.compute(this._context.configuration, this._context.model, null); + this._isSampling = samplingState.isSampling; + this._samplingRatio = samplingState.samplingRatio; + this._minimapLines = samplingState.minimapLines; this._shouldCheckSampling = false; - this._recreateLineSampling(true); this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); @@ -552,7 +721,7 @@ export class Minimap extends ViewPart implements IMinimapModel { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { let shouldRender = false; - shouldRender = this._recreateLineSampling(false) || shouldRender; + shouldRender = this._recreateLineSampling() || shouldRender; shouldRender = this._onOptionsMaybeChanged() || shouldRender; return shouldRender; } @@ -662,7 +831,7 @@ export class Minimap extends ViewPart implements IMinimapModel { public prepareRender(ctx: RenderingContext): void { if (this._shouldCheckSampling) { this._shouldCheckSampling = false; - this._recreateLineSampling(false); + this._recreateLineSampling(); } } @@ -696,165 +865,49 @@ export class Minimap extends ViewPart implements IMinimapModel { //#region IMinimapModel - private _createLineSampling(minimapLineCount: number, modelLineCount: number, ratio: number): number[] { - let result: number[] = []; - result[0] = 1; - if (minimapLineCount > 1) { - const halfRatio = ratio / 2; - for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) { - result[i] = Math.round(i * ratio + halfRatio); - } - result[minimapLineCount - 1] = modelLineCount; - } - return result; - } - - private _recreateLineSampling(ctor: boolean): boolean { + private _recreateLineSampling(): boolean { const wasSampling = this._isSampling; - const options = this._context.configuration.options; - const minimapOpts = options.get(EditorOption.minimap); - if (!minimapOpts.entireDocument) { - // no sampling! - this._isSampling = false; - this._minimapLines = []; - this._samplingRatio = 1; - if (!ctor && wasSampling) { - // was sampling => sampling stopped - return this._onOptionsMaybeChanged(); - } - return false; - } + const [samplingState, events] = MinimapSamplingState.compute(this._context.configuration, this._context.model, this._minimapLines); + this._isSampling = samplingState.isSampling; + this._samplingRatio = samplingState.samplingRatio; + this._minimapLines = samplingState.minimapLines; - const layoutInfo = options.get(EditorOption.layoutInfo); - const pixelRatio = options.get(EditorOption.pixelRatio); - const lineHeight = options.get(EditorOption.lineHeight); - const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); - const modelLineCount = this._context.model.getLineCount(); - - const extraLinesBeyondLastLine = scrollBeyondLastLine ? (layoutInfo.height / lineHeight - 1) : 0; - const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); - const minimapLineCount = Math.floor(modelLineCount / desiredRatio); - const ratio = modelLineCount / minimapLineCount; - - if (!this._minimapLines || this._minimapLines.length === 0) { - this._isSampling = true; - this._minimapLines = this._createLineSampling(minimapLineCount, modelLineCount, ratio); - this._samplingRatio = ratio; - if (!ctor && !wasSampling) { - // was not sampling => now is sampling - return this._onOptionsMaybeChanged(); - } - return false; - } - - const halfRatio = ratio / 2; - const oldMinimapLines = this._minimapLines; - const oldLength = oldMinimapLines.length; - let result: number[] = []; - let oldIndex = 0; - let oldDeltaLineCount = 0; - let minModelLineNumber = 1; - const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data - let eventCount = 0; - let currentDeleteStart = 0, currentDeleteEnd = 0; - let currentInsertStart = 0, currentInsertEnd = 0; - for (let i = 0; i < minimapLineCount; i++) { - const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio)); - const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio)); - - while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) { - if (eventCount < MAX_EVENT_COUNT) { - if (currentInsertEnd !== 0) { - // must deliver previous insert event - this._actual.onLinesInserted(currentInsertStart, currentInsertEnd); - oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); - currentInsertStart = 0; - currentInsertEnd = 0; - eventCount++; - } - - if (currentDeleteStart === 0) { - currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; - currentDeleteEnd = currentDeleteStart; - } else { - currentDeleteEnd++; + let optionsMightHaveChanged: boolean; + if (wasSampling) { + if (this._isSampling) { + console.log(events); + // was sampling, is sampling + for (const event of events) { + switch(event.type) { + case 'deleted': + this._actual.onLinesDeleted(event.deleteFromLineNumber, event.deleteToLineNumber); + break; + case 'inserted': + this._actual.onLinesInserted(event.insertFromLineNumber, event.insertToLineNumber); + break; + case 'flush': + this._actual.onFlushed(); + break; } } - oldIndex++; - } - - let selectedModelLineNumber: number; - if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) { - // reuse the old sampled line - selectedModelLineNumber = oldMinimapLines[oldIndex]; - oldIndex++; + optionsMightHaveChanged = false; } else { - if (i === 0) { - selectedModelLineNumber = 1; - } else if (i + 1 === minimapLineCount) { - selectedModelLineNumber = modelLineCount; - } else { - selectedModelLineNumber = Math.round(i * ratio + halfRatio); - } - if (eventCount < MAX_EVENT_COUNT) { - if (currentDeleteEnd !== 0) { - // must deliver previous delete event - this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd); - oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); - currentDeleteStart = 0; - currentDeleteEnd = 0; - eventCount++; - } - - if (currentInsertStart === 0) { - currentInsertStart = oldIndex + 1 + oldDeltaLineCount; - currentInsertEnd = currentInsertStart; - } else { - currentInsertEnd++; - } - } - } - - result[i] = selectedModelLineNumber; - if (selectedModelLineNumber === modelLineCount) { - minModelLineNumber = selectedModelLineNumber; - } else { - minModelLineNumber = selectedModelLineNumber + 1; - } - } - - if (eventCount < MAX_EVENT_COUNT) { - if (currentInsertEnd !== 0) { - // must deliver previous insert event - this._actual.onLinesInserted(currentInsertStart, currentInsertEnd); - oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); - currentInsertStart = 0; - currentInsertEnd = 0; - } - while (oldIndex < oldLength) { - if (currentDeleteStart === 0) { - currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; - currentDeleteEnd = currentDeleteStart; - } else { - currentDeleteEnd++; - } - oldIndex++; - } - if (currentDeleteEnd !== 0) { - // must deliver previous delete event - this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd); - oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); - currentDeleteStart = 0; - currentDeleteEnd = 0; + // was not sampling, is sampling + optionsMightHaveChanged = true; } } else { - // too many events, just give up - this._actual.onFlushed(); + if (this._isSampling) { + // was not sampling, is sampling + optionsMightHaveChanged = true; + } else { + // was not sampling, is not sampling + optionsMightHaveChanged = false; + } } - this._isSampling = true; - this._samplingRatio = ratio; - this._minimapLines = result; + if (optionsMightHaveChanged) { + return this._onOptionsMaybeChanged(); + } return false; } From f82223f3186153336642cdc99f131134fecd0d75 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 13 Feb 2020 22:29:18 +0100 Subject: [PATCH 083/192] Fix event generation --- .../browser/viewParts/minimap/minimap.ts | 73 ++++++------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 04218f80d2c..a0acf222ab5 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -498,12 +498,14 @@ interface IMinimapRenderingContext { interface SamplingStateLinesDeletedEvent { type: 'deleted'; + _oldIndex: number; deleteFromLineNumber: number; deleteToLineNumber: number; } interface SamplingStateLinesInsertedEvent { type: 'inserted'; + _i: number; insertFromLineNumber: number; insertToLineNumber: number; } @@ -555,32 +557,22 @@ class MinimapSamplingState { let minModelLineNumber = 1; const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data let events: SamplingStateEvent[] = []; - let currentDeleteStart = 0, currentDeleteEnd = 0; - let currentInsertStart = 0, currentInsertEnd = 0; + let lastEvent: SamplingStateEvent | null = null; for (let i = 0; i < minimapLineCount; i++) { const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio)); const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio)); - console.log(`LINE AT ${i} SHOULD FIT BTW ${fromModelLineNumber} to ${toModelLineNumber}`); - while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) { if (events.length < MAX_EVENT_COUNT) { - if (currentInsertEnd !== 0) { - // generate previous insert event - events.push({ type: 'inserted', insertFromLineNumber: currentInsertStart, insertToLineNumber: currentInsertEnd }); - oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); - currentInsertStart = 0; - currentInsertEnd = 0; - } - - if (currentDeleteStart === 0) { - currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; - currentDeleteEnd = currentDeleteStart; + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'deleted' && lastEvent._oldIndex === oldIndex - 1) { + lastEvent.deleteToLineNumber++; } else { - currentDeleteEnd++; + lastEvent = { type: 'deleted', _oldIndex: oldIndex, deleteFromLineNumber: oldMinimapLineNumber, deleteToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); } + oldDeltaLineCount--; } - console.log(` => deleting ${oldMinimapLines[oldIndex]}`); oldIndex++; } @@ -588,7 +580,6 @@ class MinimapSamplingState { if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) { // reuse the old sampled line selectedModelLineNumber = oldMinimapLines[oldIndex]; - console.log(` => keeping ${oldMinimapLines[oldIndex]}`); oldIndex++; } else { if (i === 0) { @@ -598,22 +589,15 @@ class MinimapSamplingState { } else { selectedModelLineNumber = Math.round(i * ratio + halfRatio); } - console.log(` => inserting ${selectedModelLineNumber}`); if (events.length < MAX_EVENT_COUNT) { - if (currentDeleteEnd !== 0) { - // generate previous delete event - events.push({ type: 'deleted', deleteFromLineNumber: currentDeleteStart, deleteToLineNumber: currentDeleteEnd }); - oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); - currentDeleteStart = 0; - currentDeleteEnd = 0; - } - - if (currentInsertStart === 0) { - currentInsertStart = oldIndex + 1 + oldDeltaLineCount; - currentInsertEnd = currentInsertStart; + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'inserted' && lastEvent._i === i - 1) { + lastEvent.insertToLineNumber++; } else { - currentInsertEnd++; + lastEvent = { type: 'inserted', _i: i, insertFromLineNumber: oldMinimapLineNumber, insertToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); } + oldDeltaLineCount++; } } @@ -626,29 +610,17 @@ class MinimapSamplingState { } if (events.length < MAX_EVENT_COUNT) { - if (currentInsertEnd !== 0) { - // generate previous insert event - events.push({ type: 'inserted', insertFromLineNumber: currentInsertStart, insertToLineNumber: currentInsertEnd }); - oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1); - currentInsertStart = 0; - currentInsertEnd = 0; - } while (oldIndex < oldLength) { - if (currentDeleteStart === 0) { - currentDeleteStart = oldIndex + 1 + oldDeltaLineCount; - currentDeleteEnd = currentDeleteStart; + const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount; + if (lastEvent && lastEvent.type === 'deleted' && lastEvent._oldIndex === oldIndex - 1) { + lastEvent.deleteToLineNumber++; } else { - currentDeleteEnd++; + lastEvent = { type: 'deleted', _oldIndex: oldIndex, deleteFromLineNumber: oldMinimapLineNumber, deleteToLineNumber: oldMinimapLineNumber }; + events.push(lastEvent); } + oldDeltaLineCount--; oldIndex++; } - if (currentDeleteEnd !== 0) { - // generate previous delete event - events.push({ type: 'deleted', deleteFromLineNumber: currentDeleteStart, deleteToLineNumber: currentDeleteEnd }); - oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1); - currentDeleteStart = 0; - currentDeleteEnd = 0; - } } else { // too many events, just give up events = [{ type: 'flush' }]; @@ -875,10 +847,9 @@ export class Minimap extends ViewPart implements IMinimapModel { let optionsMightHaveChanged: boolean; if (wasSampling) { if (this._isSampling) { - console.log(events); // was sampling, is sampling for (const event of events) { - switch(event.type) { + switch (event.type) { case 'deleted': this._actual.onLinesDeleted(event.deleteFromLineNumber, event.deleteToLineNumber); break; From 799be872a07bc1abfea69dd9f4ba926eb4ac6837 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 00:37:13 +0100 Subject: [PATCH 084/192] Cover case when rendering entire document and there are few lines --- .../browser/viewParts/minimap/minimap.ts | 83 +++++++++++++------ 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index a0acf222ab5..3e0a325df4c 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -48,6 +48,8 @@ class MinimapOptions { public readonly renderMinimap: RenderMinimap; + public readonly entireDocument: boolean; + public readonly scrollBeyondLastLine: boolean; public readonly showSlider: 'always' | 'mouseover'; @@ -58,10 +60,6 @@ class MinimapOptions { public readonly lineHeight: number; - public readonly fontScale: number; - - public readonly charRenderer: () => MinimapCharRenderer; - /** * container dom node left position (in CSS px) */ @@ -95,22 +93,23 @@ class MinimapOptions { public readonly backgroundColor: RGBA8; + public readonly fontScale: number; public readonly minimapLineHeight: number; public readonly minimapCharWidth: number; + public readonly charRenderer: () => MinimapCharRenderer; - constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker, isSampling: boolean) { + constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker, modelLineCount: number, isSampling: boolean) { const options = configuration.options; const pixelRatio = options.get(EditorOption.pixelRatio); const layoutInfo = options.get(EditorOption.layoutInfo); const fontInfo = options.get(EditorOption.fontInfo); + const minimapOpts = options.get(EditorOption.minimap); this.isSampling = isSampling; this.renderMinimap = layoutInfo.renderMinimap | 0; + this.entireDocument = minimapOpts.entireDocument; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); - const minimapOpts = options.get(EditorOption.minimap); this.showSlider = minimapOpts.showSlider; - this.fontScale = (isSampling ? 1 : Math.round(minimapOpts.scale * pixelRatio)); - this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); this.pixelRatio = pixelRatio; this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this.lineHeight = options.get(EditorOption.lineHeight); @@ -126,9 +125,32 @@ class MinimapOptions { this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker); - const baseCharHeight = (isSampling ? 1 : this.renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); - this.minimapLineHeight = baseCharHeight * this.fontScale; + const baseCharHeight = (this.renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); + + let fontScale: number; + let minimapLineHeight: number; + if (minimapOpts.entireDocument) { + if (isSampling) { + fontScale = 1; + minimapLineHeight = 1; + } else { + const extraLinesBeyondLastLine = this.scrollBeyondLastLine ? (layoutInfo.height / this.lineHeight - 1) : 0; + const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); + minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); + // fontScale = Math.round(minimapOpts.scale * pixelRatio); + fontScale = Math.max(1, Math.floor(minimapLineHeight / baseCharHeight / 2)); + const actualMinimapHeight = (modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight; + this.canvasInnerHeight = Math.ceil(actualMinimapHeight); + } + } else { + fontScale = Math.round(minimapOpts.scale * pixelRatio); + minimapLineHeight = baseCharHeight * fontScale; + } + + this.fontScale = fontScale; + this.minimapLineHeight = minimapLineHeight; this.minimapCharWidth = Constants.BASE_CHAR_WIDTH * this.fontScale; + this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); } private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 { @@ -142,6 +164,7 @@ class MinimapOptions { public equals(other: MinimapOptions): boolean { return (this.isSampling === other.isSampling && this.renderMinimap === other.renderMinimap + && this.entireDocument === other.entireDocument && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider && this.pixelRatio === other.pixelRatio @@ -224,12 +247,12 @@ class MinimapLayout { public static create( options: MinimapOptions, - samplingRatio: number, viewportStartLineNumber: number, viewportEndLineNumber: number, viewportHeight: number, viewportContainsWhitespaceGaps: boolean, lineCount: number, + realLineCount: number, scrollTop: number, scrollHeight: number, previousLayout: MinimapLayout | null @@ -239,9 +262,12 @@ class MinimapLayout { const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); const lineHeight = options.lineHeight; - if (options.isSampling) { - const expectedViewportLineCount = viewportHeight / lineHeight; - const sliderHeight = Math.max(1, Math.floor(expectedViewportLineCount * minimapLineHeight / pixelRatio / samplingRatio)); + if (options.entireDocument) { + const logicalScrollHeight = ( + realLineCount * options.lineHeight + + (options.scrollBeyondLastLine ? viewportHeight - options.lineHeight : 0) + ); + const sliderHeight = Math.max(1, Math.floor(viewportHeight * viewportHeight / logicalScrollHeight)); const maxMinimapSliderTop = Math.max(0, options.minimapHeight - sliderHeight); // The slider can move from 0 to `maxMinimapSliderTop` // in the same way `scrollTop` can move from 0 to `scrollHeight` - `viewportHeight`. @@ -470,6 +496,7 @@ export interface IMinimapModel { readonly options: MinimapOptions; getLineCount(): number; + getRealLineCount(): number; getLineContent(lineNumber: number): string; getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[]; getSelections(): Selection[]; @@ -480,7 +507,6 @@ export interface IMinimapModel { } interface IMinimapRenderingContext { - readonly samplingRatio: number; readonly viewportContainsWhitespaceGaps: boolean; readonly scrollWidth: number; @@ -536,6 +562,10 @@ class MinimapSamplingState { const minimapLineCount = Math.floor(modelLineCount / desiredRatio); const ratio = modelLineCount / minimapLineCount; + if (ratio <= 1) { + return [new MinimapSamplingState(false, 1, []), []]; + } + if (!oldMinimapLines || oldMinimapLines.length === 0) { let result: number[] = []; result[0] = 1; @@ -602,11 +632,7 @@ class MinimapSamplingState { } result[i] = selectedModelLineNumber; - if (selectedModelLineNumber === modelLineCount) { - minModelLineNumber = selectedModelLineNumber; - } else { - minModelLineNumber = selectedModelLineNumber + 1; - } + minModelLineNumber = selectedModelLineNumber; } if (events.length < MAX_EVENT_COUNT) { @@ -665,7 +691,7 @@ export class Minimap extends ViewPart implements IMinimapModel { this._shouldCheckSampling = false; this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); - this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); + this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._context.model.getLineCount(), this._isSampling); this._actual = new InnerMinimap(context.theme, this); } @@ -680,7 +706,7 @@ export class Minimap extends ViewPart implements IMinimapModel { } private _onOptionsMaybeChanged(): boolean { - const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling); + const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._context.model.getLineCount(), this._isSampling); if (this.options.equals(opts)) { return false; } @@ -745,6 +771,9 @@ export class Minimap extends ViewPart implements IMinimapModel { this._shouldCheckSampling = true; return true; } else { + if (this.options.entireDocument && this._onOptionsMaybeChanged()) { + return true; + } return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber); } } @@ -761,6 +790,9 @@ export class Minimap extends ViewPart implements IMinimapModel { this._shouldCheckSampling = true; return true; } else { + if (this.options.entireDocument && this._onOptionsMaybeChanged()) { + return true; + } return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber); } } @@ -817,7 +849,6 @@ export class Minimap extends ViewPart implements IMinimapModel { } const minimapCtx: IMinimapRenderingContext = { - samplingRatio: this._samplingRatio, viewportContainsWhitespaceGaps: (ctx.viewportData.whitespaceViewportData.length > 0), scrollWidth: ctx.scrollWidth, @@ -940,6 +971,10 @@ export class Minimap extends ViewPart implements IMinimapModel { return this._context.model.getLineCount(); } + public getRealLineCount(): number { + return this._context.model.getLineCount(); + } + public getLineContent(lineNumber: number): string { if (this._isSampling) { return this._context.model.getLineContent(this._minimapLines[lineNumber - 1]); @@ -1317,12 +1352,12 @@ class InnerMinimap extends Disposable { const layout = MinimapLayout.create( this._model.options, - renderingCtx.samplingRatio, renderingCtx.viewportStartLineNumber, renderingCtx.viewportEndLineNumber, renderingCtx.viewportHeight, renderingCtx.viewportContainsWhitespaceGaps, this._model.getLineCount(), + this._model.getRealLineCount(), renderingCtx.scrollTop, renderingCtx.scrollHeight, this._lastRenderData ? this._lastRenderData.renderedLayout : null From e2bc02523e999d9273405d9592517a00df5d99fe Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Thu, 13 Feb 2020 16:51:22 -0800 Subject: [PATCH 085/192] Properly await setting the active settings sync account, fixes #90636 --- .../contrib/userDataSync/browser/userDataSync.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 5e756cd3704..6af68ea3feb 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -147,13 +147,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } if (sessions.length === 0) { - this.setActiveAccount(undefined); + await this.setActiveAccount(undefined); return; } if (sessions.length === 1) { this.logAuthenticatedEvent(sessions[0]); - this.setActiveAccount(sessions[0]); + await this.setActiveAccount(sessions[0]); return; } @@ -167,7 +167,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (selectedAccount) { const selected = sessions.filter(account => selectedAccount.id === account.id)[0]; this.logAuthenticatedEvent(selected); - this.setActiveAccount(selected); + await this.setActiveAccount(selected); } } @@ -565,7 +565,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async signIn(): Promise { try { - this.setActiveAccount(await this.authenticationService.login(this.userDataSyncStore!.authenticationProviderId, ['https://management.core.windows.net/.default', 'offline_access'])); + await this.setActiveAccount(await this.authenticationService.login(this.userDataSyncStore!.authenticationProviderId, ['https://management.core.windows.net/.default', 'offline_access'])); } catch (e) { this.notificationService.error(e); throw e; @@ -575,7 +575,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private async signOut(): Promise { if (this.activeAccount) { await this.authenticationService.logout(this.userDataSyncStore!.authenticationProviderId, this.activeAccount.id); - this.setActiveAccount(undefined); + await this.setActiveAccount(undefined); } } From 4c9161a3f125f5a788aafa73a4ecfcb27dcfc319 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 13 Feb 2020 17:22:19 -0800 Subject: [PATCH 086/192] Remove unused action --- .../browser/searchEditor.contribution.ts | 6 +----- .../browser/searchEditorActions.ts | 19 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 653cf67456b..1884a02ec44 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -26,7 +26,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE import * as SearchConstants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; -import { OpenResultsInEditorAction, OpenSearchEditorAction, ReRunSearchEditorSearchAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; +import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; @@ -164,10 +164,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const registry = Registry.as(ActionExtensions.WorkbenchActions); const category = localize('search', "Search Editor"); -registry.registerWorkbenchAction( - SyncActionDescriptor.create(ReRunSearchEditorSearchAction, ReRunSearchEditorSearchAction.ID, ReRunSearchEditorSearchAction.LABEL), - 'Search Editor: Rerun search', category, ContextKeyExpr.and(SearchEditorConstants.InSearchEditor)); - registry.registerWorkbenchAction( SyncActionDescriptor.create(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL, { mac: { primary: KeyMod.CtrlCmd | KeyCode.Enter } }, diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index cabfbd9a6ca..e07b696c2cf 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -112,25 +112,6 @@ export class OpenResultsInEditorAction extends Action { } } - -export class ReRunSearchEditorSearchAction extends Action { - - static readonly ID = 'searchEditor.rerunSerach'; - static readonly LABEL = localize('search.rerunSearch', "Rerun Search in Editor"); - - constructor(id: string, label: string, - @IEditorService private readonly editorService: IEditorService) { - super(id, label); - } - - async run() { - const input = this.editorService.activeEditor; - if (input instanceof SearchEditorInput) { - await (this.editorService.activeControl as SearchEditor).runSearch(false, true); - } - } -} - const openNewSearchEditor = async (accessor: ServicesAccessor) => { const editorService = accessor.get(IEditorService); From b62b2280ea0ea828b898df94c43a455194639218 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 14 Feb 2020 07:24:17 +0100 Subject: [PATCH 087/192] fixes #90620 --- extensions/git/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index f3d75519653..f51154cebf6 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -127,7 +127,7 @@ "config.showProgress": "Controls whether git actions should show progress.", "config.rebaseWhenSync": "Force git to use rebase when running the sync command.", "config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.", - "config.fetchOnPull": "Fetch all branches when pulling or just the current one.", + "config.fetchOnPull": "When enabled, fetch all branches when pulling. Otherwise, fetch just the current one.", "config.pullTags": "Fetch all tags when pulling.", "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", From 7a8596fd4b6039c7536f351953a2aede526f8429 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Feb 2020 09:26:14 +0100 Subject: [PATCH 088/192] text files - rewrite save to use async-await :crossed_fingers: --- .../textfile/common/textFileEditorModel.ts | 112 ++++++++++-------- .../common/textFileEditorModelManager.ts | 8 +- .../test/browser/textFileEditorModel.test.ts | 3 - 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index fb425e6ac9f..15c92a28b1b 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -79,7 +79,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private lastResolvedFileStat: IFileStatWithMetadata | undefined; private readonly saveSequentializer = new TaskSequentializer(); - private lastSaveAttemptTime = 0; private dirty = false; private inConflictMode = false; @@ -553,16 +552,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return false; // if model is in save conflict or error, do not save unless save reason is explicit } + // Actually do save and log this.logService.trace('[text file model] save() - enter', this.resource.toString()); - await this.doSave(options); - this.logService.trace('[text file model] save() - exit', this.resource.toString()); return true; } - private doSave(options: ITextFileSaveOptions): Promise { + private async doSave(options: ITextFileSaveOptions): Promise { if (typeof options.reason !== 'number') { options.reason = SaveReason.EXPLICIT; } @@ -587,7 +585,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (!options.force && !this.dirty) { this.logService.trace(`[text file model] doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource.toString()); - return Promise.resolve(); + return; } // Return if currently saving by storing this save request as the next save that should happen. @@ -618,26 +616,26 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.textEditorModel.pushStackElement(); } - // A save participant can still change the model now and since we are so close to saving - // we do not want to trigger another auto save or similar, so we block this - // In addition we update our version right after in case it changed because of a model change - // - // Save participants can also be skipped through API. const saveParticipantCancellation = new CancellationTokenSource(); - let saveParticipantPromise: Promise = Promise.resolve(versionId); - if (this.isResolved() && this.textFileService.saveParticipant && !options.skipSaveParticipants) { - const onCompleteOrError = () => { - this.ignoreDirtyOnModelContentChange = false; - return this.versionId; - }; + return this.saveSequentializer.setPending(versionId, (async () => { - this.ignoreDirtyOnModelContentChange = true; - saveParticipantPromise = this.textFileService.saveParticipant.participate(this, { reason: options.reason }, saveParticipantCancellation.token).then(onCompleteOrError, onCompleteOrError); - } + // A save participant can still change the model now and since we are so close to saving + // we do not want to trigger another auto save or similar, so we block this + // In addition we update our version right after in case it changed because of a model change + // + // Save participants can also be skipped through API. + if (this.isResolved() && this.textFileService.saveParticipant && !options.skipSaveParticipants) { + try { + this.ignoreDirtyOnModelContentChange = true; - // mark the save participant as current pending save operation - return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => { + await this.textFileService.saveParticipant.participate(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveParticipantCancellation.token); + } catch (error) { + // Ignore + } finally { + this.ignoreDirtyOnModelContentChange = false; + } + } // We have to protect against being disposed at this point. It could be that the save() operation // was triggerd followed by a dispose() operation right after without waiting. Typically we cannot @@ -661,32 +659,41 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // - the model is not dirty (otherwise we know there are changed which needs to go to the file) // - the model is not in orphan mode (because in that case we know the file does not exist on disk) // - the model version did not change due to save participants running - if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { - return this.doTouch(newVersionId, options.reason); + if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === this.versionId) { + return this.doTouch(this.versionId, options.reason); } // update versionId with its new value (if pre-save changes happened) - versionId = newVersionId; + versionId = this.versionId; // Clear error flag since we are trying to save again this.inErrorMode = false; - // Remember when this model was saved last - this.lastSaveAttemptTime = Date.now(); - - // Save to Disk - // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) + // Save to Disk. We mark the save operation as currently pending with + // the latest versionId because it might have changed from a save + // participant triggering this.logService.trace(`[text file model] doSave(${versionId}) - before write()`, this.resource.toString()); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); - return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { - overwriteReadonly: options.overwriteReadonly, - overwriteEncoding: options.overwriteEncoding, - mtime: lastResolvedFileStat.mtime, - encoding: this.getEncoding(), - etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, this.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, - writeElevated: options.writeElevated - }).then(stat => this.handleSaveSuccess(stat, versionId, options), error => this.handleSaveError(error, versionId, options))); - }), () => saveParticipantCancellation.cancel()); + + const textFileEdiorModel = this; + + return this.saveSequentializer.setPending(versionId, (async () => { + try { + const stat = await this.textFileService.write(lastResolvedFileStat.resource, textFileEdiorModel.createSnapshot(), { + overwriteReadonly: options.overwriteReadonly, + overwriteEncoding: options.overwriteEncoding, + mtime: lastResolvedFileStat.mtime, + encoding: this.getEncoding(), + etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource, textFileEdiorModel.getMode())) ? ETAG_DISABLED : lastResolvedFileStat.etag, + writeElevated: options.writeElevated + }); + + this.handleSaveSuccess(stat, versionId, options); + } catch (error) { + this.handleSaveError(error, versionId, options); + } + })()); + })(), () => saveParticipantCancellation.cancel()); } private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveOptions): void { @@ -733,19 +740,24 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private doTouch(this: TextFileEditorModel & IResolvedTextFileEditorModel, versionId: number, reason: SaveReason): Promise { const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); - return this.saveSequentializer.setPending(versionId, this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { - mtime: lastResolvedFileStat.mtime, - encoding: this.getEncoding(), - etag: lastResolvedFileStat.etag - }).then(stat => { - // Updated resolved stat with updated stat since touching it might have changed mtime - this.updateLastResolvedFileStat(stat); + return this.saveSequentializer.setPending(versionId, (async () => { + try { + const stat = await this.textFileService.write(lastResolvedFileStat.resource, this.createSnapshot(), { + mtime: lastResolvedFileStat.mtime, + encoding: this.getEncoding(), + etag: lastResolvedFileStat.etag + }); - // Emit File Saved Event - this._onDidSave.fire(reason); + // Updated resolved stat with updated stat since touching it might have changed mtime + this.updateLastResolvedFileStat(stat); - }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); + // Emit File Saved Event + this._onDidSave.fire(reason); + } catch (error) { + onUnexpectedError(error); // just log any error but do not notify the user since the file was not dirty + } + })()); } private updateSavedVersionId(): void { @@ -776,10 +788,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil //#endregion - getLastSaveAttemptTime(): number { - return this.lastSaveAttemptTime; - } - hasState(state: ModelState): boolean { switch (state) { case ModelState.CONFLICT: diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 337797b264b..a4fbde9a277 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -84,7 +84,13 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // to have a size of 2 (1 running load and 1 queued load). const queue = this.modelLoadQueue.queueFor(model.resource); if (queue.size <= 1) { - queue.queue(() => model.load().then(undefined, onUnexpectedError)); + queue.queue(async () => { + try { + await model.load(); + } catch (error) { + onUnexpectedError(error); + } + }); } } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 61bddf39309..aff82ec1b83 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -120,7 +120,6 @@ suite('Files - TextFileEditorModel', () => { await pendingSave; - assert.ok(model.getLastSaveAttemptTime() <= Date.now()); assert.ok(model.hasState(ModelState.SAVED)); assert.ok(!model.isDirty()); assert.ok(savedEvent); @@ -488,8 +487,6 @@ suite('Files - TextFileEditorModel', () => { assert.ok(!accessor.textFileService.isDirty(toResource.call(this, '/path/index_async2.txt'))); assert.ok(assertIsDefined(model1.getStat()).mtime > m1Mtime); assert.ok(assertIsDefined(model2.getStat()).mtime > m2Mtime); - assert.ok(model1.getLastSaveAttemptTime() > m1Mtime); - assert.ok(model2.getLastSaveAttemptTime() > m2Mtime); model1.dispose(); model2.dispose(); From 9cda3b7bda1d4cfa851760e0077667f8068f36ab Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Feb 2020 09:31:20 +0100 Subject: [PATCH 089/192] debt - do not ignore dirty when invoking save participants if a save participant changes the model and the save fails, the document is not marked as dirty --- .../workbench/services/textfile/common/textFileEditorModel.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 15c92a28b1b..d5957f7c7d4 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -627,13 +627,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save participants can also be skipped through API. if (this.isResolved() && this.textFileService.saveParticipant && !options.skipSaveParticipants) { try { - this.ignoreDirtyOnModelContentChange = true; - await this.textFileService.saveParticipant.participate(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveParticipantCancellation.token); } catch (error) { // Ignore - } finally { - this.ignoreDirtyOnModelContentChange = false; } } From a7ed9c9572f51eee964b5d290ac0d170d9a01439 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Feb 2020 09:56:08 +0100 Subject: [PATCH 090/192] :up: playwright@0.11.0 (#90663) --- .../src/singlefolder-tests/window.test.ts | 8 +---- package.json | 2 +- test/automation/src/playwrightDriver.ts | 8 +++-- test/integration/browser/package.json | 1 + test/integration/browser/src/index.ts | 10 +++--- test/integration/browser/yarn.lock | 5 +++ test/unit/browser/index.js | 3 +- yarn.lock | 33 ++++++++++--------- 8 files changed, 38 insertions(+), 32 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index a8fef8ec2ad..620ce762632 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, env, UIKind } from 'vscode'; +import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind } from 'vscode'; import { join } from 'path'; import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; @@ -146,12 +146,6 @@ suite('window namespace tests', () => { }); test('active editor not always correct... #49125', async function () { - if (env.uiKind === UIKind.Web) { - // https://github.com/microsoft/vscode/issues/90470 - // https://github.com/microsoft/playwright/issues/979 - this.skip(); - return; - } const randomFile1 = await createRandomFile(); const randomFile2 = await createRandomFile(); diff --git a/package.json b/package.json index 531ea49e514..e8d355e18cd 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "opn": "^6.0.0", "optimist": "0.3.5", "p-all": "^1.0.0", - "playwright": "^0.10.0", + "playwright": "0.11.0", "pump": "^1.0.1", "queue": "3.0.6", "rcedit": "^1.1.0", diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index c18e01808d3..40ee4c82851 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -141,10 +141,12 @@ export function connect(engine: 'chromium' | 'webkit' | 'firefox' = 'chromium'): return new Promise(async (c) => { const browser = await playwright[engine].launch({ // Run in Edge dev on macOS - // executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev' + // executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev', + headless: false }); - const page = (await browser.defaultContext().pages())[0]; - await page.setViewport({ width, height }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.setViewportSize({ width, height }); await page.goto(`${endpoint}&folder=vscode-remote://localhost:9888${URI.file(workspacePath!).path}`); const result = { client: { dispose: () => browser.close() && teardown() }, diff --git a/test/integration/browser/package.json b/test/integration/browser/package.json index d43ee230c46..beb4142c55d 100644 --- a/test/integration/browser/package.json +++ b/test/integration/browser/package.json @@ -9,6 +9,7 @@ "devDependencies": { "@types/mkdirp": "0.5.1", "@types/node": "^12.11.7", + "@types/optimist": "0.0.29", "@types/rimraf": "2.0.2", "@types/tmp": "^0.1.0", "rimraf": "^2.6.1", diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 2ed75099d31..7972dd76521 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -11,8 +11,9 @@ import * as tmp from 'tmp'; import * as rimraf from 'rimraf'; import { URI } from 'vscode-uri'; import * as kill from 'tree-kill'; +import * as optimistLib from 'optimist'; -const optimist = require('optimist') +const optimist = optimistLib .describe('workspacePath', 'path to the workspace to open in the test').string('workspacePath') .describe('extensionDevelopmentPath', 'path to the extension to test').string('extensionDevelopmentPath') .describe('extensionTestsPath', 'path to the extension tests').string('extensionTestsPath') @@ -28,11 +29,12 @@ if (optimist.argv.help) { const width = 1200; const height = 800; -async function runTestsInBrowser(browserType: string, endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise { +async function runTestsInBrowser(browserType: 'chromium' | 'firefox' | 'webkit', endpoint: url.UrlWithStringQuery, server: cp.ChildProcess): Promise { const args = process.platform === 'linux' && browserType === 'chromium' ? ['--no-sandbox'] : undefined; // disable sandbox to run chrome on certain Linux distros const browser = await playwright[browserType].launch({ headless: !Boolean(optimist.argv.debug), dumpio: true, args }); - const page = (await browser.defaultContext().pages())[0]; - await page.setViewport({ width, height }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.setViewportSize({ width, height }); const host = endpoint.host; const protocol = 'vscode-remote'; diff --git a/test/integration/browser/yarn.lock b/test/integration/browser/yarn.lock index 35884dcfc51..126da8138c5 100644 --- a/test/integration/browser/yarn.lock +++ b/test/integration/browser/yarn.lock @@ -38,6 +38,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9" integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA== +"@types/optimist@0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/optimist/-/optimist-0.0.29.tgz#a8873580b3a84b69ac1e687323b15fbbeb90479a" + integrity sha1-qIc1gLOoS2msHmhzI7Ffu+uQR5o= + "@types/rimraf@2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 884cb5de667..d6e15c5e28d 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -120,7 +120,8 @@ const testModules = (async function () { async function runTestsInBrowser(testModules, browserType) { const args = process.platform === 'linux' && browserType === 'chromium' ? ['--no-sandbox'] : undefined; // disable sandbox to run chrome on certain Linux distros const browser = await playwright[browserType].launch({ headless: !Boolean(argv.debug), dumpio: true, args }); - const page = (await browser.defaultContext().pages())[0] + const context = await browser.newContext(); + const page = await context.newPage(); const target = url.pathToFileURL(path.join(__dirname, 'renderer.html')); if (argv.build) { target.search = `?build=true`; diff --git a/yarn.lock b/yarn.lock index e55bdc53b9f..e4f5accd3c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5878,11 +5878,6 @@ mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3: - version "2.4.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== - mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -6927,29 +6922,28 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -playwright-core@=0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-0.10.0.tgz#86699c9cc3e613d733e6635a54aceea1993013d5" - integrity sha512-yernA6yrrBhmb8M5eO6GZsJOrBKWOZszlu65Luz8LP7ryaDExN1sE9XjQBNbiwJ5Gfs8cehtAO7GfTDJt+Z2cQ== +playwright-core@=0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-0.11.0.tgz#a2372833f6ec4e7886c4409e3da93df997aee61b" + integrity sha512-9UPP/Max65PMiZJz9DNWB3ZRWtTlYlceLFnm6JO8aU7m6Vw3gwCvuSGoC5W69H67q98jH0VPSPp546+EnkiR2g== dependencies: debug "^4.1.0" extract-zip "^1.6.6" https-proxy-agent "^3.0.0" jpeg-js "^0.3.6" - mime "^2.0.3" pngjs "^3.4.0" progress "^2.0.3" proxy-from-env "^1.0.0" - rimraf "^2.6.1" + rimraf "^3.0.2" uuid "^3.4.0" ws "^6.1.0" -playwright@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-0.10.0.tgz#d37f7e42e0e868dcc4ec35cb0a8dbc6248457642" - integrity sha512-f3VRME/PIO5NbcWnlCDfXwPC0DAZJ7ETkcAdE+sensLCOkfDtLh97E71ZuxNCaPYsUA6FIPi5syD8pHJW/4hQQ== +playwright@0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-0.11.0.tgz#2abec99ea278b220bcd3902d7520ec22abc2d97e" + integrity sha512-cTJZ06OhwseMC9+D6KX1NmZXyEoaJl0o6GLkDhwmou3IFTrUFVOw7KYMBpcbJz0Rhb/de5ZPFlDTffLfEy/9lg== dependencies: - playwright-core "=0.10.0" + playwright-core "=0.11.0" plist@^3.0.1: version "3.0.1" @@ -7952,6 +7946,13 @@ rimraf@^2.4.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: dependencies: glob "^7.0.5" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" From 968b019aa5d786955a0cd136bca69d65ab01fb58 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Feb 2020 10:17:10 +0100 Subject: [PATCH 091/192] text files - add tests for save from within participant --- .../textfile/common/textFileEditorModel.ts | 2 - .../test/browser/textFileEditorModel.test.ts | 59 ++++++++++++++++--- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index d5957f7c7d4..6186973a5b8 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -670,9 +670,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // participant triggering this.logService.trace(`[text file model] doSave(${versionId}) - before write()`, this.resource.toString()); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); - const textFileEdiorModel = this; - return this.saveSequentializer.setPending(versionId, (async () => { try { const stat = await this.textFileService.write(lastResolvedFileStat.resource, textFileEdiorModel.createSnapshot(), { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index aff82ec1b83..c835deecece 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -503,12 +503,11 @@ suite('Files - TextFileEditorModel', () => { }); accessor.textFileService.saveParticipant = { - participate: model => { + participate: async model => { assert.ok(model.isDirty()); model.textEditorModel!.setValue('bar'); assert.ok(model.isDirty()); eventCounter++; - return Promise.resolve(); } }; @@ -542,8 +541,8 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); accessor.textFileService.saveParticipant = { - participate: (model) => { - return Promise.reject(new Error('boom')); + participate: async model => { + new Error('boom'); } }; @@ -560,10 +559,9 @@ suite('Files - TextFileEditorModel', () => { let participations: boolean[] = []; accessor.textFileService.saveParticipant = { - participate: (model) => { - return timeout(10).then(() => { - participations.push(true); - }); + participate: async model => { + await timeout(10); + participations.push(true); } }; @@ -583,4 +581,49 @@ suite('Files - TextFileEditorModel', () => { assert.equal(participations.length, 1); model.dispose(); }); + + test('Save Participant, calling save from within is unsupported but does not explode (sync save)', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + await testSaveFromSaveParticipant(model, false); + + model.dispose(); + }); + + test('Save Participant, calling save from within is unsupported but does not explode (async save)', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + await testSaveFromSaveParticipant(model, true); + + model.dispose(); + }); + + async function testSaveFromSaveParticipant(model: TextFileEditorModel, async: boolean): Promise { + let savePromise: Promise; + let breakLoop = false; + + accessor.textFileService.saveParticipant = { + participate: async model => { + if (breakLoop) { + return; + } + + breakLoop = true; + + if (async) { + await timeout(10); + } + const newSavePromise = model.save(); + + // assert that this is the same promise as the outer one + assert.equal(savePromise, newSavePromise); + } + }; + + await model.load(); + model.textEditorModel!.setValue('foo'); + + savePromise = model.save(); + await savePromise; + } }); From 245358ab239977e275a2c076e0376d86781e7d0e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Feb 2020 10:30:12 +0100 Subject: [PATCH 092/192] sort categories with uncheck items atop, https://github.com/microsoft/vscode/issues/90664 --- .../contrib/bulkEdit/browser/bulkEditPane.ts | 3 +- .../bulkEdit/browser/bulkEditPreview.ts | 11 +++++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 36 ++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 2d0de4bbb3d..0970caecdbb 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -6,7 +6,7 @@ import 'vs/css!./bulkEdit'; import { WorkbenchAsyncDataTree, TreeResourceNavigator, IOpenEvent } from 'vs/platform/list/browser/listService'; import { WorkspaceEdit } from 'vs/editor/common/modes'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; +import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree'; import { FuzzyScore } from 'vs/base/common/filters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; @@ -135,6 +135,7 @@ export class BulkEditPane extends ViewPane { expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, keyboardNavigationLabelProvider: new BulkEditNaviLabelProvider(), + sorter: new BulkEditSorter() } ); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts index 8d7ec503497..06bdb85376a 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPreview.ts @@ -99,6 +99,15 @@ export class BulkFileOperation { this.newUri = edit.newUri; } } + + needsConfirmation(): boolean { + for (let [, edit] of this.originalEdits) { + if (!this.parent.checked.isChecked(edit)) { + return true; + } + } + return false; + } } export class BulkCategory { @@ -230,7 +239,7 @@ export class BulkFileOperations { } operationByResource.forEach(value => this.fileOperations.push(value)); - operationByCategory.forEach(value => value.metadata.needsConfirmation ? this.categories.unshift(value) : this.categories.push(value)); + operationByCategory.forEach(value => this.categories.push(value)); // "correct" invalid parent-check child states that is // unchecked file edits (rename, create, delete) uncheck diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 4a50a0b6b89..544fa1027d4 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; @@ -24,6 +24,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { basename } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { WorkspaceFileEdit } from 'vs/editor/common/modes'; +import { compare } from 'vs/base/common/strings'; // --- VIEW MODEL @@ -248,6 +249,39 @@ export class BulkEditDataSource implements IAsyncDataSource { + + compare(a: BulkEditElement, b: BulkEditElement): number { + if (a instanceof CategoryElement && b instanceof CategoryElement) { + // + const aConfirm = BulkEditSorter._needsConfirmation(a.category); + const bConfirm = BulkEditSorter._needsConfirmation(b.category); + if (aConfirm === bConfirm) { + return a.category.metadata.label.localeCompare(b.category.metadata.label); + } else if (aConfirm) { + return -1; + } else { + return 1; + } + } + + if (a instanceof FileElement && b instanceof FileElement) { + return compare(a.edit.uri.toString(), b.edit.uri.toString()); + } + + if (a instanceof TextEditElement && b instanceof TextEditElement) { + return Range.compareRangesUsingStarts(a.edit.textEdit.edit.range, b.edit.textEdit.edit.range); + } + + return 0; + } + + private static _needsConfirmation(a: BulkCategory): boolean { + return a.fileOperations.some(ops => ops.needsConfirmation()); + } +} + // --- ACCESSI export class BulkEditAccessibilityProvider implements IAccessibilityProvider { From 9b31cacba04749a4bacd0e9198841a79737f2424 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 10:31:00 +0100 Subject: [PATCH 093/192] better error message #89658 --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index f751bebda62..da729176ce9 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -601,7 +601,7 @@ export class RawDebugSession implements IDisposable { private send(command: string, args: any, token?: CancellationToken, timeout?: number): Promise { return new Promise((completeDispatch, errorDispatch) => { if (!this.debugAdapter) { - errorDispatch(new Error('no debug adapter found')); + errorDispatch(new Error(nls.localize('noDebugAdapter', "No debug adapter found. Can not send '{0}'.", command))); return; } let cancelationListener: IDisposable; From 8bac008949a6e2bff1d95a4ff08b0ff758083bb3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 14 Feb 2020 10:59:32 +0100 Subject: [PATCH 094/192] web - allow to provide commands as workbench construction options --- src/vs/code/browser/workbench/workbench.ts | 55 +++----- .../browser/openInDesktop.web.contribution.ts | 8 +- src/vs/workbench/workbench.web.api.ts | 125 ++++++++++-------- 3 files changed, 94 insertions(+), 94 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index d17a34fbf0c..45f6f17ce06 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLinkProvider, IApplicationLink } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLink } from 'vs/workbench/workbench.web.api'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { streamToBuffer } from 'vs/base/common/buffer'; @@ -279,39 +279,6 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -class ApplicationLinkProvider { - - private links: IApplicationLink[] | undefined = undefined; - - constructor(workspace: IWorkspace) { - this.computeLink(workspace); - } - - private computeLink(workspace: IWorkspace): void { - if (!workspace) { - return; // not for empty workspaces - } - - const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined; - if (workspaceUri) { - this.links = [{ - uri: URI.from({ - scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders', - authority: Schemas.vscodeRemote, - path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path), - query: workspaceUri.query, - fragment: workspaceUri.fragment, - }), - label: localize('openInDesktop', "Open in Desktop") - }]; - } - } - - get provider(): IApplicationLinkProvider { - return () => this.links; - } -} - (function () { // Find config by checking for DOM @@ -375,12 +342,30 @@ class ApplicationLinkProvider { } } + // Application links ("Open in Desktop") + let applicationLinks: IApplicationLink[] | undefined = undefined; + if (workspace) { + const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined; + if (workspaceUri) { + applicationLinks = [{ + uri: URI.from({ + scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders', + authority: Schemas.vscodeRemote, + path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path), + query: workspaceUri.query, + fragment: workspaceUri.fragment, + }), + label: localize('openInDesktop', "Open in Desktop") + }]; + } + } + // Finally create workbench create(document.body, { ...config, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider(), - applicationLinkProvider: new ApplicationLinkProvider(workspace).provider + applicationLinks: applicationLinks }); })(); diff --git a/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts b/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts index 0b959f3abd4..9854d0f0628 100644 --- a/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts +++ b/src/vs/workbench/contrib/openInDesktop/browser/openInDesktop.web.contribution.ts @@ -29,13 +29,13 @@ export class OpenInDesktopIndicator extends Disposable implements IWorkbenchCont ) { super(); - const links = environmentService.options?.applicationLinkProvider?.(); + const links = environmentService.options?.applicationLinks; if (Array.isArray(links) && links?.length > 0) { this.installOpenInDesktopIndicator(links); } } - private installOpenInDesktopIndicator(links: IApplicationLink[]): void { + private installOpenInDesktopIndicator(links: readonly IApplicationLink[]): void { // Register action to trigger "Open In Desktop" const registry = Registry.as(ActionExtensions.WorkbenchActions); @@ -71,7 +71,7 @@ export class OpenInDesktopAction extends Action { } async run(): Promise { - const links = this.environmentService.options?.applicationLinkProvider?.(); + const links = this.environmentService.options?.applicationLinks; if (Array.isArray(links)) { if (links.length === 1) { return this.openApplicationLink(links[0]); @@ -83,7 +83,7 @@ export class OpenInDesktopAction extends Action { return true; } - private async runWithPicker(links: IApplicationLink[]): Promise { + private async runWithPicker(links: readonly IApplicationLink[]): Promise { // Show a picker with choices const quickPick = this.quickInputService.createQuickPick(); diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 4cd646930bb..140711b300e 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -80,17 +80,29 @@ interface IApplicationLink { uri: URI; /** - * A label for the link to display. + * A label for the application link to display. */ label: string; } -interface IApplicationLinkProvider { - (): IApplicationLink[] | undefined +interface ICommand { + + /** + * An identifier for the command. Commands can be executed from extensions + * using the `vscode.commands.executeCommand` API using that command ID. + */ + id: string, + + /** + * A function that is being executed with any arguments passed over. + */ + handler: (...args: any[]) => void; } interface IWorkbenchConstructionOptions { + //#region Connection related configuration + /** * The remote authority is the IP:PORT from where the workbench is served * from. It is for example being used for the websocket connections as address. @@ -108,6 +120,36 @@ interface IWorkbenchConstructionOptions { */ readonly webviewEndpoint?: string; + /** + * A factory for web sockets. + */ + readonly webSocketFactory?: IWebSocketFactory; + + /** + * A provider for resource URIs. + */ + readonly resourceUriProvider?: IResourceUriProvider; + + /** + * Resolves an external uri before it is opened. + */ + readonly resolveExternalUri?: IExternalUriResolver; + + /** + * Support for creating tunnels. + */ + readonly tunnelFactory?: ITunnelFactory; + + /** + * Support for filtering candidate ports + */ + readonly showCandidate?: IShowCandidate; + + //#endregion + + + //#region Workbench configuration + /** * A handler for opening workspaces and providing the initial workspace. */ @@ -119,16 +161,6 @@ interface IWorkbenchConstructionOptions { */ userDataProvider?: IFileSystemProvider; - /** - * A factory for web sockets. - */ - readonly webSocketFactory?: IWebSocketFactory; - - /** - * A provider for resource URIs. - */ - readonly resourceUriProvider?: IResourceUriProvider; - /** * The credentials provider to store and retrieve secrets. */ @@ -154,21 +186,6 @@ interface IWorkbenchConstructionOptions { */ readonly resolveCommonTelemetryProperties?: ICommontTelemetryPropertiesResolver; - /** - * Resolves an external uri before it is opened. - */ - readonly resolveExternalUri?: IExternalUriResolver; - - /** - * Support for creating tunnels. - */ - readonly tunnelFactory?: ITunnelFactory; - - /** - * Support for filtering candidate ports - */ - readonly showCandidate?: IShowCandidate; - /** * Provide entries for the "Open in Desktop" feature. * @@ -179,7 +196,20 @@ interface IWorkbenchConstructionOptions { * - N elements: there will be a "Open in Desktop" affordance that opens * a picker on click to select which application to open. */ - readonly applicationLinkProvider?: IApplicationLinkProvider; + readonly applicationLinks?: readonly IApplicationLink[]; + + /** + * A set of optional commands that should be registered with the commands + * registry. + * + * Note: commands can be called from extensions if the identifier is known! + */ + readonly commands?: readonly ICommand[]; + + //#endregion + + + //#region Diagnostics /** * Current logging level. Default is `LogLevel.Info`. @@ -190,20 +220,8 @@ interface IWorkbenchConstructionOptions { * Whether to enable the smoke test driver. */ readonly driver?: boolean; -} -interface ICommandHandler { - (...args: any[]): void; -} - -interface IWorkbench { - - /** - * Register a command with the provided identifier and handler with - * the workbench. The command can be called from extensions using the - * `vscode.commands.executeCommand` API. - */ - registerCommand(id: string, command: ICommandHandler): IDisposable; + //#endregion } /** @@ -211,24 +229,22 @@ interface IWorkbench { * * @param domElement the container to create the workbench in * @param options for setting up the workbench - * - * @returns the workbench facade with additional methods to call on. */ -async function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { +async function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { // Startup workbench await main(domElement, options); - // Return facade - return { - registerCommand: (id: string, command: ICommandHandler): IDisposable => { - return CommandsRegistry.registerCommand(id, (accessor, ...args: any[]) => { + // Register commands if any + if (Array.isArray(options.commands)) { + for (const command of options.commands) { + CommandsRegistry.registerCommand(command.id, (accessor, ...args: any[]) => { // we currently only pass on the arguments but not the accessor // to the command to reduce our exposure of internal API. - command(...args); + command.handler(...args); }); } - }; + } } export { @@ -237,9 +253,6 @@ export { create, IWorkbenchConstructionOptions, - // Workbench Facade - IWorkbench, - ICommandHandler, // Basic Types URI, @@ -291,5 +304,7 @@ export { // Protocol Links IApplicationLink, - IApplicationLinkProvider + + // Commands + ICommand }; From 7bc12be7468b775918deb5faf7568b0538d08f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 14 Feb 2020 11:10:34 +0100 Subject: [PATCH 095/192] fixes #90632 (#90675) --- .../contrib/update/browser/update.ts | 60 ++++++++++--------- .../host/browser/browserHostService.ts | 4 ++ .../workbench/services/host/browser/host.ts | 5 ++ .../electron-browser/desktopHostService.ts | 10 ++++ .../test/browser/workbenchTestServices.ts | 1 + 5 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 91dc0c6fcaf..786014463b6 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -127,39 +127,41 @@ export class ProductContribution implements IWorkbenchContribution { @IHostService hostService: IHostService, @IProductService productService: IProductService ) { - if (!hostService.hasFocus) { - return; - } + hostService.hadLastFocus().then(hadLastFocus => { + if (!hadLastFocus) { + return; + } - const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); - const shouldShowReleaseNotes = configurationService.getValue('update.showReleaseNotes'); + const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, ''); + const shouldShowReleaseNotes = configurationService.getValue('update.showReleaseNotes'); - // was there an update? if so, open release notes - const releaseNotesUrl = productService.releaseNotesUrl; - if (shouldShowReleaseNotes && !environmentService.args['skip-release-notes'] && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { - showReleaseNotes(instantiationService, productService.version) - .then(undefined, () => { - notificationService.prompt( - severity.Info, - nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", productService.nameLong, productService.version), - [{ - label: nls.localize('releaseNotes', "Release Notes"), - run: () => { - const uri = URI.parse(releaseNotesUrl); - openerService.open(uri); - } - }], - { sticky: true } - ); - }); - } + // was there an update? if so, open release notes + const releaseNotesUrl = productService.releaseNotesUrl; + if (shouldShowReleaseNotes && !environmentService.args['skip-release-notes'] && releaseNotesUrl && lastVersion && productService.version !== lastVersion) { + showReleaseNotes(instantiationService, productService.version) + .then(undefined, () => { + notificationService.prompt( + severity.Info, + nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", productService.nameLong, productService.version), + [{ + label: nls.localize('releaseNotes', "Release Notes"), + run: () => { + const uri = URI.parse(releaseNotesUrl); + openerService.open(uri); + } + }], + { sticky: true } + ); + }); + } - // should we show the new license? - if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) { - notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl)); - } + // should we show the new license? + if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) { + notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl)); + } - storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL); + storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL); + }); } } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 7c0b27ba490..85de4203e54 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -95,6 +95,10 @@ export class BrowserHostService extends Disposable implements IHostService { return document.hasFocus(); } + async hadLastFocus(): Promise { + return true; + } + async focus(): Promise { window.focus(); } diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index f97c8048de2..ad10de5ba65 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -25,6 +25,11 @@ export interface IHostService { */ readonly hasFocus: boolean; + /** + * Find out if the window had the last focus. + */ + hadLastFocus(): Promise; + /** * Attempt to bring the window to the foreground and focus it. */ diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts index 6fd4fa2404e..b009d228026 100644 --- a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts @@ -36,6 +36,16 @@ export class DesktopHostService extends Disposable implements IHostService { return document.hasFocus(); } + async hadLastFocus(): Promise { + const activeWindowId = await this.electronService.getActiveWindowId(); + + if (typeof activeWindowId === 'undefined') { + return false; + } + + return activeWindowId === this.electronEnvironmentService.windowId; + } + openWindow(options?: IOpenEmptyWindowOptions): Promise; openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise; openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 04ae1141412..67ef5397a99 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -865,6 +865,7 @@ export class TestHostService implements IHostService { _serviceBrand: undefined; readonly hasFocus: boolean = true; + async hadLastFocus(): Promise { return true; } readonly onDidChangeFocus: Event = Event.None; async restart(): Promise { } From 30d22f5f33b374ff3b7f21ca2bc829386c34998a Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 11:22:28 +0100 Subject: [PATCH 096/192] fixes #90660 --- src/vs/workbench/contrib/debug/browser/startView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 923462eee94..94de2fe1094 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -20,13 +20,14 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/arrays'; -import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; const $ = dom.$; interface DebugStartMetrics { @@ -51,7 +52,7 @@ function createClickElement(textContent: string, action: () => any): HTMLSpanEle return clickElement; } -export class StartView extends ViewPane { +export class StartView extends BaseDebugViewPane { static ID = 'workbench.debug.startView'; static LABEL = localize('start', "Start"); From adfa13a302a8f54cf6371c16fd2b7d108066b0c5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 11:31:14 +0100 Subject: [PATCH 097/192] Render narrow characters for larger font scales --- .../browser/viewParts/minimap/minimap.ts | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 3e0a325df4c..0a67ece8052 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -138,7 +138,15 @@ class MinimapOptions { const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); // fontScale = Math.round(minimapOpts.scale * pixelRatio); - fontScale = Math.max(1, Math.floor(minimapLineHeight / baseCharHeight / 2)); + + const configuredFontScale = Math.round(minimapOpts.scale * pixelRatio); + fontScale = Math.min(4, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); + + const fontScaleRatio = fontScale / configuredFontScale; + if (fontScaleRatio > 1) { + this.canvasInnerWidth = Math.floor(this.canvasInnerWidth * fontScaleRatio); + } + const actualMinimapHeight = (modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight; this.canvasInnerHeight = Math.ceil(actualMinimapHeight); } @@ -170,7 +178,6 @@ class MinimapOptions { && this.pixelRatio === other.pixelRatio && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth && this.lineHeight === other.lineHeight - && this.fontScale === other.fontScale && this.minimapLeft === other.minimapLeft && this.minimapWidth === other.minimapWidth && this.minimapHeight === other.minimapHeight @@ -179,6 +186,9 @@ class MinimapOptions { && this.canvasOuterWidth === other.canvasOuterWidth && this.canvasOuterHeight === other.canvasOuterHeight && this.backgroundColor.equals(other.backgroundColor) + && this.fontScale === other.fontScale + && this.minimapLineHeight === other.minimapLineHeight + && this.minimapCharWidth === other.minimapCharWidth ); } } @@ -1529,7 +1539,6 @@ class InnerMinimap extends Disposable { const useLighterFont = tokensColorTracker.backgroundIsLight(); const renderMinimap = this._model.options.renderMinimap; const charRenderer = this._model.options.charRenderer(); - const isSampling = this._model.options.isSampling; const fontScale = this._model.options.fontScale; const minimapCharWidth = this._model.options.minimapCharWidth; @@ -1550,7 +1559,7 @@ class InnerMinimap extends Disposable { tabSize, lineInfo[lineIndex]!, fontScale, - isSampling + minimapLineHeight ); } renderedLines[lineIndex] = new MinimapLine(dy); @@ -1677,7 +1686,7 @@ class InnerMinimap extends Disposable { tabSize: number, lineData: ViewLineData, fontScale: number, - isSampling: boolean + minimapLineHeight: number ): void { const content = lineData.content; const tokens = lineData.tokens; @@ -1715,7 +1724,7 @@ class InnerMinimap extends Disposable { if (renderMinimap === RenderMinimap.Blocks) { minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, isSampling); + minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, minimapLineHeight === 1); } dx += charWidth; From c379e3226e5458348ebb115c03931bb807b76d7b Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 11:31:35 +0100 Subject: [PATCH 098/192] debug: remove BaseDebugViewPane --- src/vs/workbench/contrib/debug/browser/baseDebugView.ts | 9 --------- .../workbench/contrib/debug/browser/breakpointsView.ts | 6 +++--- src/vs/workbench/contrib/debug/browser/callStackView.ts | 8 ++++---- .../workbench/contrib/debug/browser/loadedScriptsView.ts | 7 ++++--- src/vs/workbench/contrib/debug/browser/startView.ts | 6 +++--- src/vs/workbench/contrib/debug/browser/variablesView.ts | 7 ++++--- .../contrib/debug/browser/watchExpressionsView.ts | 7 ++++--- 7 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 3b75c468a51..5d4be69a867 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -19,7 +19,6 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; import { once } from 'vs/base/common/functional'; -import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export const twistiePixels = 20; @@ -233,11 +232,3 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer; private needsRefresh = false; @@ -82,6 +81,7 @@ export class BreakpointsView extends BaseDebugViewPane { public renderBody(container: HTMLElement): void { super.renderBody(container); + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-breakpoints'); const delegate = new BreakpointsDelegate(this.debugService); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 805bac9c916..5ae2c952efe 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -13,12 +13,12 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction, Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ILabelService } from 'vs/platform/label/common/label'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -74,7 +74,7 @@ export function getContextForContributedActions(element: CallStackItem | null): return ''; } -export class CallStackView extends BaseDebugViewPane { +export class CallStackView extends ViewPane { private pauseMessage!: HTMLSpanElement; private pauseMessageLabel!: HTMLSpanElement; private onCallStackChangeScheduler: RunOnceScheduler; @@ -158,7 +158,7 @@ export class CallStackView extends BaseDebugViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); - + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-call-stack'); const treeContainer = renderViewTree(container); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index bf56471c4a4..ceb03abfcc9 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -7,12 +7,12 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { normalize, isAbsolute, posix } from 'vs/base/common/path'; -import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -402,7 +402,7 @@ function asTreeElement(item: BaseTreeItem, viewState?: IViewState): ITreeElement }; } -export class LoadedScriptsView extends BaseDebugViewPane { +export class LoadedScriptsView extends ViewPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; @@ -435,6 +435,7 @@ export class LoadedScriptsView extends BaseDebugViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-loaded-scripts'); dom.addClass(container, 'show-file-icons'); diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 94de2fe1094..f561b8cc42b 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -20,14 +20,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/arrays'; -import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; const $ = dom.$; interface DebugStartMetrics { @@ -52,7 +51,7 @@ function createClickElement(textContent: string, action: () => any): HTMLSpanEle return clickElement; } -export class StartView extends BaseDebugViewPane { +export class StartView extends ViewPane { static ID = 'workbench.debug.startView'; static LABEL = localize('start', "Start"); @@ -171,6 +170,7 @@ export class StartView extends BaseDebugViewPane { })); attachButtonStyler(this.debugButton, this.themeService); + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-start-view'); this.secondMessageContainer = $('.section'); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 8e9dd8dc2b0..4dd9c4bfac0 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -12,12 +12,12 @@ import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IViewMod import { Variable, Scope } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IAction, Action } from 'vs/base/common/actions'; import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -39,7 +39,7 @@ let forgetScopes = true; export const variableSetEmitter = new Emitter(); -export class VariablesView extends BaseDebugViewPane { +export class VariablesView extends ViewPane { private onFocusStackFrameScheduler: RunOnceScheduler; private needsRefresh = false; @@ -90,6 +90,7 @@ export class VariablesView extends BaseDebugViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-variables'); const treeContainer = renderViewTree(container); diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index df6770a121f..99b9cc6d32e 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -16,9 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -38,7 +38,7 @@ const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreVariableSetEmitter = false; let useCachedEvaluation = false; -export class WatchExpressionsView extends BaseDebugViewPane { +export class WatchExpressionsView extends ViewPane { private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh = false; @@ -67,6 +67,7 @@ export class WatchExpressionsView extends BaseDebugViewPane { renderBody(container: HTMLElement): void { super.renderBody(container); + dom.addClass(this.element, 'debug-pane'); dom.addClass(container, 'debug-watch'); const treeContainer = renderViewTree(container); From 6158372c890059fd03b0224e19b1af0e385e5725 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Feb 2020 12:05:01 +0100 Subject: [PATCH 099/192] more verbose integration test failures --- .../src/singlefolder-tests/workspace.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 25b34c5827b..5810129fdc1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -245,6 +245,10 @@ suite('workspace-namespace', () => { sub.dispose(); }); + function assertEqualPath(a: string, b: string): void { + assert.ok(pathEquals(a, b), `${a} <-> ${b}`); + } + test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', async () => { const file = await createRandomFile(); let disposables: vscode.Disposable[] = []; @@ -252,19 +256,19 @@ suite('workspace-namespace', () => { let pendingAsserts: Function[] = []; let onDidOpenTextDocument = false; disposables.push(vscode.workspace.onDidOpenTextDocument(e => { - pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); onDidOpenTextDocument = true; })); let onDidChangeTextDocument = false; disposables.push(vscode.workspace.onDidChangeTextDocument(e => { - pendingAsserts.push(() => assert.ok(pathEquals(e.document.uri.fsPath, file.fsPath))); + pendingAsserts.push(() => assertEqualPath(e.document.uri.fsPath, file.fsPath)); onDidChangeTextDocument = true; })); let onDidSaveTextDocument = false; disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); onDidSaveTextDocument = true; })); @@ -291,7 +295,7 @@ suite('workspace-namespace', () => { let onDidSaveTextDocument = false; disposables.push(vscode.workspace.onDidSaveTextDocument(e => { - pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath))); + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); onDidSaveTextDocument = true; })); From bcdf991bd47c5bf11d8a86eec4ae0b708c2708ed Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Feb 2020 12:05:57 +0100 Subject: [PATCH 100/192] test: saveAll before asserting a certain save event --- .../vscode-api-tests/src/singlefolder-tests/workspace.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 5810129fdc1..3e56f927168 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -253,6 +253,8 @@ suite('workspace-namespace', () => { const file = await createRandomFile(); let disposables: vscode.Disposable[] = []; + await vscode.workspace.saveAll(); + let pendingAsserts: Function[] = []; let onDidOpenTextDocument = false; disposables.push(vscode.workspace.onDidOpenTextDocument(e => { From b3a55da2dcbe5111026518cc4f10eca0f2a0ca1e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 12:18:53 +0100 Subject: [PATCH 101/192] Center lines vertically --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 0a67ece8052..06a7f83f5fe 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1542,6 +1542,10 @@ class InnerMinimap extends Disposable { const fontScale = this._model.options.fontScale; const minimapCharWidth = this._model.options.minimapCharWidth; + const baseCharHeight = (renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); + const renderMinimapLineHeight = baseCharHeight * fontScale; + const innerLinePadding = (minimapLineHeight > renderMinimapLineHeight ? Math.floor((minimapLineHeight - renderMinimapLineHeight) / 2) : 0); + // Render the rest of lines let dy = 0; const renderedLines: MinimapLine[] = []; @@ -1556,6 +1560,7 @@ class InnerMinimap extends Disposable { tokensColorTracker, charRenderer, dy, + innerLinePadding, tabSize, lineInfo[lineIndex]!, fontScale, @@ -1683,6 +1688,7 @@ class InnerMinimap extends Disposable { colorTracker: MinimapTokensColorTracker, minimapCharRenderer: MinimapCharRenderer, dy: number, + innerLinePadding: number, tabSize: number, lineData: ViewLineData, fontScale: number, @@ -1722,9 +1728,9 @@ class InnerMinimap extends Disposable { for (let i = 0; i < count; i++) { if (renderMinimap === RenderMinimap.Blocks) { - minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont); + minimapCharRenderer.blockRenderChar(target, dx, dy + innerLinePadding, tokenColor, backgroundColor, useLighterFont); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, minimapLineHeight === 1); + minimapCharRenderer.renderChar(target, dx, dy + innerLinePadding, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, minimapLineHeight === 1); } dx += charWidth; From aa9f9695c42ee71e155548eb29ddc10b425de6a1 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 12:22:43 +0100 Subject: [PATCH 102/192] debug: fix some css when view moved to napel --- .../contrib/debug/browser/media/debug.contribution.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index 12a93e85e7d..2fb7b93bd76 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -75,7 +75,7 @@ /* Expressions */ -.monaco-workbench .debug-viewlet .monaco-list-row .expression, +.monaco-workbench .debug-pane .monaco-list-row .expression, .monaco-workbench .debug-hover-widget .monaco-list-row .expression { font-size: 13px; overflow: hidden; @@ -84,7 +84,7 @@ white-space: pre; } -.monaco-workbench.mac .debug-viewlet .monaco-list-row .expression, +.monaco-workbench.mac .debug-pane .monaco-list-row .expression, .monaco-workbench.mac .debug-hover-widget .monaco-list-row .expression { font-size: 11px; } From e588a9cd1ad1d2794a592e5d14cb3ab5bd2818ea Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 14 Feb 2020 12:47:52 +0100 Subject: [PATCH 103/192] Introduce evaluatable expression API; fixes #89084 --- src/vs/editor/common/modes.ts | 33 ++++++++++ src/vs/monaco.d.ts | 25 ++++++++ src/vs/vscode.proposed.d.ts | 60 ++++++++++++++++- .../api/browser/mainThreadLanguageFeatures.ts | 10 +++ .../workbench/api/common/extHost.api.impl.ts | 4 ++ .../workbench/api/common/extHost.protocol.ts | 2 + .../api/common/extHostLanguageFeatures.ts | 35 +++++++++- .../api/common/extHostTypeConverters.ts | 14 ++++ src/vs/workbench/api/common/extHostTypes.ts | 11 ++++ .../contrib/debug/browser/debugHover.ts | 64 +++++++++++++++---- 10 files changed, 243 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 7d925b4bc2c..0340900a5c4 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -265,6 +265,34 @@ export interface HoverProvider { provideHover(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are + * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. + */ +export interface EvaluatableExpression { + /** + * The range to which this expression applies. + */ + range: IRange; + /* + * This expression overrides the expression extracted from the range. + */ + expression?: string; +} + +/** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ +export interface EvaluatableExpressionProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + export const enum CompletionItemKind { Method, Function, @@ -1595,6 +1623,11 @@ export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ff031806fb3..bee004f486d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5238,6 +5238,31 @@ declare namespace monaco.languages { provideHover(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } + /** + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are + * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. + */ + export interface EvaluatableExpression { + /** + * The range to which this expression applies. + */ + range: IRange; + expression?: string; + } + + /** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ + export interface EvaluatableExpressionProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideEvaluatableExpression(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + } + export enum CompletionItemKind { Method = 0, Function = 1, diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a782d93c1a6..deb1bae0050 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -876,7 +876,65 @@ declare module 'vscode' { //#endregion - //#region Debug: + //#region locate evaluatable expressions for debug hover: https://github.com/microsoft/vscode/issues/89084 + + /** + * An EvaluatableExpression represents an expression in a document that can be evaluated by an active debugger or runtime. + * The result of this evaluation is shown in a tooltip-like widget. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + * In this case the range is still used to highlight the range in the document. + */ + export class EvaluatableExpression { + /* + * The range is used to extract the evaluatable expression from the underlying document and to highlight it. + */ + readonly range: Range; + /* + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string; + + /** + * Creates a new evaluatable expression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. + */ + export interface EvaluatableExpressionProvider { + + /** + * Provide an evaluatable expression for the given document and position. + * The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression. + * + * @param document The document in which the command was invoked. + * @param position The position where the command was invoked. + * @param token A cancellation token. + * @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + export namespace languages { + /** + * Register a provider that locates evaluatable expressions in text documents. + * + * If multiple providers are registered for a language an arbitrary provider will be used. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An evaluatable expression provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; + } // deprecated diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 678a44ca3f3..de204a88dbd 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -213,6 +213,16 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } + // --- debug hover + + $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, modes.EvaluatableExpressionProviderRegistry.register(selector, { + provideEvaluatableExpression: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { + return this._proxy.$provideEvaluatableExpression(handle, model.uri, position, token); + } + })); + } + // --- occurrences $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3952717d220..61353fa2683 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -345,6 +345,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier); }, + registerEvaluatableExpressionProvider(selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider): vscode.Disposable { + return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier); + }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, @@ -926,6 +929,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I DocumentLink: extHostTypes.DocumentLink, DocumentSymbol: extHostTypes.DocumentSymbol, EndOfLine: extHostTypes.EndOfLine, + EvaluatableExpression: extHostTypes.EvaluatableExpression, EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, CustomExecution: extHostTypes.CustomExecution, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c181215c41d..73af521adef 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -353,6 +353,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerImplementationSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void; @@ -1208,6 +1209,7 @@ export interface ExtHostLanguageFeaturesShape { $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; + $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b90e3ddeb24..65d12628b10 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -276,6 +276,27 @@ class HoverAdapter { } } +class EvaluatableExpressionAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.EvaluatableExpressionProvider, + ) { } + + public provideEvaluatableExpression(resource: URI, position: IPosition, token: CancellationToken): Promise { + + const doc = this._documents.getDocument(resource); + const pos = typeConvert.Position.to(position); + + return asPromise(() => this._provider.provideEvaluatableExpression(doc, pos, token)).then(value => { + if (value) { + return typeConvert.EvaluatableExpression.from(value); + } + return undefined; + }); + } +} + class DocumentHighlightAdapter { constructor( @@ -1329,7 +1350,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter - | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter; + | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter; class AdapterData { constructor( @@ -1549,6 +1570,18 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token), undefined); } + // --- debug hover + + registerEvaluatableExpressionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider, extensionId?: ExtensionIdentifier): vscode.Disposable { + const handle = this._addNewAdapter(new EvaluatableExpressionAdapter(this._documents, provider), extension); + this._proxy.$registerEvaluatableExpressionProvider(handle, this._transformDocumentSelector(selector)); + return this._createDisposable(handle); + } + + $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined); + } + // --- occurrences registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 72c238ee6ba..c998bd2c5f8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -737,6 +737,20 @@ export namespace Hover { return new types.Hover(info.contents.map(MarkdownString.to), Range.to(info.range)); } } + +export namespace EvaluatableExpression { + export function from(expression: vscode.EvaluatableExpression): modes.EvaluatableExpression { + return { + range: Range.from(expression.range), + expression: expression.expression + }; + } + + export function to(info: modes.EvaluatableExpression): types.EvaluatableExpression { + return new types.EvaluatableExpression(Range.to(info.range), info.expression); + } +} + export namespace DocumentHighlight { export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight { return { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index f9f9de26758..f6871d7e2bd 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2283,6 +2283,17 @@ export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInli } } +@es5ClassCompat +export class EvaluatableExpression implements vscode.EvaluatableExpression { + readonly range: vscode.Range; + readonly expression?: string; + + constructor(range: vscode.Range, expression?: string) { + this.range = range; + this.expression = expression; + } +} + export enum LogLevel { Trace = 1, Debug = 2, diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 0571de088e8..bb510b66fd7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -11,7 +11,7 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDebugService, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; @@ -30,6 +30,8 @@ import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { coalesce } from 'vs/base/common/arrays'; import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { EvaluatableExpressionProviderRegistry } from 'vs/editor/common/modes'; +import { CancellationToken } from 'vs/base/common/cancellation'; const $ = dom.$; const MAX_TREE_HEIGHT = 324; @@ -174,18 +176,52 @@ export class DebugHoverWidget implements IContentWidget { } async showAt(range: Range, focus: boolean): Promise { - const pos = range.getStartPosition(); const session = this.debugService.getViewModel().focusedSession; - if (!this.editor.hasModel()) { + + if (!session || !this.editor.hasModel()) { return Promise.resolve(this.hide()); } - const lineContent = this.editor.getModel().getLineContent(pos.lineNumber); - const { start, end } = getExactExpressionStartAndEnd(lineContent, range.startColumn, range.endColumn); - // use regex to extract the sub-expression #9821 - const matchingExpression = lineContent.substring(start - 1, end); - if (!matchingExpression || !session) { + const model = this.editor.getModel(); + const pos = range.getStartPosition(); + + let rng: IRange | undefined = undefined; + let matchingExpression: string | undefined; + + if (EvaluatableExpressionProviderRegistry.has(model)) { + const supports = EvaluatableExpressionProviderRegistry.ordered(model); + + const promises = supports.map(support => { + return Promise.resolve(support.provideEvaluatableExpression(model, pos, CancellationToken.None)).then(expression => { + return expression; + }, err => { + //onUnexpectedExternalError(err); + return undefined; + }); + }); + + const results = await Promise.all(promises).then(coalesce); + if (results.length > 0) { + matchingExpression = results[0].expression; + rng = results[0].range; + + if (!matchingExpression) { + const lineContent = model.getLineContent(pos.lineNumber); + matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn); + } + } + + } else { // old one-size-fits-all strategy + const lineContent = model.getLineContent(pos.lineNumber); + const { start, end } = getExactExpressionStartAndEnd(lineContent, range.startColumn, range.endColumn); + + // use regex to extract the sub-expression #9821 + matchingExpression = lineContent.substring(start - 1, end); + rng = new Range(pos.lineNumber, start, pos.lineNumber, start + matchingExpression.length); + } + + if (!matchingExpression) { return Promise.resolve(this.hide()); } @@ -202,13 +238,15 @@ export class DebugHoverWidget implements IContentWidget { if (!expression || (expression instanceof Expression && !expression.available)) { this.hide(); - return undefined; + return; } - this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{ - range: new Range(pos.lineNumber, start, pos.lineNumber, start + matchingExpression.length), - options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS - }]); + if (rng) { + this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{ + range: rng, + options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS + }]); + } return this.doShow(pos, expression, focus); } From 802fdbac9f2aa6670fb426a59081737f4b22f91e Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 14 Feb 2020 12:53:37 +0100 Subject: [PATCH 104/192] don't work too much unneccesarily... --- .../contrib/debug/browser/debugEditorContribution.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index c22007db2f2..a8cba2d296b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -136,17 +136,19 @@ function getWordToLineNumbersMap(model: ITextModel | null): Map Date: Fri, 14 Feb 2020 14:42:16 +0100 Subject: [PATCH 105/192] Adjust minimap width based on effective font scale --- .../browser/viewParts/minimap/minimap.ts | 19 +++------ .../common/config/commonEditorConfig.ts | 9 ++-- src/vs/editor/common/config/editorOptions.ts | 41 +++++++++++++++++-- .../viewLayout/editorLayoutProvider.test.ts | 31 ++++++++++++++ src/vs/monaco.d.ts | 2 + 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 06a7f83f5fe..c82c8f5566d 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -128,31 +128,22 @@ class MinimapOptions { const baseCharHeight = (this.renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); let fontScale: number; - let minimapLineHeight: number; - if (minimapOpts.entireDocument) { + const minimapLineHeight = layoutInfo.minimapLineHeight; + if (this.renderMinimap === RenderMinimap.None) { + fontScale = Math.round(minimapOpts.scale * pixelRatio); + } else if (minimapOpts.entireDocument) { if (isSampling) { fontScale = 1; - minimapLineHeight = 1; } else { + this.canvasInnerWidth = Math.floor(this.canvasInnerWidth * layoutInfo.minimapWidthMultiplier); const extraLinesBeyondLastLine = this.scrollBeyondLastLine ? (layoutInfo.height / this.lineHeight - 1) : 0; - const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); - minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); - // fontScale = Math.round(minimapOpts.scale * pixelRatio); - - const configuredFontScale = Math.round(minimapOpts.scale * pixelRatio); fontScale = Math.min(4, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); - const fontScaleRatio = fontScale / configuredFontScale; - if (fontScaleRatio > 1) { - this.canvasInnerWidth = Math.floor(this.canvasInnerWidth * fontScaleRatio); - } - const actualMinimapHeight = (modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight; this.canvasInnerHeight = Math.ceil(actualMinimapHeight); } } else { fontScale = Math.round(minimapOpts.scale * pixelRatio); - minimapLineHeight = baseCharHeight * fontScale; } this.fontScale = fontScale; diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index ed8c20c1674..7f488a873dc 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -287,6 +287,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC public options!: ComputedEditorOptions; private _isDominatedByLongLines: boolean; + private _maxLineNumber: number; private _lineNumbersDigitCount: number; private _rawOptions: IEditorOptions; @@ -298,6 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC this.isSimpleWidget = isSimpleWidget; this._isDominatedByLongLines = false; + this._maxLineNumber = 1; this._lineNumbersDigitCount = 1; this._rawOptions = deepCloneAndMigrateOptions(_options); @@ -347,6 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC fontInfo: this.readConfiguration(bareFontInfo), extraEditorClassName: partialEnv.extraEditorClassName, isDominatedByLongLines: this._isDominatedByLongLines, + maxLineNumber: this._maxLineNumber, lineNumbersDigitCount: this._lineNumbersDigitCount, emptySelectionClipboard: partialEnv.emptySelectionClipboard, pixelRatio: partialEnv.pixelRatio, @@ -405,11 +408,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC } public setMaxLineNumber(maxLineNumber: number): void { - let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber); - if (this._lineNumbersDigitCount === digitCount) { + if (this._maxLineNumber === maxLineNumber) { return; } - this._lineNumbersDigitCount = digitCount; + this._maxLineNumber = maxLineNumber; + this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber); this._recomputeOptions(); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 29e137cf5d8..b99da721783 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -8,6 +8,7 @@ import * as platform from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { Constants } from 'vs/base/common/uint'; +// import { Constants as MinimapConstants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; @@ -672,6 +673,7 @@ export interface IEnvironmentalOptions { readonly fontInfo: FontInfo; readonly extraEditorClassName: string; readonly isDominatedByLongLines: boolean; + readonly maxLineNumber: number; readonly lineNumbersDigitCount: number; readonly emptySelectionClipboard: boolean; readonly pixelRatio: number; @@ -1685,6 +1687,8 @@ export interface EditorLayoutInfo { * The width of the minimap */ readonly minimapWidth: number; + readonly minimapLineHeight: number; + readonly minimapWidthMultiplier: number; /** * Minimap render type @@ -1718,6 +1722,7 @@ export interface EditorLayoutInfoComputerEnv { outerWidth: number; outerHeight: number; lineHeight: number; + maxLineNumber: number; lineNumbersDigitCount: number; typicalHalfwidthCharacterWidth: number; maxDigitWidth: number; @@ -1741,6 +1746,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption= 2 ? Math.round(minimap.scale * 2) : minimap.scale); + let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale); const minimapMaxColumn = minimap.maxColumn | 0; + const minimapEntireDocument = minimap.entireDocument; const scrollbar = options.get(EditorOption.scrollbar); const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0; @@ -1805,19 +1813,42 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption 1) { + minimapScale = 1; + minimapLineHeight = 1; + minimapCharWidth = minimapScale / pixelRatio; + } else { + const configuredFontScale = minimapScale; + minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); + minimapScale = Math.min(4, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); + minimapWidthMultiplier = Math.min(1, minimapScale / configuredFontScale); + minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; + } + } + renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks; // Given: @@ -1875,6 +1906,8 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption { outerWidth: input.outerWidth, outerHeight: input.outerHeight, lineHeight: input.lineHeight, + maxLineNumber: Math.pow(10, input.lineNumbersDigitCount) - 1, lineNumbersDigitCount: input.lineNumbersDigitCount, typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth, maxDigitWidth: input.maxDigitWidth, @@ -126,6 +127,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 98, verticalScrollbarWidth: 0, @@ -180,6 +183,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 97, verticalScrollbarWidth: 11, @@ -234,6 +239,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -288,6 +295,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -342,6 +351,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -396,6 +407,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 83, verticalScrollbarWidth: 0, @@ -450,6 +463,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 83, verticalScrollbarWidth: 0, @@ -504,6 +519,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 82, verticalScrollbarWidth: 0, @@ -558,6 +575,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 171, verticalScrollbarWidth: 0, @@ -612,6 +631,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapLineHeight: 1, + minimapWidthMultiplier: 1, viewportColumn: 169, verticalScrollbarWidth: 0, @@ -666,6 +687,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapLineHeight: 2, + minimapWidthMultiplier: 1, viewportColumn: 89, verticalScrollbarWidth: 0, @@ -720,6 +743,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapLineHeight: 4, + minimapWidthMultiplier: 1, viewportColumn: 89, verticalScrollbarWidth: 0, @@ -774,6 +799,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 945, minimapWidth: 55, + minimapLineHeight: 4, + minimapWidthMultiplier: 1, viewportColumn: 93, verticalScrollbarWidth: 0, @@ -828,6 +855,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 0, minimapWidth: 55, + minimapLineHeight: 4, + minimapWidthMultiplier: 1, viewportColumn: 93, verticalScrollbarWidth: 0, @@ -882,6 +911,8 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 1096, minimapWidth: 91, + minimapLineHeight: 4, + minimapWidthMultiplier: 1, viewportColumn: 83, verticalScrollbarWidth: 14, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index bd959e1267d..d234e6d9159 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3330,6 +3330,8 @@ declare namespace monaco.editor { * The width of the minimap */ readonly minimapWidth: number; + readonly minimapLineHeight: number; + readonly minimapWidthMultiplier: number; /** * Minimap render type */ From ee74224b8898d4f060a857b61dedb21467dd299e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 15:28:18 +0100 Subject: [PATCH 106/192] linux: automatically set force-renderer-accessibility when user sets editor.accessibilitySupport #90446 --- src/main.js | 3 ++- .../relauncher/browser/relauncher.contribution.ts | 12 +++++++++++- .../electron-browser/desktop.contribution.ts | 4 ++++ .../accessibility/node/accessibilityService.ts | 13 +++++++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main.js b/src/main.js index 7242b0cb156..4bbbf263dab 100644 --- a/src/main.js +++ b/src/main.js @@ -131,7 +131,8 @@ function configureCommandlineSwitchesSync(cliArgs) { 'disable-hardware-acceleration', // provided by Electron - 'disable-color-correct-rendering' + 'disable-color-correct-rendering', + 'force-renderer-accessibility' ]; // Read argv config diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index a152b50423d..8ff6aff2343 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; -import { isMacintosh, isNative } from 'vs/base/common/platform'; +import { isMacintosh, isNative, isLinux } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -26,6 +26,7 @@ interface IConfiguration extends IWindowsConfiguration { telemetry: { enableCrashReporter: boolean }; workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; + editor: { accessibilitySupport: 'on' | 'off' | 'auto' }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -38,6 +39,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private enableCrashReporter: boolean | undefined; private treeHorizontalScrolling: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; + private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; constructor( @IHostService private readonly hostService: IHostService, @@ -103,6 +105,14 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo this.enableCrashReporter = config.telemetry.enableCrashReporter; changed = true; } + + // On linux turning on accessibility support will also pass this flag to the chrome renderer, thus a restart is required + if (isLinux && typeof config.editor?.accessibilitySupport === 'string' && config.editor.accessibilitySupport !== this.accessibilitySupport) { + this.accessibilitySupport = config.editor.accessibilitySupport; + if (this.accessibilitySupport === 'on') { + changed = true; + } + } } // Notify only when changed and we are the focused window (avoids notification spam across windows) diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 61e2e2e5885..4064bb05c05 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -341,6 +341,10 @@ import product from 'vs/platform/product/common/product'; 'disable-color-correct-rendering': { type: 'boolean', description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.') + }, + 'force-renderer-accessibility': { + type: 'boolean', + description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.') } } }); diff --git a/src/vs/workbench/services/accessibility/node/accessibilityService.ts b/src/vs/workbench/services/accessibility/node/accessibilityService.ts index faedc3fd318..27c4d124954 100644 --- a/src/vs/workbench/services/accessibility/node/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/node/accessibilityService.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; 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'; interface AccessibilityMetrics { enabled: boolean; @@ -29,10 +30,18 @@ export class NodeAccessibilityService extends AccessibilityService implements IA @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService private readonly _telemetryService: ITelemetryService + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IJSONEditingService jsonEditingService: IJSONEditingService ) { super(contextKeyService, configurationService); this.setAccessibilitySupport(environmentService.configuration.accessibilitySupport ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); + if (isLinux) { + this._register(this.onDidChangeScreenReaderOptimized(async () => { + if (this.isScreenReaderOptimized()) { + await jsonEditingService.write(environmentService.argvResource, [{ key: 'force-renderer-accessibility', value: true }], true); + } + })); + } } alwaysUnderlineAccessKeys(): Promise { From f4df26f80ef5f4d74778e3548960f5f342bde934 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 15:30:52 +0100 Subject: [PATCH 107/192] Revert "linux: automatically set force-renderer-accessibility when user sets editor.accessibilitySupport" This reverts commit ee74224b8898d4f060a857b61dedb21467dd299e. --- src/main.js | 3 +-- .../relauncher/browser/relauncher.contribution.ts | 12 +----------- .../electron-browser/desktop.contribution.ts | 4 ---- .../accessibility/node/accessibilityService.ts | 13 ++----------- 4 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/main.js b/src/main.js index 4bbbf263dab..7242b0cb156 100644 --- a/src/main.js +++ b/src/main.js @@ -131,8 +131,7 @@ function configureCommandlineSwitchesSync(cliArgs) { 'disable-hardware-acceleration', // provided by Electron - 'disable-color-correct-rendering', - 'force-renderer-accessibility' + 'disable-color-correct-rendering' ]; // Read argv config diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 8ff6aff2343..a152b50423d 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten 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 { isMacintosh, isNative } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -26,7 +26,6 @@ interface IConfiguration extends IWindowsConfiguration { telemetry: { enableCrashReporter: boolean }; workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; - editor: { accessibilitySupport: 'on' | 'off' | 'auto' }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -39,7 +38,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private enableCrashReporter: boolean | undefined; private treeHorizontalScrolling: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; - private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; constructor( @IHostService private readonly hostService: IHostService, @@ -105,14 +103,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo this.enableCrashReporter = config.telemetry.enableCrashReporter; changed = true; } - - // On linux turning on accessibility support will also pass this flag to the chrome renderer, thus a restart is required - if (isLinux && typeof config.editor?.accessibilitySupport === 'string' && config.editor.accessibilitySupport !== this.accessibilitySupport) { - this.accessibilitySupport = config.editor.accessibilitySupport; - if (this.accessibilitySupport === 'on') { - changed = true; - } - } } // Notify only when changed and we are the focused window (avoids notification spam across windows) diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 4064bb05c05..61e2e2e5885 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -341,10 +341,6 @@ import product from 'vs/platform/product/common/product'; 'disable-color-correct-rendering': { type: 'boolean', description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.') - }, - 'force-renderer-accessibility': { - type: 'boolean', - description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.') } } }); diff --git a/src/vs/workbench/services/accessibility/node/accessibilityService.ts b/src/vs/workbench/services/accessibility/node/accessibilityService.ts index 27c4d124954..faedc3fd318 100644 --- a/src/vs/workbench/services/accessibility/node/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/node/accessibilityService.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { isWindows, isLinux } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; 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'; interface AccessibilityMetrics { enabled: boolean; @@ -30,18 +29,10 @@ export class NodeAccessibilityService extends AccessibilityService implements IA @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IJSONEditingService jsonEditingService: IJSONEditingService + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { super(contextKeyService, configurationService); this.setAccessibilitySupport(environmentService.configuration.accessibilitySupport ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled); - if (isLinux) { - this._register(this.onDidChangeScreenReaderOptimized(async () => { - if (this.isScreenReaderOptimized()) { - await jsonEditingService.write(environmentService.argvResource, [{ key: 'force-renderer-accessibility', value: true }], true); - } - })); - } } alwaysUnderlineAccessKeys(): Promise { From 798baf0534a16e63400384fd95d9a62accc60ed9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 15:37:07 +0100 Subject: [PATCH 108/192] Move more minimap layout to EditorLayoutInfoComputer --- .../browser/viewParts/minimap/minimap.ts | 44 +++++-------------- src/vs/editor/common/config/editorOptions.ts | 29 ++++++++++-- src/vs/monaco.d.ts | 7 ++- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index c82c8f5566d..b8526ae9e4d 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -91,12 +91,12 @@ class MinimapOptions { */ public readonly canvasOuterHeight: number; - public readonly backgroundColor: RGBA8; - public readonly fontScale: number; public readonly minimapLineHeight: number; public readonly minimapCharWidth: number; + public readonly charRenderer: () => MinimapCharRenderer; + public readonly backgroundColor: RGBA8; constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker, modelLineCount: number, isSampling: boolean) { const options = configuration.options; @@ -117,39 +117,17 @@ class MinimapOptions { this.minimapWidth = layoutInfo.minimapWidth; this.minimapHeight = layoutInfo.height; - this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth); - this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight); + this.canvasInnerWidth = layoutInfo.minimapCanvasInnerWidth; + this.canvasInnerHeight = layoutInfo.minimapCanvasInnerHeight; + this.canvasOuterWidth = layoutInfo.minimapCanvasOuterWidth; + this.canvasOuterHeight = layoutInfo.minimapCanvasOuterHeight; - this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio; - this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; - - this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker); - - const baseCharHeight = (this.renderMinimap === RenderMinimap.Text ? Constants.BASE_CHAR_HEIGHT : Constants.BASE_CHAR_HEIGHT + 1); - - let fontScale: number; - const minimapLineHeight = layoutInfo.minimapLineHeight; - if (this.renderMinimap === RenderMinimap.None) { - fontScale = Math.round(minimapOpts.scale * pixelRatio); - } else if (minimapOpts.entireDocument) { - if (isSampling) { - fontScale = 1; - } else { - this.canvasInnerWidth = Math.floor(this.canvasInnerWidth * layoutInfo.minimapWidthMultiplier); - const extraLinesBeyondLastLine = this.scrollBeyondLastLine ? (layoutInfo.height / this.lineHeight - 1) : 0; - fontScale = Math.min(4, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); - - const actualMinimapHeight = (modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight; - this.canvasInnerHeight = Math.ceil(actualMinimapHeight); - } - } else { - fontScale = Math.round(minimapOpts.scale * pixelRatio); - } - - this.fontScale = fontScale; - this.minimapLineHeight = minimapLineHeight; + this.fontScale = layoutInfo.minimapScale; + this.minimapLineHeight = layoutInfo.minimapLineHeight; this.minimapCharWidth = Constants.BASE_CHAR_WIDTH * this.fontScale; + this.charRenderer = once(() => MinimapCharRendererFactory.create(this.fontScale, fontInfo.fontFamily)); + this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker); } private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 { @@ -176,10 +154,10 @@ class MinimapOptions { && this.canvasInnerHeight === other.canvasInnerHeight && this.canvasOuterWidth === other.canvasOuterWidth && this.canvasOuterHeight === other.canvasOuterHeight - && this.backgroundColor.equals(other.backgroundColor) && this.fontScale === other.fontScale && this.minimapLineHeight === other.minimapLineHeight && this.minimapCharWidth === other.minimapCharWidth + && this.backgroundColor.equals(other.backgroundColor) ); } } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index b99da721783..b312fd966fd 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1687,8 +1687,13 @@ export interface EditorLayoutInfo { * The width of the minimap */ readonly minimapWidth: number; + readonly minimapIsSampling: boolean; + readonly minimapScale: number; readonly minimapLineHeight: number; - readonly minimapWidthMultiplier: number; + readonly minimapCanvasInnerWidth: number; + readonly minimapCanvasInnerHeight: number; + readonly minimapCanvasOuterWidth: number; + readonly minimapCanvasOuterHeight: number; /** * Minimap render type @@ -1817,17 +1822,24 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption 1) { + minimapIsSampling = true; minimapScale = 1; minimapLineHeight = 1; minimapCharWidth = minimapScale / pixelRatio; @@ -1846,6 +1859,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption Date: Fri, 14 Feb 2020 15:42:25 +0100 Subject: [PATCH 109/192] linux: automatically set force-renderer-accessibility when user sets editor.accessibilitySupport --- src/main.js | 3 ++- .../browser/relauncher.contribution.ts | 12 ++++++++- .../electron-browser/desktop.contribution.ts | 4 +++ .../node/accessibilityService.ts | 26 ++++++++++++++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/main.js b/src/main.js index 7242b0cb156..4bbbf263dab 100644 --- a/src/main.js +++ b/src/main.js @@ -131,7 +131,8 @@ function configureCommandlineSwitchesSync(cliArgs) { 'disable-hardware-acceleration', // provided by Electron - 'disable-color-correct-rendering' + 'disable-color-correct-rendering', + 'force-renderer-accessibility' ]; // Read argv config diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index a152b50423d..8ff6aff2343 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; -import { isMacintosh, isNative } from 'vs/base/common/platform'; +import { isMacintosh, isNative, isLinux } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -26,6 +26,7 @@ interface IConfiguration extends IWindowsConfiguration { telemetry: { enableCrashReporter: boolean }; workbench: { list: { horizontalScrolling: boolean } }; debug: { console: { wordWrap: boolean } }; + editor: { accessibilitySupport: 'on' | 'off' | 'auto' }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -38,6 +39,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private enableCrashReporter: boolean | undefined; private treeHorizontalScrolling: boolean | undefined; private debugConsoleWordWrap: boolean | undefined; + private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; constructor( @IHostService private readonly hostService: IHostService, @@ -103,6 +105,14 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo this.enableCrashReporter = config.telemetry.enableCrashReporter; changed = true; } + + // On linux turning on accessibility support will also pass this flag to the chrome renderer, thus a restart is required + if (isLinux && typeof config.editor?.accessibilitySupport === 'string' && config.editor.accessibilitySupport !== this.accessibilitySupport) { + this.accessibilitySupport = config.editor.accessibilitySupport; + if (this.accessibilitySupport === 'on') { + changed = true; + } + } } // Notify only when changed and we are the focused window (avoids notification spam across windows) diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 61e2e2e5885..4064bb05c05 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -341,6 +341,10 @@ import product from 'vs/platform/product/common/product'; 'disable-color-correct-rendering': { type: 'boolean', description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.') + }, + 'force-renderer-accessibility': { + type: 'boolean', + description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.') } } }); diff --git a/src/vs/workbench/services/accessibility/node/accessibilityService.ts b/src/vs/workbench/services/accessibility/node/accessibilityService.ts index faedc3fd318..a2911ba04a5 100644 --- a/src/vs/workbench/services/accessibility/node/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/node/accessibilityService.ts @@ -4,13 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Registry } from 'vs/platform/registry/common/platform'; import { AccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; 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 { IEnvironmentService } from 'vs/platform/environment/common/environment'; interface AccessibilityMetrics { enabled: boolean; @@ -65,3 +70,22 @@ export class NodeAccessibilityService extends AccessibilityService implements IA } registerSingleton(IAccessibilityService, NodeAccessibilityService, true); + +// On linux we do not automatically detect that a screen reader is detected, thus we have to implicitly notify the renderer to enable accessibility when user configures it in settings +class LinuxAccessibilityContribution implements IWorkbenchContribution { + constructor( + @IJSONEditingService jsonEditingService: IJSONEditingService, + @IAccessibilityService accessibilityService: AccessibilityService, + @IEnvironmentService environmentService: IEnvironmentService + ) { + accessibilityService.onDidChangeScreenReaderOptimized(async () => { + if (accessibilityService.isScreenReaderOptimized()) { + await jsonEditingService.write(environmentService.argvResource, [{ key: 'force-renderer-accessibility', value: true }], true); + } + }); + } +} + +if (isLinux) { + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LinuxAccessibilityContribution, LifecyclePhase.Ready); +} From 6e2782fa5a2d7e511b1c5dfd8b11474df9d2b884 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Feb 2020 15:58:25 +0100 Subject: [PATCH 110/192] fix https://github.com/microsoft/vscode/issues/90697 --- .../contrib/outline/browser/outlinePane.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 7214dddd208..c4ba64d1e82 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -27,7 +27,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IResourceInput, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; +import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; @@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; -import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { OutlineConfigKeys, OutlineViewFocused, OutlineViewFiltered } from 'vs/editor/contrib/documentSymbols/outline'; import { FuzzyScore } from 'vs/base/common/filters'; import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree'; @@ -49,6 +49,7 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDeco import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; class RequestState { @@ -261,7 +262,7 @@ export class OutlinePane extends ViewPane { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IThemeService private readonly _themeService: IThemeService, @IStorageService private readonly _storageService: IStorageService, - @IEditorService private readonly _editorService: IEditorService, + @ICodeEditorService private readonly _editorService: ICodeEditorService, @IMarkerDecorationsService private readonly _markerDecorationService: IMarkerDecorationsService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, @@ -347,7 +348,7 @@ export class OutlinePane extends ViewPane { this._disposables.push(this._tree); this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this)); - this._disposables.push(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => { + this._disposables.push(this.viewDescriptorService.onDidChangeLocation(({ views }) => { if (views.some(v => v.id === this.id)) { this._tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } }); } @@ -629,15 +630,18 @@ export class OutlinePane extends ViewPane { } private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, focus: boolean, aside: boolean): Promise { - - await this._editorService.openEditor({ - resource: model.textModel.uri, - options: { - preserveFocus: !focus, - selection: Range.collapseToStart(element.symbol.selectionRange), - selectionRevealType: TextEditorSelectionRevealType.NearTop, - } - } as IResourceInput, aside ? SIDE_GROUP : ACTIVE_GROUP); + await this._editorService.openCodeEditor( + { + resource: model.textModel.uri, + options: { + preserveFocus: !focus, + selection: Range.collapseToStart(element.symbol.selectionRange), + selectionRevealType: TextEditorSelectionRevealType.NearTop, + } + }, + this._editorService.getActiveCodeEditor(), + aside + ); } private _revealEditorSelection(model: OutlineModel, selection: Selection): void { From 5970b514342781984475e21d0d81c212c9f97afa Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 16:04:33 +0100 Subject: [PATCH 111/192] only allow forceRendererAccessibility flag on linux --- src/main.js | 6 ++++-- .../electron-browser/desktop.contribution.ts | 18 +++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main.js b/src/main.js index 4bbbf263dab..0e90f4504c8 100644 --- a/src/main.js +++ b/src/main.js @@ -131,9 +131,11 @@ function configureCommandlineSwitchesSync(cliArgs) { 'disable-hardware-acceleration', // provided by Electron - 'disable-color-correct-rendering', - 'force-renderer-accessibility' + 'disable-color-correct-rendering' ]; + if (process.platform === 'linux') { + SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility'); + } // Read argv config const argvConfig = readArgvConfigSync(); diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 4064bb05c05..f1d8f9af253 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -22,6 +22,7 @@ import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench import { IElectronService } from 'vs/platform/electron/node/electron'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import product from 'vs/platform/product/common/product'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; // Actions (function registerActions(): void { @@ -321,8 +322,7 @@ import product from 'vs/platform/product/common/product'; (function registerJSONSchemas(): void { const argvDefinitionFileSchemaId = 'vscode://schemas/argv'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); - - jsonRegistry.registerSchema(argvDefinitionFileSchemaId, { + const schema: IJSONSchema = { id: argvDefinitionFileSchemaId, allowComments: true, allowTrailingCommas: true, @@ -341,11 +341,15 @@ import product from 'vs/platform/product/common/product'; 'disable-color-correct-rendering': { type: 'boolean', description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.') - }, - 'force-renderer-accessibility': { - type: 'boolean', - description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.') } } - }); + }; + if (isLinux) { + schema.properties!['force-renderer-accessibility'] = { + type: 'boolean', + description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.'), + }; + } + + jsonRegistry.registerSchema(argvDefinitionFileSchemaId, schema); })(); From 59ac0a8e57ab35245f3ca2d49eff1772a1714940 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 14 Feb 2020 07:09:45 -0800 Subject: [PATCH 112/192] Fix typo --- .../platform/userDataSync/common/userDataAutoSyncService.ts | 2 +- src/vs/workbench/common/views.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 7ebe69a2c9c..e4d1c0ae4f8 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -98,7 +98,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto async triggerAutoSync(): Promise { if (this.enabled) { return this.syncDelayer.trigger(() => { - this.logService.info('Auto Sync: Triggerred.'); + this.logService.info('Auto Sync: Triggered.'); return this.sync(false, true); }, this.successiveFailures ? 1000 * 1 * Math.min(this.successiveFailures, 60) /* Delay by number of seconds as number of failures up to 1 minute */ diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 0a6073ca70b..1620263f6cb 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -57,12 +57,12 @@ export interface IViewContainerDescriptor { export interface IViewContainersRegistry { /** - * An event that is triggerred when a view container is registered. + * An event that is triggered when a view container is registered. */ readonly onDidRegister: Event<{ viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation }>; /** - * An event that is triggerred when a view container is deregistered. + * An event that is triggered when a view container is deregistered. */ readonly onDidDeregister: Event<{ viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation }>; From 6f4962b27b7a437cdc73172b1f789adc1c5c4a55 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 16:33:39 +0100 Subject: [PATCH 113/192] Clean up minimap sampling logic --- .../browser/viewParts/minimap/minimap.ts | 368 ++++++++---------- src/vs/editor/common/config/editorOptions.ts | 26 +- 2 files changed, 194 insertions(+), 200 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index b8526ae9e4d..8301788bdc9 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -13,7 +13,7 @@ import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH } from 'vs/editor/common/config/editorOptions'; +import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { RGBA8 } from 'vs/editor/common/core/rgba'; import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; @@ -24,7 +24,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewLineData, ViewModelDecoration, IViewModel } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; @@ -44,8 +44,6 @@ const GUTTER_DECORATION_WIDTH = 2; class MinimapOptions { - public readonly isSampling: boolean; - public readonly renderMinimap: RenderMinimap; public readonly entireDocument: boolean; @@ -98,14 +96,13 @@ class MinimapOptions { public readonly charRenderer: () => MinimapCharRenderer; public readonly backgroundColor: RGBA8; - constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker, modelLineCount: number, isSampling: boolean) { + constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker) { const options = configuration.options; const pixelRatio = options.get(EditorOption.pixelRatio); const layoutInfo = options.get(EditorOption.layoutInfo); const fontInfo = options.get(EditorOption.fontInfo); const minimapOpts = options.get(EditorOption.minimap); - this.isSampling = isSampling; this.renderMinimap = layoutInfo.renderMinimap | 0; this.entireDocument = minimapOpts.entireDocument; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); @@ -139,8 +136,7 @@ class MinimapOptions { } public equals(other: MinimapOptions): boolean { - return (this.isSampling === other.isSampling - && this.renderMinimap === other.renderMinimap + return (this.renderMinimap === other.renderMinimap && this.entireDocument === other.entireDocument && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider @@ -523,42 +519,41 @@ type SamplingStateEvent = SamplingStateLinesInsertedEvent | SamplingStateLinesDe class MinimapSamplingState { - public static compute(configuration: IConfiguration, model: IViewModel, oldMinimapLines: number[] | null): [MinimapSamplingState, SamplingStateEvent[]] { - const options = configuration.options; + public static compute(options: IComputedEditorOptions, modelLineCount: number, oldSamplingState: MinimapSamplingState | null): [MinimapSamplingState | null, SamplingStateEvent[]] { const minimapOpts = options.get(EditorOption.minimap); - if (minimapOpts.enabled === false || !minimapOpts.entireDocument) { - return [new MinimapSamplingState(false, 1, []), []]; + const layoutInfo = options.get(EditorOption.layoutInfo); + if (!minimapOpts.enabled || !layoutInfo.minimapIsSampling) { + return [null, []]; } - const layoutInfo = options.get(EditorOption.layoutInfo); + // ratio is intentionally not part of the layout to avoid the layout changing all the time + // so we need to recompute it again... const pixelRatio = options.get(EditorOption.pixelRatio); const lineHeight = options.get(EditorOption.lineHeight); const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); - const modelLineCount = model.getLineCount(); - - const extraLinesBeyondLastLine = scrollBeyondLastLine ? (layoutInfo.height / lineHeight - 1) : 0; - const desiredRatio = (modelLineCount + extraLinesBeyondLastLine) / (pixelRatio * layoutInfo.height); - const minimapLineCount = Math.floor(modelLineCount / desiredRatio); + const { minimapLineCount } = EditorLayoutInfoComputer.computeEntireDocumentMinimapLineCount({ + modelLineCount: modelLineCount, + scrollBeyondLastLine: scrollBeyondLastLine, + height: layoutInfo.height, + lineHeight: lineHeight, + pixelRatio: pixelRatio + }); const ratio = modelLineCount / minimapLineCount; + const halfRatio = ratio / 2; - if (ratio <= 1) { - return [new MinimapSamplingState(false, 1, []), []]; - } - - if (!oldMinimapLines || oldMinimapLines.length === 0) { + if (!oldSamplingState || oldSamplingState.minimapLines.length === 0) { let result: number[] = []; result[0] = 1; if (minimapLineCount > 1) { - const halfRatio = ratio / 2; for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) { result[i] = Math.round(i * ratio + halfRatio); } result[minimapLineCount - 1] = modelLineCount; } - return [new MinimapSamplingState(true, ratio, result), []]; + return [new MinimapSamplingState(ratio, result), []]; } - const halfRatio = ratio / 2; + const oldMinimapLines = oldSamplingState.minimapLines; const oldLength = oldMinimapLines.length; let result: number[] = []; let oldIndex = 0; @@ -631,46 +626,118 @@ class MinimapSamplingState { events = [{ type: 'flush' }]; } - return [new MinimapSamplingState(true, ratio, result), events]; + return [new MinimapSamplingState(ratio, result), events]; } constructor( - public readonly isSampling: boolean, public readonly samplingRatio: number, public readonly minimapLines: number[] ) { } + + public modelLineToMinimapLine(lineNumber: number): number { + return Math.min(this.minimapLines.length, Math.max(1, Math.round(lineNumber / this.samplingRatio))); + } + + /** + * Will return null if the model line ranges are not intersecting with a sampled model line. + */ + public modelLineRangeToMinimapLineRange(fromLineNumber: number, toLineNumber: number): [number, number] | null { + let fromLineIndex = this.modelLineToMinimapLine(fromLineNumber) - 1; + while (fromLineIndex > 0 && this.minimapLines[fromLineIndex - 1] >= fromLineNumber) { + fromLineIndex--; + } + let toLineIndex = this.modelLineToMinimapLine(toLineNumber) - 1; + while (toLineIndex + 1 < this.minimapLines.length && this.minimapLines[toLineIndex + 1] <= toLineNumber) { + toLineIndex++; + } + if (fromLineIndex === toLineIndex) { + const sampledLineNumber = this.minimapLines[fromLineIndex]; + if (sampledLineNumber < fromLineNumber || sampledLineNumber > toLineNumber) { + // This line is not part of the sampled lines ==> nothing to do + return null; + } + } + return [fromLineIndex + 1, toLineIndex + 1]; + } + + /** + * Will always return a range, even if it is not intersecting with a sampled model line. + */ + public decorationLineRangeToMinimapLineRange(startLineNumber: number, endLineNumber: number): [number, number] { + let minimapLineStart = this.modelLineToMinimapLine(startLineNumber); + let minimapLineEnd = this.modelLineToMinimapLine(endLineNumber); + if (startLineNumber !== endLineNumber && minimapLineEnd === minimapLineStart) { + if (minimapLineEnd === this.minimapLines.length) { + if (minimapLineStart > 1) { + minimapLineStart--; + } + } else { + minimapLineEnd++; + } + } + return [minimapLineStart, minimapLineEnd]; + } + + public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): [number, number] { + // have the mapping be sticky + const deletedLineCount = e.toLineNumber - e.fromLineNumber + 1; + let changeStartIndex = this.minimapLines.length; + let changeEndIndex = 0; + for (let i = this.minimapLines.length - 1; i >= 0; i--) { + if (this.minimapLines[i] < e.fromLineNumber) { + break; + } + if (this.minimapLines[i] <= e.toLineNumber) { + // this line got deleted => move to previous available + this.minimapLines[i] = Math.max(1, e.fromLineNumber - 1); + changeStartIndex = Math.min(changeStartIndex, i); + changeEndIndex = Math.max(changeEndIndex, i); + } else { + this.minimapLines[i] -= deletedLineCount; + } + } + return [changeStartIndex, changeEndIndex]; + } + + public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void { + // have the mapping be sticky + const insertedLineCount = e.toLineNumber - e.fromLineNumber + 1; + for (let i = this.minimapLines.length - 1; i >= 0; i--) { + if (this.minimapLines[i] < e.fromLineNumber) { + break; + } + this.minimapLines[i] += insertedLineCount; + } + } } export class Minimap extends ViewPart implements IMinimapModel { - private _selections: Selection[]; - private _minimapSelections: Selection[]; - - private _isSampling: boolean; - private _samplingRatio: number; - private _minimapLines: number[]; - private _shouldCheckSampling: boolean; - public readonly tokensColorTracker: MinimapTokensColorTracker; + + private _selections: Selection[]; + private _minimapSelections: Selection[] | null; + public options: MinimapOptions; + private _samplingState: MinimapSamplingState | null; + private _shouldCheckSampling: boolean; + private _actual: InnerMinimap; constructor(context: ViewContext) { super(context); - this._selections = []; - this._minimapSelections = []; - - const [samplingState,] = MinimapSamplingState.compute(this._context.configuration, this._context.model, null); - this._isSampling = samplingState.isSampling; - this._samplingRatio = samplingState.samplingRatio; - this._minimapLines = samplingState.minimapLines; - this._shouldCheckSampling = false; - this.tokensColorTracker = MinimapTokensColorTracker.getInstance(); - this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._context.model.getLineCount(), this._isSampling); + + this._selections = []; + this._minimapSelections = null; + + this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker); + const [samplingState,] = MinimapSamplingState.compute(this._context.configuration.options, this._context.model.getLineCount(), null); + this._samplingState = samplingState; + this._shouldCheckSampling = false; this._actual = new InnerMinimap(context.theme, this); } @@ -685,11 +752,12 @@ export class Minimap extends ViewPart implements IMinimapModel { } private _onOptionsMaybeChanged(): boolean { - const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._context.model.getLineCount(), this._isSampling); + const opts = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker); if (this.options.equals(opts)) { return false; } this.options = opts; + this._recreateLineSampling(); this._actual.onDidChangeOptions(); return true; } @@ -697,14 +765,11 @@ export class Minimap extends ViewPart implements IMinimapModel { // ---- begin view event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - let shouldRender = false; - shouldRender = this._recreateLineSampling() || shouldRender; - shouldRender = this._onOptionsMaybeChanged() || shouldRender; - return shouldRender; + return this._onOptionsMaybeChanged(); } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { this._selections = e.selections; - this._recomputeMinimapSelections(); + this._minimapSelections = null; return this._actual.onSelectionChanged(); } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { @@ -714,8 +779,8 @@ export class Minimap extends ViewPart implements IMinimapModel { return this._actual.onFlushed(); } public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { - if (this._isSampling) { - const minimapLineRange = this._modelLineRangeToMinimapLineRange(e.fromLineNumber, e.toLineNumber); + if (this._samplingState) { + const minimapLineRange = this._samplingState.modelLineRangeToMinimapLineRange(e.fromLineNumber, e.toLineNumber); if (minimapLineRange) { return this._actual.onLinesChanged(minimapLineRange[0], minimapLineRange[1]); } else { @@ -726,52 +791,23 @@ export class Minimap extends ViewPart implements IMinimapModel { } } public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { - if (this._isSampling) { - // have the mapping be sticky - const deletedLineCount = e.toLineNumber - e.fromLineNumber + 1; - let changeStartIndex = this._minimapLines.length; - let changeEndIndex = 0; - for (let i = this._minimapLines.length - 1; i >= 0; i--) { - if (this._minimapLines[i] < e.fromLineNumber) { - break; - } - if (this._minimapLines[i] <= e.toLineNumber) { - // this line got deleted => move to previous available - this._minimapLines[i] = Math.max(1, e.fromLineNumber - 1); - changeStartIndex = Math.min(changeStartIndex, i); - changeEndIndex = Math.max(changeEndIndex, i); - } else { - this._minimapLines[i] -= deletedLineCount; - } - } + if (this._samplingState) { + const [changeStartIndex, changeEndIndex] = this._samplingState.onLinesDeleted(e); if (changeStartIndex <= changeEndIndex) { this._actual.onLinesChanged(changeStartIndex + 1, changeEndIndex + 1); } this._shouldCheckSampling = true; return true; } else { - if (this.options.entireDocument && this._onOptionsMaybeChanged()) { - return true; - } return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber); } } public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { - if (this._isSampling) { - // have the mapping be sticky - const insertedLineCount = e.toLineNumber - e.fromLineNumber + 1; - for (let i = this._minimapLines.length - 1; i >= 0; i--) { - if (this._minimapLines[i] < e.fromLineNumber) { - break; - } - this._minimapLines[i] += insertedLineCount; - } + if (this._samplingState) { + this._samplingState.onLinesInserted(e); this._shouldCheckSampling = true; return true; } else { - if (this.options.entireDocument && this._onOptionsMaybeChanged()) { - return true; - } return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber); } } @@ -785,10 +821,10 @@ export class Minimap extends ViewPart implements IMinimapModel { return true; } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { - if (this._isSampling) { + if (this._samplingState) { let ranges: { fromLineNumber: number; toLineNumber: number; }[] = []; for (const range of e.ranges) { - const minimapLineRange = this._modelLineRangeToMinimapLineRange(range.fromLineNumber, range.toLineNumber); + const minimapLineRange = this._samplingState.modelLineRangeToMinimapLineRange(range.fromLineNumber, range.toLineNumber); if (minimapLineRange) { ranges.push({ fromLineNumber: minimapLineRange[0], toLineNumber: minimapLineRange[1] }); } @@ -822,9 +858,9 @@ export class Minimap extends ViewPart implements IMinimapModel { let viewportStartLineNumber = ctx.visibleRange.startLineNumber; let viewportEndLineNumber = ctx.visibleRange.endLineNumber; - if (this._isSampling) { - viewportStartLineNumber = this._modelLineToMinimapLine(viewportStartLineNumber); - viewportEndLineNumber = this._modelLineToMinimapLine(viewportEndLineNumber); + if (this._samplingState) { + viewportStartLineNumber = this._samplingState.modelLineToMinimapLine(viewportStartLineNumber); + viewportEndLineNumber = this._samplingState.modelLineToMinimapLine(viewportEndLineNumber); } const minimapCtx: IMinimapRenderingContext = { @@ -847,105 +883,34 @@ export class Minimap extends ViewPart implements IMinimapModel { //#region IMinimapModel - private _recreateLineSampling(): boolean { - const wasSampling = this._isSampling; - const [samplingState, events] = MinimapSamplingState.compute(this._context.configuration, this._context.model, this._minimapLines); - this._isSampling = samplingState.isSampling; - this._samplingRatio = samplingState.samplingRatio; - this._minimapLines = samplingState.minimapLines; + private _recreateLineSampling(): void { + this._minimapSelections = null; - let optionsMightHaveChanged: boolean; - if (wasSampling) { - if (this._isSampling) { - // was sampling, is sampling - for (const event of events) { - switch (event.type) { - case 'deleted': - this._actual.onLinesDeleted(event.deleteFromLineNumber, event.deleteToLineNumber); - break; - case 'inserted': - this._actual.onLinesInserted(event.insertFromLineNumber, event.insertToLineNumber); - break; - case 'flush': - this._actual.onFlushed(); - break; - } + const wasSampling = Boolean(this._samplingState); + const [samplingState, events] = MinimapSamplingState.compute(this._context.configuration.options, this._context.model.getLineCount(), this._samplingState); + this._samplingState = samplingState; + + if (wasSampling && this._samplingState) { + // was sampling, is sampling + for (const event of events) { + switch (event.type) { + case 'deleted': + this._actual.onLinesDeleted(event.deleteFromLineNumber, event.deleteToLineNumber); + break; + case 'inserted': + this._actual.onLinesInserted(event.insertFromLineNumber, event.insertToLineNumber); + break; + case 'flush': + this._actual.onFlushed(); + break; } - optionsMightHaveChanged = false; - } else { - // was not sampling, is sampling - optionsMightHaveChanged = true; - } - } else { - if (this._isSampling) { - // was not sampling, is sampling - optionsMightHaveChanged = true; - } else { - // was not sampling, is not sampling - optionsMightHaveChanged = false; - } - } - - if (optionsMightHaveChanged) { - return this._onOptionsMaybeChanged(); - } - return false; - } - - private _modelLineToMinimapLine(lineNumber: number): number { - return Math.min(this._minimapLines.length, Math.max(1, Math.round(lineNumber / this._samplingRatio))); - } - - /** - * Will return null if the model line ranges are not intersecting with a sampled model line. - */ - private _modelLineRangeToMinimapLineRange(fromLineNumber: number, toLineNumber: number): [number, number] | null { - let fromLineIndex = this._modelLineToMinimapLine(fromLineNumber) - 1; - while (fromLineIndex > 0 && this._minimapLines[fromLineIndex - 1] >= fromLineNumber) { - fromLineIndex--; - } - let toLineIndex = this._modelLineToMinimapLine(toLineNumber) - 1; - while (toLineIndex + 1 < this._minimapLines.length && this._minimapLines[toLineIndex + 1] <= toLineNumber) { - toLineIndex++; - } - if (fromLineIndex === toLineIndex) { - const sampledLineNumber = this._minimapLines[fromLineIndex]; - if (sampledLineNumber < fromLineNumber || sampledLineNumber > toLineNumber) { - // This line is not part of the sampled lines ==> nothing to do - return null; - } - } - return [fromLineIndex + 1, toLineIndex + 1]; - } - - private _recomputeMinimapSelections(): void { - this._minimapSelections = []; - for (const selection of this._selections) { - if (this._isSampling) { - const isMultiline = (selection.startLineNumber !== selection.endLineNumber); - - let minimapLineStart = this._modelLineToMinimapLine(selection.startLineNumber); - let minimapLineEnd = this._modelLineToMinimapLine(selection.endLineNumber); - if (isMultiline && minimapLineEnd === minimapLineStart) { - if (minimapLineEnd === this._minimapLines.length) { - if (minimapLineStart > 1) { - minimapLineStart--; - } - } else { - minimapLineEnd++; - } - } - - this._minimapSelections.push(new Selection(minimapLineStart, selection.startColumn, minimapLineEnd, selection.endColumn)); - } else { - this._minimapSelections.push(selection); } } } public getLineCount(): number { - if (this._isSampling) { - return this._minimapLines.length; + if (this._samplingState) { + return this._samplingState.minimapLines.length; } return this._context.model.getLineCount(); } @@ -955,18 +920,18 @@ export class Minimap extends ViewPart implements IMinimapModel { } public getLineContent(lineNumber: number): string { - if (this._isSampling) { - return this._context.model.getLineContent(this._minimapLines[lineNumber - 1]); + if (this._samplingState) { + return this._context.model.getLineContent(this._samplingState.minimapLines[lineNumber - 1]); } return this._context.model.getLineContent(lineNumber); } public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): (ViewLineData | null)[] { - if (this._isSampling) { + if (this._samplingState) { let result: (ViewLineData | null)[] = []; for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) { if (needed[lineIndex]) { - result[lineIndex] = this._context.model.getViewLineData(this._minimapLines[startLineNumber + lineIndex - 1]); + result[lineIndex] = this._context.model.getViewLineData(this._samplingState.minimapLines[startLineNumber + lineIndex - 1]); } else { result[lineIndex] = null; } @@ -977,29 +942,40 @@ export class Minimap extends ViewPart implements IMinimapModel { } public getSelections(): Selection[] { + if (this._minimapSelections === null) { + if (this._samplingState) { + this._minimapSelections = []; + for (const selection of this._selections) { + const [minimapLineStart, minimapLineEnd] = this._samplingState.decorationLineRangeToMinimapLineRange(selection.startLineNumber, selection.endLineNumber); + this._minimapSelections.push(new Selection(minimapLineStart, selection.startColumn, minimapLineEnd, selection.endColumn)); + } + } else { + this._minimapSelections = this._selections; + } + } return this._minimapSelections; } public getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[] { let visibleRange: Range; - if (this._isSampling) { - const modelStartLineNumber = this._minimapLines[startLineNumber - 1]; - const modelEndLineNumber = this._minimapLines[endLineNumber - 1]; + if (this._samplingState) { + const modelStartLineNumber = this._samplingState.minimapLines[startLineNumber - 1]; + const modelEndLineNumber = this._samplingState.minimapLines[endLineNumber - 1]; visibleRange = new Range(modelStartLineNumber, 1, modelEndLineNumber, this._context.model.getLineMaxColumn(modelEndLineNumber)); } else { visibleRange = new Range(startLineNumber, 1, endLineNumber, this._context.model.getLineMaxColumn(endLineNumber)); } const decorations = this._context.model.getDecorationsInViewport(visibleRange); - if (this._isSampling) { + if (this._samplingState) { let result: ViewModelDecoration[] = []; for (const decoration of decorations) { if (!decoration.options.minimap) { continue; } const range = decoration.range; - const minimapStartLineNumber = this._modelLineToMinimapLine(range.startLineNumber); - const minimapEndLineNumber = this._modelLineToMinimapLine(range.endLineNumber); + const minimapStartLineNumber = this._samplingState.modelLineToMinimapLine(range.startLineNumber); + const minimapEndLineNumber = this._samplingState.modelLineToMinimapLine(range.endLineNumber); result.push(new ViewModelDecoration(new Range(minimapStartLineNumber, range.startColumn, minimapEndLineNumber, range.endColumn), decoration.options)); } return result; @@ -1012,8 +988,8 @@ export class Minimap extends ViewPart implements IMinimapModel { } public revealLineNumber(lineNumber: number): void { - if (this._isSampling) { - lineNumber = this._minimapLines[lineNumber - 1]; + if (this._samplingState) { + lineNumber = this._samplingState.minimapLines[lineNumber - 1]; } this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( 'mouse', diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index b312fd966fd..0a9ae7435d6 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -8,7 +8,6 @@ import * as platform from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { Constants } from 'vs/base/common/uint'; -// import { Constants as MinimapConstants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; @@ -1759,6 +1758,19 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption 1) { From 51cbc75279d93ab169c69bfffe68530c7cb77779 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 14 Feb 2020 07:42:46 -0800 Subject: [PATCH 114/192] debug: update js-debug-nightly to "2020.2.1117" @ 2020-02-14T01:05:56.75Z --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index c2755a8478b..eeacf8c78bc 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -46,7 +46,7 @@ }, { "name": "ms-vscode.js-debug-nightly", - "version": "2020.2.1117", + "version": "2020.2.1317", "forQualities": [ "insider" ], From 8f286708860325a176d1a7d0eb80e0ca55cacb56 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 14 Feb 2020 07:42:58 -0800 Subject: [PATCH 115/192] remove default view positions from cache --- .../views/browser/viewDescriptorService.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index 14366966e16..230a76fe556 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -537,6 +537,22 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor } } + // If a value is not present in the cache, it must be reset to default + this.viewContainersRegistry.all.forEach(viewContainer => { + const viewDescriptorCollection = this.getViewDescriptors(viewContainer); + viewDescriptorCollection.allViewDescriptors.forEach(viewDescriptor => { + if (!newCachedPositions.has(viewDescriptor.id)) { + const currentContainer = this.getViewContainer(viewDescriptor.id); + const defaultContainer = this.getDefaultContainer(viewDescriptor.id); + if (currentContainer && defaultContainer && currentContainer !== defaultContainer) { + this.moveViews([viewDescriptor], currentContainer, defaultContainer); + } + + this.cachedViewInfo.delete(viewDescriptor.id); + } + }); + }); + this.cachedViewInfo = this.getCachedViewPositions(); } } @@ -571,6 +587,16 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor }); }); + // Do no save default positions to the cache + // so that default changes can be recognized + // https://github.com/microsoft/vscode/issues/90414 + for (const [viewId, containerInfo] of this.cachedViewInfo) { + const defaultContainer = this.getDefaultContainer(viewId); + if (defaultContainer?.id === containerInfo.containerId) { + this.cachedViewInfo.delete(viewId); + } + } + this.cachedViewPositionsValue = JSON.stringify([...this.cachedViewInfo]); } From a42a6c29776da12701e33eaa09f99546aa69d20d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 16:48:27 +0100 Subject: [PATCH 116/192] Fix tests --- .../viewLayout/editorLayoutProvider.test.ts | 105 +++++++++++++++--- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index 24a6677c3b3..5f70e4e8d40 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -127,8 +127,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 800, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 800, viewportColumn: 98, verticalScrollbarWidth: 0, @@ -183,8 +188,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 800, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 800, viewportColumn: 97, verticalScrollbarWidth: 11, @@ -239,8 +249,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 800, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 800, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -295,8 +310,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -351,8 +371,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 88, verticalScrollbarWidth: 0, @@ -407,8 +432,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 83, verticalScrollbarWidth: 0, @@ -463,8 +493,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 83, verticalScrollbarWidth: 0, @@ -519,8 +554,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 82, verticalScrollbarWidth: 0, @@ -575,8 +615,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 171, verticalScrollbarWidth: 0, @@ -631,8 +676,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 1, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 0, + minimapCanvasInnerHeight: 900, + minimapCanvasOuterWidth: 0, + minimapCanvasOuterHeight: 900, viewportColumn: 169, verticalScrollbarWidth: 0, @@ -687,8 +737,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapIsSampling: false, + minimapScale: 1, minimapLineHeight: 2, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 97, + minimapCanvasInnerHeight: 800, + minimapCanvasOuterWidth: 97, + minimapCanvasOuterHeight: 800, viewportColumn: 89, verticalScrollbarWidth: 0, @@ -743,8 +798,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapIsSampling: false, + minimapScale: 2, minimapLineHeight: 4, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 194, + minimapCanvasInnerHeight: 1600, + minimapCanvasOuterWidth: 97, + minimapCanvasOuterHeight: 800, viewportColumn: 89, verticalScrollbarWidth: 0, @@ -799,8 +859,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 945, minimapWidth: 55, + minimapIsSampling: false, + minimapScale: 2, minimapLineHeight: 4, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 220, + minimapCanvasInnerHeight: 3200, + minimapCanvasOuterWidth: 55, + minimapCanvasOuterHeight: 800, viewportColumn: 93, verticalScrollbarWidth: 0, @@ -855,8 +920,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 0, minimapWidth: 55, + minimapIsSampling: false, + minimapScale: 2, minimapLineHeight: 4, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 220, + minimapCanvasInnerHeight: 3200, + minimapCanvasOuterWidth: 55, + minimapCanvasOuterHeight: 800, viewportColumn: 93, verticalScrollbarWidth: 0, @@ -911,8 +981,13 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 1096, minimapWidth: 91, + minimapIsSampling: false, + minimapScale: 2, minimapLineHeight: 4, - minimapWidthMultiplier: 1, + minimapCanvasInnerWidth: 182, + minimapCanvasInnerHeight: 844, + minimapCanvasOuterWidth: 91, + minimapCanvasOuterHeight: 422, viewportColumn: 83, verticalScrollbarWidth: 14, From b51e12159525ded4b690e34338014ab37efe0b3d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 14 Feb 2020 07:55:57 -0800 Subject: [PATCH 117/192] panel width --- src/vs/workbench/browser/parts/panel/panelPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index b1cdfc6f4eb..f9d079be854 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -56,7 +56,7 @@ export class PanelPart extends CompositePart implements IPanelService { //#region IView - readonly minimumWidth: number = 420; + readonly minimumWidth: number = 300; readonly maximumWidth: number = Number.POSITIVE_INFINITY; readonly minimumHeight: number = 77; readonly maximumHeight: number = Number.POSITIVE_INFINITY; From a48bce6f28e814c3c8a5917c59b8deda0069c9a9 Mon Sep 17 00:00:00 2001 From: Bailey Date: Fri, 14 Feb 2020 08:59:30 -0700 Subject: [PATCH 118/192] Revert "Add explorerView check to see if element is visible" This reverts commit 615c24ca3a921879ac4f72d3eb60d8d86ae55102. --- src/vs/base/browser/ui/list/listWidget.ts | 10 ---------- src/vs/base/browser/ui/tree/abstractTree.ts | 13 ------------- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ---- .../contrib/files/browser/views/explorerView.ts | 6 +----- 4 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 339cc5af913..026344c5e1b 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1546,16 +1546,6 @@ export class List implements ISpliceable, IDisposable { return this.getFocus().map(i => this.view.element(i)); } - isElementVisible(index: number) { - const viewTop = this.view.getScrollTop(); - const viewBottom = this.view.renderHeight + viewTop; - - const elementTop = this.view.elementTop(index); - const elementBottom = this.view.elementHeight(index) + elementTop; - - return (elementTop >= viewTop && elementBottom <= viewBottom); - } - reveal(index: number, relativeTop?: number): void { if (index < 0 || index >= this.length) { throw new ListError(this.user, `Invalid index ${index}`); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1032a5a723f..9e584efcc56 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1589,19 +1589,6 @@ export abstract class AbstractTree implements IDisposable this.view.open(indexes, browserEvent); } - isElementVisible(location: TRef) { - const index = this.model.getListIndex(location); - - if (index === -1) { - return false; - } - - const parentNode = this.model.getNode(this.model.getParentNodeLocation(location)); - const parentCollapsed = !parentNode.visible || parentNode.collapsed; - - return this.view.isElementVisible(index) && !parentCollapsed; - } - reveal(location: TRef, relativeTop?: number): void { this.model.expandTo(location); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 7edd15a2f62..bbdc7bc6054 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -662,10 +662,6 @@ export class AsyncDataTree implements IDisposable this.tree.open(nodes, browserEvent); } - isElementVisible(element: T) { - return this.tree.isElementVisible(this.getDataNode(element)); - } - reveal(element: T, relativeTop?: number): void { this.tree.reveal(this.getDataNode(element), relativeTop); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index cce6ba126a0..f7537747649 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -675,11 +675,7 @@ export class ExplorerView extends ViewPane { if (item.isDisposed) { return this.onSelectResource(resource, reveal, retry + 1); } - - // Don't scroll to the item if it's already visible - if (!this.tree.isElementVisible(item)) { - this.tree.reveal(item, 0.5); - } + this.tree.reveal(item, 0.5); } this.tree.setFocus([item]); From 631bbceb65911abb6474fa322f83cc6f31fc5e71 Mon Sep 17 00:00:00 2001 From: Bailey Date: Fri, 14 Feb 2020 09:00:43 -0700 Subject: [PATCH 119/192] Switch to getRelativeTop --- .../workbench/contrib/files/browser/views/explorerView.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index f7537747649..c913755c7b4 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -675,7 +675,11 @@ export class ExplorerView extends ViewPane { if (item.isDisposed) { return this.onSelectResource(resource, reveal, retry + 1); } - this.tree.reveal(item, 0.5); + + // Don't scroll to the item if it's already visible + if (this.tree.getRelativeTop(item) === null) { + this.tree.reveal(item, 0.5); + } } this.tree.setFocus([item]); From 2afb93e0083e7606895969292a0ff8034bdbc993 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 17:24:02 +0100 Subject: [PATCH 120/192] debug hover fix margins fixes #90341 --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 5 ++++- src/vs/workbench/contrib/debug/browser/media/debugHover.css | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index bb510b66fd7..292c33392fc 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -109,8 +109,10 @@ export class DebugHoverWidget implements IContentWidget { accessibilityProvider: new DebugHoverAccessibilityProvider(), mouseSupport: false, horizontalScrolling: true, + useShadows: false, overrideStyles: { - listBackground: editorHoverBackground + listBackground: editorHoverBackground, + } }); @@ -307,6 +309,7 @@ export class DebugHoverWidget implements IContentWidget { } hide(): void { + return; if (!this._isVisible) { return; } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugHover.css b/src/vs/workbench/contrib/debug/browser/media/debugHover.css index b01a3d1193d..a0406499aa3 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugHover.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugHover.css @@ -12,7 +12,6 @@ user-select: text; -webkit-user-select: text; word-break: break-all; - padding: 4px 5px; } .monaco-editor .debug-hover-widget .complex-value { @@ -62,6 +61,7 @@ overflow: auto; font-family: var(--monaco-monospace-font); max-height: 500px; + padding: 4px 5px; } .monaco-editor .debug-hover-widget .error { From 7e1c6dcf0fba3c1b066552074e6d2ce4eb2faa7d Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 17:29:51 +0100 Subject: [PATCH 121/192] fix stupid commit --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 292c33392fc..81638da9912 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -309,7 +309,6 @@ export class DebugHoverWidget implements IContentWidget { } hide(): void { - return; if (!this._isVisible) { return; } From d32013dfd01d790c6bbb9fd5568a2602b58b7c27 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Feb 2020 17:31:09 +0100 Subject: [PATCH 122/192] minor lint --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 81638da9912..5de2c78eea2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -111,8 +111,7 @@ export class DebugHoverWidget implements IContentWidget { horizontalScrolling: true, useShadows: false, overrideStyles: { - listBackground: editorHoverBackground, - + listBackground: editorHoverBackground } }); From 57b0310b21dd2ba4890e2dc7773cfe8a2bbdf6ba Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 17:39:59 +0100 Subject: [PATCH 123/192] Add tests for minimap entireDocument computation --- .../viewLayout/editorLayoutProvider.test.ts | 132 +++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index 5f70e4e8d40..a69efcd6a92 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -17,6 +17,7 @@ interface IEditorLayoutProviderOpts { readonly showLineNumbers: boolean; readonly lineNumbersMinChars: number; readonly lineNumbersDigitCount: number; + maxLineNumber?: number; readonly lineDecorationsWidth: number; @@ -32,6 +33,7 @@ interface IEditorLayoutProviderOpts { readonly minimapSide: 'left' | 'right'; readonly minimapRenderCharacters: boolean; readonly minimapMaxColumn: number; + minimapEntireDocument?: boolean; readonly pixelRatio: number; } @@ -50,7 +52,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { maxColumn: input.minimapMaxColumn, showSlider: 'mouseover', scale: 1, - entireDocument: false + entireDocument: input.minimapEntireDocument || false }; options._write(EditorOption.minimap, minimapOptions); const scrollbarOptions: InternalEditorScrollbarOptions = { @@ -78,7 +80,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { outerWidth: input.outerWidth, outerHeight: input.outerHeight, lineHeight: input.lineHeight, - maxLineNumber: Math.pow(10, input.lineNumbersDigitCount) - 1, + maxLineNumber: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1, lineNumbersDigitCount: input.lineNumbersDigitCount, typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth, maxDigitWidth: input.maxDigitWidth, @@ -941,6 +943,132 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { }); }); + test('EditorLayoutProvider 11 - render entire document in minimap without sampling', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 3, + maxLineNumber: 120, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapSide: 'right', + minimapRenderCharacters: true, + minimapMaxColumn: 150, + minimapEntireDocument: true, + pixelRatio: 2, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + + decorationsLeft: 0, + decorationsWidth: 10, + + contentLeft: 10, + contentWidth: 818, + + renderMinimap: RenderMinimap.Text, + minimapLeft: 828, + minimapWidth: 172, + minimapIsSampling: false, + minimapScale: 4, + minimapLineHeight: 13, + minimapCanvasInnerWidth: 344, + minimapCanvasInnerHeight: 1560, + minimapCanvasOuterWidth: 172, + minimapCanvasOuterHeight: 800, + viewportColumn: 81, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 12 - render entire document in minimap with sampling', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 4, + maxLineNumber: 2500, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapSide: 'right', + minimapRenderCharacters: true, + minimapMaxColumn: 150, + minimapEntireDocument: true, + pixelRatio: 2, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + + decorationsLeft: 0, + decorationsWidth: 10, + + contentLeft: 10, + contentWidth: 935, + + renderMinimap: RenderMinimap.Text, + minimapLeft: 945, + minimapWidth: 55, + minimapIsSampling: true, + minimapScale: 1, + minimapLineHeight: 1, + minimapCanvasInnerWidth: 110, + minimapCanvasInnerHeight: 1600, + minimapCanvasOuterWidth: 55, + minimapCanvasOuterHeight: 800, + viewportColumn: 93, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + test('issue #31312: When wrapping, leave 2px for the cursor', () => { doTest({ outerWidth: 1201, From f2ca025aa05005d7bcb6d833a545e382961685c5 Mon Sep 17 00:00:00 2001 From: chrisdias Date: Fri, 14 Feb 2020 11:05:49 -0800 Subject: [PATCH 124/192] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8d355e18cd..2712fd40e09 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "a87981a5cc52fed24735d447e954adccb9c9f914", + "distro": "821d61f0e38d2eb3e02bec3962fe61e4e5a61527", "author": { "name": "Microsoft Corporation" }, From c03b581840ec6627b68041655e7cd0e40dad3a20 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Feb 2020 16:23:26 -0800 Subject: [PATCH 125/192] Add test for copying text from webview --- .../src/singlefolder-tests/webview.test.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index e785f1d4afb..df8af33f6b2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -7,7 +7,7 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; import { join } from 'path'; -import { closeAllEditors, disposeAll, conditionalTest } from '../utils'; +import { closeAllEditors, disposeAll, conditionalTest, delay } from '../utils'; const webviewId = 'myWebview'; @@ -332,7 +332,27 @@ suite('Webview tests', () => { webview.webview.postMessage({ value: 1 }); await firstResponse; assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); + }); + test('webview can copy text from webview', async () => { + const expectedText = `webview text from: ${Date.now()}!`; + + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); + const ready = getMesssage(webview); + + + webview.webview.html = createHtmlDocumentWithBody(/*html*/` + ${expectedText} + `); + await ready; + + await vscode.commands.executeCommand('editor.action.webvieweditor.copy'); + await delay(200); // Make sure copy has time to reach webview + assert.strictEqual(await vscode.env.clipboard.readText(), expectedText); }); }); From 20fae66e378e5af6bcf8f51f650ff790f697f58e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Feb 2020 16:28:45 -0800 Subject: [PATCH 126/192] Add type annotations --- src/vs/workbench/contrib/webview/browser/pre/main.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 63c9af47e20..d82a472b172 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -449,6 +449,10 @@ }, 0); }); + /** + * @param {Document} contentDocument + * @param {Window} contentWindow + */ const onLoad = (contentDocument, contentWindow) => { if (contentDocument && contentDocument.body) { // Workaround for https://github.com/Microsoft/vscode/issues/12865 @@ -492,10 +496,12 @@ }, 200); newFrame.contentWindow.addEventListener('load', function (e) { + const contentDocument = /** @type {Document} */ (e.target); + if (loadTimeout) { clearTimeout(loadTimeout); loadTimeout = undefined; - onLoad(e.target, this); + onLoad(contentDocument, this); } }); From 8fd47cd5090f92d5fed25def26d6aa608ef6e3f8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Feb 2020 16:54:17 -0800 Subject: [PATCH 127/192] Enable select all in iframe based webviews --- .../webview/browser/baseWebviewElement.ts | 6 +++ .../browser/dynamicWebviewEditorOverlay.ts | 1 + .../contrib/webview/browser/pre/main.js | 39 +++++++++++-------- .../webview/browser/webview.contribution.ts | 7 +++- .../contrib/webview/browser/webview.ts | 2 + .../webview/browser/webviewCommands.ts | 24 ++++++++++++ .../contrib/webview/browser/webviewEditor.ts | 6 +++ .../electron-browser/webview.contribution.ts | 2 - .../electron-browser/webviewCommands.ts | 22 ----------- 9 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 5cf9df18ef8..d8f0b88ebc3 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -286,4 +286,10 @@ export abstract class BaseWebview extends Disposable { this.element.style.pointerEvents = ''; } } + + public selectAll() { + if (this.element) { + this._send('execCommand', 'selectAll'); + } + } } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 9848ce0cb70..fe444e8070d 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -187,6 +187,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd showFind(): void { this.withWebview(webview => webview.showFind()); } hideFind(): void { this.withWebview(webview => webview.hideFind()); } runFindAction(previous: boolean): void { this.withWebview(webview => webview.runFindAction(previous)); } + selectAll(): void { this.withWebview(webview => webview.selectAll()); } public getInnerWebview() { return this._webview.value; diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index d82a472b172..aa318da9d65 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -232,23 +232,23 @@ * @param {MouseEvent} event */ const handleAuxClick = - (event) => { - // Prevent middle clicks opening a broken link in the browser - if (!event.view || !event.view.document) { - return; - } - - if (event.button === 1) { - let node = /** @type {any} */ (event.target); - while (node) { - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { - event.preventDefault(); - break; - } - node = node.parentNode; + (event) => { + // Prevent middle clicks opening a broken link in the browser + if (!event.view || !event.view.document) { + return; } - } - }; + + if (event.button === 1) { + let node = /** @type {any} */ (event.target); + while (node) { + if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { + event.preventDefault(); + break; + } + node = node.parentNode; + } + } + }; /** * @param {KeyboardEvent} e @@ -545,6 +545,13 @@ initData.initialScrollProgress = progress; }); + host.onMessage('execCommand', (_event, data) => { + const target = getActiveFrame(); + if (!target) { + return; + } + target.contentDocument.execCommand(data); + }); trackFocus({ onFocus: () => host.postMessage('did-focus'), diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index c1ce99713e4..b8240e1bc17 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -14,7 +14,7 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; +import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand, SelectAllWebviewEditorCommand } from '../browser/webviewCommands'; import { WebviewEditor } from './webviewEditor'; import { WebviewInput } from './webviewEditorInput'; import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkbenchService'; @@ -50,6 +50,11 @@ registerAction2(class extends WebViewEditorFindPreviousCommand { constructor() { super(webviewActiveContextKeyExpr); } }); +registerAction2(class extends SelectAllWebviewEditorCommand { + constructor() { super(webviewActiveContextKeyExpr); } +}); + + const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction( SyncActionDescriptor.create(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 784bef65db4..b65f28416f2 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -93,6 +93,8 @@ export interface Webview extends IDisposable { hideFind(): void; runFindAction(previous: boolean): void; + selectAll(): void; + windowDidDragStart(): void; windowDidDragEnd(): void; } diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 9845fb414d9..5f870d1d15e 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -13,6 +13,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; export class ShowWebViewEditorFindWidgetAction extends Action2 { public static readonly ID = 'editor.action.webvieweditor.showFind'; @@ -97,6 +98,29 @@ export class WebViewEditorFindPreviousCommand extends Action2 { getActiveWebviewEditor(accessor)?.find(true); } } + +export class SelectAllWebviewEditorCommand extends Action2 { + public static readonly ID = 'editor.action.webvieweditor.selectAll'; + public static readonly LABEL = nls.localize('editor.action.webvieweditor.selectAll', 'Select all'); + + constructor(contextKeyExpr: ContextKeyExpr) { + const precondition = ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)); + super({ + id: SelectAllWebviewEditorCommand.ID, + title: SelectAllWebviewEditorCommand.LABEL, + keybinding: { + when: precondition, + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + weight: KeybindingWeight.EditorContrib + } + }); + } + + public run(accessor: ServicesAccessor, args: any): void { + getActiveWebviewEditor(accessor)?.selectAll(); + } +} + export class ReloadWebviewAction extends Action { static readonly ID = 'workbench.action.webview.reloadWebviewAction'; static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index a0e741c2917..449d93aebfa 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -88,6 +88,12 @@ export class WebviewEditor extends BaseEditor { }); } + public selectAll() { + this.withWebview(webview => { + webview.selectAll(); + }); + } + public reload() { this.withWebview(webview => webview.reload()); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 538e36abc33..b0471a3d0a1 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -26,8 +26,6 @@ actionRegistry.registerWorkbenchAction( function registerWebViewCommands(editorId: string): void { const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */)!; - registerAction2(class extends webviewCommands.SelectAllWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); - // These commands are only needed on MacOS where we have to disable the menu bar commands if (isMacintosh) { registerAction2(class extends webviewCommands.CopyWebviewEditorCommand { constructor() { super(contextKeyExpr); } }); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index 611622ed72b..b06bef4aafd 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -38,28 +38,6 @@ export class OpenWebviewDeveloperToolsAction extends Action { } } -export class SelectAllWebviewEditorCommand extends Action2 { - public static readonly ID = 'editor.action.webvieweditor.selectAll'; - public static readonly LABEL = nls.localize('editor.action.webvieweditor.selectAll', 'Select all'); - - constructor(contextKeyExpr: ContextKeyExpr) { - const precondition = ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)); - super({ - id: SelectAllWebviewEditorCommand.ID, - title: SelectAllWebviewEditorCommand.LABEL, - keybinding: { - when: precondition, - primary: KeyMod.CtrlCmd | KeyCode.KEY_A, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor, args: any): void { - withActiveWebviewBasedWebview(accessor, webview => webview.selectAll()); - } -} - export class CopyWebviewEditorCommand extends Action2 { public static readonly ID = 'editor.action.webvieweditor.copy'; public static readonly LABEL = nls.localize('editor.action.webvieweditor.copy', "Copy2"); From 8b087788744d8c2986602f56fc5d23c074b032b8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 13 Feb 2020 16:59:39 -0800 Subject: [PATCH 128/192] Replace withWebview with optional chaining usage --- .../contrib/webview/browser/webviewEditor.ts | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 449d93aebfa..97eb2c50bd0 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -15,7 +15,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -71,37 +71,33 @@ export class WebviewEditor extends BaseEditor { } public showFind() { - this.withWebview(webview => { - webview.showFind(); + if (this.webview) { + this.webview.showFind(); this._findWidgetVisible.set(true); - }); + } } public hideFind() { this._findWidgetVisible.reset(); - this.withWebview(webview => webview.hideFind()); + this.webview?.hideFind(); } public find(previous: boolean) { - this.withWebview(webview => { - webview.runFindAction(previous); - }); + this.webview?.runFindAction(previous); } public selectAll() { - this.withWebview(webview => { - webview.selectAll(); - }); + this.webview?.selectAll(); } public reload() { - this.withWebview(webview => webview.reload()); + this.webview?.reload(); } public layout(dimension: DOM.Dimension): void { this._dimension = dimension; - if (this.input && this.input instanceof WebviewInput) { - this.synchronizeWebviewContainerDimensions(this.input.webview, dimension); + if (this.webview) { + this.synchronizeWebviewContainerDimensions(this.webview, dimension); } } @@ -115,22 +111,19 @@ export class WebviewEditor extends BaseEditor { } }); } - this.withWebview(webview => webview.focus()); + this.webview?.focus(); } - public withWebview(f: (element: Webview) => void): void { - if (this.input && this.input instanceof WebviewInput) { - f(this.input.webview); - } + public get webview(): WebviewEditorOverlay | undefined { + return this.input instanceof WebviewInput ? this.input.webview : undefined; } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - if (this.input instanceof WebviewInput) { - const webview = this.input.webview; + if (this.input instanceof WebviewInput && this.webview) { if (visible) { - webview.claim(this); + this.webview.claim(this); } else { - webview.release(this); + this.webview.release(this); } this.claimWebview(this.input); } @@ -138,8 +131,8 @@ export class WebviewEditor extends BaseEditor { } public clearInput() { - if (this.input && this.input instanceof WebviewInput) { - this.input.webview.release(this); + if (this.webview) { + this.webview.release(this); this._webviewVisibleDisposables.clear(); } @@ -151,8 +144,8 @@ export class WebviewEditor extends BaseEditor { return; } - if (this.input && this.input instanceof WebviewInput) { - this.input.webview.release(this); + if (this.webview) { + this.webview.release(this); } await super.setInput(input, options, token); @@ -195,15 +188,11 @@ export class WebviewEditor extends BaseEditor { } this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { - if (this.input instanceof WebviewInput) { - this.input.webview.windowDidDragStart(); - } + this.webview?.windowDidDragStart(); })); const onDragEnd = () => { - if (this.input instanceof WebviewInput) { - this.input.webview.windowDidDragEnd(); - } + this.webview?.windowDidDragEnd(); }; this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { From 31eb2b1d25d5a985f7506d047026de21c1db0e18 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 12:22:20 -0800 Subject: [PATCH 129/192] Also switch getActiveWebviewBasedWebview to use optional chaining --- .../electron-browser/webviewCommands.ts | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index b06bef4aafd..ebbe40c7f42 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -55,7 +55,7 @@ export class CopyWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - withActiveWebviewBasedWebview(accessor, webview => webview.copy()); + getActiveWebviewBasedWebview(accessor)?.copy(); } } @@ -76,7 +76,7 @@ export class PasteWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - withActiveWebviewBasedWebview(accessor, webview => webview.paste()); + getActiveWebviewBasedWebview(accessor)?.paste(); } } @@ -97,7 +97,7 @@ export class CutWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor): void { - withActiveWebviewBasedWebview(accessor, webview => webview.cut()); + getActiveWebviewBasedWebview(accessor)?.cut(); } } @@ -118,7 +118,7 @@ export class UndoWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor, args: any): void { - withActiveWebviewBasedWebview(accessor, webview => webview.undo()); + getActiveWebviewBasedWebview(accessor)?.undo(); } } @@ -141,22 +141,24 @@ export class RedoWebviewEditorCommand extends Action2 { } public run(accessor: ServicesAccessor, args: any): void { - withActiveWebviewBasedWebview(accessor, webview => webview.redo()); + getActiveWebviewBasedWebview(accessor)?.redo(); } } -function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview: ElectronWebviewBasedWebview) => void): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.withWebview(webview => { - if (webview instanceof ElectronWebviewBasedWebview) { - f(webview); - } else if ((webview as WebviewEditorOverlay).getInnerWebview) { - const innerWebview = (webview as WebviewEditorOverlay).getInnerWebview(); - if (innerWebview instanceof ElectronWebviewBasedWebview) { - f(innerWebview); - } - } - }); +function getActiveWebviewBasedWebview(accessor: ServicesAccessor): ElectronWebviewBasedWebview | undefined { + const webview = getActiveWebviewEditor(accessor)?.webview; + if (!webview) { + return undefined; } + + if (webview instanceof ElectronWebviewBasedWebview) { + return webview; + } else if ((webview as WebviewEditorOverlay).getInnerWebview) { + const innerWebview = (webview as WebviewEditorOverlay).getInnerWebview(); + if (innerWebview instanceof ElectronWebviewBasedWebview) { + return innerWebview; + } + } + + return undefined; } From 69b30f6ba7ff6d6997a533595a2fdbc2cd85f2c0 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 10 Feb 2020 14:49:59 -0500 Subject: [PATCH 130/192] Adds paging support (wip) --- extensions/git/src/timelineProvider.ts | 8 +-- src/vs/vscode.proposed.d.ts | 39 ++++++++++++- .../api/browser/mainThreadTimeline.ts | 10 +--- .../workbench/api/common/extHost.protocol.ts | 6 +- .../workbench/api/common/extHostTimeline.ts | 24 +++++--- .../contrib/timeline/browser/timelinePane.ts | 4 +- .../contrib/timeline/common/timeline.ts | 22 ++++++-- .../timeline/common/timelineService.ts | 55 ++++--------------- 8 files changed, 92 insertions(+), 76 deletions(-) diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 3aa36a0dccb..a83436dc29e 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -6,7 +6,7 @@ import * as dayjs from 'dayjs'; import * as advancedFormat from 'dayjs/plugin/advancedFormat'; import * as relativeTime from 'dayjs/plugin/relativeTime'; -import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace, TimelineChangeEvent } from 'vscode'; +import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode'; import { Model } from './model'; import { Repository } from './repository'; import { debounce } from './decorators'; @@ -44,7 +44,7 @@ export class GitTimelineProvider implements TimelineProvider { this._disposable.dispose(); } - async provideTimeline(uri: Uri, _token: CancellationToken): Promise { + async provideTimeline(uri: Uri, _cursor: TimelineCursor, _token: CancellationToken): Promise { // console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`); const repo = this._model.getRepository(uri); @@ -53,7 +53,7 @@ export class GitTimelineProvider implements TimelineProvider { this._repoStatusDate = undefined; this._repo = undefined; - return []; + return { items: [] }; } if (this._repo?.root !== repo.root) { @@ -181,7 +181,7 @@ export class GitTimelineProvider implements TimelineProvider { items.push(item); } - return items; + return { items: items }; } private onRepositoriesChanged(_repo: Repository) { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index deb1bae0050..637dff30817 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1611,6 +1611,40 @@ declare module 'vscode' { uri?: Uri; } + export interface TimelineCursor { + /** + * A provider-defined cursor specifing the range of timeline items to be returned. Must be serializable. + */ + cursor?: any; + + /** + * A flag to specify whether the timeline items requested are before or after (default) the provided cursor. + */ + before?: boolean; + + /** + * The maximum number of timeline items that should be returned. + */ + limit?: number; + } + + export interface Timeline { + /** + * A provider-defined cursor specifing the range of timeline items returned. Must be serializable. + */ + cursor?: any; + + /** + * A flag which indicates whether there are any more items that weren't returned. + */ + more?: boolean; + + /** + * An array of [timeline items](#TimelineItem). + */ + items: TimelineItem[]; + } + export interface TimelineProvider { /** * An optional event to signal that the timeline for a source has changed. @@ -1633,10 +1667,11 @@ declare module 'vscode' { * * @param uri The [uri](#Uri) of the file to provide the timeline for. * @param token A cancellation token. - * @return An array of timeline items or a thenable that resolves to such. The lack of a result + * @param cursor TBD + * @return The [timeline result](#TimelineResult) or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ - provideTimeline(uri: Uri, token: CancellationToken): ProviderResult; + provideTimeline(uri: Uri, cursor: TimelineCursor, token: CancellationToken): ProviderResult; } export namespace workspace { diff --git a/src/vs/workbench/api/browser/mainThreadTimeline.ts b/src/vs/workbench/api/browser/mainThreadTimeline.ts index 919e4a18ff5..f65a89b432c 100644 --- a/src/vs/workbench/api/browser/mainThreadTimeline.ts +++ b/src/vs/workbench/api/browser/mainThreadTimeline.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { ITimelineService, TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent } from 'vs/workbench/contrib/timeline/common/timeline'; +import { TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline'; @extHostNamedCustomer(MainContext.MainThreadTimeline) export class MainThreadTimeline implements MainThreadTimelineShape { @@ -24,10 +24,6 @@ export class MainThreadTimeline implements MainThreadTimelineShape { this._proxy = context.getProxy(ExtHostContext.ExtHostTimeline); } - $getTimeline(uri: URI, token: CancellationToken): Promise { - return this._timelineService.getTimeline(uri, token); - } - $registerTimelineProvider(provider: TimelineProviderDescriptor): void { this.logService.trace(`MainThreadTimeline#registerTimelineProvider: id=${provider.id}`); @@ -43,8 +39,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape { this._timelineService.registerTimelineProvider({ ...provider, onDidChange: onDidChange.event, - provideTimeline(uri: URI, token: CancellationToken) { - return proxy.$getTimeline(provider.id, uri, token); + provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) { + return proxy.$getTimeline(provider.id, uri, cursor, token); }, dispose() { emitters.delete(provider.id); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 73af521adef..21d4884818e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -49,7 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; -import { TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent, TimelineItemWithSource } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -803,8 +803,6 @@ export interface MainThreadTimelineShape extends IDisposable { $registerTimelineProvider(provider: TimelineProviderDescriptor): void; $unregisterTimelineProvider(source: string): void; $emitTimelineChangeEvent(e: TimelineChangeEvent): void; - - $getTimeline(uri: UriComponents, token: CancellationToken): Promise; } // -- extension host @@ -1459,7 +1457,7 @@ export interface ExtHostTunnelServiceShape { } export interface ExtHostTimelineShape { - $getTimeline(source: string, uri: UriComponents, token: CancellationToken): Promise; + $getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken): Promise; } // --- proxy identifiers diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index 1ce8f7813ff..c4e3832649a 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { UriComponents, URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineCursor, TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; @@ -16,7 +16,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtHostTimeline extends ExtHostTimelineShape { readonly _serviceBrand: undefined; - $getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise; + $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise; } export const IExtHostTimeline = createDecorator('IExtHostTimeline'); @@ -34,9 +34,9 @@ export class ExtHostTimeline implements IExtHostTimeline { this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline); } - async $getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise { + async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise { const provider = this._providers.get(id); - return provider?.provideTimeline(URI.revive(uri), token) ?? []; + return provider?.provideTimeline(URI.revive(uri), cursor, token); } registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable { @@ -53,15 +53,21 @@ export class ExtHostTimeline implements IExtHostTimeline { ...provider, scheme: scheme, onDidChange: undefined, - async provideTimeline(uri: URI, token: CancellationToken) { + async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) { timelineDisposables.clear(); - const results = await provider.provideTimeline(uri, token); + const result = await provider.provideTimeline(uri, cursor, token); // Intentional == we don't know how a provider will respond // eslint-disable-next-line eqeqeq - return results != null - ? results.map(item => convertTimelineItem(item)) - : []; + if (result == null) { + return undefined; + } + + return { + ...result, + source: provider.id, + items: result.items.map(convertTimelineItem) + }; }, dispose() { disposable?.dispose(); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 241ee3dabc0..dfdc574d6a0 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -187,7 +187,7 @@ export class TimelinePane extends ViewPane { let request = this._pendingRequests.get(source); request?.tokenSource.dispose(true); - request = this.timelineService.getTimelineRequest(source, this._uri, new CancellationTokenSource())!; + request = this.timelineService.getTimeline(source, this._uri, {}, new CancellationTokenSource())!; this._pendingRequests.set(source, request); request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source)); @@ -199,7 +199,7 @@ export class TimelinePane extends ViewPane { private async handleRequest(request: TimelineRequest) { let items; try { - items = await this.progressService.withProgress({ location: VIEWLET_ID }, () => request.items); + items = await this.progressService.withProgress({ location: VIEWLET_ID }, () => request.result.then(r => r?.items ?? [])); } catch { } diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index 65fe341ffc0..4d3ece3caa7 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -37,10 +37,24 @@ export interface TimelineChangeEvent { uri?: URI; } +export interface TimelineCursor { + cursor?: any; + before?: boolean; + limit?: number; +} + +export interface Timeline { + source: string; + items: TimelineItemWithSource[]; + + cursor?: any; + more?: boolean; +} + export interface TimelineProvider extends TimelineProviderDescriptor, IDisposable { onDidChange?: Event; - provideTimeline(uri: URI, token: CancellationToken): Promise; + provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken): Promise; } export interface TimelineProviderDescriptor { @@ -55,7 +69,7 @@ export interface TimelineProvidersChangeEvent { } export interface TimelineRequest { - readonly items: Promise; + readonly result: Promise; readonly source: string; readonly tokenSource: CancellationTokenSource; readonly uri: URI; @@ -72,9 +86,7 @@ export interface ITimelineService { getSources(): string[]; - getTimeline(uri: URI, token: CancellationToken): Promise; - - getTimelineRequest(id: string, uri: URI, tokenSource: CancellationTokenSource): TimelineRequest | undefined; + getTimeline(id: string, uri: URI, pagination: TimelineCursor, tokenSource: CancellationTokenSource): TimelineRequest | undefined; } const TIMELINE_SERVICE_ID = 'timeline'; diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index eda52441831..3fc416e7464 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; // import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; -import { ITimelineService, TimelineProvider, TimelineItem, TimelineChangeEvent, TimelineProvidersChangeEvent } from './timeline'; +import { ITimelineService, TimelineChangeEvent, TimelineCursor, TimelineProvidersChangeEvent, TimelineProvider } from './timeline'; export class TimelineService implements ITimelineService { _serviceBrand: undefined; @@ -81,42 +81,7 @@ export class TimelineService implements ITimelineService { return [...this._providers.keys()]; } - async getTimeline(uri: URI, token: CancellationToken, predicate?: (provider: TimelineProvider) => boolean) { - this.logService.trace(`TimelineService#getTimeline(${uri.toString(true)})`); - - const requests: Promise<[string, TimelineItem[]]>[] = []; - - for (const provider of this._providers.values()) { - if (typeof provider.scheme === 'string') { - if (provider.scheme !== '*' && provider.scheme !== uri.scheme) { - continue; - } - } else if (!provider.scheme.includes(uri.scheme)) { - continue; - } - if (!(predicate?.(provider) ?? true)) { - continue; - } - - requests.push(provider.provideTimeline(uri, token).then(p => [provider.id, p])); - } - - const timelines = await Promise.all(requests); - - const timeline = []; - for (const [source, items] of timelines) { - if (items.length === 0) { - continue; - } - - timeline.push(...items.map(item => ({ ...item, source: source }))); - } - - timeline.sort((a, b) => b.timestamp - a.timestamp); - return timeline; - } - - getTimelineRequest(id: string, uri: URI, tokenSource: CancellationTokenSource) { + getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource) { this.logService.trace(`TimelineService#getTimeline(${id}): uri=${uri.toString(true)}`); const provider = this._providers.get(id); @@ -133,12 +98,16 @@ export class TimelineService implements ITimelineService { } return { - items: provider.provideTimeline(uri, tokenSource.token) - .then(items => { - items = items.map(item => ({ ...item, source: provider.id })); - items.sort((a, b) => (b.timestamp - a.timestamp) || b.source.localeCompare(a.source, undefined, { numeric: true, sensitivity: 'base' })); + result: provider.provideTimeline(uri, cursor, tokenSource.token) + .then(result => { + if (result === undefined) { + return undefined; + } - return items; + result.items = result.items.map(item => ({ ...item, source: provider.id })); + result.items.sort((a, b) => (b.timestamp - a.timestamp) || b.source.localeCompare(a.source, undefined, { numeric: true, sensitivity: 'base' })); + + return result; }), source: provider.id, tokenSource: tokenSource, From 9ae0fd36c91dd523cbc3483e4fbf5c897e4ae0c3 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 10 Feb 2020 10:24:33 -0500 Subject: [PATCH 131/192] Enhances timeline - commands, timestamp, etc Adds contributable commands to timeline items Adds right-aligned timestamp to timeline items Adds Open Changes to Git timeline items Adds Copy Commit ID to Git timeline items Adds Copy Commit Message to Git timeline items --- extensions/git/package.json | 54 +- extensions/git/package.nls.json | 3 + extensions/git/src/commands.ts | 39 +- extensions/git/src/timelineProvider.ts | 93 ++-- extensions/git/yarn.lock | 504 +----------------- src/vs/base/common/date.ts | 47 ++ src/vs/platform/actions/common/actions.ts | 4 +- .../api/browser/mainThreadTimeline.ts | 4 +- .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTimeline.ts | 128 +++-- .../api/common/menusExtensionPoint.ts | 12 + .../timeline/browser/media/timelinePane.css | 17 + .../contrib/timeline/browser/timelinePane.ts | 231 ++++++-- .../contrib/timeline/common/timeline.ts | 14 +- .../timeline/common/timelineService.ts | 4 +- 16 files changed, 535 insertions(+), 623 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c1cd8b5135c..b1ae36f57a7 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -447,6 +447,22 @@ "command": "git.stashDrop", "title": "%command.stashDrop%", "category": "Git" + }, + { + "command": "git.timeline.openDiff", + "title": "%command.timelineOpenDiff%", + "icon": "$(compare-changes)", + "category": "Git" + }, + { + "command": "git.timeline.copyCommitId", + "title": "%command.timelineCopyCommitId%", + "category": "Git" + }, + { + "command": "git.timeline.copyCommitMessage", + "title": "%command.timelineCopyCommitMessage%", + "category": "Git" } ], "menus": { @@ -718,6 +734,18 @@ { "command": "git.stashDrop", "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.timeline.openDiff", + "when": "false" + }, + { + "command": "git.timeline.copyCommitId", + "when": "false" + }, + { + "command": "git.timeline.copyCommitMessage", + "when": "false" } ], "scm/title": [ @@ -1248,6 +1276,28 @@ "command": "git.revertChange", "when": "originalResourceScheme == git" } + ], + "timeline/item/context": [ + { + "command": "git.timeline.openDiff", + "group": "inline", + "when": "timelineItem =~ /git:file\\b/" + }, + { + "command": "git.timeline.openDiff", + "group": "1_timeline", + "when": "timelineItem =~ /git:file\\b/" + }, + { + "command": "git.timeline.copyCommitId", + "group": "2_timeline@1", + "when": "timelineItem =~ /git:file:commit\\b/" + }, + { + "command": "git.timeline.copyCommitMessage", + "group": "2_timeline@2", + "when": "timelineItem =~ /git:file:commit\\b/" + } ] }, "configuration": { @@ -1779,10 +1829,10 @@ "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", "@types/node": "^12.11.7", + "@types/vscode": "^1.42", "@types/which": "^1.0.28", "mocha": "^3.2.0", "mocha-junit-reporter": "^1.23.3", - "mocha-multi-reporters": "^1.1.7", - "vscode": "^1.1.36" + "mocha-multi-reporters": "^1.1.7" } } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index f51154cebf6..534ec69429a 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -70,6 +70,9 @@ "command.stashApply": "Apply Stash...", "command.stashApplyLatest": "Apply Latest Stash", "command.stashDrop": "Drop Stash...", + "command.timelineOpenDiff": "Open Changes", + "command.timelineCopyCommitId": "Copy Commit ID", + "command.timelineCopyCommitMessage": "Copy Commit Message", "config.enabled": "Whether git is enabled.", "config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows).", "config.autoRepositoryDetection": "Configures when repositories should be automatically detected.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index dba21595ea8..9d8fe2a4ef4 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git'; @@ -17,6 +17,7 @@ import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineC import { fromGitUri, toGitUri, isGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; import { Log, LogLevel } from './log'; +import { GitTimelineItem } from './timelineProvider'; const localize = nls.loadMessageBundle(); @@ -2331,23 +2332,47 @@ export class CommandCenter { return result && result.stash; } - @command('git.openDiff', { repository: false }) - async openDiff(uri: Uri, lhs: string, rhs: string) { + @command('git.timeline.openDiff', { repository: false }) + async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) { + // eslint-disable-next-line eqeqeq + if (uri == null || !GitTimelineItem.is(item)) { + return undefined; + } + const basename = path.basename(uri.fsPath); let title; - if ((lhs === 'HEAD' || lhs === '~') && rhs === '') { + if ((item.previousRef === 'HEAD' || item.previousRef === '~') && item.ref === '') { title = `${basename} (Working Tree)`; } - else if (lhs === 'HEAD' && rhs === '~') { + else if (item.previousRef === 'HEAD' && item.ref === '~') { title = `${basename} (Index)`; } else { - title = `${basename} (${lhs.endsWith('^') ? `${lhs.substr(0, 8)}^` : lhs.substr(0, 8)}) \u27f7 ${basename} (${rhs.endsWith('^') ? `${rhs.substr(0, 8)}^` : rhs.substr(0, 8)})`; + title = `${basename} (${item.shortPreviousRef}) \u27f7 ${basename} (${item.shortRef})`; } - return commands.executeCommand('vscode.diff', toGitUri(uri, lhs), rhs === '' ? uri : toGitUri(uri, rhs), title); + return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title); } + @command('git.timeline.copyCommitId', { repository: false }) + async timelineCopyCommitId(item: TimelineItem, _uri: Uri | undefined, _source: string) { + if (!GitTimelineItem.is(item)) { + return; + } + + env.clipboard.writeText(item.ref); + } + + @command('git.timeline.copyCommitMessage', { repository: false }) + async timelineCopyCommitMessage(item: TimelineItem, _uri: Uri | undefined, _source: string) { + if (!GitTimelineItem.is(item)) { + return; + } + + env.clipboard.writeText(item.message); + } + + private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any { const result = (...args: any[]) => { let result: Promise; diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index a83436dc29e..9db3495aec7 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -5,7 +5,6 @@ import * as dayjs from 'dayjs'; import * as advancedFormat from 'dayjs/plugin/advancedFormat'; -import * as relativeTime from 'dayjs/plugin/relativeTime'; import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode'; import { Model } from './model'; import { Repository } from './repository'; @@ -13,11 +12,55 @@ import { debounce } from './decorators'; import { Status } from './api/git'; dayjs.extend(advancedFormat); -dayjs.extend(relativeTime); // TODO[ECA]: Localize all the strings // TODO[ECA]: Localize or use a setting for date format +export class GitTimelineItem extends TimelineItem { + static is(item: TimelineItem): item is GitTimelineItem { + return item instanceof GitTimelineItem; + } + + readonly ref: string; + readonly previousRef: string; + readonly message: string; + + constructor( + ref: string, + previousRef: string, + message: string, + timestamp: number, + id: string, + contextValue: string + ) { + const index = message.indexOf('\n'); + const label = index !== -1 ? `${message.substring(0, index)} \u2026` : message; + + super(label, timestamp); + + this.ref = ref; + this.previousRef = previousRef; + this.message = message; + this.id = id; + this.contextValue = contextValue; + } + + get shortRef() { + return this.shortenRef(this.ref); + } + + get shortPreviousRef() { + return this.shortenRef(this.previousRef); + } + + private shortenRef(ref: string): string { + if (ref === '' || ref === '~' || ref === 'HEAD') { + return ref; + } + return ref.endsWith('^') ? `${ref.substr(0, 8)}^` : ref.substr(0, 8); + } +} + export class GitTimelineProvider implements TimelineProvider { private _onDidChange = new EventEmitter(); get onDidChange(): Event { @@ -72,25 +115,17 @@ export class GitTimelineProvider implements TimelineProvider { const commits = await repo.logFile(uri); let dateFormatter: dayjs.Dayjs; - const items = commits.map(c => { - let message = c.message; - - const index = message.indexOf('\n'); - if (index !== -1) { - message = `${message.substring(0, index)} \u2026`; - } - + const items = commits.map(c => { dateFormatter = dayjs(c.authorDate); - const item = new TimelineItem(message, c.authorDate?.getTime() ?? 0); - item.id = c.hash; + const item = new GitTimelineItem(c.hash, `${c.hash}^`, c.message, c.authorDate?.getTime() ?? 0, c.hash, 'git:file:commit'); item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = `${dateFormatter.fromNow()} \u2022 ${c.authorName}`; - item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`; + item.description = c.authorName; + item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n\n${c.message}`; item.command = { - title: 'Open Diff', - command: 'git.openDiff', - arguments: [uri, `${c.hash}^`, c.hash] + title: 'Open Comparison', + command: 'git.timeline.openDiff', + arguments: [uri, this.id, item] }; return item; @@ -123,16 +158,15 @@ export class GitTimelineProvider implements TimelineProvider { break; } - const item = new TimelineItem('Staged Changes', date.getTime()); - item.id = 'index'; + const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index'); // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = `${dateFormatter.fromNow()} \u2022 You`; - item.detail = `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`; + item.description = 'You'; + item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; item.command = { title: 'Open Comparison', - command: 'git.openDiff', - arguments: [uri, 'HEAD', '~'] + command: 'git.timeline.openDiff', + arguments: [uri, this.id, item] }; items.push(item); @@ -166,16 +200,15 @@ export class GitTimelineProvider implements TimelineProvider { break; } - const item = new TimelineItem('Uncommited Changes', date.getTime()); - item.id = 'working'; + const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working'); // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = `${dateFormatter.fromNow()} \u2022 You`; - item.detail = `You \u2014 Working Tree\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`; + item.description = 'You'; + item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; item.command = { title: 'Open Comparison', - command: 'git.openDiff', - arguments: [uri, index ? '~' : 'HEAD', ''] + command: 'git.timeline.openDiff', + arguments: [uri, this.id, item] }; items.push(item); @@ -208,6 +241,6 @@ export class GitTimelineProvider implements TimelineProvider { @debounce(500) private fireChanged() { - this._onDidChange.fire(); + this._onDidChange.fire({}); } } diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index af5b10127dc..ff2f8994487 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -31,28 +31,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== +"@types/vscode@^1.42": + version "1.42.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.42.0.tgz#0ad891a9487e91e34be7c56985058a179031eb76" + integrity sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw== + "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= -agent-base@4, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -ajv@^6.5.5: - version "6.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" - integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -67,45 +55,11 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" - integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -119,43 +73,16 @@ browser-stdout@1.3.0: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -168,23 +95,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - dayjs@1.8.19: version "1.8.19" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe" @@ -197,13 +112,6 @@ debug@2.6.8: dependencies: ms "2.0.0" -debug@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -218,11 +126,6 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -240,92 +143,21 @@ diff@3.2.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - escape-string-regexp@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - file-type@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74" integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q= -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - glob@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" @@ -338,98 +170,26 @@ glob@7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.2: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -455,61 +215,21 @@ is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - jschardet@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json3@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -580,19 +300,7 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== - dependencies: - mime-db "1.43.0" - -minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -648,23 +356,6 @@ mocha@^3.2.0: mkdirp "0.5.1" supports-color "3.1.2" -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== - dependencies: - browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" - diff "3.5.0" - escape-string-regexp "1.0.5" - glob "7.1.2" - growl "1.10.5" - he "1.1.1" - minimatch "3.0.4" - mkdirp "0.5.1" - supports-color "5.4.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -675,11 +366,6 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -692,73 +378,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -psl@^1.1.24: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - -request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -768,39 +388,6 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== -semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -source-map-support@^0.5.0: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -815,62 +402,6 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== - dependencies: - has-flag "^3.0.0" - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -url-parse@^1.4.4: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -883,32 +414,11 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== -vscode-test@^0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" - integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== - dependencies: - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - vscode-uri@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== -vscode@^1.1.36: - version "1.1.36" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" - integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== - dependencies: - glob "^7.1.2" - mocha "^5.2.0" - request "^2.88.0" - semver "^5.4.1" - source-map-support "^0.5.0" - url-parse "^1.4.4" - vscode-test "^0.4.1" - which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" diff --git a/src/vs/base/common/date.ts b/src/vs/base/common/date.ts index a6da9d5ae33..acfc19d6edb 100644 --- a/src/vs/base/common/date.ts +++ b/src/vs/base/common/date.ts @@ -5,6 +5,53 @@ import { pad } from './strings'; +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const month = day * 30; +const year = day * 365; + +// TODO[ECA]: Localize strings +export function fromNow(date: number | Date) { + if (typeof date !== 'number') { + date = date.getTime(); + } + + const seconds = Math.round((new Date().getTime() - date) / 1000); + if (seconds < 30) { + return 'now'; + } + + let value: number; + let unit: string; + if (seconds < minute) { + value = seconds; + unit = 'sec'; + } else if (seconds < hour) { + value = Math.floor(seconds / minute); + unit = 'min'; + } else if (seconds < day) { + value = Math.floor(seconds / hour); + unit = 'hr'; + } else if (seconds < week) { + value = Math.floor(seconds / day); + unit = 'day'; + } else if (seconds < month) { + value = Math.floor(seconds / week); + unit = 'wk'; + } else if (seconds < year) { + value = Math.floor(seconds / month); + unit = 'mo'; + } else { + value = Math.floor(seconds / year); + unit = 'yr'; + } + + return `${value} ${unit}${value === 1 ? '' : 's'}`; + +} + export function toLocalISOString(date: Date): string { return date.getFullYear() + '-' + pad(date.getMonth() + 1, 2) + diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 25a4db0972c..c30c37e1394 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -114,7 +114,9 @@ export class MenuId { static readonly CommentActions = new MenuId('CommentActions'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); - + static readonly TimelineItemContext = new MenuId('TimelineItemContext'); + static readonly TimelineTitle = new MenuId('TimelineTitle'); + static readonly TimelineTitleContext = new MenuId('TimelineTitleContext'); readonly id: number; readonly _debugName: string; diff --git a/src/vs/workbench/api/browser/mainThreadTimeline.ts b/src/vs/workbench/api/browser/mainThreadTimeline.ts index f65a89b432c..428bf0ed2d9 100644 --- a/src/vs/workbench/api/browser/mainThreadTimeline.ts +++ b/src/vs/workbench/api/browser/mainThreadTimeline.ts @@ -39,8 +39,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape { this._timelineService.registerTimelineProvider({ ...provider, onDidChange: onDidChange.event, - provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) { - return proxy.$getTimeline(provider.id, uri, cursor, token); + provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) { + return proxy.$getTimeline(provider.id, uri, cursor, token, options); }, dispose() { emitters.delete(provider.id); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 61353fa2683..0d25c1176e7 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -133,7 +133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHostLabelService, new ExtHostLabelService(rpcProtocol)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); - const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol)); + const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 21d4884818e..b780bccbd4d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1457,7 +1457,7 @@ export interface ExtHostTunnelServiceShape { } export interface ExtHostTimelineShape { - $getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken): Promise; + $getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }): Promise; } // --- proxy identifiers diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index c4e3832649a..f5f63a2ce4d 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -7,55 +7,77 @@ import * as vscode from 'vscode'; import { UriComponents, URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { Timeline, TimelineCursor, TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineCursor, TimelineItem, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; +import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ThemeIcon } from 'vs/workbench/api/common/extHostTypes'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtHostTimeline extends ExtHostTimelineShape { readonly _serviceBrand: undefined; - $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise; + $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise; } export const IExtHostTimeline = createDecorator('IExtHostTimeline'); export class ExtHostTimeline implements IExtHostTimeline { + private static handlePool = 0; + _serviceBrand: undefined; private _proxy: MainThreadTimelineShape; private _providers = new Map(); + private _itemsBySourceByUriMap = new Map>>(); + constructor( mainContext: IMainContext, + commands: ExtHostCommands, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline); + + commands.registerArgumentProcessor({ + processArgument: arg => { + if (arg && arg.$mid === 11) { + const uri = arg.uri === undefined ? undefined : URI.revive(arg.uri); + return this._itemsBySourceByUriMap.get(getUriKey(uri))?.get(arg.source)?.get(arg.handle); + } + + return arg; + } + }); } - async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise { + async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise { const provider = this._providers.get(id); - return provider?.provideTimeline(URI.revive(uri), cursor, token); + return provider?.provideTimeline(URI.revive(uri), cursor, token, options); } - registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable { + registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, _extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable { const timelineDisposables = new DisposableStore(); - const convertTimelineItem = this.convertTimelineItem(provider.id, commandConverter, timelineDisposables); + const convertTimelineItem = this.convertTimelineItem(provider.id, commandConverter, timelineDisposables).bind(this); let disposable: IDisposable | undefined; if (provider.onDidChange) { disposable = provider.onDidChange(this.emitTimelineChangeEvent(provider.id), this); } + const itemsBySourceByUriMap = this._itemsBySourceByUriMap; return this.registerTimelineProviderCore({ ...provider, scheme: scheme, onDidChange: undefined, - async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) { + async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) { timelineDisposables.clear(); + // For now, only allow the caching of a single Uri + if (options?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) { + itemsBySourceByUriMap.clear(); + } + const result = await provider.provideTimeline(uri, cursor, token); // Intentional == we don't know how a provider will respond // eslint-disable-next-line eqeqeq @@ -63,10 +85,12 @@ export class ExtHostTimeline implements IExtHostTimeline { return undefined; } + // TODO: Determine if we should cache dependent on who calls us (internal vs external) + const convertItem = convertTimelineItem(uri, options?.cacheResults ?? false); return { ...result, source: provider.id, - items: result.items.map(convertTimelineItem) + items: result.items.map(convertItem) }; }, dispose() { @@ -76,39 +100,72 @@ export class ExtHostTimeline implements IExtHostTimeline { }); } - private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore): (item: vscode.TimelineItem) => TimelineItemWithSource { - return (item: vscode.TimelineItem) => { - const { iconPath, ...props } = item; + private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore) { + return (uri: URI, cacheResults: boolean) => { + let itemsMap: Map | undefined; + if (cacheResults) { + const uriKey = getUriKey(uri); - let icon; - let iconDark; - let themeIcon; - if (item.iconPath) { - if (iconPath instanceof ThemeIcon) { - themeIcon = { id: iconPath.id }; + let sourceMap = this._itemsBySourceByUriMap.get(uriKey); + if (sourceMap === undefined) { + sourceMap = new Map(); + this._itemsBySourceByUriMap.set(uriKey, sourceMap); } - else if (URI.isUri(iconPath)) { - icon = iconPath; - iconDark = iconPath; - } - else { - ({ light: icon, dark: iconDark } = iconPath as { light: URI; dark: URI }); + + itemsMap = sourceMap.get(source); + if (itemsMap === undefined) { + itemsMap = new Map(); + sourceMap.set(source, itemsMap); } } - return { - ...props, - source: source, - command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined, - icon: icon, - iconDark: iconDark, - themeIcon: themeIcon + return (item: vscode.TimelineItem): TimelineItem => { + const { iconPath, ...props } = item; + + const handle = `${source}|${item.id ?? `${item.timestamp}-${ExtHostTimeline.handlePool++}`}`; + itemsMap?.set(handle, item); + + let icon; + let iconDark; + let themeIcon; + if (item.iconPath) { + if (iconPath instanceof ThemeIcon) { + themeIcon = { id: iconPath.id }; + } + else if (URI.isUri(iconPath)) { + icon = iconPath; + iconDark = iconPath; + } + else { + ({ light: icon, dark: iconDark } = iconPath as { light: URI; dark: URI }); + } + } + + return { + ...props, + handle: handle, + source: source, + command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined, + icon: icon, + iconDark: iconDark, + themeIcon: themeIcon + }; }; }; } private emitTimelineChangeEvent(id: string) { return (e: vscode.TimelineChangeEvent) => { + // Clear caches + if (e?.uri === undefined) { + for (const sourceMap of this._itemsBySourceByUriMap.values()) { + sourceMap.get(id)?.clear(); + } + } + else { + this._itemsBySourceByUriMap.get(getUriKey(e.uri))?.clear(); + } + this._proxy.$emitTimelineChangeEvent({ ...e, id: id }); }; } @@ -129,9 +186,18 @@ export class ExtHostTimeline implements IExtHostTimeline { this._providers.set(provider.id, provider); return toDisposable(() => { + for (const sourceMap of this._itemsBySourceByUriMap.values()) { + sourceMap.get(provider.id)?.clear(); + } + this._providers.delete(provider.id); this._proxy.$unregisterTimelineProvider(provider.id); provider.dispose(); }); } } + +function getUriKey(uri: URI | undefined): string | undefined { + return uri?.toString(); +} + diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index c79a3c16ab0..ab474c9825f 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -52,6 +52,8 @@ namespace schema { case 'comments/comment/title': return MenuId.CommentTitle; case 'comments/comment/context': return MenuId.CommentActions; case 'extension/context': return MenuId.ExtensionContext; + case 'timeline/title': return MenuId.TimelineTitle; + case 'timeline/item/context': return MenuId.TimelineItemContext; } return undefined; @@ -215,6 +217,16 @@ namespace schema { type: 'array', items: menuItem }, + 'timeline/title': { + description: localize('view.timelineTitle', "The Timeline view title menu"), + type: 'array', + items: menuItem + }, + 'timeline/item/context': { + description: localize('view.timelineContext', "The Timeline view item context menu"), + type: 'array', + items: menuItem + }, } }; diff --git a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css index ae431e80709..ad6200c6561 100644 --- a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css +++ b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css @@ -13,3 +13,20 @@ position: absolute; pointer-events: none; } + +.timeline-tree-view .monaco-list .monaco-list-row .custom-view-tree-node-item .monaco-icon-label { + flex: 1; + text-overflow: ellipsis; + overflow: hidden; +} + +.timeline-tree-view .monaco-list .monaco-list-row .custom-view-tree-node-item .timeline-timestamp-container { + margin-left: 2px; + margin-right: 4px; + text-overflow: ellipsis; + overflow: hidden; +} + +.timeline-tree-view .monaco-list .monaco-list-row .custom-view-tree-node-item .timeline-timestamp-container .timeline-timestamp { + opacity: 0.5; +} diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index dfdc574d6a0..1afbedc6563 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -8,11 +8,11 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IListVirtualDelegate, IIdentityProvider, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeRenderer, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { TreeResourceNavigator, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -20,7 +20,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TimelineItem, ITimelineService, TimelineChangeEvent, TimelineProvidersChangeEvent, TimelineRequest, TimelineItemWithSource } from 'vs/workbench/contrib/timeline/common/timeline'; +import { ITimelineService, TimelineChangeEvent, TimelineProvidersChangeEvent, TimelineRequest, TimelineItem } from 'vs/workbench/contrib/timeline/common/timeline'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { SideBySideEditor, toResource } from 'vs/workbench/common/editor'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -31,10 +31,20 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { VIEWLET_ID } from 'vs/workbench/contrib/files/common/files'; import { debounce } from 'vs/base/common/decorators'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IActionViewItemProvider, ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, ActionRunner } from 'vs/base/common/actions'; +import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuItemAction, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { fromNow } from 'vs/base/common/date'; + +// TODO[ECA]: Localize all the strings type TreeElement = TimelineItem; -// TODO[ECA]: Localize all the strings +interface TimelineActionContext { + uri: URI | undefined; + item: TreeElement; +} export class TimelinePane extends ViewPane { static readonly ID = 'timeline'; @@ -44,10 +54,12 @@ export class TimelinePane extends ViewPane { private _messageElement!: HTMLDivElement; private _treeElement!: HTMLDivElement; private _tree!: WorkbenchObjectTree; + private _treeRenderer: TimelineTreeRenderer | undefined; + private _menus: TimelineMenus; private _visibilityDisposables: DisposableStore | undefined; // private _excludedSources: Set | undefined; - private _items: TimelineItemWithSource[] = []; + private _items: TimelineItem[] = []; private _loadingMessageTimer: any | undefined; private _pendingRequests = new Map(); private _uri: URI | undefined; @@ -67,7 +79,9 @@ export class TimelinePane extends ViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...options, titleMenuId: MenuId.TimelineTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + + this._menus = this._register(this.instantiationService.createInstance(TimelineMenus, this.id)); const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); scopedContextKeyService.createKey('view', TimelinePane.ID); @@ -88,6 +102,7 @@ export class TimelinePane extends ViewPane { } this._uri = uri; + this._treeRenderer?.setUri(uri); this.loadTimeline(); } @@ -187,7 +202,7 @@ export class TimelinePane extends ViewPane { let request = this._pendingRequests.get(source); request?.tokenSource.dispose(true); - request = this.timelineService.getTimeline(source, this._uri, {}, new CancellationTokenSource())!; + request = this.timelineService.getTimeline(source, this._uri, {}, new CancellationTokenSource(), { cacheResults: true })!; this._pendingRequests.set(source, request); request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source)); @@ -211,7 +226,7 @@ export class TimelinePane extends ViewPane { this.replaceItems(request.source, items); } - private replaceItems(source: string, items?: TimelineItemWithSource[]) { + private replaceItems(source: string, items?: TimelineItem[]) { const hasItems = this._items.length !== 0; if (items?.length) { @@ -291,17 +306,20 @@ export class TimelinePane extends ViewPane { // DOM.addClass(this._treeElement, 'show-file-icons'); container.appendChild(this._treeElement); - const renderer = this.instantiationService.createInstance(TimelineTreeRenderer); - this._tree = >this.instantiationService.createInstance(WorkbenchObjectTree, 'TimelinePane', this._treeElement, new TimelineListVirtualDelegate(), [renderer], { + this._treeRenderer = this.instantiationService.createInstance(TimelineTreeRenderer, this._menus); + this._tree = >this.instantiationService.createInstance(WorkbenchObjectTree, 'TimelinePane', + this._treeElement, new TimelineListVirtualDelegate(), [this._treeRenderer], { identityProvider: new TimelineIdentityProvider(), keyboardNavigationLabelProvider: new TimelineKeyboardNavigationLabelProvider(), overrideStyles: { - listBackground: this.getBackgroundColor() + listBackground: this.getBackgroundColor(), + } }); const customTreeNavigator = new TreeResourceNavigator(this._tree, { openOnFocus: false, openOnSelection: false }); this._register(customTreeNavigator); + this._register(this._tree.onContextMenu(e => this.onContextMenu(this._menus, e))); this._register( customTreeNavigator.onDidOpenResource(e => { if (!e.browserEvent) { @@ -316,36 +334,112 @@ export class TimelinePane extends ViewPane { }) ); } -} -export class TimelineElementTemplate { - static readonly id = 'TimelineElementTemplate'; + private onContextMenu(menus: TimelineMenus, treeEvent: ITreeContextMenuEvent): void { + const item = treeEvent.element; + if (item === null) { + return; + } + const event: UIEvent = treeEvent.browserEvent; - constructor( - readonly container: HTMLElement, - readonly iconLabel: IconLabel, - readonly icon: HTMLElement - ) { } -} + event.preventDefault(); + event.stopPropagation(); -export class TimelineIdentityProvider implements IIdentityProvider { - getId(item: TimelineItem): { toString(): string } { - return `${item.id}|${item.timestamp}`; + this._tree.setFocus([item]); + const actions = menus.getResourceContextActions(item); + if (!actions.length) { + return; + } + + this.contextMenuService.showContextMenu({ + getAnchor: () => treeEvent.anchor, + getActions: () => actions, + getActionViewItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionViewItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return undefined; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this._tree.domFocus(); + } + }, + getActionsContext: (): TimelineActionContext => ({ uri: this._uri, item: item }), + actionRunner: new TimelineActionRunner() + }); } } -export class TimelineKeyboardNavigationLabelProvider implements IKeyboardNavigationLabelProvider { - getKeyboardNavigationLabel(element: TimelineItem): { toString(): string } { +export class TimelineElementTemplate implements IDisposable { + static readonly id = 'TimelineElementTemplate'; + + readonly actionBar: ActionBar; + readonly icon: HTMLElement; + readonly iconLabel: IconLabel; + readonly timestamp: HTMLSpanElement; + + constructor( + readonly container: HTMLElement, + actionViewItemProvider: IActionViewItemProvider + ) { + DOM.addClass(container, 'custom-view-tree-node-item'); + this.icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); + + this.iconLabel = new IconLabel(container, { supportHighlights: true, supportCodicons: true }); + + const timestampContainer = DOM.append(this.iconLabel.element, DOM.$('.timeline-timestamp-container')); + this.timestamp = DOM.append(timestampContainer, DOM.$('span.timeline-timestamp')); + + const actionsContainer = DOM.append(this.iconLabel.element, DOM.$('.actions')); + this.actionBar = new ActionBar(actionsContainer, { actionViewItemProvider: actionViewItemProvider }); + } + + dispose() { + this.iconLabel.dispose(); + this.actionBar.dispose(); + } + + reset() { + this.actionBar.clear(); + } +} + +export class TimelineIdentityProvider implements IIdentityProvider { + getId(item: TreeElement): { toString(): string } { + return item.handle; + } +} + +class TimelineActionRunner extends ActionRunner { + + runAction(action: IAction, { uri, item }: TimelineActionContext): Promise { + return action.run(...[ + { + $mid: 11, + handle: item.handle, + source: item.source, + uri: uri + }, + uri, + item.source, + ]); + } +} + +export class TimelineKeyboardNavigationLabelProvider implements IKeyboardNavigationLabelProvider { + getKeyboardNavigationLabel(element: TreeElement): { toString(): string } { return element.label; } } -export class TimelineListVirtualDelegate implements IListVirtualDelegate { - getHeight(_element: TimelineItem): number { +export class TimelineListVirtualDelegate implements IListVirtualDelegate { + getHeight(_element: TreeElement): number { return 22; } - getTemplateId(element: TimelineItem): string { + getTemplateId(element: TreeElement): string { return TimelineElementTemplate.id; } } @@ -353,14 +447,25 @@ export class TimelineListVirtualDelegate implements IListVirtualDelegate { readonly templateId: string = TimelineElementTemplate.id; - constructor(@IThemeService private _themeService: IThemeService) { } + private _actionViewItemProvider: IActionViewItemProvider; + + constructor( + private readonly _menus: TimelineMenus, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IThemeService private _themeService: IThemeService + ) { + this._actionViewItemProvider = (action: IAction) => action instanceof MenuItemAction + ? this.instantiationService.createInstance(ContextAwareMenuEntryActionViewItem, action) + : undefined; + } + + private _uri: URI | undefined; + setUri(uri: URI | undefined) { + this._uri = uri; + } renderTemplate(container: HTMLElement): TimelineElementTemplate { - DOM.addClass(container, 'custom-view-tree-node-item'); - const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - - const iconLabel = new IconLabel(container, { supportHighlights: true, supportCodicons: true }); - return new TimelineElementTemplate(container, iconLabel, icon); + return new TimelineElementTemplate(container, this._actionViewItemProvider); } renderElement( @@ -369,30 +474,74 @@ class TimelineTreeRenderer implements ITreeRenderer /^inline/.test(g)); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + } +} diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index 4d3ece3caa7..7ffca608646 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -16,9 +16,11 @@ export function toKey(extension: ExtensionIdentifier | string, source: string) { } export interface TimelineItem { + handle: string; + source: string; + timestamp: number; label: string; - id?: string; icon?: URI, iconDark?: URI, themeIcon?: { id: string }, @@ -28,10 +30,6 @@ export interface TimelineItem { contextValue?: string; } -export interface TimelineItemWithSource extends TimelineItem { - source: string; -} - export interface TimelineChangeEvent { id: string; uri?: URI; @@ -45,7 +43,7 @@ export interface TimelineCursor { export interface Timeline { source: string; - items: TimelineItemWithSource[]; + items: TimelineItem[]; cursor?: any; more?: boolean; @@ -54,7 +52,7 @@ export interface Timeline { export interface TimelineProvider extends TimelineProviderDescriptor, IDisposable { onDidChange?: Event; - provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken): Promise; + provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }): Promise; } export interface TimelineProviderDescriptor { @@ -86,7 +84,7 @@ export interface ITimelineService { getSources(): string[]; - getTimeline(id: string, uri: URI, pagination: TimelineCursor, tokenSource: CancellationTokenSource): TimelineRequest | undefined; + getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource, options?: { cacheResults?: boolean }): TimelineRequest | undefined; } const TIMELINE_SERVICE_ID = 'timeline'; diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 3fc416e7464..27038106272 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -81,7 +81,7 @@ export class TimelineService implements ITimelineService { return [...this._providers.keys()]; } - getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource) { + getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource, options?: { cacheResults?: boolean }) { this.logService.trace(`TimelineService#getTimeline(${id}): uri=${uri.toString(true)}`); const provider = this._providers.get(id); @@ -98,7 +98,7 @@ export class TimelineService implements ITimelineService { } return { - result: provider.provideTimeline(uri, cursor, tokenSource.token) + result: provider.provideTimeline(uri, cursor, tokenSource.token, options) .then(result => { if (result === undefined) { return undefined; From 2f9c79a8470f42854c9ca81aa28762993fbbfb7a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 12:39:29 -0800 Subject: [PATCH 132/192] Only enable webview copy tests on MacOS --- .../src/singlefolder-tests/webview.test.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index df8af33f6b2..3a7220ec34d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'mocha'; import * as assert from 'assert'; -import * as vscode from 'vscode'; +import 'mocha'; +import * as os from 'os'; import { join } from 'path'; -import { closeAllEditors, disposeAll, conditionalTest, delay } from '../utils'; +import * as vscode from 'vscode'; +import { closeAllEditors, conditionalTest, delay, disposeAll } from '../utils'; const webviewId = 'myWebview'; @@ -334,26 +335,28 @@ suite('Webview tests', () => { assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); }); - test('webview can copy text from webview', async () => { - const expectedText = `webview text from: ${Date.now()}!`; + if (os.platform() === 'darwin') { + test('webview can copy text from webview', async () => { + const expectedText = `webview text from: ${Date.now()}!`; - const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); - const ready = getMesssage(webview); + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); + const ready = getMesssage(webview); - webview.webview.html = createHtmlDocumentWithBody(/*html*/` + webview.webview.html = createHtmlDocumentWithBody(/*html*/` ${expectedText} `); - await ready; + await ready; - await vscode.commands.executeCommand('editor.action.webvieweditor.copy'); - await delay(200); // Make sure copy has time to reach webview - assert.strictEqual(await vscode.env.clipboard.readText(), expectedText); - }); + await vscode.commands.executeCommand('editor.action.webvieweditor.copy'); + await delay(200); // Make sure copy has time to reach webview + assert.strictEqual(await vscode.env.clipboard.readText(), expectedText); + }); + } }); function createHtmlDocumentWithBody(body: string): string { From 10787693138ce600935e7163341a5767b4419fc9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 14 Feb 2020 21:44:23 +0100 Subject: [PATCH 133/192] Tweaks for rendering the minimap with few lines and entire document --- src/vs/editor/common/config/editorOptions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 0a9ae7435d6..2ec1caef507 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1874,8 +1874,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption configuredFontScale) { + minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale); + } minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; minimapCanvasInnerHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight); } From 91fc43dbeeb7e06f2056f5209ef068f44ded5732 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 13:02:14 -0800 Subject: [PATCH 134/192] Add workaround for preferences editor strict function error For #81574 --- src/vs/platform/instantiation/common/instantiation.ts | 2 +- .../contrib/preferences/browser/preferencesEditor.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/instantiation/common/instantiation.ts b/src/vs/platform/instantiation/common/instantiation.ts index 10ff5a25051..7e130cc3963 100644 --- a/src/vs/platform/instantiation/common/instantiation.ts +++ b/src/vs/platform/instantiation/common/instantiation.ts @@ -29,7 +29,7 @@ export interface IConstructorSignature0 { } export interface IConstructorSignature1 { - new(first: A1, ...services: BrandedService[]): T; + new (first: A1, ...services: Services): T; } export interface IConstructorSignature2 { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 60bcbfd4ba4..a8865d6ec60 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -29,7 +29,7 @@ import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/multicursor' import * as nls from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -984,7 +984,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor { private static _getContributions(): IEditorContributionDescription[] { const skipContributions = [FoldingController.ID, SelectionHighlighter.ID, FindController.ID]; const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1); - contributions.push({ id: DefaultSettingsEditorContribution.ID, ctor: DefaultSettingsEditorContribution }); + contributions.push({ id: DefaultSettingsEditorContribution.ID, ctor: DefaultSettingsEditorContribution as IConstructorSignature1 }); return contributions; } From 5c017be321106b790c0a6ebd709e1bd9db5ec94a Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 14 Feb 2020 18:55:11 -0500 Subject: [PATCH 135/192] Fixes broken test with git extension --- extensions/git/package.json | 3 +- extensions/git/yarn.lock | 72 ++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index b1ae36f57a7..9d6d4f43f3d 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1833,6 +1833,7 @@ "@types/which": "^1.0.28", "mocha": "^3.2.0", "mocha-junit-reporter": "^1.23.3", - "mocha-multi-reporters": "^1.1.7" + "mocha-multi-reporters": "^1.1.7", + "vscode-test": "^1.3.0" } } diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index ff2f8994487..0ae8237610c 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -41,6 +41,13 @@ resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -112,6 +119,13 @@ debug@2.6.8: dependencies: ms "2.0.0" +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^2.2.0: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -143,6 +157,18 @@ diff@3.2.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + escape-string-regexp@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -170,6 +196,18 @@ glob@7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -190,6 +228,22 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +https-proxy-agent@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -300,7 +354,7 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" -minimatch@^3.0.2: +minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -378,6 +432,13 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -414,6 +475,15 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-test@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.3.0.tgz#3310ab385d9b887b4c82e8f52be1030e7cf9493d" + integrity sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.4" + rimraf "^2.6.3" + vscode-uri@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" From 8503705b114c93eb54840f23954d830264b7bbad Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 16:57:25 -0800 Subject: [PATCH 136/192] Don't show reference code lens for both class and ctor in es5 classes Fixes #90396 --- .../src/features/referencesCodeLens.ts | 8 ++++++++ .../src/test/referencesCodeLens.test.ts | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 5e92ce06e01..7aa228f8f8e 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -99,6 +99,14 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens case PConst.Kind.memberSetAccessor: case PConst.Kind.constructorImplementation: case PConst.Kind.memberVariable: + // Don't show if child and parent have same start + // For https://github.com/microsoft/vscode/issues/90396 + if (parent && + typeConverters.Position.fromLocation(parent.spans[0].start).isEqual(typeConverters.Position.fromLocation(item.spans[0].start)) + ) { + return null; + } + // Only show if parent is a class type object (not a literal) switch (parent?.kind) { case PConst.Kind.class: diff --git a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts index 7ca35ed89cc..89689d57ae6 100644 --- a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts +++ b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts @@ -91,6 +91,19 @@ suite('TypeScript References', () => { const codeLenses = await getCodeLenses(testDocumentUri); assert.strictEqual(codeLenses?.length, 0); }); + + test('Should not show duplicate references on ES5 class (https://github.com/microsoft/vscode/issues/90396)', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test3.js'); + await createTestEditor(testDocumentUri, + `function A() {`, + ` console.log("hi");`, + `}`, + `A.x = {};`, + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 1); + }); }); function getCodeLenses(document: vscode.Uri): Thenable { From 672bc17e2260619b25b09e9479478ddcf4b35264 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 16:57:35 -0800 Subject: [PATCH 137/192] Remove extra cast --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c998bd2c5f8..6027377e166 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -257,7 +257,7 @@ export namespace MarkdownString { } else if (htmlContent.isMarkdownString(markup)) { res = markup; } else if (typeof markup === 'string') { - res = { value: markup }; + res = { value: markup }; } else { res = { value: '' }; } From 1a81711a85e38ccf784110568ebf3784ab9094a5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 14 Feb 2020 17:10:28 -0800 Subject: [PATCH 138/192] Add reopen with to editor title menu Fixes #89604 --- .../contrib/customEditor/browser/commands.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 6814383e70b..71c474a0b15 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -40,7 +40,7 @@ CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAcces // #region Reopen With const REOPEN_WITH_COMMAND_ID = 'reOpenWith'; -const REOPEN_WITH_TITLE = { value: nls.localize('reopenWith.title', 'Reopen With'), original: 'Reopen With' }; +const REOPEN_WITH_TITLE = { value: nls.localize('reopenWith.title', 'Reopen With...'), original: 'Reopen With' }; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REOPEN_WITH_COMMAND_ID, @@ -83,6 +83,17 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { when: CONTEXT_HAS_CUSTOM_EDITORS, }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: REOPEN_WITH_COMMAND_ID, + title: REOPEN_WITH_TITLE, + category: viewCategory, + }, + group: '3_open', + order: 20, + when: CONTEXT_HAS_CUSTOM_EDITORS, +}); + // #endregion From 50e5b8fde59a7f5cf7e0b6963cc242c485b7c0e1 Mon Sep 17 00:00:00 2001 From: Victor Henrique Pecora Gomes Date: Fri, 14 Feb 2020 22:18:44 -0300 Subject: [PATCH 139/192] smartSelect fix --- .../smartSelect/test/smartSelect.test.ts | 127 ++++++++++++------ .../contrib/smartSelect/wordSelections.ts | 2 +- 2 files changed, 88 insertions(+), 41 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 2b19d53b201..249380f044a 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -180,9 +180,11 @@ suite('SmartSelect', () => { // -- bracket selections async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise { + let index = value.indexOf('|'); + value = value.replace('|', ''); let model = modelService.createModel(value, new StaticLanguageSelector(mode.getLanguageIdentifier()), URI.parse('fake:lang')); - let pos = model.getPositionAt(value.indexOf('|')); + let pos = model.getPositionAt(index); let all = await provider.provideSelectionRanges(model, [pos], CancellationToken.None); let ranges = all![0]; @@ -197,18 +199,18 @@ suite('SmartSelect', () => { test('bracket selection', async () => { await assertRanges(new BracketSelectionRangeProvider(), '(|)', - new Range(1, 2, 1, 3), new Range(1, 1, 1, 4) + new Range(1, 2, 1, 2), new Range(1, 1, 1, 3) ); await assertRanges(new BracketSelectionRangeProvider(), '[[[](|)]]', - new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), // () - new Range(1, 3, 1, 8), new Range(1, 2, 1, 9), // [[]()] - new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), // [[[]()]] + new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), // () + new Range(1, 3, 1, 7), new Range(1, 2, 1, 8), // [[]()] + new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), // [[[]()]] ); await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]', - new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), - new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), + new Range(1, 6, 1, 6), new Range(1, 5, 1, 7), + new Range(1, 2, 1, 8), new Range(1, 1, 1, 9), ); // no bracket @@ -219,23 +221,23 @@ suite('SmartSelect', () => { await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]'); // edge - await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); - await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); + await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9)); + await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 8), new Range(1, 1, 1, 9)); - await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 16), new Range(1, 12, 1, 17)); - await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 17), new Range(1, 13, 1, 18), new Range(1, 2, 1, 26), new Range(1, 1, 1, 27)); + await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 15), new Range(1, 12, 1, 16)); + await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 16), new Range(1, 13, 1, 17), new Range(1, 2, 1, 25), new Range(1, 1, 1, 26)); }); test('bracket with leading/trailing', async () => { await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(|);\n}', - new Range(2, 7, 2, 8), new Range(2, 6, 2, 9), + new Range(2, 7, 2, 7), new Range(2, 6, 2, 8), new Range(1, 13, 3, 1), new Range(1, 12, 3, 2), new Range(1, 1, 3, 2), new Range(1, 1, 3, 2), ); await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(|);\n}', - new Range(3, 7, 3, 8), new Range(3, 6, 3, 9), + new Range(3, 7, 3, 7), new Range(3, 6, 3, 8), new Range(2, 2, 4, 1), new Range(2, 1, 4, 2), new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), ); @@ -244,60 +246,60 @@ suite('SmartSelect', () => { test('in-word ranges', async () => { await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar', - new Range(1, 1, 1, 5), // foo - new Range(1, 1, 1, 8), // fooBar - new Range(1, 1, 1, 8), // doc + new Range(1, 1, 1, 4), // foo + new Range(1, 1, 1, 7), // fooBar + new Range(1, 1, 1, 7), // doc ); await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba', - new Range(1, 1, 1, 5), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), + new Range(1, 1, 1, 4), + new Range(1, 1, 1, 7), + new Range(1, 1, 1, 7), ); await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba', - new Range(1, 1, 1, 5), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), + new Range(1, 1, 1, 4), + new Range(1, 1, 1, 7), + new Range(1, 1, 1, 7), ); }); test('Default selection should select current word/hump first in camelCase #67493', async function () { await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect', - new Range(1, 1, 1, 10), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 1, 1, 9), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect', - new Range(1, 9, 1, 15), - new Range(1, 1, 1, 21), - new Range(1, 1, 1, 21), + new Range(1, 9, 1, 14), + new Range(1, 1, 1, 20), + new Range(1, 1, 1, 20), ); }); @@ -321,4 +323,49 @@ suite('SmartSelect', () => { reg.dispose(); }); + + test('Expand selection in words with underscores is inconsistent #90589', async function () { + + await assertRanges(new WordSelectionRangeProvider(), 'Hel|lo_World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello_Wo|rld', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|_World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello_|World', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|-World', + new Range(1, 1, 1, 6), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello-|World', + new Range(1, 7, 1, 12), + new Range(1, 1, 1, 12), + new Range(1, 1, 1, 12), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Hello|World', + new Range(1, 6, 1, 11), + new Range(1, 1, 1, 11), + new Range(1, 1, 1, 11), + ); + }); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index 7402202af54..280663f59a8 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -40,7 +40,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { // LEFT anchor (start) for (; start >= 0; start--) { let ch = word.charCodeAt(start); - if (ch === CharCode.Underline || ch === CharCode.Dash) { + if ((start !== offset) && (ch === CharCode.Underline || ch === CharCode.Dash)) { // foo-bar OR foo_bar break; } else if (isLowerAsciiLetter(ch) && isUpperAsciiLetter(lastCh)) { From feecdaeacf4e354a7a1e8ac264a84132e1d03548 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sat, 15 Feb 2020 12:49:11 -0800 Subject: [PATCH 140/192] Disable webview copy test on web --- .../vscode-api-tests/src/singlefolder-tests/webview.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index 3a7220ec34d..06d284c3762 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -336,7 +336,7 @@ suite('Webview tests', () => { }); if (os.platform() === 'darwin') { - test('webview can copy text from webview', async () => { + conditionalTest('webview can copy text from webview', async () => { const expectedText = `webview text from: ${Date.now()}!`; const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); From 6ec6f9e3f4d17f0cef0480688a1f350b9141e567 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sat, 15 Feb 2020 13:13:22 -0800 Subject: [PATCH 141/192] Fix git integration tests Move back to using normal vscode dependency for now . We will look into moving off it later --- extensions/git/package.json | 3 +- extensions/git/yarn.lock | 457 ++++++++++++++++++++++++++++++++++-- 2 files changed, 437 insertions(+), 23 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 9d6d4f43f3d..044c37c18b4 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1829,11 +1829,10 @@ "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", "@types/node": "^12.11.7", - "@types/vscode": "^1.42", "@types/which": "^1.0.28", "mocha": "^3.2.0", "mocha-junit-reporter": "^1.23.3", "mocha-multi-reporters": "^1.1.7", - "vscode-test": "^1.3.0" + "vscode": "^1.1.36" } } diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 0ae8237610c..ac135e43a80 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -31,11 +31,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== -"@types/vscode@^1.42": - version "1.42.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.42.0.tgz#0ad891a9487e91e34be7c56985058a179031eb76" - integrity sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw== - "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" @@ -48,6 +43,16 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +ajv@^6.5.5: + version "6.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" + integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -62,11 +67,45 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -80,16 +119,43 @@ browser-stdout@1.3.0: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -102,11 +168,23 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + dayjs@1.8.19: version "1.8.19" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe" @@ -140,6 +218,11 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -157,6 +240,19 @@ diff@3.2.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k= +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -174,16 +270,62 @@ escape-string-regexp@1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + file-type@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74" integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q= +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + glob@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" @@ -196,7 +338,19 @@ glob@7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3: +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -213,16 +367,39 @@ glob@^7.1.3: resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -236,7 +413,16 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -https-proxy-agent@^2.2.4: +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -269,21 +455,61 @@ is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + jschardet@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + json3@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -354,7 +580,19 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" -minimatch@^3.0.2, minimatch@^3.0.4: +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -410,6 +648,23 @@ mocha@^3.2.0: mkdirp "0.5.1" supports-color "3.1.2" +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -420,6 +675,11 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -432,14 +692,68 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -"safer-buffer@>= 2.1.2 < 3": +psl@^1.1.28: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -449,6 +763,39 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +source-map-support@^0.5.0: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -463,6 +810,62 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -475,20 +878,32 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== -vscode-test@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.3.0.tgz#3310ab385d9b887b4c82e8f52be1030e7cf9493d" - integrity sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw== +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== dependencies: http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.4" - rimraf "^2.6.3" + https-proxy-agent "^2.2.1" vscode-uri@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== +vscode@^1.1.36: + version "1.1.36" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" + integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" + which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" From d6280d5c7eb1dea0abcd0683e70d4f14309e4603 Mon Sep 17 00:00:00 2001 From: nrayburn Date: Sat, 15 Feb 2020 17:37:38 -0600 Subject: [PATCH 142/192] Close monaco editor tokens hover with escape key --- src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 3c32536b239..3e2d797c71e 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -6,6 +6,7 @@ import 'vs/css!./inspectTokens'; import { CharCode } from 'vs/base/common/charCode'; import { Color } from 'vs/base/common/color'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { escape } from 'vs/base/common/strings'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; @@ -48,6 +49,7 @@ class InspectTokensController extends Disposable implements IEditorContribution this._register(this._editor.onDidChangeModel((e) => this.stop())); this._register(this._editor.onDidChangeModelLanguage((e) => this.stop())); this._register(TokenizationRegistry.onDidChange((e) => this.stop())); + this._register(this._editor.onKeyUp((e) => e.keyCode === KeyCode.Escape && this.stop())); } public dispose(): void { From d8e7eb36a2286a9f7ef37771493e963284fa2d0a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 10:42:31 +0100 Subject: [PATCH 143/192] Text save participants are overwritten for each extension host (fixes #90359) --- .../src/singlefolder-tests/workspace.test.ts | 7 - .../api/browser/mainThreadSaveParticipant.ts | 349 +----------------- .../browser/codeEditor.contribution.ts | 1 + .../codeEditor/browser/saveParticipants.ts | 316 ++++++++++++++++ .../test/browser/saveParticipant.test.ts} | 4 +- .../textfile/browser/textFileService.ts | 4 +- .../textfile/common/textFileEditorModel.ts | 22 +- .../common/textFileEditorModelManager.ts | 19 +- .../common/textFileSaveParticipant.ts | 69 ++++ .../services/textfile/common/textfiles.ts | 31 +- .../test/browser/textFileEditorModel.test.ts | 86 ++++- .../test/browser/workbenchTestServices.ts | 15 + 12 files changed, 535 insertions(+), 388 deletions(-) create mode 100644 src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts rename src/vs/workbench/{test/electron-browser/api/mainThreadSaveParticipant.test.ts => contrib/codeEditor/test/browser/saveParticipant.test.ts} (98%) create mode 100644 src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 3e56f927168..c26c88671dd 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -215,13 +215,6 @@ suite('workspace-namespace', () => { }); test('eol, change via onWillSave', async function () { - if (vscode.env.uiKind === vscode.UIKind.Web) { - // TODO@Jo Test seems to fail when running in web due to - // onWillSaveTextDocument not getting called - this.skip(); - return; - } - let called = false; let sub = vscode.workspace.onWillSaveTextDocument(e => { called = true; diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 288a349b183..ce7aa7a7b53 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -3,304 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IdleValue, raceCancellation } from 'vs/base/common/async'; -import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; -import * as strings from 'vs/base/common/strings'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; -import { ITextModel } from 'vs/editor/common/model'; -import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; -import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; -import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; -import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; -import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { localize } from 'vs/nls'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; +import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { ISaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileSaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { SaveReason } from 'vs/workbench/common/editor'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol'; -import { ILabelService } from 'vs/platform/label/common/label'; import { canceled } from 'vs/base/common/errors'; +import { IDisposable } from 'vs/base/common/lifecycle'; -export interface ISaveParticipantParticipant { - participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }, progress: IProgress, token: CancellationToken): Promise; -} - -class TrimWhitespaceParticipant implements ISaveParticipantParticipant { - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @ICodeEditorService private readonly codeEditorService: ICodeEditorService - ) { - // Nothing - } - - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { - if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { - this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); - } - } - - private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void { - let prevSelection: Selection[] = []; - let cursors: Position[] = []; - - const editor = findEditor(model, this.codeEditorService); - if (editor) { - // Find `prevSelection` in any case do ensure a good undo stack when pushing the edit - // Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump - prevSelection = editor.getSelections(); - if (isAutoSaved) { - cursors = prevSelection.map(s => s.getPosition()); - const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange(); - if (snippetsRange) { - for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) { - cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber))); - } - } - } - } - - const ops = trimTrailingWhitespace(model, cursors); - if (!ops.length) { - return; // Nothing to do - } - - model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection); - } -} - -function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null { - let candidate: IActiveCodeEditor | null = null; - - if (model.isAttachedToEditor()) { - for (const editor of codeEditorService.listCodeEditors()) { - if (editor.hasModel() && editor.getModel() === model) { - if (editor.hasTextFocus()) { - return editor; // favour focused editor if there are multiple - } - - candidate = editor; - } - } - } - - return candidate; -} - -export class FinalNewLineParticipant implements ISaveParticipantParticipant { - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @ICodeEditorService private readonly codeEditorService: ICodeEditorService - ) { - // Nothing - } - - async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise { - if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { - this.doInsertFinalNewLine(model.textEditorModel); - } - } - - private doInsertFinalNewLine(model: ITextModel): void { - const lineCount = model.getLineCount(); - const lastLine = model.getLineContent(lineCount); - const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1; - - if (!lineCount || lastLineIsEmptyOrWhitespace) { - return; - } - - const edits = [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())]; - const editor = findEditor(model, this.codeEditorService); - if (editor) { - editor.executeEdits('insertFinalNewLine', edits, editor.getSelections()); - } else { - model.pushEditOperations([], edits, () => null); - } - } -} - -export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant { - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @ICodeEditorService private readonly codeEditorService: ICodeEditorService - ) { - // Nothing - } - - async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { - if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { - this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO); - } - } - - /** - * returns 0 if the entire file is empty or whitespace only - */ - private findLastLineWithContent(model: ITextModel): number { - for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) { - const lineContent = model.getLineContent(lineNumber); - if (strings.lastNonWhitespaceIndex(lineContent) !== -1) { - // this line has content - return lineNumber; - } - } - // no line has content - return 0; - } - - private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void { - const lineCount = model.getLineCount(); - - // Do not insert new line if file does not end with new line - if (lineCount === 1) { - return; - } - - let prevSelection: Selection[] = []; - let cannotTouchLineNumber = 0; - const editor = findEditor(model, this.codeEditorService); - if (editor) { - prevSelection = editor.getSelections(); - if (isAutoSaved) { - for (let i = 0, len = prevSelection.length; i < len; i++) { - const positionLineNumber = prevSelection[i].positionLineNumber; - if (positionLineNumber > cannotTouchLineNumber) { - cannotTouchLineNumber = positionLineNumber; - } - } - } - } - - const lastLineNumberWithContent = this.findLastLineWithContent(model); - const deleteFromLineNumber = Math.max(lastLineNumberWithContent + 1, cannotTouchLineNumber + 1); - const deletionRange = model.validateRange(new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount))); - - if (deletionRange.isEmpty()) { - return; - } - - model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection); - - if (editor) { - editor.setSelections(prevSelection); - } - } -} - -class FormatOnSaveParticipant implements ISaveParticipantParticipant { - - constructor( - @IConfigurationService private readonly _configurationService: IConfigurationService, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - ) { - // Nothing - } - - async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress, token: CancellationToken): Promise { - - const model = editorModel.textEditorModel; - const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }; - - if (env.reason === SaveReason.AUTO || !this._configurationService.getValue('editor.formatOnSave', overrides)) { - return undefined; - } - - progress.report({ message: localize('formatting', "Formatting") }); - const editorOrModel = findEditor(model, this._codeEditorService) || model; - await this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token); - } -} - -class CodeActionOnSaveParticipant implements ISaveParticipantParticipant { - - constructor( - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - ) { } - - async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress, token: CancellationToken): Promise { - if (env.reason === SaveReason.AUTO) { - return undefined; - } - const model = editorModel.textEditorModel; - - const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource }; - const setting = this._configurationService.getValue<{ [kind: string]: boolean }>('editor.codeActionsOnSave', settingsOverrides); - if (!setting) { - return undefined; - } - - const codeActionsOnSave = Object.keys(setting) - .filter(x => setting[x]).map(x => new CodeActionKind(x)) - .sort((a, b) => { - if (CodeActionKind.SourceFixAll.contains(a)) { - if (CodeActionKind.SourceFixAll.contains(b)) { - return 0; - } - return -1; - } - if (CodeActionKind.SourceFixAll.contains(b)) { - return 1; - } - return 0; - }); - - if (!codeActionsOnSave.length) { - return undefined; - } - - const excludedActions = Object.keys(setting) - .filter(x => setting[x] === false) - .map(x => new CodeActionKind(x)); - - progress.report({ message: localize('codeaction', "Quick Fixes") }); - await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token); - } - - private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise { - for (const codeActionKind of codeActionsOnSave) { - const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token); - try { - await this.applyCodeActions(actionsToRun.validActions); - } catch { - // Failure to apply a code action should not block other on save actions - } finally { - actionsToRun.dispose(); - } - } - } - - private async applyCodeActions(actionsToRun: readonly CodeAction[]) { - for (const action of actionsToRun) { - await this._instantiationService.invokeFunction(applyCodeAction, action); - } - } - - private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) { - return getCodeActions(model, model.getFullModelRange(), { - type: CodeActionTriggerType.Auto, - filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true }, - }, token); - } -} - -class ExtHostSaveParticipant implements ISaveParticipantParticipant { +class ExtHostSaveParticipant implements ITextFileSaveParticipant { private readonly _proxy: ExtHostDocumentSaveParticipantShape; @@ -336,65 +51,19 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant { // The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace @extHostCustomer -export class SaveParticipant implements ISaveParticipant { +export class SaveParticipant { - private readonly _saveParticipants: IdleValue; + private _saveParticipantDisposable: IDisposable; constructor( extHostContext: IExtHostContext, @IInstantiationService instantiationService: IInstantiationService, - @IProgressService private readonly _progressService: IProgressService, - @ILogService private readonly _logService: ILogService, - @ILabelService private readonly _labelService: ILabelService, @ITextFileService private readonly _textFileService: ITextFileService ) { - this._saveParticipants = new IdleValue(() => [ - instantiationService.createInstance(TrimWhitespaceParticipant), - instantiationService.createInstance(CodeActionOnSaveParticipant), - instantiationService.createInstance(FormatOnSaveParticipant), - instantiationService.createInstance(FinalNewLineParticipant), - instantiationService.createInstance(TrimFinalNewLinesParticipant), - instantiationService.createInstance(ExtHostSaveParticipant, extHostContext), - ]); - // Set as save participant for all text files - this._textFileService.saveParticipant = this; + this._saveParticipantDisposable = this._textFileService.files.addSaveParticipant(instantiationService.createInstance(ExtHostSaveParticipant, extHostContext)); } dispose(): void { - this._textFileService.saveParticipant = undefined; - this._saveParticipants.dispose(); - } - - async participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise { - - const cts = new CancellationTokenSource(token); - - return this._progressService.withProgress({ - title: localize('saveParticipants', "Running Save Participants for '{0}'", this._labelService.getUriLabel(model.resource, { relative: true })), - location: ProgressLocation.Notification, - cancellable: true, - delay: model.isDirty() ? 3000 : 5000 - }, async progress => { - // undoStop before participation - model.textEditorModel.pushStackElement(); - - for (let p of this._saveParticipants.getValue()) { - if (cts.token.isCancellationRequested) { - break; - } - try { - const promise = p.participate(model, context, progress, cts.token); - await raceCancellation(promise, cts.token); - } catch (err) { - this._logService.warn(err); - } - } - - // undoStop after participation - model.textEditorModel.pushStackElement(); - }, () => { - // user cancel - cts.dispose(true); - }); + this._saveParticipantDisposable.dispose(); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index dc8c179721c..49cf081d967 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -9,6 +9,7 @@ import './diffEditorHelper'; import './inspectKeybindings'; import './largeFileOptimizations'; import './inspectEditorTokens/inspectEditorTokens'; +import './saveParticipants'; import './toggleMinimap'; import './toggleMultiCursorModifier'; import './toggleRenderControlCharacter'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts new file mode 100644 index 00000000000..ea6fc68420f --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.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 { CancellationToken } from 'vs/base/common/cancellation'; +import * as strings from 'vs/base/common/strings'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ITextModel } from 'vs/editor/common/model'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; +import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; +import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; +import { IResolvedTextFileEditorModel, ITextFileService, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; +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'; + +class TrimWhitespaceParticipant implements ITextFileSaveParticipant { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService + ) { + // Nothing + } + + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { + if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { + this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); + } + } + + private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void { + let prevSelection: Selection[] = []; + let cursors: Position[] = []; + + const editor = findEditor(model, this.codeEditorService); + if (editor) { + // Find `prevSelection` in any case do ensure a good undo stack when pushing the edit + // Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump + prevSelection = editor.getSelections(); + if (isAutoSaved) { + cursors = prevSelection.map(s => s.getPosition()); + const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange(); + if (snippetsRange) { + for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) { + cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber))); + } + } + } + } + + const ops = trimTrailingWhitespace(model, cursors); + if (!ops.length) { + return; // Nothing to do + } + + model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection); + } +} + +function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null { + let candidate: IActiveCodeEditor | null = null; + + if (model.isAttachedToEditor()) { + for (const editor of codeEditorService.listCodeEditors()) { + if (editor.hasModel() && editor.getModel() === model) { + if (editor.hasTextFocus()) { + return editor; // favour focused editor if there are multiple + } + + candidate = editor; + } + } + } + + return candidate; +} + +export class FinalNewLineParticipant implements ITextFileSaveParticipant { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService + ) { + // Nothing + } + + async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise { + if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { + this.doInsertFinalNewLine(model.textEditorModel); + } + } + + private doInsertFinalNewLine(model: ITextModel): void { + const lineCount = model.getLineCount(); + const lastLine = model.getLineContent(lineCount); + const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1; + + if (!lineCount || lastLineIsEmptyOrWhitespace) { + return; + } + + const edits = [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())]; + const editor = findEditor(model, this.codeEditorService); + if (editor) { + editor.executeEdits('insertFinalNewLine', edits, editor.getSelections()); + } else { + model.pushEditOperations([], edits, () => null); + } + } +} + +export class TrimFinalNewLinesParticipant implements ITextFileSaveParticipant { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService + ) { + // Nothing + } + + async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise { + if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) { + this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO); + } + } + + /** + * returns 0 if the entire file is empty or whitespace only + */ + private findLastLineWithContent(model: ITextModel): number { + for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) { + const lineContent = model.getLineContent(lineNumber); + if (strings.lastNonWhitespaceIndex(lineContent) !== -1) { + // this line has content + return lineNumber; + } + } + // no line has content + return 0; + } + + private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void { + const lineCount = model.getLineCount(); + + // Do not insert new line if file does not end with new line + if (lineCount === 1) { + return; + } + + let prevSelection: Selection[] = []; + let cannotTouchLineNumber = 0; + const editor = findEditor(model, this.codeEditorService); + if (editor) { + prevSelection = editor.getSelections(); + if (isAutoSaved) { + for (let i = 0, len = prevSelection.length; i < len; i++) { + const positionLineNumber = prevSelection[i].positionLineNumber; + if (positionLineNumber > cannotTouchLineNumber) { + cannotTouchLineNumber = positionLineNumber; + } + } + } + } + + const lastLineNumberWithContent = this.findLastLineWithContent(model); + const deleteFromLineNumber = Math.max(lastLineNumberWithContent + 1, cannotTouchLineNumber + 1); + const deletionRange = model.validateRange(new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount))); + + if (deletionRange.isEmpty()) { + return; + } + + model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection); + + if (editor) { + editor.setSelections(prevSelection); + } + } +} + +class FormatOnSaveParticipant implements ITextFileSaveParticipant { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + // Nothing + } + + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress, token: CancellationToken): Promise { + const model = editorModel.textEditorModel; + const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri }; + + if (env.reason === SaveReason.AUTO || !this.configurationService.getValue('editor.formatOnSave', overrides)) { + return undefined; + } + + progress.report({ message: localize('formatting', "Formatting") }); + const editorOrModel = findEditor(model, this.codeEditorService) || model; + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token); + } +} + +class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { } + + async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress, token: CancellationToken): Promise { + if (env.reason === SaveReason.AUTO) { + return undefined; + } + const model = editorModel.textEditorModel; + + const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource }; + const setting = this.configurationService.getValue<{ [kind: string]: boolean }>('editor.codeActionsOnSave', settingsOverrides); + if (!setting) { + return undefined; + } + + const codeActionsOnSave = Object.keys(setting) + .filter(x => setting[x]).map(x => new CodeActionKind(x)) + .sort((a, b) => { + if (CodeActionKind.SourceFixAll.contains(a)) { + if (CodeActionKind.SourceFixAll.contains(b)) { + return 0; + } + return -1; + } + if (CodeActionKind.SourceFixAll.contains(b)) { + return 1; + } + return 0; + }); + + if (!codeActionsOnSave.length) { + return undefined; + } + + const excludedActions = Object.keys(setting) + .filter(x => setting[x] === false) + .map(x => new CodeActionKind(x)); + + progress.report({ message: localize('codeaction', "Quick Fixes") }); + await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token); + } + + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise { + for (const codeActionKind of codeActionsOnSave) { + const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token); + try { + await this.applyCodeActions(actionsToRun.validActions); + } catch { + // Failure to apply a code action should not block other on save actions + } finally { + actionsToRun.dispose(); + } + } + } + + private async applyCodeActions(actionsToRun: readonly CodeAction[]) { + for (const action of actionsToRun) { + await this.instantiationService.invokeFunction(applyCodeAction, action); + } + } + + private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) { + return getCodeActions(model, model.getFullModelRange(), { + type: CodeActionTriggerType.Auto, + filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true }, + }, token); + } +} + +export class SaveParticipantsContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextFileService private readonly textFileService: ITextFileService + ) { + super(); + + this.registerSaveParticipants(); + } + + private registerSaveParticipants(): void { + this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(TrimWhitespaceParticipant))); + this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(CodeActionOnSaveParticipant))); + this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(FormatOnSaveParticipant))); + this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(FinalNewLineParticipant))); + this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(TrimFinalNewLinesParticipant))); + } +} + +const workbenchContributionsRegistry = Registry.as(WorkbenchContributionsExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(SaveParticipantsContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts similarity index 98% rename from src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts rename to src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts index e9c8770111e..c695bd3eaef 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts @@ -5,9 +5,9 @@ import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { FinalNewLineParticipant, TrimFinalNewLinesParticipant } from 'vs/workbench/api/browser/mainThreadSaveParticipant'; +import { FinalNewLineParticipant, TrimFinalNewLinesParticipant } from 'vs/workbench/contrib/codeEditor/browser/saveParticipants'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 1f6c9878fa5..c2d6d00c6ce 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { Emitter, AsyncEmitter } from 'vs/base/common/event'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions, ITextFileEditorModelManager, ISaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModel, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions, ITextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, IEncodingSupport } from 'vs/workbench/common/editor'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IFileService, FileOperationError, FileOperationResult, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; @@ -69,8 +69,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex }; })(); - saveParticipant: ISaveParticipant | undefined = undefined; - abstract get encoding(): IResourceEncodings; constructor( diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 6186973a5b8..b9ac2040100 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -616,7 +616,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.textEditorModel.pushStackElement(); } - const saveParticipantCancellation = new CancellationTokenSource(); + const saveCancellation = new CancellationTokenSource(); return this.saveSequentializer.setPending(versionId, (async () => { @@ -625,14 +625,26 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // In addition we update our version right after in case it changed because of a model change // // Save participants can also be skipped through API. - if (this.isResolved() && this.textFileService.saveParticipant && !options.skipSaveParticipants) { + if (this.isResolved() && !options.skipSaveParticipants) { try { - await this.textFileService.saveParticipant.participate(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveParticipantCancellation.token); + await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); } catch (error) { - // Ignore + this.logService.error(`[text file model] runSaveParticipants(${versionId}) - resulted in an error: ${error.toString()}`, this.resource.toString()); } } + // It is possible that a subsequent save is cancelling this + // running save. As such we return early when we detect that + // However, we do not pass the token into the file service + // because that is an atomic operation currently without + // cancellation support, so we dispose the cancellation if + // it was not cancelled yet. + if (saveCancellation.token.isCancellationRequested) { + return; + } else { + saveCancellation.dispose(); + } + // We have to protect against being disposed at this point. It could be that the save() operation // was triggerd followed by a dispose() operation right after without waiting. Typically we cannot // be disposed if we are dirty, but if we are not dirty, save() and dispose() can still be triggered @@ -687,7 +699,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.handleSaveError(error, versionId, options); } })()); - })(), () => saveParticipantCancellation.cancel()); + })(), () => saveCancellation.cancel()); } private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveOptions): void { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index a4fbde9a277..12040a85830 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/event'; 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, IModelLoadOrCreateOptions, ITextFileModelLoadEvent, ITextFileModelSaveEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel, ITextFileEditorModelManager, IModelLoadOrCreateOptions, ITextFileModelLoadEvent, ITextFileModelSaveEvent, ITextFileSaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; @@ -15,6 +15,9 @@ import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files'; import { distinct, coalesce } from 'vs/base/common/arrays'; import { ResourceQueue } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { TextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textFileSaveParticipant'; +import { SaveReason } from 'vs/workbench/common/editor'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { @@ -227,6 +230,20 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE } } + //#region Save participants + + private readonly saveParticipants = this._register(this.instantiationService.createInstance(TextFileSaveParticipant)); + + addSaveParticipant(participant: ITextFileSaveParticipant): IDisposable { + return this.saveParticipants.addSaveParticipant(participant); + } + + runSaveParticipants(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise { + return this.saveParticipants.participate(model, context, token); + } + + //#endregion + clear(): void { // model caches diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts new file mode 100644 index 00000000000..2ba91174b67 --- /dev/null +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { raceCancellation } from 'vs/base/common/async'; +import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; +import { localize } from 'vs/nls'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { ITextFileSaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/common/editor'; +import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; + +export class TextFileSaveParticipant extends Disposable { + + private readonly saveParticipants: ITextFileSaveParticipant[] = []; + + constructor( + @IProgressService private readonly progressService: IProgressService, + @ILogService private readonly logService: ILogService + ) { + super(); + } + + addSaveParticipant(participant: ITextFileSaveParticipant): IDisposable { + this.saveParticipants.push(participant); + + return toDisposable(() => this.saveParticipants.splice(this.saveParticipants.indexOf(participant), 1)); + } + + participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise { + const cts = new CancellationTokenSource(token); + + return this.progressService.withProgress({ + title: localize('saveParticipants', "Running Save Participants for '{0}'", model.name), + location: ProgressLocation.Notification, + cancellable: true, + delay: model.isDirty() ? 3000 : 5000 + }, async progress => { + + // undoStop before participation + model.textEditorModel.pushStackElement(); + + for (const saveParticipant of this.saveParticipants) { + if (cts.token.isCancellationRequested) { + break; + } + + try { + const promise = saveParticipant.participate(model, context, progress, cts.token); + await raceCancellation(promise, cts.token); + } catch (err) { + this.logService.warn(err); + } + } + + // undoStop after participation + model.textEditorModel.pushStackElement(); + }, () => { + // user cancel + cts.dispose(true); + }); + } + + dispose(): void { + this.saveParticipants.splice(0, this.saveParticipants.length); + } +} diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index c3c1f68d02c..d89c66af2c9 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -17,6 +17,7 @@ import { isNative } from 'vs/base/common/platform'; import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IUntitledTextEditorModelManager } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; export const ITextFileService = createDecorator('textFileService'); @@ -57,11 +58,6 @@ export interface ITextFileService extends IDisposable { */ saveErrorHandler: ISaveErrorHandler; - /** - * The save participant if any. By default, no save participant is registered. - */ - saveParticipant: ISaveParticipant | undefined; - /** * A resource is dirty if it has unsaved changes or is an untitled file not yet saved. * @@ -226,14 +222,6 @@ export interface ISaveErrorHandler { onSaveError(error: Error, model: ITextFileEditorModel): void; } -export interface ISaveParticipant { - - /** - * Participate in a save of a model. Allows to change the model before it is being saved to disk. - */ - participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason }, token: CancellationToken): Promise; -} - /** * States the text file editor model can be in. */ @@ -357,6 +345,20 @@ export interface ITextFileModelLoadEvent { reason: LoadReason; } +export interface ITextFileSaveParticipant { + + /** + * Participate in a save of a model. Allows to change the model + * before it is being saved to disk. + */ + participate( + model: IResolvedTextFileEditorModel, + context: { reason: SaveReason }, + progress: IProgress, + token: CancellationToken + ): Promise; +} + export interface ITextFileEditorModelManager { readonly onDidLoad: Event; @@ -372,6 +374,9 @@ export interface ITextFileEditorModelManager { resolve(resource: URI, options?: IModelLoadOrCreateOptions): Promise; + addSaveParticipant(participant: ITextFileSaveParticipant): IDisposable; + runSaveParticipants(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise + disposeModel(model: ITextFileEditorModel): void; } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index c835deecece..af2880b2e16 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -502,55 +502,98 @@ suite('Files - TextFileEditorModel', () => { eventCounter++; }); - accessor.textFileService.saveParticipant = { + const participant = accessor.textFileService.files.addSaveParticipant({ participate: async model => { assert.ok(model.isDirty()); model.textEditorModel!.setValue('bar'); assert.ok(model.isDirty()); eventCounter++; } - }; + }); await model.load(); model.textEditorModel!.setValue('foo'); await model.save(); - model.dispose(); assert.equal(eventCounter, 2); + + participant.dispose(); + model.textEditorModel!.setValue('bar'); + + await model.save(); + assert.equal(eventCounter, 3); + + model.dispose(); + }); + + test('Save Participant - skip', async function () { + let eventCounter = 0; + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + const participant = accessor.textFileService.files.addSaveParticipant({ + participate: async model => { + eventCounter++; + } + }); + + await model.load(); + model.textEditorModel!.setValue('foo'); + + await model.save({ skipSaveParticipants: true }); + assert.equal(eventCounter, 0); + + participant.dispose(); + model.dispose(); }); test('Save Participant, async participant', async function () { + let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - accessor.textFileService.saveParticipant = { - participate: (model) => { + model.onDidSave(e => { + assert.ok(!model.isDirty()); + eventCounter++; + }); + + const participant = accessor.textFileService.files.addSaveParticipant({ + participate: model => { + assert.ok(model.isDirty()); + model.textEditorModel!.setValue('bar'); + assert.ok(model.isDirty()); + eventCounter++; + return timeout(10); } - }; + }); await model.load(); model.textEditorModel!.setValue('foo'); const now = Date.now(); await model.save(); + assert.equal(eventCounter, 2); assert.ok(Date.now() - now >= 10); + model.dispose(); + participant.dispose(); }); test('Save Participant, bad participant', async function () { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - accessor.textFileService.saveParticipant = { + const participant = accessor.textFileService.files.addSaveParticipant({ participate: async model => { new Error('boom'); } - }; + }); await model.load(); model.textEditorModel!.setValue('foo'); await model.save(); + model.dispose(); + participant.dispose(); }); test('Save Participant, participant cancelled when saved again', async function () { @@ -558,12 +601,15 @@ suite('Files - TextFileEditorModel', () => { let participations: boolean[] = []; - accessor.textFileService.saveParticipant = { - participate: async model => { + const participant = accessor.textFileService.files.addSaveParticipant({ + participate: async (model, context, progress, token) => { await timeout(10); - participations.push(true); + + if (!token.isCancellationRequested) { + participations.push(true); + } } - }; + }); await model.load(); @@ -574,12 +620,16 @@ suite('Files - TextFileEditorModel', () => { const p2 = model.save(); model.textEditorModel!.setValue('foo 2'); - await model.save(); + const p3 = model.save(); - await p1; - await p2; + model.textEditorModel!.setValue('foo 3'); + const p4 = model.save(); + + await Promise.all([p1, p2, p3, p4]); assert.equal(participations.length, 1); + model.dispose(); + participant.dispose(); }); test('Save Participant, calling save from within is unsupported but does not explode (sync save)', async function () { @@ -602,7 +652,7 @@ suite('Files - TextFileEditorModel', () => { let savePromise: Promise; let breakLoop = false; - accessor.textFileService.saveParticipant = { + const participant = accessor.textFileService.files.addSaveParticipant({ participate: async model => { if (breakLoop) { return; @@ -618,12 +668,14 @@ suite('Files - TextFileEditorModel', () => { // assert that this is the same promise as the outer one assert.equal(savePromise, newSavePromise); } - }; + }); await model.load(); model.textEditorModel!.setValue('foo'); savePromise = model.save(); await savePromise; + + participant.dispose(); } }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 67ef5397a99..783d4d74a56 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -89,6 +89,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { Direction } from 'vs/base/browser/ui/grid/grid'; +import { IProgressService, IProgressOptions, IProgressWindowOptions, IProgressNotificationOptions, IProgressCompositeOptions, IProgress, IProgressStep, emptyProgress } from 'vs/platform/progress/common/progress'; export import TestTextResourcePropertiesService = CommonWorkbenchTestServices.TestTextResourcePropertiesService; export import TestContextService = CommonWorkbenchTestServices.TestContextService; @@ -175,6 +176,7 @@ export function workbenchInstantiationService(overrides?: { textFileService?: (i instantiationService.stub(IEnvironmentService, TestEnvironmentService); const contextKeyService = instantiationService.createInstance(MockContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); + instantiationService.stub(IProgressService, new TestProgressService()); const workspaceContextService = new TestContextService(TestWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceContextService); const configService = new TestConfigurationService(); @@ -217,6 +219,19 @@ export function workbenchInstantiationService(overrides?: { textFileService?: (i return instantiationService; } +export class TestProgressService implements IProgressService { + + _serviceBrand: undefined; + + withProgress( + options: IProgressOptions | IProgressWindowOptions | IProgressNotificationOptions | IProgressCompositeOptions, + task: (progress: IProgress) => Promise, + onDidCancel?: ((choice?: number | undefined) => void) | undefined + ): Promise { + return task(emptyProgress); + } +} + export class TestAccessibilityService implements IAccessibilityService { _serviceBrand: undefined; From 42e21c2e6332dd81e00055d69c7116eec4b63b45 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 10:44:25 +0100 Subject: [PATCH 144/192] electron - configure to disable LayoutNG (workaround for #88873) --- src/main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index 0e90f4504c8..6a6cb060acc 100644 --- a/src/main.js +++ b/src/main.js @@ -82,7 +82,7 @@ app.once('ready', function () { traceOptions: args['trace-options'] || 'record-until-full,enable-sampling' }; - contentTracing.startRecording(traceOptions, () => onReady()); + contentTracing.startRecording(traceOptions).finally(() => onReady()); } else { onReady(); } @@ -162,6 +162,9 @@ function configureCommandlineSwitchesSync(cliArgs) { app.commandLine.appendSwitch('js-flags', jsFlags); } + // TODO@Ben TODO@Deepak Electron 7 workaround for https://github.com/microsoft/vscode/issues/88873 + app.commandLine.appendSwitch('disable-features', 'LayoutNG'); + return argvConfig; } From d9de4afd7a5bdeae266b06a5c98b227c1e9e88c7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 10:46:07 +0100 Subject: [PATCH 145/192] progress - remove unused CSS rules --- .../services/progress/browser/media/progressService.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/vs/workbench/services/progress/browser/media/progressService.css b/src/vs/workbench/services/progress/browser/media/progressService.css index 6b73a2f3a4e..a1e92a76439 100644 --- a/src/vs/workbench/services/progress/browser/media/progressService.css +++ b/src/vs/workbench/services/progress/browser/media/progressService.css @@ -3,14 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress { - padding-left: 5px; -} - -.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress .spinner-container { - padding-right: 5px; -} - .monaco-workbench .progress-badge > .badge-content::before { mask: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); -webkit-mask: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); From 0f42828c4f512e8401512e0c12e27c85e4361115 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 11:02:19 +0100 Subject: [PATCH 146/192] types - reduce type duplication --- .../notification/common/notification.ts | 10 ++-- src/vs/workbench/browser/labels.ts | 2 +- src/vs/workbench/browser/layout.ts | 28 +++++------ src/vs/workbench/browser/part.ts | 2 +- .../workbench/browser/parts/compositeBar.ts | 4 +- .../browser/parts/editor/baseEditor.ts | 2 +- .../browser/parts/editor/editorGroupView.ts | 2 +- .../browser/parts/panel/panelPart.ts | 2 +- src/vs/workbench/browser/viewlet.ts | 2 +- src/vs/workbench/browser/workbench.ts | 8 +-- src/vs/workbench/common/notifications.ts | 49 +++++++++---------- src/vs/workbench/common/resources.ts | 6 +-- 12 files changed, 55 insertions(+), 62 deletions(-) diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 93429296cfe..aff1675b214 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -6,7 +6,7 @@ import BaseSeverity from 'vs/base/common/severity'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IAction } from 'vs/base/common/actions'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; export import Severity = BaseSeverity; @@ -318,16 +318,14 @@ export class NoOpNotification implements INotificationHandle { readonly progress = new NoOpProgress(); - private readonly _onDidClose: Emitter = new Emitter(); - readonly onDidClose: Event = this._onDidClose.event; + readonly onDidClose = Event.None; + readonly onDidChangeVisibility = Event.None; updateSeverity(severity: Severity): void { } updateMessage(message: NotificationMessage): void { } updateActions(actions?: INotificationActions): void { } - close(): void { - this._onDidClose.dispose(); - } + close(): void { } } export class NoOpProgress implements INotificationProgress { diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 5c87849e9ea..bcbae8f4e2d 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -239,7 +239,7 @@ enum Redraw { class ResourceLabelWidget extends IconLabel { private _onDidRender = this._register(new Emitter()); - readonly onDidRender: Event = this._onDidRender.event; + readonly onDidRender = this._onDidRender.event; private readonly renderDisposables = this._register(new DisposableStore()); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index f94fe18534f..66320713617 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, Dimension, toggleClass, position, size } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -87,26 +87,26 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi //#region Events - private readonly _onZenModeChange: Emitter = this._register(new Emitter()); - readonly onZenModeChange: Event = this._onZenModeChange.event; + private readonly _onZenModeChange = this._register(new Emitter()); + readonly onZenModeChange = this._onZenModeChange.event; - private readonly _onFullscreenChange: Emitter = this._register(new Emitter()); - readonly onFullscreenChange: Event = this._onFullscreenChange.event; + private readonly _onFullscreenChange = this._register(new Emitter()); + readonly onFullscreenChange = this._onFullscreenChange.event; - private readonly _onCenteredLayoutChange: Emitter = this._register(new Emitter()); - readonly onCenteredLayoutChange: Event = this._onCenteredLayoutChange.event; + private readonly _onCenteredLayoutChange = this._register(new Emitter()); + readonly onCenteredLayoutChange = this._onCenteredLayoutChange.event; - private readonly _onMaximizeChange: Emitter = this._register(new Emitter()); - readonly onMaximizeChange: Event = this._onMaximizeChange.event; + private readonly _onMaximizeChange = this._register(new Emitter()); + readonly onMaximizeChange = this._onMaximizeChange.event; - private readonly _onPanelPositionChange: Emitter = this._register(new Emitter()); - readonly onPanelPositionChange: Event = this._onPanelPositionChange.event; + private readonly _onPanelPositionChange = this._register(new Emitter()); + readonly onPanelPositionChange = this._onPanelPositionChange.event; - private readonly _onPartVisibilityChange: Emitter = this._register(new Emitter()); - readonly onPartVisibilityChange: Event = this._onPartVisibilityChange.event; + private readonly _onPartVisibilityChange = this._register(new Emitter()); + readonly onPartVisibilityChange = this._onPartVisibilityChange.event; private readonly _onLayout = this._register(new Emitter()); - readonly onLayout: Event = this._onLayout.event; + readonly onLayout = this._onLayout.event; //#endregion diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index 1e07ec23451..cec01038567 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -34,7 +34,7 @@ export abstract class Part extends Component implements ISerializableView { get dimension(): Dimension | undefined { return this._dimension; } protected _onDidVisibilityChange = this._register(new Emitter()); - readonly onDidVisibilityChange: Event = this._onDidVisibilityChange.event; + readonly onDidVisibilityChange = this._onDidVisibilityChange.event; private parent: HTMLElement | undefined; private titleArea: HTMLElement | undefined; diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 4f824d94c74..4ed709af080 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -19,7 +19,7 @@ import { Widget } from 'vs/base/browser/ui/widget'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; import { ITheme } from 'vs/platform/theme/common/themeService'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; export interface ICompositeBarItem { id: string; @@ -61,7 +61,7 @@ export class CompositeBar extends Widget implements ICompositeBar { private compositeTransfer: LocalSelectionTransfer; private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; + readonly onDidChange = this._onDidChange.event; constructor( items: ICompositeBarItem[], diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 583bdb727a7..19781fbb59f 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -39,7 +39,7 @@ export abstract class BaseEditor extends Panel implements IEditor { readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height; readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height; - readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined> = Event.None; + readonly onDidSizeConstraintsChange = Event.None; protected _input: EditorInput | undefined; protected _options: EditorOptions | undefined; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 11f5d5a0a04..cb05c88239b 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1580,7 +1580,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { get maximumHeight(): number { return this.editorControl.maximumHeight; } private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>()); - readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event; + readonly onDidChange = this._onDidChange.event; layout(width: number, height: number): void { this.dimension = new Dimension(width, height); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index f9d079be854..6ee4f8cff77 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -76,7 +76,7 @@ export class PanelPart extends CompositePart implements IPanelService { //#endregion get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean; }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } - readonly onDidPanelClose: Event = this.onDidCompositeClose.event; + readonly onDidPanelClose = this.onDidCompositeClose.event; private activePanelContextKey: IContextKey; private panelFocusContextKey: IContextKey; diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 2ea6bcae00f..ddcb76a0a95 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -68,7 +68,7 @@ export abstract class Viewlet extends PaneComposite implements IViewlet { */ export class ViewletDescriptor extends CompositeDescriptor { - public static create( + static create( ctor: { new(...services: Services): Viewlet }, id: string, name: string, diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 604e4d1e914..a24435cd241 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -6,7 +6,7 @@ import 'vs/workbench/browser/style'; import { localize } from 'vs/nls'; -import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; +import { Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; import { addClasses, addClass, removeClasses } from 'vs/base/browser/dom'; import { runWhenIdle } from 'vs/base/common/async'; import { getZoomLevel, isFirefox, isSafari, isChrome } from 'vs/base/browser/browser'; @@ -50,13 +50,13 @@ import { Extensions as PanelExtensions, PanelRegistry } from 'vs/workbench/brows export class Workbench extends Layout { private readonly _onBeforeShutdown = this._register(new Emitter()); - readonly onBeforeShutdown: Event = this._onBeforeShutdown.event; + readonly onBeforeShutdown = this._onBeforeShutdown.event; private readonly _onWillShutdown = this._register(new Emitter()); - readonly onWillShutdown: Event = this._onWillShutdown.event; + readonly onWillShutdown = this._onWillShutdown.event; private readonly _onShutdown = this._register(new Emitter()); - readonly onShutdown: Event = this._onShutdown.event; + readonly onShutdown = this._onShutdown.event; constructor( parent: HTMLElement, diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index a6b19e5df2c..01d7271a3b9 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -16,9 +16,7 @@ import { find, equals } from 'vs/base/common/arrays'; export interface INotificationsModel { - // - // Notifications as Toasts/Center - // + //#region Notifications as Toasts/Center readonly notifications: INotificationViewItem[]; @@ -29,15 +27,18 @@ export interface INotificationsModel { setFilter(filter: NotificationsFilter): void; - // - // Notifications as Status - // + //#endregion + + + //#region Notifications as Status readonly statusMessage: IStatusMessageViewItem | undefined; readonly onDidStatusMessageChange: Event; showStatusMessage(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable; + + //#endregion } export const enum NotificationChangeType { @@ -130,13 +131,13 @@ export class NotificationsModel extends Disposable implements INotificationsMode private static readonly NO_OP_NOTIFICATION = new NoOpNotification(); private readonly _onDidNotificationChange = this._register(new Emitter()); - readonly onDidNotificationChange: Event = this._onDidNotificationChange.event; + readonly onDidNotificationChange = this._onDidNotificationChange.event; private readonly _onDidStatusMessageChange = this._register(new Emitter()); - readonly onDidStatusMessageChange: Event = this._onDidStatusMessageChange.event; + readonly onDidStatusMessageChange = this._onDidStatusMessageChange.event; private readonly _onDidFilterChange = this._register(new Emitter()); - readonly onDidFilterChange: Event = this._onDidFilterChange.event; + readonly onDidFilterChange = this._onDidFilterChange.event; private readonly _notifications: INotificationViewItem[] = []; get notifications(): INotificationViewItem[] { return this._notifications; } @@ -171,10 +172,10 @@ export class NotificationsModel extends Disposable implements INotificationsMode this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD }); // Wrap into handle - return new NotificationHandle(item, item => this.closeItem(item)); + return new NotificationHandle(item, item => this.onClose(item)); } - private closeItem(item: INotificationViewItem): void { + private onClose(item: INotificationViewItem): void { const liveItem = this.findNotification(item); if (liveItem && liveItem !== item) { liveItem.close(); // item could have been replaced with another one, make sure to close the live item @@ -309,8 +310,8 @@ export interface INotificationViewItemProgress extends INotificationProgress { export class NotificationViewItemProgress extends Disposable implements INotificationViewItemProgress { private readonly _state: INotificationViewItemProgressState; - private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange = this._onDidChange.event; constructor() { super(); @@ -405,14 +406,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie private _actions: INotificationActions | undefined; private _progress: NotificationViewItemProgress | undefined; - private readonly _onDidExpansionChange: Emitter = this._register(new Emitter()); - readonly onDidExpansionChange: Event = this._onDidExpansionChange.event; + private readonly _onDidExpansionChange = this._register(new Emitter()); + readonly onDidExpansionChange = this._onDidExpansionChange.event; - private readonly _onDidClose: Emitter = this._register(new Emitter()); - readonly onDidClose: Event = this._onDidClose.event; + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; - private readonly _onDidLabelChange: Emitter = this._register(new Emitter()); - readonly onDidLabelChange: Event = this._onDidLabelChange.event; + private readonly _onDidLabelChange = this._register(new Emitter()); + readonly onDidLabelChange = this._onDidLabelChange.event; static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { @@ -656,8 +657,8 @@ export class NotificationViewItem extends Disposable implements INotificationVie export class ChoiceAction extends Action { - private readonly _onDidRun = new Emitter(); - readonly onDidRun: Event = this._onDidRun.event; + private readonly _onDidRun = this._register(new Emitter()); + readonly onDidRun = this._onDidRun.event; private readonly _keepOpen: boolean; @@ -679,12 +680,6 @@ export class ChoiceAction extends Action { get keepOpen(): boolean { return this._keepOpen; } - - dispose(): void { - super.dispose(); - - this._onDidRun.dispose(); - } } class StatusMessageViewItem { diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index c509716fc49..408dcca11ac 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import * as objects from 'vs/base/common/objects'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { basename, extname, relativePath } from 'vs/base/common/resources'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -106,8 +106,8 @@ export class ResourceGlobMatcher extends Disposable { private static readonly NO_ROOT: string | null = null; - private readonly _onExpressionChange: Emitter = this._register(new Emitter()); - readonly onExpressionChange: Event = this._onExpressionChange.event; + private readonly _onExpressionChange = this._register(new Emitter()); + readonly onExpressionChange = this._onExpressionChange.event; private readonly mapRootToParsedExpression: Map = new Map(); private readonly mapRootToExpressionConfig: Map = new Map(); From 8d66874a193e86663587380063753db44e3a0674 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 11:03:21 +0100 Subject: [PATCH 147/192] debt - use progress service for progress notifications --- .../extensions/browser/extensionUrlHandler.ts | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 41423b188fa..01d63f94a76 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -13,7 +13,7 @@ import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementSer import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IURLHandler, IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -26,6 +26,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; @@ -100,7 +101,8 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { @IHostService private readonly hostService: IHostService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IStorageService private readonly storageService: IStorageService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProgressService private readonly progressService: IProgressService ) { this.storage = new ConfirmedExtensionIdStorage(storageService); @@ -273,32 +275,20 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return; } - let notificationHandle: INotificationHandle | null = this.notificationService.notify({ severity: Severity.Info, message: localize('Installing', "Installing Extension '{0}'...", galleryExtension.displayName || galleryExtension.name) }); - notificationHandle.progress.infinite(); - notificationHandle.onDidClose(() => notificationHandle = null); - try { - await this.extensionManagementService.installFromGallery(galleryExtension); - const reloadMessage = localize('reload', "Would you like to reload the window and open the URL '{0}'?", uri.toString()); - const reloadActionLabel = localize('Reload', "Reload Window and Open"); + await this.progressService.withProgress({ + location: ProgressLocation.Notification, + title: localize('Installing', "Installing Extension '{0}'...", galleryExtension.displayName || galleryExtension.name) + }, () => this.extensionManagementService.installFromGallery(galleryExtension)); - if (notificationHandle) { - notificationHandle.progress.done(); - notificationHandle.updateMessage(reloadMessage); - notificationHandle.updateActions({ - primary: [new Action('reloadWindow', reloadActionLabel, undefined, true, () => this.reloadAndHandle(uri))] - }); - } else { - this.notificationService.prompt(Severity.Info, reloadMessage, [{ label: reloadActionLabel, run: () => this.reloadAndHandle(uri) }], { sticky: true }); - } - } catch (e) { - if (notificationHandle) { - notificationHandle.progress.done(); - notificationHandle.updateSeverity(Severity.Error); - notificationHandle.updateMessage(e); - } else { - this.notificationService.error(e); - } + this.notificationService.prompt( + Severity.Info, + localize('reload', "Would you like to reload the window and open the URL '{0}'?", uri.toString()), + [{ label: localize('Reload', "Reload Window and Open"), run: () => this.reloadAndHandle(uri) }], + { sticky: true } + ); + } catch (error) { + this.notificationService.error(error); } } } From 25b36c1c375b967c012fd65e74e352537ba33cc9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 11:25:17 +0100 Subject: [PATCH 148/192] rename empty view contents to view welcome --- build/lib/i18n.resources.json | 4 - extensions/git/package.json | 56 +++++---- .../common/codeActions.contribution.ts | 4 + .../common/documentationContribution.ts | 84 ++++++++++++++ .../common/documentationExtensionPoint.ts | 35 ------ .../common/documentationContribution.ts | 108 ------------------ .../common/viewsWelcome.contribution.ts} | 8 +- .../common/viewsWelcomeContribution.ts | 46 ++++++++ .../common/viewsWelcomeExtensionPoint.ts | 53 +++++++++ src/vs/workbench/workbench.common.main.ts | 4 +- 10 files changed, 220 insertions(+), 182 deletions(-) create mode 100644 src/vs/workbench/contrib/codeActions/common/documentationContribution.ts rename src/vs/workbench/contrib/{documentation => codeActions}/common/documentationExtensionPoint.ts (65%) delete mode 100644 src/vs/workbench/contrib/documentation/common/documentationContribution.ts rename src/vs/workbench/contrib/{documentation/common/documentation.contribution.ts => welcome/common/viewsWelcome.contribution.ts} (68%) create mode 100644 src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts create mode 100644 src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 715a478c135..fe693b695b4 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -62,10 +62,6 @@ "name": "vs/workbench/contrib/debug", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/documentation", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" diff --git a/extensions/git/package.json b/extensions/git/package.json index 922a16d0bf0..596f0422887 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1762,35 +1762,33 @@ ] } }, - "documentation": { - "view": [ - { - "view": "workbench.scm", - "contents": "%view.workbench.scm.disabled%", - "when": "!config.git.enabled" - }, - { - "view": "workbench.scm", - "contents": "%view.workbench.scm.missing%", - "when": "config.git.enabled && git.missing" - }, - { - "view": "workbench.scm", - "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && !git.missing && workbenchState == empty" - }, - { - "view": "workbench.scm", - "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && !git.missing && workbenchState == folder" - }, - { - "view": "workbench.scm", - "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && !git.missing && workbenchState == workspace" - } - ] - } + "viewsWelcome": [ + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.disabled%", + "when": "!config.git.enabled" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.missing%", + "when": "config.git.enabled && git.missing" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.empty%", + "when": "config.git.enabled && !git.missing && workbenchState == empty" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.folder%", + "when": "config.git.enabled && !git.missing && workbenchState == folder" + }, + { + "view": "workbench.scm", + "contents": "%view.workbench.scm.workspace%", + "when": "config.git.enabled && !git.missing && workbenchState == workspace" + } + ] }, "dependencies": { "byline": "^5.0.0", diff --git a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts index ccdbb435b25..c7969c9d1c0 100644 --- a/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts +++ b/src/vs/workbench/contrib/codeActions/common/codeActions.contribution.ts @@ -10,9 +10,12 @@ 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'; import { CodeActionsExtensionPoint, codeActionsExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint'; +import { CodeActionDocumentationContribution } from 'vs/workbench/contrib/codeActions/common/documentationContribution'; +import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/documentationExtensionPoint'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; const codeActionsExtensionPoint = ExtensionsRegistry.registerExtensionPoint(codeActionsExtensionPointDescriptor); +const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint(documentationExtensionPointDescriptor); Registry.as(Extensions.Configuration) .registerConfiguration(editorConfiguration); @@ -22,6 +25,7 @@ class WorkbenchConfigurationContribution { @IInstantiationService instantiationService: IInstantiationService, ) { instantiationService.createInstance(CodeActionsContribution, codeActionsExtensionPoint); + instantiationService.createInstance(CodeActionDocumentationContribution, documentationExtensionPoint); } } diff --git a/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts b/src/vs/workbench/contrib/codeActions/common/documentationContribution.ts new file mode 100644 index 00000000000..9c2b75c277d --- /dev/null +++ b/src/vs/workbench/contrib/codeActions/common/documentationContribution.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 { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { ITextModel } from 'vs/editor/common/model'; +import * as modes from 'vs/editor/common/modes'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { DocumentationExtensionPoint } from './documentationExtensionPoint'; + + +export class CodeActionDocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { + + private contributions: { + title: string; + when: ContextKeyExpr; + command: string; + }[] = []; + + private readonly emptyCodeActionsList = { + actions: [], + dispose: () => { } + }; + + constructor( + extensionPoint: IExtensionPoint, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(); + + this._register(modes.CodeActionProviderRegistry.register('*', this)); + + extensionPoint.setHandler(points => { + this.contributions = []; + for (const documentation of points) { + if (!documentation.value.refactoring) { + continue; + } + + for (const contribution of documentation.value.refactoring) { + const precondition = ContextKeyExpr.deserialize(contribution.when); + if (!precondition) { + continue; + } + + this.contributions.push({ + title: contribution.title, + when: precondition, + command: contribution.command + }); + + } + } + }); + } + + async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: modes.CodeActionContext, _token: CancellationToken): Promise { + return this.emptyCodeActionsList; + } + + public _getAdditionalMenuItems(context: modes.CodeActionContext, actions: readonly modes.CodeAction[]): modes.Command[] { + if (context.only !== CodeActionKind.Refactor.value) { + if (!actions.some(action => action.kind && CodeActionKind.Refactor.contains(new CodeActionKind(action.kind)))) { + return []; + } + } + + return this.contributions + .filter(contribution => this.contextKeyService.contextMatchesRules(contribution.when)) + .map(contribution => { + return { + id: contribution.command, + title: contribution.title + }; + }); + } +} diff --git a/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts b/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint.ts similarity index 65% rename from src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts rename to src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint.ts index d2099c4a8bc..bb848f8d64d 100644 --- a/src/vs/workbench/contrib/documentation/common/documentationExtensionPoint.ts +++ b/src/vs/workbench/contrib/codeActions/common/documentationExtensionPoint.ts @@ -11,8 +11,6 @@ export enum DocumentationExtensionPointFields { when = 'when', title = 'title', command = 'command', - view = 'view', - contents = 'contents', } export interface RefactoringDocumentationExtensionPoint { @@ -21,15 +19,8 @@ export interface RefactoringDocumentationExtensionPoint { readonly [DocumentationExtensionPointFields.command]: string; } -export interface ViewDocumentationExtensionPoint { - readonly [DocumentationExtensionPointFields.view]: string; - readonly [DocumentationExtensionPointFields.contents]: string; - readonly [DocumentationExtensionPointFields.when]: string; -} - export interface DocumentationExtensionPoint { readonly refactoring?: readonly RefactoringDocumentationExtensionPoint[]; - readonly view?: readonly ViewDocumentationExtensionPoint[]; } const documentationExtensionPointSchema = Object.freeze({ @@ -62,32 +53,6 @@ const documentationExtensionPointSchema = Object.freeze(ViewContainerExtensions.ViewsRegistry); - -export class DocumentationContribution extends Disposable implements IWorkbenchContribution, modes.CodeActionProvider { - - private codeActionContributions: ICodeActionContribution[] = []; - private readonly emptyCodeActionsList = { actions: [], dispose: () => { } }; - - private emptyViewContents = new Map(); - - constructor( - extensionPoint: IExtensionPoint, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - super(); - - this._register(modes.CodeActionProviderRegistry.register('*', this)); - - extensionPoint.setHandler((points, { added, removed }) => { - for (const documentation of points) { - if (documentation.value.refactoring) { - for (const contribution of documentation.value.refactoring) { - const precondition = ContextKeyExpr.deserialize(contribution.when); - if (!precondition) { - continue; - } - - this.codeActionContributions.push({ - title: contribution.title, - when: precondition, - command: contribution.command - }); - } - } - } - - for (const documentation of removed) { - if (documentation.value.view) { - for (const contribution of documentation.value.view) { - const disposable = this.emptyViewContents.get(contribution); - - if (disposable) { - disposable.dispose(); - } - } - } - } - - for (const documentation of added) { - if (documentation.value.view) { - for (const contribution of documentation.value.view) { - const disposable = viewsRegistry.registerEmptyViewContent(contribution.view, { - content: contribution.contents, - when: ContextKeyExpr.deserialize(contribution.when) - }); - - this.emptyViewContents.set(contribution, disposable); - } - } - } - }); - } - - async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: modes.CodeActionContext, _token: CancellationToken): Promise { - return this.emptyCodeActionsList; - } - - public _getAdditionalMenuItems(context: modes.CodeActionContext, actions: readonly modes.CodeAction[]): modes.Command[] { - if (context.only !== CodeActionKind.Refactor.value) { - if (!actions.some(action => action.kind && CodeActionKind.Refactor.contains(new CodeActionKind(action.kind)))) { - return []; - } - } - - return this.codeActionContributions - .filter(contribution => this.contextKeyService.contextMatchesRules(contribution.when)) - .map(contribution => { - return { - id: contribution.command, - title: contribution.title - }; - }); - } -} diff --git a/src/vs/workbench/contrib/documentation/common/documentation.contribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts similarity index 68% rename from src/vs/workbench/contrib/documentation/common/documentation.contribution.ts rename to src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts index 13f09eee187..9153e3c1a4d 100644 --- a/src/vs/workbench/contrib/documentation/common/documentation.contribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcome.contribution.ts @@ -7,17 +7,17 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { DocumentationContribution } from 'vs/workbench/contrib/documentation/common/documentationContribution'; -import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/documentation/common/documentationExtensionPoint'; +import { ViewsWelcomeContribution } from 'vs/workbench/contrib/welcome/common/viewsWelcomeContribution'; +import { ViewsWelcomeExtensionPoint, viewsWelcomeExtensionPointDescriptor } from 'vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint(documentationExtensionPointDescriptor); +const extensionPoint = ExtensionsRegistry.registerExtensionPoint(viewsWelcomeExtensionPointDescriptor); class WorkbenchConfigurationContribution { constructor( @IInstantiationService instantiationService: IInstantiationService, ) { - instantiationService.createInstance(DocumentationContribution, documentationExtensionPoint); + instantiationService.createInstance(ViewsWelcomeContribution, extensionPoint); } } diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts new file mode 100644 index 00000000000..d943b90176c --- /dev/null +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ViewsWelcomeExtensionPoint, ViewWelcome } from './viewsWelcomeExtensionPoint'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; + +const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); + +export class ViewsWelcomeContribution extends Disposable implements IWorkbenchContribution { + + private viewWelcomeContents = new Map(); + + constructor(extensionPoint: IExtensionPoint) { + super(); + + extensionPoint.setHandler((_, { added, removed }) => { + for (const contribution of removed) { + for (const welcome of contribution.value) { + const disposable = this.viewWelcomeContents.get(welcome); + + if (disposable) { + disposable.dispose(); + } + } + } + + for (const contribution of added) { + for (const welcome of contribution.value) { + const disposable = viewsRegistry.registerEmptyViewContent(welcome.view, { + content: welcome.contents, + when: ContextKeyExpr.deserialize(welcome.when) + }); + + this.viewWelcomeContents.set(welcome, disposable); + } + } + }); + } +} diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts new file mode 100644 index 00000000000..28a5bc02dfa --- /dev/null +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; + +export enum ViewsWelcomeExtensionPointFields { + view = 'view', + contents = 'contents', + when = 'when', +} + +export interface ViewWelcome { + readonly [ViewsWelcomeExtensionPointFields.view]: string; + readonly [ViewsWelcomeExtensionPointFields.contents]: string; + readonly [ViewsWelcomeExtensionPointFields.when]: string; +} + +export type ViewsWelcomeExtensionPoint = ViewWelcome[]; + +const viewsWelcomeExtensionPointSchema = Object.freeze({ + type: 'array', + description: nls.localize('contributes.viewsWelcome', "Contributed views welcome content."), + items: { + type: 'object', + description: nls.localize('contributes.viewsWelcome.view', "Contributed welcome content for a specific view."), + required: [ + ViewsWelcomeExtensionPointFields.view, + ViewsWelcomeExtensionPointFields.contents + ], + properties: { + [ViewsWelcomeExtensionPointFields.view]: { + type: 'string', + description: nls.localize('contributes.viewsWelcome.view.view', "View identifier for this welcome content."), + }, + [ViewsWelcomeExtensionPointFields.contents]: { + type: 'string', + description: nls.localize('contributes.viewsWelcome.view.contents', "Welcome content."), + }, + [ViewsWelcomeExtensionPointFields.when]: { + type: 'string', + description: nls.localize('contributes.viewsWelcome.view.when', "When clause for this welcome content."), + }, + } + } +}); + +export const viewsWelcomeExtensionPointDescriptor = { + extensionPoint: 'viewsWelcome', + jsonSchema: viewsWelcomeExtensionPointSchema +}; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index ee4edc24c59..1e943601b66 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -272,8 +272,8 @@ import 'vs/workbench/contrib/userDataSync/browser/userDataSync.contribution'; // Code Actions import 'vs/workbench/contrib/codeActions/common/codeActions.contribution'; -// Documentation -import 'vs/workbench/contrib/documentation/common/documentation.contribution'; +// Welcome +import 'vs/workbench/contrib/welcome/common/viewsWelcome.contribution'; // Timeline import 'vs/workbench/contrib/timeline/browser/timeline.contribution'; From 61d7743219788f6375b1e8e9df1e6fb1f5973965 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 11:29:36 +0100 Subject: [PATCH 149/192] notifications - show progress in status bar when notification is closed (part of #90274) --- src/vs/platform/progress/common/progress.ts | 36 ++-- .../parts/notifications/notificationsList.ts | 2 +- .../progress/browser/progressService.ts | 157 ++++++++++++------ 3 files changed, 124 insertions(+), 71 deletions(-) diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 2b7e3b8b385..576ae6d82fb 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -17,7 +17,11 @@ export interface IProgressService { _serviceBrand: undefined; - withProgress(options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions, task: (progress: IProgress) => Promise, onDidCancel?: (choice?: number) => void): Promise; + withProgress( + options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions, + task: (progress: IProgress) => Promise, + onDidCancel?: (choice?: number) => void + ): Promise; } export interface IProgressIndicator { @@ -45,19 +49,19 @@ export const enum ProgressLocation { } export interface IProgressOptions { - location: ProgressLocation | string; - title?: string; - source?: string; - total?: number; - cancellable?: boolean; - buttons?: string[]; + readonly location: ProgressLocation | string; + readonly title?: string; + readonly source?: string; + readonly total?: number; + readonly cancellable?: boolean; + readonly buttons?: string[]; } export interface IProgressNotificationOptions extends IProgressOptions { readonly location: ProgressLocation.Notification; readonly primaryActions?: ReadonlyArray; readonly secondaryActions?: ReadonlyArray; - delay?: number; + readonly delay?: number; } export interface IProgressWindowOptions extends IProgressOptions { @@ -66,8 +70,8 @@ export interface IProgressWindowOptions extends IProgressOptions { } export interface IProgressCompositeOptions extends IProgressOptions { - location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string; - delay?: number; + readonly location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string; + readonly delay?: number; } export interface IProgressStep { @@ -96,20 +100,14 @@ export interface IProgress { export class Progress implements IProgress { - private _callback: (data: T) => void; private _value?: T; + get value(): T | undefined { return this._value; } - constructor(callback: (data: T) => void) { - this._callback = callback; - } - - get value(): T | undefined { - return this._value; - } + constructor(private callback: (data: T) => void) { } report(item: T) { this._value = item; - this._callback(this._value); + this.callback(this._value); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index ae4d2e69aff..2d585698cb9 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -181,7 +181,7 @@ export class NotificationsList extends Themable { } // Restore DOM focus if we had focus before - if (listHasDOMFocus) { + if (this.isVisible && listHasDOMFocus) { list.domFocus(); } } diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 4a5831b5667..4ffbfbbd4c8 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -12,9 +12,9 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusbarAlignment, IStatusbarService } from 'vs/workbench/services/statusbar/common/statusbar'; import { timeout } from 'vs/base/common/async'; import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; -import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity, INotificationHandle } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; @@ -143,10 +143,72 @@ export class ProgressService extends Disposable implements IProgressService { } } - private withNotificationProgress

, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: (choice?: number) => void): P { - const toDispose = new DisposableStore(); + private withNotificationProgress

, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress) => P, onDidCancel?: (choice?: number) => void): P { + + const progressStateModel = new class extends Disposable { + + private readonly _onDidReport = this._register(new Emitter()); + readonly onDidReport = this._onDidReport.event; + + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; + + private _step: IProgressStep | undefined = undefined; + get step() { return this._step; } + + private _done = false; + get done() { return this._done; } + + readonly promise: P; + + constructor() { + super(); + + this.promise = callback(this); + + this.promise.finally(() => { + this.dispose(); + }); + } + + report(step: IProgressStep): void { + this._step = step; + + this._onDidReport.fire(step); + } + + cancel(choice?: number): void { + onDidCancel?.(choice); + + this.dispose(); + } + + dispose(): void { + this._done = true; + this._onDispose.fire(); + + super.dispose(); + } + }; + + const createWindowProgress = () => { + this.withWindowProgress({ + location: ProgressLocation.Window, + title: options.title + }, progress => { + if (progressStateModel.step) { + progress.report(progressStateModel.step); + } + + const disposable = progressStateModel.onDidReport(step => progress.report(step)); + Event.once(progressStateModel.onDispose)(() => disposable.dispose()); + + return progressStateModel.promise; + }); + }; const createNotification = (message: string, increment?: number): INotificationHandle => { + const notificationDisposables = new DisposableStore(); const primaryActions = options.primaryActions ? Array.from(options.primaryActions) : []; const secondaryActions = options.secondaryActions ? Array.from(options.secondaryActions) : []; @@ -158,16 +220,11 @@ export class ProgressService extends Disposable implements IProgressService { super(`progress.button.${button}`, button, undefined, true); } - run(): Promise { - if (typeof onDidCancel === 'function') { - onDidCancel(index); - } - - return Promise.resolve(undefined); + async run(): Promise { + progressStateModel.cancel(index); } }; - - toDispose.add(buttonAction); + notificationDisposables.add(buttonAction); primaryActions.push(buttonAction); }); @@ -179,31 +236,35 @@ export class ProgressService extends Disposable implements IProgressService { super('progress.cancel', localize('cancel', "Cancel"), undefined, true); } - run(): Promise { - if (typeof onDidCancel === 'function') { - onDidCancel(); - } - - return Promise.resolve(undefined); + async run(): Promise { + progressStateModel.cancel(); } }; - toDispose.add(cancelAction); + notificationDisposables.add(cancelAction); primaryActions.push(cancelAction); } - const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; const handle = this.notificationService.notify({ severity: Severity.Info, message, source: options.source, - actions + actions: { primary: primaryActions, secondary: secondaryActions } }); updateProgress(handle, increment); Event.once(handle.onDidClose)(() => { - toDispose.dispose(); + + // Switch to window based progress once the notification + // is being closed even though still running and not + // cancelled. + if (!progressStateModel.done) { + createWindowProgress(); + } + + // Clear disposables + notificationDisposables.dispose(); }); return handle; @@ -218,60 +279,54 @@ export class ProgressService extends Disposable implements IProgressService { } }; - let handle: INotificationHandle | undefined; - let handleSoon: any | undefined; - + let notificationHandle: INotificationHandle | undefined; + let notificationTimeout: any | undefined; let titleAndMessage: string | undefined; // hoisted to make sure a delayed notification shows the most recent message - const updateNotification = (message?: string, increment?: number): void => { + const updateNotification = (step?: IProgressStep): void => { // full message (inital or update) - if (message && options.title) { - titleAndMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) + if (step?.message && options.title) { + titleAndMessage = `${options.title}: ${step.message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) } else { - titleAndMessage = options.title || message; + titleAndMessage = options.title || step?.message; } - if (!handle && titleAndMessage) { + if (!notificationHandle && titleAndMessage) { + // create notification now or after a delay if (typeof options.delay === 'number' && options.delay > 0) { - if (typeof handleSoon !== 'number') { - handleSoon = setTimeout(() => handle = createNotification(titleAndMessage!, increment), options.delay); + if (typeof notificationTimeout !== 'number') { + notificationTimeout = setTimeout(() => notificationHandle = createNotification(titleAndMessage!, step?.increment), options.delay); } } else { - handle = createNotification(titleAndMessage, increment); + notificationHandle = createNotification(titleAndMessage, step?.increment); } } - if (handle) { + if (notificationHandle) { if (titleAndMessage) { - handle.updateMessage(titleAndMessage); + notificationHandle.updateMessage(titleAndMessage); } - if (typeof increment === 'number') { - updateProgress(handle, increment); + + if (typeof step?.increment === 'number') { + updateProgress(notificationHandle, step.increment); } } }; // Show initially - updateNotification(); - - // Update based on progress - const promise = callback({ - report: progress => { - updateNotification(progress.message, progress.increment); - } - }); + updateNotification(progressStateModel.step); + const listener = progressStateModel.onDidReport(step => updateNotification(step)); + Event.once(progressStateModel.onDispose)(() => listener.dispose()); // Show progress for at least 800ms and then hide once done or canceled - Promise.all([timeout(800), promise]).finally(() => { - clearTimeout(handleSoon); - if (handle) { - handle.close(); - } + Promise.all([timeout(800), progressStateModel.promise]).finally(() => { + clearTimeout(notificationTimeout); + notificationHandle?.close(); }); - return promise; + return progressStateModel.promise; } private withViewletProgress

, R = unknown>(viewletId: string, task: (progress: IProgress) => P, options: IProgressCompositeOptions): P { From 71cd13766854103d765594bb921c55e10c7587fc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 11:34:45 +0100 Subject: [PATCH 150/192] views: empty -> welcome --- .../browser/parts/views/media/views.css | 8 +-- .../browser/parts/views/viewPaneContainer.ts | 72 +++++++++---------- src/vs/workbench/common/views.ts | 28 ++++---- .../contrib/scm/browser/repositoryPane.ts | 2 +- .../contrib/scm/browser/scmViewlet.ts | 6 +- .../common/viewsWelcomeContribution.ts | 2 +- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 2e4de06c27b..bd12bd60f9b 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -71,7 +71,7 @@ display: none; } -.monaco-workbench .pane > .pane-body > .empty-view { +.monaco-workbench .pane > .pane-body > .welcome-view { width: 100%; height: 100%; padding: 0 20px 0 20px; @@ -79,12 +79,12 @@ box-sizing: border-box; } -.monaco-workbench .pane > .pane-body:not(.empty) > .empty-view, -.monaco-workbench .pane > .pane-body.empty > :not(.empty-view) { +.monaco-workbench .pane > .pane-body:not(.welcome) > .welcome-view, +.monaco-workbench .pane > .pane-body.welcome > :not(.welcome-view) { display: none; } -.monaco-workbench .pane > .pane-body > .empty-view .monaco-button { +.monaco-workbench .pane > .pane-body > .welcome-view .monaco-button { max-width: 260px; margin-left: auto; margin-right: auto; diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index d380b02c9e8..bd0b2276d37 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -65,18 +65,18 @@ interface IItem { visible: boolean; } -class EmptyViewsController { +class ViewWelcomeController { private _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - private placeholderItem: IItem | undefined; + private defaultItem: IItem | undefined; private items: IItem[] = []; - get viewContentDescriptors(): IViewContentDescriptor[] { + get contents(): IViewContentDescriptor[] { const visibleItems = this.items.filter(v => v.visible); - if (visibleItems.length === 0 && this.placeholderItem) { - return [this.placeholderItem.descriptor]; + if (visibleItems.length === 0 && this.defaultItem) { + return [this.defaultItem.descriptor]; } return visibleItems.map(v => v.descriptor); @@ -93,18 +93,18 @@ class EmptyViewsController { this.disposables.add(this.contextKeyService); contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); - Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id)(this.onDidChangeEmptyViewContent, this, this.disposables); - this.onDidChangeEmptyViewContent(); + Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables); + this.onDidChangeViewWelcomeContent(); } - private onDidChangeEmptyViewContent(): void { - const contentDescriptors = viewsRegistry.getEmptyViewContent(this.id); + private onDidChangeViewWelcomeContent(): void { + const descriptors = viewsRegistry.getViewWelcomeContent(this.id); this.items = []; - for (const descriptor of contentDescriptors) { - if (descriptor.when === 'placeholder') { - this.placeholderItem = { descriptor, visible: true }; + for (const descriptor of descriptors) { + if (descriptor.when === 'default') { + this.defaultItem = { descriptor, visible: true }; } else { const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true; this.items.push({ descriptor, visible }); @@ -118,7 +118,7 @@ class EmptyViewsController { let didChange = false; for (const item of this.items) { - if (!item.descriptor.when || item.descriptor.when === 'placeholder') { + if (!item.descriptor.when || item.descriptor.when === 'default') { continue; } @@ -158,8 +158,8 @@ export abstract class ViewPane extends Pane implements IView { protected _onDidChangeTitleArea = this._register(new Emitter()); readonly onDidChangeTitleArea: Event = this._onDidChangeTitleArea.event; - protected _onDidChangeEmptyState = this._register(new Emitter()); - readonly onDidChangeEmptyState: Event = this._onDidChangeEmptyState.event; + protected _onDidChangeViewWelcomeState = this._register(new Emitter()); + readonly onDidChangeViewWelcomeState: Event = this._onDidChangeViewWelcomeState.event; private focusedViewContextKey: IContextKey; @@ -177,9 +177,9 @@ export abstract class ViewPane extends Pane implements IView { protected twistiesContainer?: HTMLElement; private bodyContainer!: HTMLElement; - private emptyViewContainer!: HTMLElement; - private emptyViewDisposable: IDisposable = Disposable.None; - private emptyViewsController: EmptyViewsController; + private viewWelcomeContainer!: HTMLElement; + private viewWelcomeDisposable: IDisposable = Disposable.None; + private viewWelcomeController: ViewWelcomeController; constructor( options: IViewPaneOptions, @@ -203,7 +203,7 @@ export abstract class ViewPane extends Pane implements IView { this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext)); this._register(this.menuActions.onDidChangeTitle(() => this.updateActions())); - this.emptyViewsController = new EmptyViewsController(this.id, contextKeyService); + this.viewWelcomeController = new ViewWelcomeController(this.id, contextKeyService); } setVisible(visible: boolean): void { @@ -291,11 +291,11 @@ export abstract class ViewPane extends Pane implements IView { protected renderBody(container: HTMLElement): void { this.bodyContainer = container; - this.emptyViewContainer = append(container, $('.empty-view', { tabIndex: 0 })); + this.viewWelcomeContainer = append(container, $('.welcome-view', { tabIndex: 0 })); - const onEmptyViewContentChange = Event.any(this.emptyViewsController.onDidChange, this.onDidChangeEmptyState); - this._register(onEmptyViewContentChange(this.updateEmptyContents, this)); - this.updateEmptyContents(); + const onViewWelcomeChange = Event.any(this.viewWelcomeController.onDidChange, this.onDidChangeViewWelcomeState); + this._register(onViewWelcomeChange(this.updateViewWelcome, this)); + this.updateViewWelcome(); } protected layoutBody(height: number, width: number): void { @@ -368,26 +368,26 @@ export abstract class ViewPane extends Pane implements IView { // Subclasses to implement for saving state } - private updateEmptyContents(): void { - this.emptyViewDisposable.dispose(); + private updateViewWelcome(): void { + this.viewWelcomeDisposable.dispose(); - if (!this.isEmpty()) { - removeClass(this.bodyContainer, 'empty'); - this.emptyViewContainer.innerHTML = ''; + if (!this.shouldShowWelcome()) { + removeClass(this.bodyContainer, 'welcome'); + this.viewWelcomeContainer.innerHTML = ''; return; } - const contents = this.emptyViewsController.viewContentDescriptors; + const contents = this.viewWelcomeController.contents; if (contents.length === 0) { - removeClass(this.bodyContainer, 'empty'); - this.emptyViewContainer.innerHTML = ''; + removeClass(this.bodyContainer, 'welcome'); + this.viewWelcomeContainer.innerHTML = ''; return; } const disposables = new DisposableStore(); - addClass(this.bodyContainer, 'empty'); - this.emptyViewContainer.innerHTML = ''; + addClass(this.bodyContainer, 'welcome'); + this.viewWelcomeContainer.innerHTML = ''; for (const { content } of contents) { const lines = content.split('\n'); @@ -399,7 +399,7 @@ export abstract class ViewPane extends Pane implements IView { continue; } - const p = append(this.emptyViewContainer, $('p')); + const p = append(this.viewWelcomeContainer, $('p')); const linkedText = parseLinkedText(line); for (const node of linkedText) { @@ -421,10 +421,10 @@ export abstract class ViewPane extends Pane implements IView { } } - this.emptyViewDisposable = disposables; + this.viewWelcomeDisposable = disposables; } - isEmpty(): boolean { + shouldShowWelcome(): boolean { return false; } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index d1c4eba4294..cb64eeb0933 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -213,7 +213,7 @@ export interface IViewDescriptorCollection extends IDisposable { export interface IViewContentDescriptor { readonly content: string; - readonly when?: ContextKeyExpr | 'placeholder'; + readonly when?: ContextKeyExpr | 'default'; } export interface IViewsRegistry { @@ -236,9 +236,9 @@ export interface IViewsRegistry { getViewContainer(id: string): ViewContainer | null; - readonly onDidChangeEmptyViewContent: Event; - registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable; - getEmptyViewContent(id: string): IViewContentDescriptor[]; + readonly onDidChangeViewWelcomeContent: Event; + registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable; + getViewWelcomeContent(id: string): IViewContentDescriptor[]; } function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number { @@ -256,12 +256,12 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>()); readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event; - private readonly _onDidChangeEmptyViewContent: Emitter = this._register(new Emitter()); - readonly onDidChangeEmptyViewContent: Event = this._onDidChangeEmptyViewContent.event; + private readonly _onDidChangeViewWelcomeContent: Emitter = this._register(new Emitter()); + readonly onDidChangeViewWelcomeContent: Event = this._onDidChangeViewWelcomeContent.event; private _viewContainers: ViewContainer[] = []; private _views: Map = new Map(); - private _emptyViewContents = new SetMap(); + private _viewWelcomeContents = new SetMap(); registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { this.addViews(views, viewContainer); @@ -311,20 +311,20 @@ class ViewsRegistry extends Disposable implements IViewsRegistry { return null; } - registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable { - this._emptyViewContents.add(id, viewContent); - this._onDidChangeEmptyViewContent.fire(id); + registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable { + this._viewWelcomeContents.add(id, viewContent); + this._onDidChangeViewWelcomeContent.fire(id); return toDisposable(() => { - this._emptyViewContents.delete(id, viewContent); - this._onDidChangeEmptyViewContent.fire(id); + this._viewWelcomeContents.delete(id, viewContent); + this._onDidChangeViewWelcomeContent.fire(id); }); } - getEmptyViewContent(id: string): IViewContentDescriptor[] { + getViewWelcomeContent(id: string): IViewContentDescriptor[] { const result: IViewContentDescriptor[] = []; result.sort(compareViewContentDescriptors); - this._emptyViewContents.forEach(id, descriptor => result.push(descriptor)); + this._viewWelcomeContents.forEach(id, descriptor => result.push(descriptor)); return result; } diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 3f65b434ea8..ab9c041d125 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -615,7 +615,7 @@ export class RepositoryPane extends ViewPane { protected contextKeyService: IContextKeyService; private commitTemplate = ''; - isEmpty() { return true; } + shouldShowWelcome() { return true; } constructor( readonly repository: ISCMRepository, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 26a7fd69aab..035f4617e54 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -60,7 +60,7 @@ export class EmptyPane extends ViewPane { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); } - isEmpty(): boolean { + shouldShowWelcome(): boolean { return true; } } @@ -131,9 +131,9 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - viewsRegistry.registerEmptyViewContent(EmptyPane.ID, { + viewsRegistry.registerViewWelcomeContent(EmptyPane.ID, { content: localize('no open repo', "No source control providers registered."), - when: 'placeholder' + when: 'default' }); viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer); diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index d943b90176c..c1bde25446a 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -33,7 +33,7 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo for (const contribution of added) { for (const welcome of contribution.value) { - const disposable = viewsRegistry.registerEmptyViewContent(welcome.view, { + const disposable = viewsRegistry.registerViewWelcomeContent(welcome.view, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when) }); From ae74703f28124a5f66064bdf1a2d10ef2d1b064c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 11:41:42 +0100 Subject: [PATCH 151/192] notifications - use disposable for handle --- src/vs/workbench/common/notifications.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 01d7271a3b9..433adb6ed20 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -88,19 +88,22 @@ export interface IStatusMessageChangeEvent { kind: StatusMessageChangeType; } -export class NotificationHandle implements INotificationHandle { +export class NotificationHandle extends Disposable implements INotificationHandle { - private readonly _onDidClose: Emitter = new Emitter(); - readonly onDidClose: Event = this._onDidClose.event; + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; + + constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) { + super(); - constructor(private readonly item: INotificationViewItem, private readonly closeItem: (item: INotificationViewItem) => void) { this.registerListeners(); } private registerListeners(): void { Event.once(this.item.onDidClose)(() => { this._onDidClose.fire(); - this._onDidClose.dispose(); + + this.dispose(); }); } @@ -121,8 +124,9 @@ export class NotificationHandle implements INotificationHandle { } close(): void { - this.closeItem(this.item); - this._onDidClose.dispose(); + this.onClose(this.item); + + this.dispose(); } } From c6e0aaa322323eb38f38f3d662b887594f606ee9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 11:56:11 +0100 Subject: [PATCH 152/192] views welcome: check for proposed api flag --- .../welcome/common/viewsWelcomeContribution.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index c1bde25446a..51fb3194e95 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -7,9 +7,10 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ViewsWelcomeExtensionPoint, ViewWelcome } from './viewsWelcomeExtensionPoint'; +import { ViewsWelcomeExtensionPoint, ViewWelcome, viewsWelcomeExtensionPointDescriptor } from './viewsWelcomeExtensionPoint'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; +import { localize } from 'vs/nls'; const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -22,6 +23,11 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo extensionPoint.setHandler((_, { added, removed }) => { for (const contribution of removed) { + // Proposed API check + if (!contribution.description.enableProposedApi) { + continue; + } + for (const welcome of contribution.value) { const disposable = this.viewWelcomeContents.get(welcome); @@ -32,6 +38,12 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo } for (const contribution of added) { + // Proposed API check + if (!contribution.description.enableProposedApi) { + contribution.collector.error(localize('proposedAPI.invalid', "The '{0}' contribution is a proposed API and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", viewsWelcomeExtensionPointDescriptor.extensionPoint, contribution.description.identifier.value)); + continue; + } + for (const welcome of contribution.value) { const disposable = viewsRegistry.registerViewWelcomeContent(welcome.view, { content: welcome.contents, From 12215af89154f3b3eaeb92e7dc77fa7d024b47de Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 12:09:40 +0100 Subject: [PATCH 153/192] notifications - change event names to follow conventions --- .../notifications/notificationsAlerts.ts | 8 +-- .../notifications/notificationsCenter.ts | 4 +- .../notifications/notificationsStatus.ts | 8 +-- .../notifications/notificationsToasts.ts | 10 ++-- .../notifications/notificationsViewer.ts | 2 +- src/vs/workbench/common/notifications.ts | 58 +++++++++---------- .../test/common/notifications.test.ts | 14 ++--- 7 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsAlerts.ts b/src/vs/workbench/browser/parts/notifications/notificationsAlerts.ts index 94a5a660b43..fae7f87f7cb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsAlerts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsAlerts.ts @@ -23,10 +23,10 @@ export class NotificationsAlerts extends Disposable { } private registerListeners(): void { - this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e))); + this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); } - private onDidNotificationChange(e: INotificationChangeEvent): void { + private onDidChangeNotification(e: INotificationChangeEvent): void { if (e.kind === NotificationChangeType.ADD) { // ARIA alert for screen readers @@ -46,7 +46,7 @@ export class NotificationsAlerts extends Disposable { private triggerAriaAlert(notifiation: INotificationViewItem): void { // Trigger the alert again whenever the label changes - const listener = notifiation.onDidLabelChange(e => { + const listener = notifiation.onDidChangeLabel(e => { if (e.kind === NotificationViewItemLabelKind.MESSAGE) { this.doTriggerAriaAlert(notifiation); } @@ -69,4 +69,4 @@ export class NotificationsAlerts extends Disposable { alert(alertText); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 6d27e856dbb..0b4cbe77264 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -58,7 +58,7 @@ export class NotificationsCenter extends Themable { } private registerListeners(): void { - this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e))); + this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); this._register(this.layoutService.onLayout(dimension => this.layout(dimension))); } @@ -167,7 +167,7 @@ export class NotificationsCenter extends Themable { return keybinding ? keybinding.getLabel() : null; } - private onDidNotificationChange(e: INotificationChangeEvent): void { + private onDidChangeNotification(e: INotificationChangeEvent): void { if (!this._isVisible) { return; // only if visible } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 97c9af2a622..3545ab9c02b 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -34,11 +34,11 @@ export class NotificationsStatus extends Disposable { } private registerListeners(): void { - this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e))); - this._register(this.model.onDidStatusMessageChange(e => this.onDidStatusMessageChange(e))); + this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); + this._register(this.model.onDidChangeStatusMessage(e => this.onDidChangeStatusMessage(e))); } - private onDidNotificationChange(e: INotificationChangeEvent): void { + private onDidChangeNotification(e: INotificationChangeEvent): void { if (this.isNotificationsCenterVisible) { return; // no change if notification center is visible } @@ -101,7 +101,7 @@ export class NotificationsStatus extends Disposable { } } - private onDidStatusMessageChange(e: IStatusMessageChangeEvent): void { + private onDidChangeStatusMessage(e: IStatusMessageChangeEvent): void { const statusItem = e.item; switch (e.kind) { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index ee6bbffc283..9461b4887ac 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -90,11 +90,11 @@ export class NotificationsToasts extends Themable { this.model.notifications.forEach(notification => this.addToast(notification)); // Update toasts on notification changes - this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e))); + this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); }); // Filter - this._register(this.model.onDidFilterChange(filter => { + this._register(this.model.onDidChangeFilter(filter => { if (filter === NotificationsFilter.SILENT || filter === NotificationsFilter.ERROR) { this.hide(); } @@ -114,7 +114,7 @@ export class NotificationsToasts extends Themable { ]); } - private onDidNotificationChange(e: INotificationChangeEvent): void { + private onDidChangeNotification(e: INotificationChangeEvent): void { switch (e.kind) { case NotificationChangeType.ADD: return this.addToast(e.item); @@ -194,12 +194,12 @@ export class NotificationsToasts extends Themable { this.layoutContainer(maxDimensions.height); // Update when item height changes due to expansion - itemDisposables.add(item.onDidExpansionChange(() => { + itemDisposables.add(item.onDidChangeExpansion(() => { notificationList.updateNotificationsList(0, 1, [item]); })); // Update when item height potentially changes due to label changes - itemDisposables.add(item.onDidLabelChange(e => { + itemDisposables.add(item.onDidChangeLabel(e => { if (!item.expanded) { return; // dynamic height only applies to expanded notifications } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index a41db2f2ee1..318307a8477 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -345,7 +345,7 @@ export class NotificationTemplateRenderer extends Disposable { this.renderProgress(notification); // Label Change Events - this.inputDisposables.add(notification.onDidLabelChange(event => { + this.inputDisposables.add(notification.onDidChangeLabel(event => { switch (event.kind) { case NotificationViewItemLabelKind.SEVERITY: this.renderSeverity(notification); diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 433adb6ed20..ff932b00bd8 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -20,8 +20,8 @@ export interface INotificationsModel { readonly notifications: INotificationViewItem[]; - readonly onDidNotificationChange: Event; - readonly onDidFilterChange: Event; + readonly onDidChangeNotification: Event; + readonly onDidChangeFilter: Event; addNotification(notification: INotification): INotificationHandle; @@ -34,7 +34,7 @@ export interface INotificationsModel { readonly statusMessage: IStatusMessageViewItem | undefined; - readonly onDidStatusMessageChange: Event; + readonly onDidChangeStatusMessage: Event; showStatusMessage(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable; @@ -134,14 +134,14 @@ export class NotificationsModel extends Disposable implements INotificationsMode private static readonly NO_OP_NOTIFICATION = new NoOpNotification(); - private readonly _onDidNotificationChange = this._register(new Emitter()); - readonly onDidNotificationChange = this._onDidNotificationChange.event; + private readonly _onDidChangeNotification = this._register(new Emitter()); + readonly onDidChangeNotification = this._onDidChangeNotification.event; - private readonly _onDidStatusMessageChange = this._register(new Emitter()); - readonly onDidStatusMessageChange = this._onDidStatusMessageChange.event; + private readonly _onDidChangeStatusMessage = this._register(new Emitter()); + readonly onDidChangeStatusMessage = this._onDidChangeStatusMessage.event; - private readonly _onDidFilterChange = this._register(new Emitter()); - readonly onDidFilterChange = this._onDidFilterChange.event; + private readonly _onDidChangeFilter = this._register(new Emitter()); + readonly onDidChangeFilter = this._onDidChangeFilter.event; private readonly _notifications: INotificationViewItem[] = []; get notifications(): INotificationViewItem[] { return this._notifications; } @@ -154,7 +154,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode setFilter(filter: NotificationsFilter): void { this.filter = filter; - this._onDidFilterChange.fire(filter); + this._onDidChangeFilter.fire(filter); } addNotification(notification: INotification): INotificationHandle { @@ -173,7 +173,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode this._notifications.splice(0, 0, item); // Events - this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD }); + this._onDidChangeNotification.fire({ item, index: 0, kind: NotificationChangeType.ADD }); // Wrap into handle return new NotificationHandle(item, item => this.onClose(item)); @@ -202,13 +202,13 @@ export class NotificationsModel extends Disposable implements INotificationsMode const onItemChangeEvent = () => { const index = this._notifications.indexOf(item); if (index >= 0) { - this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.CHANGE }); + this._onDidChangeNotification.fire({ item, index, kind: NotificationChangeType.CHANGE }); } }; - const itemExpansionChangeListener = item.onDidExpansionChange(() => onItemChangeEvent()); + const itemExpansionChangeListener = item.onDidChangeExpansion(() => onItemChangeEvent()); - const itemLabelChangeListener = item.onDidLabelChange(e => { + const itemLabelChangeListener = item.onDidChangeLabel(e => { // a label change in the area of actions or the message is a change that potentially has an impact // on the size of the notification and as such we emit a change event so that viewers can redraw if (e.kind === NotificationViewItemLabelKind.ACTIONS || e.kind === NotificationViewItemLabelKind.MESSAGE) { @@ -223,7 +223,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode const index = this._notifications.indexOf(item); if (index >= 0) { this._notifications.splice(index, 1); - this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.REMOVE }); + this._onDidChangeNotification.fire({ item, index, kind: NotificationChangeType.REMOVE }); } }); @@ -238,14 +238,14 @@ export class NotificationsModel extends Disposable implements INotificationsMode // Remember as current status message and fire events this._statusMessage = item; - this._onDidStatusMessageChange.fire({ kind: StatusMessageChangeType.ADD, item }); + this._onDidChangeStatusMessage.fire({ kind: StatusMessageChangeType.ADD, item }); return toDisposable(() => { // Only reset status message if the item is still the one we had remembered if (this._statusMessage === item) { this._statusMessage = undefined; - this._onDidStatusMessageChange.fire({ kind: StatusMessageChangeType.REMOVE, item }); + this._onDidChangeStatusMessage.fire({ kind: StatusMessageChangeType.REMOVE, item }); } }); } @@ -263,9 +263,9 @@ export interface INotificationViewItem { readonly expanded: boolean; readonly canCollapse: boolean; - readonly onDidExpansionChange: Event; + readonly onDidChangeExpansion: Event; readonly onDidClose: Event; - readonly onDidLabelChange: Event; + readonly onDidChangeLabel: Event; expand(): void; collapse(skipEvents?: boolean): void; @@ -410,14 +410,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie private _actions: INotificationActions | undefined; private _progress: NotificationViewItemProgress | undefined; - private readonly _onDidExpansionChange = this._register(new Emitter()); - readonly onDidExpansionChange = this._onDidExpansionChange.event; + private readonly _onDidChangeExpansion = this._register(new Emitter()); + readonly onDidChangeExpansion = this._onDidChangeExpansion.event; private readonly _onDidClose = this._register(new Emitter()); readonly onDidClose = this._onDidClose.event; - private readonly _onDidLabelChange = this._register(new Emitter()); - readonly onDidLabelChange = this._onDidLabelChange.event; + private readonly _onDidChangeLabel = this._register(new Emitter()); + readonly onDidChangeLabel = this._onDidChangeLabel.event; static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { @@ -566,7 +566,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie get progress(): INotificationViewItemProgress { if (!this._progress) { this._progress = this._register(new NotificationViewItemProgress()); - this._register(this._progress.onDidChange(() => this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.PROGRESS }))); + this._register(this._progress.onDidChange(() => this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.PROGRESS }))); } return this._progress; @@ -586,7 +586,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie updateSeverity(severity: Severity): void { this._severity = severity; - this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.SEVERITY }); + this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.SEVERITY }); } updateMessage(input: NotificationMessage): void { @@ -596,13 +596,13 @@ export class NotificationViewItem extends Disposable implements INotificationVie } this._message = message; - this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.MESSAGE }); + this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.MESSAGE }); } updateActions(actions?: INotificationActions): void { this.setActions(actions); - this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.ACTIONS }); + this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS }); } expand(): void { @@ -611,7 +611,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie } this._expanded = true; - this._onDidExpansionChange.fire(); + this._onDidChangeExpansion.fire(); } collapse(skipEvents?: boolean): void { @@ -622,7 +622,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie this._expanded = false; if (!skipEvents) { - this._onDidExpansionChange.fire(); + this._onDidChangeExpansion.fire(); } } diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index b8a5e1b1ee1..b95b7d7b1eb 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -41,7 +41,7 @@ suite('Notifications', () => { // Events let called = 0; - item1.onDidExpansionChange(() => { + item1.onDidChangeExpansion(() => { called++; }); @@ -53,7 +53,7 @@ suite('Notifications', () => { assert.equal(called, 2); called = 0; - item1.onDidLabelChange(e => { + item1.onDidChangeLabel(e => { if (e.kind === NotificationViewItemLabelKind.PROGRESS) { called++; } @@ -65,7 +65,7 @@ suite('Notifications', () => { assert.equal(called, 2); called = 0; - item1.onDidLabelChange(e => { + item1.onDidChangeLabel(e => { if (e.kind === NotificationViewItemLabelKind.MESSAGE) { called++; } @@ -74,7 +74,7 @@ suite('Notifications', () => { item1.updateMessage('message update'); called = 0; - item1.onDidLabelChange(e => { + item1.onDidChangeLabel(e => { if (e.kind === NotificationViewItemLabelKind.SEVERITY) { called++; } @@ -83,7 +83,7 @@ suite('Notifications', () => { item1.updateSeverity(Severity.Error); called = 0; - item1.onDidLabelChange(e => { + item1.onDidChangeLabel(e => { if (e.kind === NotificationViewItemLabelKind.ACTIONS) { called++; } @@ -146,12 +146,12 @@ suite('Notifications', () => { const model = new NotificationsModel(); let lastNotificationEvent!: INotificationChangeEvent; - model.onDidNotificationChange(e => { + model.onDidChangeNotification(e => { lastNotificationEvent = e; }); let lastStatusMessageEvent!: IStatusMessageChangeEvent; - model.onDidStatusMessageChange(e => { + model.onDidChangeStatusMessage(e => { lastStatusMessageEvent = e; }); From 866dffcb1d78df57a417979b529400a9c0a53eaa Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 12:20:31 +0100 Subject: [PATCH 154/192] Add minimap.mode: "contain" --- .../browser/viewParts/minimap/minimap.ts | 16 +- src/vs/editor/common/config/editorOptions.ts | 62 ++++--- .../viewLayout/editorLayoutProvider.test.ts | 171 ++++++++++++++++-- src/vs/monaco.d.ts | 10 +- 4 files changed, 212 insertions(+), 47 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 8301788bdc9..adc23678bfe 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -46,7 +46,9 @@ class MinimapOptions { public readonly renderMinimap: RenderMinimap; - public readonly entireDocument: boolean; + public readonly mode: 'actual' | 'cover' | 'contain'; + + public readonly minimapHeightIsEditorHeight: boolean; public readonly scrollBeyondLastLine: boolean; @@ -104,7 +106,8 @@ class MinimapOptions { const minimapOpts = options.get(EditorOption.minimap); this.renderMinimap = layoutInfo.renderMinimap | 0; - this.entireDocument = minimapOpts.entireDocument; + this.mode = minimapOpts.mode; + this.minimapHeightIsEditorHeight = layoutInfo.minimapHeightIsEditorHeight; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); this.showSlider = minimapOpts.showSlider; this.pixelRatio = pixelRatio; @@ -137,7 +140,8 @@ class MinimapOptions { public equals(other: MinimapOptions): boolean { return (this.renderMinimap === other.renderMinimap - && this.entireDocument === other.entireDocument + && this.mode === other.mode + && this.minimapHeightIsEditorHeight === other.minimapHeightIsEditorHeight && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider && this.pixelRatio === other.pixelRatio @@ -237,7 +241,7 @@ class MinimapLayout { const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight); const lineHeight = options.lineHeight; - if (options.entireDocument) { + if (options.minimapHeightIsEditorHeight) { const logicalScrollHeight = ( realLineCount * options.lineHeight + (options.scrollBeyondLastLine ? viewportHeight - options.lineHeight : 0) @@ -291,7 +295,7 @@ class MinimapLayout { let extraLinesAtTheBottom = 0; if (options.scrollBeyondLastLine) { const expectedViewportLineCount = viewportHeight / lineHeight; - extraLinesAtTheBottom = expectedViewportLineCount; + extraLinesAtTheBottom = expectedViewportLineCount - 1; } if (minimapLinesFitting >= lineCount + extraLinesAtTheBottom) { // All lines fit in the minimap @@ -531,7 +535,7 @@ class MinimapSamplingState { const pixelRatio = options.get(EditorOption.pixelRatio); const lineHeight = options.get(EditorOption.lineHeight); const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); - const { minimapLineCount } = EditorLayoutInfoComputer.computeEntireDocumentMinimapLineCount({ + const { minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({ modelLineCount: modelLineCount, scrollBeyondLastLine: scrollBeyondLastLine, height: layoutInfo.height, diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 2ec1caef507..07ccf3d6c98 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1686,6 +1686,7 @@ export interface EditorLayoutInfo { * The width of the minimap */ readonly minimapWidth: number; + readonly minimapHeightIsEditorHeight: boolean; readonly minimapIsSampling: boolean; readonly minimapScale: number; readonly minimapLineHeight: number; @@ -1758,7 +1759,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption= 2 ? Math.round(minimap.scale * 2) : minimap.scale); const minimapMaxColumn = minimap.maxColumn | 0; - const minimapEntireDocument = minimap.entireDocument; + const minimapMode = minimap.mode; const scrollbar = options.get(EditorOption.scrollbar); const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0; @@ -1838,6 +1839,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption 1) { + minimapHeightIsEditorHeight = true; minimapIsSampling = true; minimapScale = 1; minimapLineHeight = 1; minimapCharWidth = minimapScale / pixelRatio; } else { - const configuredFontScale = minimapScale; - minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); - minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); - if (minimapScale > configuredFontScale) { - minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale); + const effectiveMinimapHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight); + if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) { + minimapHeightIsEditorHeight = true; + const configuredFontScale = minimapScale; + minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); + minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); + if (minimapScale > configuredFontScale) { + minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale); + } + minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; + minimapCanvasInnerHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight); } - minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; - minimapCanvasInnerHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight); } } @@ -1944,6 +1951,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption>; @@ -2084,12 +2092,12 @@ class EditorMinimap extends BaseEditorOption(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']), side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']), showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']), renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters), scale: EditorIntOption.clampedInt(input.scale, 1, 1, 3), maxColumn: EditorIntOption.clampedInt(input.maxColumn, this.defaultValue.maxColumn, 1, 10000), - entireDocument: EditorBooleanOption.boolean(input.entireDocument, this.defaultValue.entireDocument), }; } } diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index a69efcd6a92..f3c94bca5e3 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -33,7 +33,7 @@ interface IEditorLayoutProviderOpts { readonly minimapSide: 'left' | 'right'; readonly minimapRenderCharacters: boolean; readonly minimapMaxColumn: number; - minimapEntireDocument?: boolean; + minimapMode?: 'actual' | 'cover' | 'contain'; readonly pixelRatio: number; } @@ -47,12 +47,12 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { options._write(EditorOption.folding, false); const minimapOptions: EditorMinimapOptions = { enabled: input.minimap, + mode: input.minimapMode || 'actual', side: input.minimapSide, renderCharacters: input.minimapRenderCharacters, maxColumn: input.minimapMaxColumn, showSlider: 'mouseover', scale: 1, - entireDocument: input.minimapEntireDocument || false }; options._write(EditorOption.minimap, minimapOptions); const scrollbarOptions: InternalEditorScrollbarOptions = { @@ -129,6 +129,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -190,6 +191,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -251,6 +253,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -312,6 +315,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -373,6 +377,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -434,6 +439,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -495,6 +501,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -556,6 +563,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -617,6 +625,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -678,6 +687,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, @@ -739,6 +749,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 2, @@ -800,6 +811,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 903, minimapWidth: 97, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 2, minimapLineHeight: 4, @@ -861,6 +873,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 945, minimapWidth: 55, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 2, minimapLineHeight: 4, @@ -922,6 +935,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 0, minimapWidth: 55, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 2, minimapLineHeight: 4, @@ -943,7 +957,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { }); }); - test('EditorLayoutProvider 11 - render entire document in minimap without sampling', () => { + test('EditorLayoutProvider 11 - minimap mode cover without sampling', () => { doTest({ outerWidth: 1000, outerHeight: 800, @@ -964,7 +978,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { minimapSide: 'right', minimapRenderCharacters: true, minimapMaxColumn: 150, - minimapEntireDocument: true, + minimapMode: 'cover', pixelRatio: 2, }, { width: 1000, @@ -980,19 +994,20 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { decorationsWidth: 10, contentLeft: 10, - contentWidth: 818, + contentWidth: 893, renderMinimap: RenderMinimap.Text, - minimapLeft: 828, - minimapWidth: 172, + minimapLeft: 903, + minimapWidth: 97, + minimapHeightIsEditorHeight: true, minimapIsSampling: false, - minimapScale: 4, + minimapScale: 3, minimapLineHeight: 13, - minimapCanvasInnerWidth: 344, + minimapCanvasInnerWidth: 291, minimapCanvasInnerHeight: 1560, - minimapCanvasOuterWidth: 172, + minimapCanvasOuterWidth: 97, minimapCanvasOuterHeight: 800, - viewportColumn: 81, + viewportColumn: 89, verticalScrollbarWidth: 0, horizontalScrollbarHeight: 0, @@ -1006,7 +1021,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { }); }); - test('EditorLayoutProvider 12 - render entire document in minimap with sampling', () => { + test('EditorLayoutProvider 12 - minimap mode cover with sampling', () => { doTest({ outerWidth: 1000, outerHeight: 800, @@ -1027,7 +1042,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { minimapSide: 'right', minimapRenderCharacters: true, minimapMaxColumn: 150, - minimapEntireDocument: true, + minimapMode: 'cover', pixelRatio: 2, }, { width: 1000, @@ -1048,6 +1063,135 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 945, minimapWidth: 55, + minimapHeightIsEditorHeight: true, + minimapIsSampling: true, + minimapScale: 1, + minimapLineHeight: 1, + minimapCanvasInnerWidth: 110, + minimapCanvasInnerHeight: 1600, + minimapCanvasOuterWidth: 55, + minimapCanvasOuterHeight: 800, + viewportColumn: 93, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 13 - minimap mode contain without sampling', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 3, + maxLineNumber: 120, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapSide: 'right', + minimapRenderCharacters: true, + minimapMaxColumn: 150, + minimapMode: 'contain', + pixelRatio: 2, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + + decorationsLeft: 0, + decorationsWidth: 10, + + contentLeft: 10, + contentWidth: 893, + + renderMinimap: RenderMinimap.Text, + minimapLeft: 903, + minimapWidth: 97, + minimapHeightIsEditorHeight: false, + minimapIsSampling: false, + minimapScale: 2, + minimapLineHeight: 4, + minimapCanvasInnerWidth: 194, + minimapCanvasInnerHeight: 1600, + minimapCanvasOuterWidth: 97, + minimapCanvasOuterHeight: 800, + viewportColumn: 89, + + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + + overviewRuler: { + top: 0, + width: 0, + height: 800, + right: 0 + } + }); + }); + + test('EditorLayoutProvider 14 - minimap mode contain with sampling', () => { + doTest({ + outerWidth: 1000, + outerHeight: 800, + showGlyphMargin: false, + lineHeight: 16, + showLineNumbers: false, + lineNumbersMinChars: 0, + lineNumbersDigitCount: 4, + maxLineNumber: 2500, + lineDecorationsWidth: 10, + typicalHalfwidthCharacterWidth: 10, + maxDigitWidth: 10, + verticalScrollbarWidth: 0, + horizontalScrollbarHeight: 0, + scrollbarArrowSize: 0, + verticalScrollbarHasArrows: false, + minimap: true, + minimapSide: 'right', + minimapRenderCharacters: true, + minimapMaxColumn: 150, + minimapMode: 'contain', + pixelRatio: 2, + }, { + width: 1000, + height: 800, + + glyphMarginLeft: 0, + glyphMarginWidth: 0, + + lineNumbersLeft: 0, + lineNumbersWidth: 0, + + decorationsLeft: 0, + decorationsWidth: 10, + + contentLeft: 10, + contentWidth: 935, + + renderMinimap: RenderMinimap.Text, + minimapLeft: 945, + minimapWidth: 55, + minimapHeightIsEditorHeight: true, minimapIsSampling: true, minimapScale: 1, minimapLineHeight: 1, @@ -1109,6 +1253,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { renderMinimap: RenderMinimap.Text, minimapLeft: 1096, minimapWidth: 91, + minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 2, minimapLineHeight: 4, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 105a3a08e0c..d5aae368b6a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3330,6 +3330,7 @@ declare namespace monaco.editor { * The width of the minimap */ readonly minimapWidth: number; + readonly minimapHeightIsEditorHeight: boolean; readonly minimapIsSampling: boolean; readonly minimapScale: number; readonly minimapLineHeight: number; @@ -3386,6 +3387,11 @@ declare namespace monaco.editor { * Defaults to 'right'. */ side?: 'right' | 'left'; + /** + * Control the minimap rendering mode. + * Defaults to 'actual'. + */ + mode?: 'actual' | 'cover' | 'contain'; /** * Control the rendering of the minimap slider. * Defaults to 'mouseover'. @@ -3405,10 +3411,6 @@ declare namespace monaco.editor { * Relative size of the font in the minimap. Defaults to 1. */ scale?: number; - /** - * Minimap covers entire document. - */ - entireDocument?: boolean; } export type EditorMinimapOptions = Readonly>; From 2d125bc3cfeb9f4d0f95ace3860836d252a1a7b5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 12:19:37 +0100 Subject: [PATCH 155/192] remove git init action from top of scm --- extensions/git/package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 2e2b8e57d9f..dcfdcd2171c 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -749,11 +749,6 @@ } ], "scm/title": [ - { - "command": "git.init", - "group": "navigation", - "when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0 && workspaceFolderCount != 0" - }, { "command": "git.commit", "group": "navigation", From f1c6f2c2002249b2432619bf6d668c7eb5456313 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 12:33:09 +0100 Subject: [PATCH 156/192] text files - move save error handler into files model manager --- .../browser/editors/textFileSaveErrorHandler.ts | 2 +- .../services/textfile/browser/textFileService.ts | 13 ------------- .../textfile/common/textFileEditorModel.ts | 2 +- .../common/textFileEditorModelManager.ts | 16 +++++++++++++++- .../services/textfile/common/textfiles.ts | 8 ++------ .../electron-browser/nativeTextFileService.ts | 4 +--- .../test/browser/workbenchTestServices.ts | 2 -- .../electron-browser/workbenchTestServices.ts | 3 --- 8 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 5c214b0641d..af798c52dd8 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -64,7 +64,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); // Set as save error handler to service for text files - this.textFileService.saveErrorHandler = this; + this.textFileService.files.saveErrorHandler = this; this.registerListeners(); } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index c2d6d00c6ce..94a61318684 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -33,8 +33,6 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { coalesce } from 'vs/base/common/arrays'; import { suggestFilename } from 'vs/base/common/mime'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -59,16 +57,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex readonly untitled: IUntitledTextEditorModelManager = this.untitledTextEditorService; - saveErrorHandler = (() => { - const notificationService = this.notificationService; - - return { - onSaveError(error: Error, model: ITextFileEditorModel): void { - notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); - } - }; - })(); - abstract get encoding(): IResourceEncodings; constructor( @@ -84,7 +72,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex @IFilesConfigurationService protected readonly filesConfigurationService: IFilesConfigurationService, @ITextModelService private readonly textModelService: ITextModelService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, - @INotificationService private readonly notificationService: INotificationService, @IRemotePathService private readonly remotePathService: IRemotePathService ) { super(); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index b9ac2040100..5b622561b3a 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -738,7 +738,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Show to user - this.textFileService.saveErrorHandler.onSaveError(error, this); + this.textFileService.files.saveErrorHandler.onSaveError(error, this); // Emit as event this._onDidSaveError.fire(); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 12040a85830..a3a6cdba937 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; @@ -18,6 +20,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { TextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textFileSaveParticipant'; import { SaveReason } from 'vs/workbench/common/editor'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { @@ -42,6 +45,16 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private readonly _onDidChangeOrphaned = this._register(new Emitter()); readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event; + saveErrorHandler = (() => { + const notificationService = this.notificationService; + + return { + onSaveError(error: Error, model: ITextFileEditorModel): void { + notificationService.error(localize('genericSaveError', "Failed to save '{0}': {1}", model.name, toErrorMessage(error, false))); + } + }; + })(); + private readonly mapResourceToModel = new ResourceMap(); private readonly mapResourceToModelListeners = new ResourceMap(); private readonly mapResourceToDisposeListener = new ResourceMap(); @@ -52,7 +65,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE constructor( @ILifecycleService private readonly lifecycleService: ILifecycleService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @INotificationService private readonly notificationService: INotificationService ) { super(); diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index d89c66af2c9..c9532133e00 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -52,12 +52,6 @@ export interface ITextFileService extends IDisposable { */ readonly encoding: IResourceEncodings; - /** - * The handler that should be called when saving fails. Can be overridden - * to handle save errors in a custom way. - */ - saveErrorHandler: ISaveErrorHandler; - /** * A resource is dirty if it has unsaved changes or is an untitled file not yet saved. * @@ -377,6 +371,8 @@ export interface ITextFileEditorModelManager { addSaveParticipant(participant: ITextFileSaveParticipant): IDisposable; runSaveParticipants(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise + saveErrorHandler: ISaveErrorHandler; + disposeModel(model: ITextFileEditorModel): void; } diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 34ffc230de4..def5df7ccbe 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -37,7 +37,6 @@ import { assign } from 'vs/base/common/objects'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; export class NativeTextFileService extends AbstractTextFileService { @@ -56,10 +55,9 @@ export class NativeTextFileService extends AbstractTextFileService { @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @ITextModelService textModelService: ITextModelService, @ICodeEditorService codeEditorService: ICodeEditorService, - @INotificationService notificationService: INotificationService, @IRemotePathService remotePathService: IRemotePathService ) { - super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, notificationService, remotePathService); + super(fileService, untitledTextEditorService, lifecycleService, instantiationService, modelService, environmentService, dialogService, fileDialogService, textResourceConfigurationService, filesConfigurationService, textModelService, codeEditorService, remotePathService); } private _encoding: EncodingOracle | undefined; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 783d4d74a56..50efaef1f8a 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -121,7 +121,6 @@ export class TestTextFileService extends BrowserTextFileService { @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @ITextModelService textModelService: ITextModelService, @ICodeEditorService codeEditorService: ICodeEditorService, - @INotificationService notificationService: INotificationService, @IRemotePathService remotePathService: IRemotePathService ) { super( @@ -137,7 +136,6 @@ export class TestTextFileService extends BrowserTextFileService { filesConfigurationService, textModelService, codeEditorService, - notificationService, remotePathService ); } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 0857cc45c11..db2309ddf7b 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -22,7 +22,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { URI } from 'vs/base/common/uri'; import { IReadTextFileOptions, ITextFileStreamContent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; @@ -62,7 +61,6 @@ export class TestTextFileService extends NativeTextFileService { @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @ITextModelService textModelService: ITextModelService, @ICodeEditorService codeEditorService: ICodeEditorService, - @INotificationService notificationService: INotificationService, @IRemotePathService remotePathService: IRemotePathService ) { super( @@ -79,7 +77,6 @@ export class TestTextFileService extends NativeTextFileService { filesConfigurationService, textModelService, codeEditorService, - notificationService, remotePathService ); } From 39becc83bf044d1a76a3720267796fcef38b10ce Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 12:34:31 +0100 Subject: [PATCH 157/192] Don't render minimap slider if it cannot be dragged --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index adc23678bfe..9eeeb5c8257 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -174,6 +174,7 @@ class MinimapLayout { */ public readonly scrollHeight: number; + public readonly sliderNeeded: boolean; private readonly _computedSliderRatio: number; /** @@ -197,6 +198,7 @@ class MinimapLayout { constructor( scrollTop: number, scrollHeight: number, + sliderNeeded: boolean, computedSliderRatio: number, sliderTop: number, sliderHeight: number, @@ -205,6 +207,7 @@ class MinimapLayout { ) { this.scrollTop = scrollTop; this.scrollHeight = scrollHeight; + this.sliderNeeded = sliderNeeded; this._computedSliderRatio = computedSliderRatio; this.sliderTop = sliderTop; this.sliderHeight = sliderHeight; @@ -252,7 +255,8 @@ class MinimapLayout { // in the same way `scrollTop` can move from 0 to `scrollHeight` - `viewportHeight`. const computedSliderRatio = (maxMinimapSliderTop) / (scrollHeight - viewportHeight); const sliderTop = (scrollTop * computedSliderRatio); - return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, 1, lineCount); + const sliderNeeded = (maxMinimapSliderTop > 0); + return new MinimapLayout(scrollTop, scrollHeight, sliderNeeded, computedSliderRatio, sliderTop, sliderHeight, 1, lineCount); } // The visible line count in a viewport can change due to a number of reasons: @@ -301,8 +305,8 @@ class MinimapLayout { // All lines fit in the minimap const startLineNumber = 1; const endLineNumber = lineCount; - - return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + const sliderNeeded = (maxMinimapSliderTop > 0); + return new MinimapLayout(scrollTop, scrollHeight, sliderNeeded, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); } else { let startLineNumber = Math.max(1, Math.floor(viewportStartLineNumber - sliderTop * pixelRatio / minimapLineHeight)); @@ -321,7 +325,7 @@ class MinimapLayout { const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1); - return new MinimapLayout(scrollTop, scrollHeight, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); + return new MinimapLayout(scrollTop, scrollHeight, true, computedSliderRatio, sliderTop, sliderHeight, startLineNumber, endLineNumber); } } } @@ -1321,6 +1325,7 @@ class InnerMinimap extends Disposable { renderingCtx.scrollHeight, this._lastRenderData ? this._lastRenderData.renderedLayout : null ); + this._slider.setDisplay(layout.sliderNeeded ? 'block' : 'none'); this._slider.setTop(layout.sliderTop); this._slider.setHeight(layout.sliderHeight); From 355eea22ea9a98a5d0517ffeae5bd176e96bb185 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 12:33:03 +0100 Subject: [PATCH 158/192] linked text: class --- src/vs/base/browser/linkedText.ts | 15 +++++++++++++-- .../browser/parts/views/viewPaneContainer.ts | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/linkedText.ts b/src/vs/base/browser/linkedText.ts index 3d69948aa42..ee268d9fdcd 100644 --- a/src/vs/base/browser/linkedText.ts +++ b/src/vs/base/browser/linkedText.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { memoize } from 'vs/base/common/decorators'; + export interface ILink { readonly label: string; readonly href: string; @@ -10,7 +12,16 @@ export interface ILink { } export type LinkedTextNode = string | ILink; -export type LinkedText = LinkedTextNode[]; + +export class LinkedText { + + constructor(readonly nodes: LinkedTextNode[]) { } + + @memoize + toString(): string { + return this.nodes.map(node => typeof node === 'string' ? node : node.label).join(''); + } +} const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi; @@ -40,5 +51,5 @@ export function parseLinkedText(text: string): LinkedText { result.push(text.substring(index)); } - return result; + return new LinkedText(result); } diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index df18c1c388b..37221c09c15 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -402,10 +402,10 @@ export abstract class ViewPane extends Pane implements IView { const p = append(this.viewWelcomeContainer, $('p')); const linkedText = parseLinkedText(line); - for (const node of linkedText) { + for (const node of linkedText.nodes) { if (typeof node === 'string') { append(p, document.createTextNode(node)); - } else if (linkedText.length === 1) { + } else if (linkedText.nodes.length === 1) { const button = new Button(p, { title: node.title }); button.label = node.label; button.onDidClick(_ => this.openerService.open(node.href), null, disposables); From 2807512bce099a4baede0441a652bd2a2309357e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 12:50:18 +0100 Subject: [PATCH 159/192] Do not stretch minimap lines so much in very small cases --- src/vs/editor/common/config/editorOptions.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 07ccf3d6c98..81949c64047 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1765,11 +1765,12 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption minimapCanvasInnerHeight) { minimapHeightIsEditorHeight = true; const configuredFontScale = minimapScale; - minimapLineHeight = Math.max(1, Math.floor(1 / desiredRatio)); + minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio))); minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); if (minimapScale > configuredFontScale) { minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale); } minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; - minimapCanvasInnerHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight); + minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, modelLineCount + extraLinesBeyondLastLine)) * minimapLineHeight); } } } From c150c04fb59799a32ae163010097f56a684a6ce6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 12:55:00 +0100 Subject: [PATCH 160/192] Remove assumption about the height ratio being the pixel ratio --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 9eeeb5c8257..56331186a6f 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1102,7 +1102,7 @@ class InnerMinimap extends Disposable { return; } const minimapLineHeight = this._model.options.minimapLineHeight; - const internalOffsetY = this._model.options.pixelRatio * e.browserEvent.offsetY; + const internalOffsetY = (this._model.options.canvasInnerHeight / this._model.options.canvasOuterHeight) * e.browserEvent.offsetY; const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber; From 22e83f212c63021ca9d754480cb11369d9657c4d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 14:00:01 +0100 Subject: [PATCH 161/192] Add click & drag on the minimap when it has the full editor height --- .../browser/viewParts/minimap/minimap.ts | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 56331186a6f..810b43e4847 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1101,6 +1101,15 @@ class InnerMinimap extends Disposable { if (!this._lastRenderData) { return; } + if (this._model.options.minimapHeightIsEditorHeight) { + if (e.leftButton && this._lastRenderData) { + // pretend the click occured in the center of the slider + const position = dom.getDomNodePagePosition(this._slider.domNode); + const initialPosY = position.top + position.height / 2; + this._startSliderDragging(e.buttons, e.posx, initialPosY, e.posy, this._lastRenderData.renderedLayout); + } + return; + } const minimapLineHeight = this._model.options.minimapLineHeight; const internalOffsetY = (this._model.options.canvasInnerHeight / this._model.options.canvasOuterHeight) * e.browserEvent.offsetY; const lineIndex = Math.floor(internalOffsetY / minimapLineHeight); @@ -1117,32 +1126,7 @@ class InnerMinimap extends Disposable { e.preventDefault(); e.stopPropagation(); if (e.leftButton && this._lastRenderData) { - - const initialMousePosition = e.posy; - const initialMouseOrthogonalPosition = e.posx; - const initialSliderState = this._lastRenderData.renderedLayout; - this._slider.toggleClassName('active', true); - - this._sliderMouseMoveMonitor.startMonitoring( - e.target, - e.buttons, - standardMouseMoveMerger, - (mouseMoveData: IStandardMouseMoveEventData) => { - const mouseOrthogonalDelta = Math.abs(mouseMoveData.posx - initialMouseOrthogonalPosition); - - if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { - // The mouse has wondered away from the scrollbar => reset dragging - this._model.setScrollTop(initialSliderState.scrollTop); - return; - } - - const mouseDelta = mouseMoveData.posy - initialMousePosition; - this._model.setScrollTop(initialSliderState.getDesiredScrollTopFromDelta(mouseDelta)); - }, - () => { - this._slider.toggleClassName('active', false); - } - ); + this._startSliderDragging(e.buttons, e.posx, e.posy, e.posy, this._lastRenderData.renderedLayout); } }); @@ -1173,6 +1157,37 @@ class InnerMinimap extends Disposable { }); } + private _startSliderDragging(initialButtons: number, initialPosX: number, initialPosY: number, posy: number, initialSliderState: MinimapLayout): void { + this._slider.toggleClassName('active', true); + + const handleMouseMove = (posy: number, posx: number) => { + const mouseOrthogonalDelta = Math.abs(posx - initialPosX); + + if (platform.isWindows && mouseOrthogonalDelta > MOUSE_DRAG_RESET_DISTANCE) { + // The mouse has wondered away from the scrollbar => reset dragging + this._model.setScrollTop(initialSliderState.scrollTop); + return; + } + + const mouseDelta = posy - initialPosY; + this._model.setScrollTop(initialSliderState.getDesiredScrollTopFromDelta(mouseDelta)); + }; + + if (posy !== initialPosY) { + handleMouseMove(posy, initialPosX); + } + + this._sliderMouseMoveMonitor.startMonitoring( + this._slider.domNode, + initialButtons, + standardMouseMoveMerger, + (mouseMoveData: IStandardMouseMoveEventData) => handleMouseMove(mouseMoveData.posy, mouseMoveData.posx), + () => { + this._slider.toggleClassName('active', false); + } + ); + } + private scrollDueToTouchEvent(touch: GestureEvent) { const startY = this._domNode.domNode.getBoundingClientRect().top; const scrollTop = this._lastRenderData!.renderedLayout.getDesiredScrollTopFromTouchLocation(touch.pageY - startY); From 9fde1ae0e081f80d644d39cefc989593789bfb7c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 14:48:04 +0100 Subject: [PATCH 162/192] Improve warning check --- .../editor/browser/viewParts/minimap/minimapCharRenderer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index 483abbaac3b..a789082e76b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -39,7 +39,8 @@ export class MinimapCharRenderer { ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; - if (dx + charWidth > target.width || dy + charHeight > target.height) { + const renderHeight = (force1pxHeight ? 1 : charHeight); + if (dx + charWidth > target.width || dy + renderHeight > target.height) { console.warn('bad render request outside image data'); return; } @@ -61,7 +62,6 @@ export class MinimapCharRenderer { let sourceOffset = charIndex * charWidth * charHeight; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; - const renderHeight = (force1pxHeight ? 1 : charHeight); for (let y = 0; y < renderHeight; y++) { let column = row; for (let x = 0; x < charWidth; x++) { From 9931bab3e98e37f1c4bacf5608d8db4e50cf59d7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 14:50:42 +0100 Subject: [PATCH 163/192] Improve rendering for minimap blocks --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 5 +++-- .../browser/viewParts/minimap/minimapCharRenderer.ts | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 810b43e4847..495ae096449 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1666,6 +1666,7 @@ class InnerMinimap extends Disposable { const content = lineData.content; const tokens = lineData.tokens; const maxDx = target.width - charWidth; + const force1pxHeight = (minimapLineHeight === 1); let dx = MINIMAP_GUTTER_WIDTH; let charIndex = 0; @@ -1697,9 +1698,9 @@ class InnerMinimap extends Disposable { for (let i = 0; i < count; i++) { if (renderMinimap === RenderMinimap.Blocks) { - minimapCharRenderer.blockRenderChar(target, dx, dy + innerLinePadding, tokenColor, backgroundColor, useLighterFont); + minimapCharRenderer.blockRenderChar(target, dx, dy + innerLinePadding, tokenColor, backgroundColor, useLighterFont, force1pxHeight); } else { // RenderMinimap.Text - minimapCharRenderer.renderChar(target, dx, dy + innerLinePadding, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, minimapLineHeight === 1); + minimapCharRenderer.renderChar(target, dx, dy + innerLinePadding, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, force1pxHeight); } dx += charWidth; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index a789082e76b..c30b720c207 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -82,11 +82,13 @@ export class MinimapCharRenderer { dy: number, color: RGBA8, backgroundColor: RGBA8, - useLighterFont: boolean + useLighterFont: boolean, + force1pxHeight: boolean ): void { const charWidth = Constants.BASE_CHAR_WIDTH * this.scale; const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale; - if (dx + charWidth > target.width || dy + charHeight > target.height) { + const renderHeight = (force1pxHeight ? 1 : charHeight); + if (dx + charWidth > target.width || dy + renderHeight > target.height) { console.warn('bad render request outside image data'); return; } @@ -110,7 +112,7 @@ export class MinimapCharRenderer { const dest = target.data; let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT; - for (let y = 0; y < charHeight; y++) { + for (let y = 0; y < renderHeight; y++) { let column = row; for (let x = 0; x < charWidth; x++) { dest[column++] = colorR; From 41781a6d2446820061ed1599e751843bc60e0032 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 15:07:41 +0100 Subject: [PATCH 164/192] move LinkedText to common --- src/vs/base/{browser => common}/linkedText.ts | 0 src/vs/base/test/{browser => common}/linkedText.test.ts | 2 +- src/vs/workbench/browser/parts/views/viewPaneContainer.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/vs/base/{browser => common}/linkedText.ts (100%) rename src/vs/base/test/{browser => common}/linkedText.test.ts (97%) diff --git a/src/vs/base/browser/linkedText.ts b/src/vs/base/common/linkedText.ts similarity index 100% rename from src/vs/base/browser/linkedText.ts rename to src/vs/base/common/linkedText.ts diff --git a/src/vs/base/test/browser/linkedText.test.ts b/src/vs/base/test/common/linkedText.test.ts similarity index 97% rename from src/vs/base/test/browser/linkedText.test.ts rename to src/vs/base/test/common/linkedText.test.ts index 2c765555ffe..eb8f85f2803 100644 --- a/src/vs/base/test/browser/linkedText.test.ts +++ b/src/vs/base/test/common/linkedText.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { parseLinkedText } from 'vs/base/browser/linkedText'; +import { parseLinkedText } from 'vs/base/common/linkedText'; suite('LinkedText', () => { test('parses correctly', () => { diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 37221c09c15..074150b643d 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -38,7 +38,7 @@ import { Component } from 'vs/workbench/common/component'; import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; -import { parseLinkedText } from 'vs/base/browser/linkedText'; +import { parseLinkedText } from 'vs/base/common/linkedText'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Button } from 'vs/base/browser/ui/button/button'; import { Link } from 'vs/platform/opener/browser/link'; From 659816653e6bd1b102aaa0f46c63a9d024227492 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 15:25:48 +0100 Subject: [PATCH 165/192] Emit a tokens change event only if there are changed tokens --- src/vs/editor/common/model/textModel.ts | 32 ++++++++++++++++++----- src/vs/editor/common/model/tokensStore.ts | 27 ++++++++++++++++++- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 2c1fcc8f248..bfd6c4b9e6a 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1713,7 +1713,7 @@ export class TextModel extends Disposable implements model.ITextModel { throw new Error('Illegal value for lineNumber'); } - this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens); + this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens, false); } public setTokens(tokens: MultilineTokens[]): void { @@ -1725,16 +1725,34 @@ export class TextModel extends Disposable implements model.ITextModel { for (let i = 0, len = tokens.length; i < len; i++) { const element = tokens[i]; - ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 }); + let minChangedLineNumber = 0; + let maxChangedLineNumber = 0; + let hasChange = false; for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) { - this.setLineTokens(element.startLineNumber + j, element.tokens[j]); + const lineNumber = element.startLineNumber + j; + if (hasChange) { + this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], false); + maxChangedLineNumber = lineNumber; + } else { + const lineHasChange = this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], true); + if (lineHasChange) { + hasChange = true; + minChangedLineNumber = lineNumber; + maxChangedLineNumber = lineNumber; + } + } + } + if (hasChange) { + ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber }); } } - this._emitModelTokensChangedEvent({ - tokenizationSupportChanged: false, - ranges: ranges - }); + if (ranges.length > 0) { + this._emitModelTokensChangedEvent({ + tokenizationSupportChanged: false, + ranges: ranges + }); + } } public setSemanticTokens(tokens: MultilineTokens2[] | null): void { diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts index e396aa054a2..c662f820c9c 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -964,10 +964,35 @@ export class TokensStore { this._len += insertCount; } - public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): void { + public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean { const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens); this._ensureLine(lineIndex); + const oldTokens = this._lineTokens[lineIndex]; this._lineTokens[lineIndex] = tokens; + + if (checkEquality) { + return !TokensStore._equals(oldTokens, tokens); + } + return false; + } + + private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) { + if (!_a || !_b) { + return !_a && !_b; + } + + const a = toUint32Array(_a); + const b = toUint32Array(_b); + + if (a.length !== b.length) { + return false; + } + for (let i = 0, len = a.length; i < len; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; } //#region Editing From fd7ea40bfedb594f51439c9b97d2838e6a1fd95c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 15:34:12 +0100 Subject: [PATCH 166/192] fix tests --- src/vs/base/test/common/linkedText.test.ts | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/base/test/common/linkedText.test.ts b/src/vs/base/test/common/linkedText.test.ts index eb8f85f2803..8e1cb887485 100644 --- a/src/vs/base/test/common/linkedText.test.ts +++ b/src/vs/base/test/common/linkedText.test.ts @@ -8,46 +8,46 @@ import { parseLinkedText } from 'vs/base/common/linkedText'; suite('LinkedText', () => { test('parses correctly', () => { - assert.deepEqual(parseLinkedText(''), []); - assert.deepEqual(parseLinkedText('hello'), ['hello']); - assert.deepEqual(parseLinkedText('hello there'), ['hello there']); - assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).'), [ + assert.deepEqual(parseLinkedText('').nodes, []); + assert.deepEqual(parseLinkedText('hello').nodes, ['hello']); + assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']); + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [ 'Some message with ', { label: 'link text', href: 'http://link.href' }, '.' ]); - assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").'), [ + assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").').nodes, [ 'Some message with ', { label: 'link text', href: 'http://link.href', title: 'and a title' }, '.' ]); - assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).'), [ + assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [ 'Some message with [link text](random stuff).' ]); - assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).'), [ + assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).').nodes, [ 'Some message with ', { label: 'https link', href: 'https://link.href' }, '.' ]); - assert.deepEqual(parseLinkedText('Some message with [https link](https:).'), [ + assert.deepEqual(parseLinkedText('Some message with [https link](https:).').nodes, [ 'Some message with [https link](https:).' ]); - assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).'), [ + assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [ 'Some message with ', { label: 'a command', href: 'command:foobar' }, '.' ]); - assert.deepEqual(parseLinkedText('Some message with [a command](command:).'), [ + assert.deepEqual(parseLinkedText('Some message with [a command](command:).').nodes, [ 'Some message with [a command](command:).' ]); - assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...'), [ + assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...').nodes, [ 'link ', { label: 'one', href: 'command:foo', title: 'nice' }, ' and link ', { label: 'two', href: 'http://foo' }, '...' ]); - assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...'), [ + assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...').nodes, [ 'link\n', { label: 'one', href: 'command:foo', title: 'nice' }, '\nand link ', From 95cc4e3d835ab7bb79b199169c506669ed5d3026 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 15:51:51 +0100 Subject: [PATCH 167/192] Have the IModelDecorationsChangedEvent contain affectsMinimap and affectsOverviewRuler to avoid repainting if not necessary --- .../browser/viewParts/minimap/minimap.ts | 5 ++- .../overviewRuler/decorationsOverviewRuler.ts | 5 ++- src/vs/editor/common/model/textModel.ts | 36 +++++++++++++++---- src/vs/editor/common/model/textModelEvents.ts | 2 ++ src/vs/editor/common/view/viewEvents.ts | 14 ++++++-- .../editor/common/viewModel/viewModelImpl.ts | 12 +++---- src/vs/monaco.d.ts | 2 ++ 7 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 495ae096449..e3cbf8ce4d3 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -781,7 +781,10 @@ export class Minimap extends ViewPart implements IMinimapModel { return this._actual.onSelectionChanged(); } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - return this._actual.onDecorationsChanged(); + if (e.affectsMinimap) { + return this._actual.onDecorationsChanged(); + } + return false; } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { return this._actual.onFlushed(); diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 0cbac3c5035..bd1aa10f8a8 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -276,7 +276,10 @@ export class DecorationsOverviewRuler extends ViewPart { return true; } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - return true; + if (e.affectsOverviewRuler) { + return true; + } + return false; } public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { return true; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index bfd6c4b9e6a..8aa9b5a53d4 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1423,19 +1423,15 @@ export class TextModel extends Disposable implements model.ITextModel { private _changeDecorations(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null { let changeAccessor: model.IModelDecorationsChangeAccessor = { addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => { - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0]; }, changeDecoration: (id: string, newRange: IRange): void => { - this._onDidChangeDecorations.fire(); this._changeDecorationImpl(id, newRange); }, changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => { - this._onDidChangeDecorations.fire(); this._changeDecorationOptionsImpl(id, _normalizeOptions(options)); }, removeDecoration: (id: string): void => { - this._onDidChangeDecorations.fire(); this._deltaDecorationsImpl(ownerId, [id], []); }, deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => { @@ -1443,7 +1439,6 @@ export class TextModel extends Disposable implements model.ITextModel { // nothing to do return []; } - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } }; @@ -1474,7 +1469,6 @@ export class TextModel extends Disposable implements model.ITextModel { try { this._onDidChangeDecorations.beginDeferredEmit(); - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } finally { this._onDidChangeDecorations.endDeferredEmit(); @@ -1622,6 +1616,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._decorationsTree.delete(node); node.reset(this.getVersionId(), startOffset, endOffset, range); this._decorationsTree.insert(node); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); } private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void { @@ -1633,6 +1628,9 @@ export class TextModel extends Disposable implements model.ITextModel { const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false); const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); + this._onDidChangeDecorations.checkAffectedAndFire(options); + if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) { // Delete + Insert due to an overview ruler status change this._decorationsTree.delete(node); @@ -1666,6 +1664,7 @@ export class TextModel extends Disposable implements model.ITextModel { // (2) remove the node from the tree (if it exists) if (node) { this._decorationsTree.delete(node); + this._onDidChangeDecorations.checkAffectedAndFire(node.options); } } @@ -1688,6 +1687,7 @@ export class TextModel extends Disposable implements model.ITextModel { node.ownerId = ownerId; node.reset(versionId, startOffset, endOffset, range); node.setOptions(options); + this._onDidChangeDecorations.checkAffectedAndFire(options); this._decorationsTree.insert(node); @@ -3101,11 +3101,15 @@ export class DidChangeDecorationsEmitter extends Disposable { private _deferredCnt: number; private _shouldFire: boolean; + private _affectsMinimap: boolean; + private _affectsOverviewRuler: boolean; constructor() { super(); this._deferredCnt = 0; this._shouldFire = false; + this._affectsMinimap = false; + this._affectsOverviewRuler = false; } public beginDeferredEmit(): void { @@ -3116,13 +3120,31 @@ export class DidChangeDecorationsEmitter extends Disposable { this._deferredCnt--; if (this._deferredCnt === 0) { if (this._shouldFire) { + const event: IModelDecorationsChangedEvent = { + affectsMinimap: this._affectsMinimap, + affectsOverviewRuler: this._affectsOverviewRuler, + }; this._shouldFire = false; - this._actual.fire({}); + this._affectsMinimap = false; + this._affectsOverviewRuler = false; + this._actual.fire(event); } } } + public checkAffectedAndFire(options: ModelDecorationOptions): void { + if (!this._affectsMinimap) { + this._affectsMinimap = options.minimap && options.minimap.position ? true : false; + } + if (!this._affectsOverviewRuler) { + this._affectsOverviewRuler = options.overviewRuler && options.overviewRuler.color ? true : false; + } + this._shouldFire = true; + } + public fire(): void { + this._affectsMinimap = true; + this._affectsOverviewRuler = true; this._shouldFire = true; } } diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index 11fc0e4165f..a09a1abf0fe 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -76,6 +76,8 @@ export interface IModelContentChangedEvent { * An event describing that model decorations have changed. */ export interface IModelDecorationsChangedEvent { + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; } /** diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index 1d9290b5e76..0f2720d8514 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -10,6 +10,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { ViewConfigurationChanged = 1, @@ -82,8 +83,17 @@ export class ViewDecorationsChangedEvent { public readonly type = ViewEventType.ViewDecorationsChanged; - constructor() { - // Nothing to do + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; + + constructor(source: IModelDecorationsChangedEvent | null) { + if (source) { + this.affectsMinimap = source.affectsMinimap; + this.affectsOverviewRuler = source.affectsOverviewRuler; + } else { + this.affectsMinimap = true; + this.affectsOverviewRuler = true; + } } } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index ed3b3382535..171a4839949 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -172,7 +172,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) { eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); this.decorations.onLineMappingChanged(); this.viewLayout.onFlushed(this.getLineCount()); @@ -185,7 +185,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (e.hasChanged(EditorOption.readOnly)) { // Must read again all decorations due to readOnly filtering this.decorations.reset(); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); } eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e)); @@ -291,7 +291,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) { eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); this.decorations.onLineMappingChanged(); } } finally { @@ -354,7 +354,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const eventsCollector = this._beginEmit(); eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); } finally { this._endEmit(); } @@ -365,7 +365,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.decorations.onModelDecorationsChanged(); try { const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(e)); } finally { this._endEmit(); } @@ -379,7 +379,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel if (lineMappingChanged) { eventsCollector.emit(new viewEvents.ViewFlushedEvent()); eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent()); - eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); + eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null)); this.decorations.onLineMappingChanged(); this.viewLayout.onFlushed(this.getLineCount()); this.viewLayout.onHeightMaybeChanged(); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d5aae368b6a..73a615757dc 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2390,6 +2390,8 @@ declare namespace monaco.editor { * An event describing that model decorations have changed. */ export interface IModelDecorationsChangedEvent { + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; } export interface IModelOptionsChangedEvent { From b29d61c3059226740d27d89fdc66eccc0b196c56 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Feb 2020 15:53:28 +0100 Subject: [PATCH 168/192] fixes #90814 --- src/vs/editor/contrib/find/findWidget.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 96c7b3a0d17..440c78fef72 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -419,8 +419,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (currentMatch) { const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn); - const lineContent = this._codeEditor.getModel()?.getLineContent(currentMatch.startLineNumber); - if (lineContent) { + const model = this._codeEditor.getModel(); + if (model && (currentMatch.startLineNumber <= model.getLineCount()) && (currentMatch.startLineNumber >= 1)) { + const lineContent = model.getLineContent(currentMatch.startLineNumber); return `${lineContent}, ${ariaLabel}`; } From 2e3bbc2da9a88fd3cbace1e8a3a0d1249a39b5ab Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 17 Feb 2020 16:30:55 +0100 Subject: [PATCH 169/192] always launch code with `--no-sandbox` --- scripts/code.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/code.sh b/scripts/code.sh index 4ba1a00b9f7..0afe96bfcb6 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -50,7 +50,7 @@ function code() { export VSCODE_LOGS= # Launch Code - exec "$CODE" . "$@" + exec "$CODE" . --no-sandbox "$@" } function code-wsl() From bb04e412a9c2ce9be4c68cecc64a09c44271d592 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 16:38:27 +0100 Subject: [PATCH 170/192] Fixes #90391: Split lines into spans of at most 16384 characters --- .../browser/view/domLineBreaksComputer.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 600f303335e..81611253b01 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -149,6 +149,10 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe return result; } +const enum Constants { + SPAN_MODULO_LIMIT = 16384 +} + function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] { sb.appendASCIIString('

Date: Mon, 17 Feb 2020 16:48:25 +0100 Subject: [PATCH 172/192] debug: make session shutdown private --- .../contrib/debug/browser/debugService.ts | 5 +-- .../contrib/debug/browser/debugSession.ts | 36 +++++++++++++------ .../workbench/contrib/debug/common/debug.ts | 3 -- .../contrib/debug/common/debugModel.ts | 9 ----- .../debug/test/browser/breakpoints.test.ts | 2 +- .../debug/test/browser/callStack.test.ts | 4 +-- .../contrib/debug/test/common/mockDebug.ts | 2 -- .../debugANSIHandling.test.ts | 2 +- 8 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 30a87cca33c..6e863fc5abe 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -117,7 +117,6 @@ export class DebugService implements IDebugService { this.model = new DebugModel(this.loadBreakpoints(), this.loadFunctionBreakpoints(), this.loadExceptionBreakpoints(), this.loadDataBreakpoints(), this.loadWatchExpressions(), this.textFileService); - this.toDispose.push(this.model); const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length)); this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService); setBreakpointsExistContext(); @@ -126,7 +125,7 @@ export class DebugService implements IDebugService { this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner); this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); - this.lifecycleService.onShutdown(this.dispose, this); + this.toDispose.push(this.lifecycleService.onShutdown(this.dispose, this)); this.toDispose.push(this.extensionHostDebugService.onAttachSession(event => { const session = this.model.getSession(event.sessionId, true); @@ -522,7 +521,6 @@ export class DebugService implements IDebugService { await this.focusStackFrame(undefined, undefined, session); } } catch (err) { - session.shutdown(); if (this.viewModel.focusedSession === session) { await this.focusStackFrame(undefined); } @@ -567,7 +565,6 @@ export class DebugService implements IDebugService { this.notificationService.error(err); } } - session.shutdown(); this.endInitializingState(); this._onDidEndSession.fire(session); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 11c7ef8329c..556180f6144 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -33,6 +33,7 @@ import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variables 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'; export class DebugSession implements IDebugSession { @@ -74,7 +75,8 @@ export class DebugSession implements IDebugSession { @IProductService private readonly productService: IProductService, @IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService, @IOpenerService private readonly openerService: IOpenerService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @ILifecycleService lifecycleService: ILifecycleService ) { this.id = generateUuid(); this._options = options || {}; @@ -83,7 +85,13 @@ export class DebugSession implements IDebugSession { } else { this.repl = (this.parentSession as DebugSession).repl; } - this.repl.onDidChangeElements(() => this._onDidChangeREPLElements.fire()); + + const toDispose: IDisposable[] = []; + toDispose.push(this.repl.onDidChangeElements(() => this._onDidChangeREPLElements.fire())); + toDispose.push(lifecycleService.onShutdown(() => { + this.shutdown(); + dispose(toDispose); + })); } getId(): string { @@ -213,6 +221,7 @@ export class DebugSession implements IDebugSession { } catch (err) { this.initialized = true; this._onDidChangeState.fire(); + this.shutdown(); throw err; } } @@ -227,8 +236,12 @@ export class DebugSession implements IDebugSession { // __sessionID only used for EH debugging (but we add it always for now...) config.__sessionId = this.getId(); - await this.raw.launchOrAttach(config); - + try { + await this.raw.launchOrAttach(config); + } catch (err) { + this.shutdown(); + throw err; + } } /** @@ -892,19 +905,22 @@ export class DebugSession implements IDebugSession { this.rawListeners.push(this.raw.onDidExitAdapter(event => { this.initialized = true; this.model.setBreakpointSessionData(this.getId(), this.capabilities, undefined); + this.shutdown(); this._onDidEndAdapter.fire(event); })); } - shutdown(): void { + // Disconnects and clears state. Session can be initialized again for a new connection. + private shutdown(): void { dispose(this.rawListeners); - if (this.raw) { - this.raw.disconnect(); - this.raw.dispose(); - } - this.raw = undefined; this.fetchThreadsScheduler = undefined; this.model.clearThreads(this.getId(), true); + if (this.raw) { + const raw = this.raw; + this.raw = undefined; + raw.disconnect(); + raw.dispose(); + } this._onDidChangeState.fire(); } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 56424879c1c..1df7822de47 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -200,9 +200,6 @@ export interface IDebugSession extends ITreeElement { readonly onDidLoadedSource: Event; readonly onDidCustomEvent: Event; - // Disconnects and clears state. Session can be initialized again for a new connection. - shutdown(): void; - // DAP request initialize(dbgr: IDebugger): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 67de4eb4bee..6995887eefe 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; -import * as lifecycle from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -819,7 +818,6 @@ export class ThreadAndSessionIds implements ITreeElement { export class DebugModel implements IDebugModel { private sessions: IDebugSession[]; - private toDispose: lifecycle.IDisposable[]; private schedulers = new Map(); private breakpointsActivated = true; private readonly _onDidChangeBreakpoints = new Emitter(); @@ -835,7 +833,6 @@ export class DebugModel implements IDebugModel { private textFileService: ITextFileService ) { this.sessions = []; - this.toDispose = []; } getId(): string { @@ -1227,10 +1224,4 @@ export class DebugModel implements IDebugModel { }); this._onDidChangeCallStack.fire(undefined); } - - dispose(): void { - // Make sure to shutdown each session, such that no debugged process is left laying around - this.sessions.forEach(s => s.shutdown()); - this.toDispose = lifecycle.dispose(this.toDispose); - } } diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index 31844187fbe..9fd5034f633 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -19,7 +19,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model'; import { MarkdownString } from 'vs/base/common/htmlContent'; function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { - return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); + return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); } function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakpointData[]): void { diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 558aa88393d..84b31372c66 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -18,7 +18,7 @@ import { getContext, getContextForContributedActions } from 'vs/workbench/contri import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; export function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { - return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); + return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); } function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFrame, secondStackFrame: StackFrame } { @@ -363,7 +363,7 @@ suite('Debug - CallStack', () => { get state(): State { return State.Stopped; } - }({ resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); + }({ resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); const runningSession = createMockSession(model); model.addSession(runningSession); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 14d4a539a74..13f7b1a7e38 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -327,8 +327,6 @@ export class MockSession implements IDebugSession { goto(threadId: number, targetId: number): Promise { throw new Error('Method not implemented.'); } - - shutdown(): void { } } export class MockRawSession { diff --git a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts index 6ba96158d52..c7eba121141 100644 --- a/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/electron-browser/debugANSIHandling.test.ts @@ -30,7 +30,7 @@ suite('Debug - ANSI Handling', () => { */ setup(() => { model = new DebugModel([], [], [], [], [], { isDirty: (e: any) => false }); - session = new DebugSession({ resolved: { name: 'test', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!); + session = new DebugSession({ resolved: { name: 'test', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); const instantiationService: TestInstantiationService = workbenchInstantiationService(); linkDetector = instantiationService.createInstance(LinkDetector); From 57ab6fb91d788068bc8f68aadc4c13a4bb210073 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 17 Feb 2020 15:58:57 +0100 Subject: [PATCH 173/192] JSON Language Server 1.2.3 --- extensions/json-language-features/server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 0c26777f01f..a21b05e4aba 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-json-languageserver", "description": "JSON language server", - "version": "1.2.2", + "version": "1.2.3", "author": "Microsoft Corporation", "license": "MIT", "engines": { From 2735ee99085cac449568b4702639134c8eb3b85c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 18:20:51 +0100 Subject: [PATCH 174/192] fix hygiene --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 274b0123c8b..d18702c4b15 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -25,7 +25,7 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; -import { minimapSelection, scrollbarShadow, minimapBackground, minimapSliderBackground, minimapSliderHoverBackground, minimapSliderActiveBackground} from 'vs/platform/theme/common/colorRegistry'; +import { minimapSelection, scrollbarShadow, minimapBackground, minimapSliderBackground, minimapSliderHoverBackground, minimapSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import { Selection } from 'vs/editor/common/core/selection'; From 772689e9f8e90574026951222dd7712a1a184dd9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 17 Feb 2020 18:36:27 +0100 Subject: [PATCH 175/192] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2712fd40e09..3d07dd60a28 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "821d61f0e38d2eb3e02bec3962fe61e4e5a61527", + "distro": "6bef94c398a2784bb9398afda4f5069d937c4bd5", "author": { "name": "Microsoft Corporation" }, From b308a365d75d1dc607591ade9380cf368f08c394 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 17 Feb 2020 10:57:41 -0800 Subject: [PATCH 176/192] Fix #90813 --- src/vs/workbench/api/browser/mainThreadSearch.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSearch.ts b/src/vs/workbench/api/browser/mainThreadSearch.ts index 426f361e786..9d2fc19ab3a 100644 --- a/src/vs/workbench/api/browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/browser/mainThreadSearch.ts @@ -80,10 +80,14 @@ class SearchOperation { } addMatch(match: IFileMatch): void { - if (this.matches.has(match.resource.toString())) { - // Merge with previous IFileMatches + const existingMatch = this.matches.get(match.resource.toString()); + if (existingMatch) { // TODO@rob clean up text/file result types - this.matches.get(match.resource.toString())!.results!.push(...match.results!); + // If a file search returns the same file twice, we would enter this branch. + // It's possible that could happen, #90813 + if (existingMatch.results && match.results) { + existingMatch.results.push(...match.results); + } } else { this.matches.set(match.resource.toString(), match); } From 37f2127173cf8d3185ec0ea27a7643fdcddbb07b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 21:01:26 +0100 Subject: [PATCH 177/192] Fixes microsoft/monaco-editor#1832: Accept `theme` to be passed in to `updateOptions` --- .../browser/standaloneCodeEditor.ts | 19 ++++++++++++++++++- src/vs/monaco.d.ts | 7 +++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 7f1e39821e7..504f3d2356f 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -124,6 +124,13 @@ export interface IGlobalEditorOptions { * Defaults to 20000. */ maxTokenizationLineLength?: number; + /** + * Theme to be used for rendering. + * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'. + * You can create custom themes via `monaco.editor.defineTheme`. + * To switch a theme, use `monaco.editor.setTheme` + */ + theme?: string; } /** @@ -334,6 +341,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon private readonly _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; + private readonly _standaloneThemeService: IStandaloneThemeService; private _ownsModel: boolean; constructor( @@ -363,6 +371,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon this._contextViewService = contextViewService; this._configurationService = configurationService; + this._standaloneThemeService = themeService; this._register(toDispose); this._register(themeDomRegistration); @@ -391,6 +400,9 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon public updateOptions(newOptions: IEditorOptions & IGlobalEditorOptions): void { applyConfigurationValues(this._configurationService, newOptions, false); + if (typeof newOptions.theme === 'string') { + this._standaloneThemeService.setTheme(newOptions.theme); + } super.updateOptions(newOptions); } @@ -414,6 +426,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon private readonly _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; + private readonly _standaloneThemeService: IStandaloneThemeService; constructor( domElement: HTMLElement, @@ -443,6 +456,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon this._contextViewService = contextViewService; this._configurationService = configurationService; + this._standaloneThemeService = themeService; this._register(toDispose); this._register(themeDomRegistration); @@ -454,8 +468,11 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon super.dispose(); } - public updateOptions(newOptions: IDiffEditorOptions): void { + public updateOptions(newOptions: IDiffEditorOptions & IGlobalEditorOptions): void { applyConfigurationValues(this._configurationService, newOptions, true); + if (typeof newOptions.theme === 'string') { + this._standaloneThemeService.setTheme(newOptions.theme); + } super.updateOptions(newOptions); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 73a615757dc..ccd369acc0d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1108,6 +1108,13 @@ declare namespace monaco.editor { * Defaults to 20000. */ maxTokenizationLineLength?: number; + /** + * Theme to be used for rendering. + * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'. + * You can create custom themes via `monaco.editor.defineTheme`. + * To switch a theme, use `monaco.editor.setTheme` + */ + theme?: string; } /** From 9d1b3eba1267a148f80bc640b04530278cf6ef69 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 21:41:09 +0100 Subject: [PATCH 178/192] Fixes microsoft/monaco-editor#1834 --- .../browser/inspectTokens/inspectTokens.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 3e2d797c71e..08d629e4260 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -224,13 +224,13 @@ class InspectTokensWidget extends Disposable implements IContentWidget { result += `
`; - let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]); + let metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null; result += ``; - result += ``; - result += ``; - result += ``; - result += ``; - result += ``; + result += ``; + result += ``; + result += ``; + result += ``; + result += ``; result += ``; result += `
`; From 26511d533651bafe31a7f8af76d43b5775c2a859 Mon Sep 17 00:00:00 2001 From: Dev Sebastian Date: Tue, 18 Feb 2020 02:14:31 +0530 Subject: [PATCH 179/192] Update README.md (#90787) removed space between 'up vote' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0de5f2f71da..1d3bc28f9bb 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ please see the document [How to Contribute](https://github.com/Microsoft/vscode/ * Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) * [Request a new feature](CONTRIBUTING.md) -* Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +* Upvote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) * [File an issue](https://github.com/Microsoft/vscode/issues) * Follow [@code](https://twitter.com/code) and let us know what you think! From 0f9807a7e763b4f9618ba449a9979abf3c9ff883 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 21:57:12 +0100 Subject: [PATCH 180/192] Fixes #67682: Bind keybindings in all editors, such that the read-only tip will show --- src/vs/editor/browser/controller/coreCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index ab975aa915d..13ec59bc49f 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -1614,7 +1614,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteLeft', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1639,7 +1639,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteRight', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, From 7169e432d8c63561a13f425ec8aa6bf317e5f4ff Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 22:19:03 +0100 Subject: [PATCH 181/192] Fixes #42649 --- .../modes/languageConfigurationRegistry.ts | 9 ++++++ .../api/browser/mainThreadLanguageFeatures.ts | 28 ++++++++++++++++++- .../workbench/api/common/extHost.protocol.ts | 7 +++++ .../api/common/extHostLanguageFeatures.ts | 6 ++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index eacb7ed17b9..f143381cd14 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -274,6 +274,15 @@ export class LanguageConfigurationRegistryImpl { return ensureValidWordDefinition(value.wordDefinition || null); } + public getWordDefinitions(): [LanguageId, RegExp][] { + const languages = this._entries.keys(); + let result: [LanguageId, RegExp][] = []; + for (const language of languages) { + result.push([language, this._entries.get(language)!.wordDefinition]); + } + return result; + } + public getFoldingRules(languageId: LanguageId): FoldingRules { let value = this._getRichEditSupport(languageId); if (!value) { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index de204a88dbd..a5af7ded508 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Range as EditorRange, IRange } from 'vs/editor/common/core/range'; -import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto } from '../common/extHost.protocol'; +import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto } from '../common/extHost.protocol'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -36,6 +36,32 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures); this._modeService = modeService; + + const updateAllWordDefinitions = () => { + const langWordPairs = LanguageConfigurationRegistry.getWordDefinitions(); + let wordDefinitionDtos: ILanguageWordDefinitionDto[] = []; + for (const [languageId, wordDefinition] of langWordPairs) { + const language = this._modeService.getLanguageIdentifier(languageId); + if (!language) { + continue; + } + wordDefinitionDtos.push({ + languageId: language.language, + regexSource: wordDefinition.source, + regexFlags: wordDefinition.flags + }); + } + this._proxy.$setWordDefinitions(wordDefinitionDtos); + }; + LanguageConfigurationRegistry.onDidChange((e) => { + const wordDefinition = LanguageConfigurationRegistry.getWordDefinition(e.languageIdentifier.id); + this._proxy.$setWordDefinitions([{ + languageId: e.languageIdentifier.language, + regexSource: wordDefinition.source, + regexFlags: wordDefinition.flags + }]); + }); + updateAllWordDefinitions(); } dispose(): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b780bccbd4d..ee4368dbe33 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1197,6 +1197,12 @@ export interface IOutgoingCallDto { to: ICallHierarchyItemDto; } +export interface ILanguageWordDefinitionDto { + languageId: string; + regexSource: string; + regexFlags: string +} + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; @@ -1239,6 +1245,7 @@ export interface ExtHostLanguageFeaturesShape { $provideCallHierarchyIncomingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise; $provideCallHierarchyOutgoingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise; $releaseCallHierarchy(handle: number, sessionId: string): void; + $setWordDefinitions(wordDefinitions: ILanguageWordDefinitionDto[]): void; } export interface ExtHostQuickOpenShape { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 65d12628b10..c0522477836 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1916,4 +1916,10 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._proxy.$setLanguageConfiguration(handle, languageId, serializedConfiguration); return this._createDisposable(handle); } + + $setWordDefinitions(wordDefinitions: extHostProtocol.ILanguageWordDefinitionDto[]): void { + for (const wordDefinition of wordDefinitions) { + this._documents.setWordDefinitionFor(wordDefinition.languageId, new RegExp(wordDefinition.regexSource, wordDefinition.regexFlags)); + } + } } From 186354f536d6de239fceca78be08a27b9abffb28 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 17 Feb 2020 22:29:16 +0100 Subject: [PATCH 182/192] Fixes #51275: push stack element before move word commands --- .../test/wordOperations.test.ts | 28 +++++++++++++++++++ .../contrib/wordOperations/wordOperations.ts | 1 + 2 files changed, 29 insertions(+) diff --git a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts index 0fa9fe4caa7..15ea81e6a44 100644 --- a/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/wordOperations.test.ts @@ -11,6 +11,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { Handler } from 'vs/editor/common/editorCommon'; +import { Cursor } from 'vs/editor/common/controller/cursor'; suite('WordOperations', () => { @@ -193,6 +195,32 @@ suite('WordOperations', () => { assert.deepEqual(actual, EXPECTED); }); + test('issue #51275 - cursorWordStartLeft does not push undo/redo stack element', () => { + function cursorCommand(cursor: Cursor, command: string, extraData?: any, overwriteSource?: string) { + cursor.trigger(overwriteSource || 'tests', command, extraData); + } + + function type(cursor: Cursor, text: string) { + for (let i = 0; i < text.length; i++) { + cursorCommand(cursor, Handler.Type, { text: text.charAt(i) }, 'keyboard'); + } + } + + withTestCodeEditor('', {}, (editor, cursor) => { + type(cursor, 'foo bar baz'); + assert.equal(editor.getValue(), 'foo bar baz'); + + cursorWordStartLeft(editor); + cursorWordStartLeft(editor); + type(cursor, 'q'); + + assert.equal(editor.getValue(), 'foo qbar baz'); + + cursorCommand(cursor, Handler.Undo, {}); + assert.equal(editor.getValue(), 'foo bar baz'); + }); + }); + test('cursorWordEndLeft', () => { const EXPECTED = ['| /*| Just| some| more| text| a|+=| 3| +|5|-|3| +| 7| */| '].join('\n'); const [text,] = deserializePipePositions(EXPECTED); diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 57d9174719d..ae5429ed0cc 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -52,6 +52,7 @@ export abstract class MoveWordCommand extends EditorCommand { return this._moveTo(sel, outPosition, this._inSelectionMode); }); + model.pushStackElement(); editor._getCursors().setStates('moveWordCommand', CursorChangeReason.NotSet, result.map(r => CursorState.fromModelSelection(r))); if (result.length === 1) { const pos = new Position(result[0].positionLineNumber, result[0].positionColumn); From cbc3ba90843cf4a3663a1a53055d04c267346a28 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 18 Feb 2020 08:23:44 +0100 Subject: [PATCH 183/192] Avoid using Map.keys() --- .../editor/common/modes/languageConfigurationRegistry.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index f143381cd14..daadeebb57e 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -275,11 +275,12 @@ export class LanguageConfigurationRegistryImpl { } public getWordDefinitions(): [LanguageId, RegExp][] { - const languages = this._entries.keys(); let result: [LanguageId, RegExp][] = []; - for (const language of languages) { - result.push([language, this._entries.get(language)!.wordDefinition]); - } + this._entries.forEach((value, language) => { + if (value) { + result.push([language, value.wordDefinition]); + } + }); return result; } From 7ad489c0133aa45810039a128bbf0efa54414683 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 18 Feb 2020 08:56:29 +0100 Subject: [PATCH 184/192] #90076 move in mem fs provider to platform --- .../files/common/inMemoryFilesystemProvider.ts} | 0 src/vs/workbench/browser/web.main.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/vs/{workbench/services/userData/common/inMemoryUserDataProvider.ts => platform/files/common/inMemoryFilesystemProvider.ts} (100%) diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/platform/files/common/inMemoryFilesystemProvider.ts similarity index 100% rename from src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts rename to src/vs/platform/files/common/inMemoryFilesystemProvider.ts diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 11df61ac725..8973e3fc36c 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -41,7 +41,6 @@ import { joinPath } from 'vs/base/common/resources'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; -import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; @@ -51,6 +50,7 @@ import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLo import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; import { coalesce } from 'vs/base/common/arrays'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; class BrowserMain extends Disposable { From 6b71b834f3650f4ea5b10f139e3e5b37debe2f6a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 18 Feb 2020 08:57:32 +0100 Subject: [PATCH 185/192] #90076 move config service to common --- src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts | 2 +- src/vs/code/electron-main/main.ts | 2 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../configuration/{node => common}/configurationService.ts | 0 .../configuration/test/node/configurationService.test.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/vs/platform/configuration/{node => common}/configurationService.ts (100%) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 9e2970756db..b0992f3baa0 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -17,7 +17,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtension import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/browser/requestService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 1c098b9e8f0..7871c40dc59 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -26,7 +26,7 @@ import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; import * as fs from 'fs'; diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index f2e8279010b..9c82546995f 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -24,7 +24,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts similarity index 100% rename from src/vs/platform/configuration/node/configurationService.ts rename to src/vs/platform/configuration/common/configurationService.ts diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts index f2e90b7d245..15b3d915ed6 100644 --- a/src/vs/platform/configuration/test/node/configurationService.test.ts +++ b/src/vs/platform/configuration/test/node/configurationService.test.ts @@ -9,7 +9,7 @@ import * as path from 'vs/base/common/path'; import * as fs from 'fs'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { testFile } from 'vs/base/test/node/utils'; From 2dad275dea941a7fbe6172d59c5e45cf5b63492d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 18 Feb 2020 09:04:36 +0100 Subject: [PATCH 186/192] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3d07dd60a28..030a080b60b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "6bef94c398a2784bb9398afda4f5069d937c4bd5", + "distro": "956ab09303fe1ee416e492809a524357a6cc14a0", "author": { "name": "Microsoft Corporation" }, @@ -174,4 +174,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 12a674acf0a4b4c8de7acfe0515b14d2e10ae68c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 18 Feb 2020 09:26:16 +0100 Subject: [PATCH 187/192] Fix bad unit tests --- .../api/browser/mainThreadLanguageFeatures.ts | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index a5af7ded508..f51e0aaedfb 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -37,31 +37,33 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures); this._modeService = modeService; - const updateAllWordDefinitions = () => { - const langWordPairs = LanguageConfigurationRegistry.getWordDefinitions(); - let wordDefinitionDtos: ILanguageWordDefinitionDto[] = []; - for (const [languageId, wordDefinition] of langWordPairs) { - const language = this._modeService.getLanguageIdentifier(languageId); - if (!language) { - continue; + if (this._modeService) { + const updateAllWordDefinitions = () => { + const langWordPairs = LanguageConfigurationRegistry.getWordDefinitions(); + let wordDefinitionDtos: ILanguageWordDefinitionDto[] = []; + for (const [languageId, wordDefinition] of langWordPairs) { + const language = this._modeService.getLanguageIdentifier(languageId); + if (!language) { + continue; + } + wordDefinitionDtos.push({ + languageId: language.language, + regexSource: wordDefinition.source, + regexFlags: wordDefinition.flags + }); } - wordDefinitionDtos.push({ - languageId: language.language, + this._proxy.$setWordDefinitions(wordDefinitionDtos); + }; + LanguageConfigurationRegistry.onDidChange((e) => { + const wordDefinition = LanguageConfigurationRegistry.getWordDefinition(e.languageIdentifier.id); + this._proxy.$setWordDefinitions([{ + languageId: e.languageIdentifier.language, regexSource: wordDefinition.source, regexFlags: wordDefinition.flags - }); - } - this._proxy.$setWordDefinitions(wordDefinitionDtos); - }; - LanguageConfigurationRegistry.onDidChange((e) => { - const wordDefinition = LanguageConfigurationRegistry.getWordDefinition(e.languageIdentifier.id); - this._proxy.$setWordDefinitions([{ - languageId: e.languageIdentifier.language, - regexSource: wordDefinition.source, - regexFlags: wordDefinition.flags - }]); - }); - updateAllWordDefinitions(); + }]); + }); + updateAllWordDefinitions(); + } } dispose(): void { From a6786e8b37a5523af9730c41d2ee660d8101d7dc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 18 Feb 2020 09:34:21 +0100 Subject: [PATCH 188/192] #90076 - Adopt to new v1 URLs - Add end to ened sync tests --- .../userDataSync/common/userDataSync.ts | 20 +- .../common/userDataSyncService.ts | 2 +- .../common/userDataSyncStoreService.ts | 9 +- .../test/common/keybindingsMerge.test.ts | 27 +- .../test/common/userDataSyncClient.ts | 239 +++++++++++ .../test/common/userDataSyncService.test.ts | 374 ++++++++++++++++++ 6 files changed, 632 insertions(+), 39 deletions(-) create mode 100644 src/vs/platform/userDataSync/test/common/userDataSyncClient.ts create mode 100644 src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 585a64b6377..3fcdefefdf3 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, joinPath } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; @@ -120,23 +120,27 @@ export interface IUserData { } export interface IUserDataSyncStore { - url: string; + url: URI; authenticationProviderId: string; } export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined { - const value = configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY); - return value && value.url && value.authenticationProviderId ? value : undefined; + const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY); + if (value && value.url && value.authenticationProviderId) { + return { + url: joinPath(URI.parse(value.url), 'v1'), + authenticationProviderId: value.authenticationProviderId + }; + } + return undefined; } export const ALL_RESOURCE_KEYS: ResourceKey[] = ['settings', 'keybindings', 'extensions', 'globalState']; export type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState'; export interface IUserDataManifest { - settings: string; - keybindings: string; - extensions: string; - globalState: string; + latest?: Record + session: string; } export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index adede4eb82d..920d5b44bc9 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -106,7 +106,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ for (const synchroniser of this.synchronisers) { try { - await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined); + await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined); } catch (e) { this.handleSyncError(e, synchroniser.source); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 5d17b1de189..5943d11c42c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -6,7 +6,6 @@ import { Disposable, } from 'vs/base/common/lifecycle'; import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, IUserDataAuthTokenService, SyncSource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync'; import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request'; -import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; @@ -33,7 +32,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString(); + const url = joinPath(this.userDataSyncStore.url, 'resource', key, 'latest').toString(); const headers: IHeaders = {}; // Disable caching as they are cached by synchronisers headers['Cache-Control'] = 'no-cache'; @@ -65,7 +64,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key).toString(); + const url = joinPath(this.userDataSyncStore.url, 'resource', key).toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; if (ref) { headers['If-Match'] = ref; @@ -89,7 +88,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', 'latest').toString(); + const url = joinPath(this.userDataSyncStore.url, 'manifest').toString(); const headers: IHeaders = { 'Content-Type': 'application/json' }; const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None); @@ -105,7 +104,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn throw new Error('No settings sync store url configured.'); } - const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource').toString(); + const url = joinPath(this.userDataSyncStore.url, 'resource').toString(); const headers: IHeaders = { 'Content-Type': 'text/plain' }; const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None); diff --git a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts index 3780af3ab57..24475726d8f 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts @@ -5,11 +5,7 @@ import * as assert from 'assert'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; -import { IStringDictionary } from 'vs/base/common/collections'; -import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -import { FormattingOptions } from 'vs/base/common/jsonFormatter'; -import { URI } from 'vs/base/common/uri'; -import type { IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { TestUserDataSyncUtilService } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; suite('KeybindingsMerge - No Conflicts', () => { @@ -613,7 +609,7 @@ suite('KeybindingsMerge - No Conflicts', () => { }); async function mergeKeybindings(localContent: string, remoteContent: string, baseContent: string | null) { - const userDataSyncUtilService = new MockUserDataSyncUtilService(); + const userDataSyncUtilService = new TestUserDataSyncUtilService(); const formattingOptions = await userDataSyncUtilService.resolveFormattingOptions(); return merge(localContent, remoteContent, baseContent, formattingOptions, userDataSyncUtilService); } @@ -621,22 +617,3 @@ async function mergeKeybindings(localContent: string, remoteContent: string, bas function stringify(value: any): string { return JSON.stringify(value, null, '\t'); } - -class MockUserDataSyncUtilService implements IUserDataSyncUtilService { - - _serviceBrand: any; - - async resolveUserBindings(userbindings: string[]): Promise> { - const keys: IStringDictionary = {}; - for (const keybinding of userbindings) { - keys[keybinding] = keybinding; - } - return keys; - } - - async resolveFormattingOptions(file?: URI): Promise { - return { eol: '\n', insertSpaces: false, tabSize: 4 }; - } - - async ignoreExtensionsToSync(extensions: IExtensionIdentifier[]): Promise { } -} diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts new file mode 100644 index 00000000000..baf1c1710c1 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataAuthTokenService, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { relative } from 'vs/base/common/path'; +import { bufferToStream, VSBuffer } from 'vs/base/common/buffer'; +import { generateUuid } from 'vs/base/common/uuid'; +import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { NullLogService, ILogService } from 'vs/platform/log/common/log'; +import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IFileService } from 'vs/platform/files/common/files'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { Schemas } from 'vs/base/common/network'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; +import { URI } from 'vs/base/common/uri'; +import { joinPath } from 'vs/base/common/resources'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; +import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; +import { IGlobalExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, DidInstallExtensionEvent, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; +import { Emitter } from 'vs/base/common/event'; + +export class UserDataSyncClient extends Disposable { + + readonly instantiationService: TestInstantiationService; + + constructor(readonly testServer: UserDataSyncTestServer = new UserDataSyncTestServer()) { + super(); + this.instantiationService = new TestInstantiationService(); + } + + async setUp(): Promise { + const userDataDirectory = URI.file('userdata').with({ scheme: Schemas.inMemory }); + const userDataSyncHome = joinPath(userDataDirectory, '.sync'); + const environmentService = this.instantiationService.stub(IEnvironmentService, >{ + userDataSyncHome, + settingsResource: joinPath(userDataDirectory, 'settings.json'), + settingsSyncPreviewResource: joinPath(userDataSyncHome, 'settings.json'), + keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'), + keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'), + argvResource: joinPath(userDataDirectory, 'argv.json'), + }); + + const logService = new NullLogService(); + this.instantiationService.stub(ILogService, logService); + + const fileService = this._register(new FileService(logService)); + fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); + this.instantiationService.stub(IFileService, fileService); + + this.instantiationService.stub(IStorageService, new InMemoryStorageService()); + + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ + 'configurationSync.store': { + url: this.testServer.url, + authenticationProviderId: 'test' + } + }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([]))); + + const configurationService = new ConfigurationService(environmentService.settingsResource, fileService); + await configurationService.initialize(); + this.instantiationService.stub(IConfigurationService, configurationService); + + this.instantiationService.stub(IRequestService, this.testServer); + this.instantiationService.stub(IUserDataAuthTokenService, >{ + onDidChangeToken: new Emitter().event, + async getToken() { return 'token'; } + }); + + this.instantiationService.stub(IUserDataSyncLogService, logService); + this.instantiationService.stub(ITelemetryService, NullTelemetryService); + this.instantiationService.stub(IUserDataSyncStoreService, this.instantiationService.createInstance(UserDataSyncStoreService)); + this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService()); + this.instantiationService.stub(IUserDataSyncEnablementService, this.instantiationService.createInstance(UserDataSyncEnablementService)); + + this.instantiationService.stub(IGlobalExtensionEnablementService, this.instantiationService.createInstance(GlobalExtensionEnablementService)); + this.instantiationService.stub(IExtensionManagementService, >{ + async getInstalled() { return []; }, + onDidInstallExtension: new Emitter().event, + onDidUninstallExtension: new Emitter().event, + }); + this.instantiationService.stub(IExtensionGalleryService, >{ + isEnabled() { return true; }, + async getCompatibleExtension() { return null; } + }); + + this.instantiationService.stub(ISettingsSyncService, this.instantiationService.createInstance(SettingsSynchroniser)); + this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService)); + } + +} + +export class UserDataSyncTestServer implements IRequestService { + + _serviceBrand: any; + + readonly url: string = 'http://host:3000'; + private session: string | null = null; + private readonly data: Map = new Map(); + + private _requests: { url: string, type: string, headers?: IHeaders }[] = []; + get requests(): { url: string, type: string, headers?: IHeaders }[] { return this._requests; } + + private _responses: { status: number }[] = []; + get responses(): { status: number }[] { return this._responses; } + reset(): void { this._requests = []; this._responses = []; } + + async resolveProxy(url: string): Promise { return url; } + + async request(options: IRequestOptions, token: CancellationToken): Promise { + const headers: IHeaders = {}; + if (options.headers) { + if (options.headers['If-None-Match']) { + headers['If-None-Match'] = options.headers['If-None-Match']; + } + if (options.headers['If-Match']) { + headers['If-Match'] = options.headers['If-Match']; + } + } + this._requests.push({ url: options.url!, type: options.type!, headers }); + const requestContext = await this.doRequest(options); + this._responses.push({ status: requestContext.res.statusCode! }); + return requestContext; + } + + private async doRequest(options: IRequestOptions): Promise { + const relativePath = relative(`${this.url}/v1/`, options.url!); + const segments = relativePath ? relativePath.split('/') : []; + if (options.type === 'GET' && segments.length === 1 && segments[0] === 'manifest') { + return this.getManifest(options.headers); + } + if (options.type === 'GET' && segments.length === 3 && segments[0] === 'resource' && segments[2] === 'latest') { + return this.getLatestData(segments[1], options.headers); + } + if (options.type === 'POST' && segments.length === 2 && segments[0] === 'resource') { + return this.writeData(segments[1], options.data, options.headers); + } + if (options.type === 'DELETE' && segments.length === 1 && segments[0] === 'resource') { + return this.clear(options.headers); + } + return this.toResponse(501); + } + + private async getManifest(headers?: IHeaders): Promise { + if (this.session) { + const latest: Record = Object.create({}); + const manifest: IUserDataManifest = { session: this.session, latest }; + this.data.forEach((value, key) => latest[key] = value.ref); + return this.toResponse(200, { 'Content-Type': 'application/json' }, JSON.stringify(manifest)); + } + return this.toResponse(204); + } + + private async getLatestData(resource: string, headers: IHeaders = {}): Promise { + const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource); + if (resourceKey) { + const data = this.data.get(resourceKey); + if (!data) { + return this.toResponse(204, { etag: '0' }); + } + if (headers['If-None-Match'] === data.ref) { + return this.toResponse(304); + } + return this.toResponse(200, { etag: data.ref }, data.content || ''); + } + return this.toResponse(204); + } + + private async writeData(resource: string, content: string = '', headers: IHeaders = {}): Promise { + if (!headers['If-Match']) { + return this.toResponse(428); + } + if (!this.session) { + this.session = generateUuid(); + } + const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource); + if (resourceKey) { + const data = this.data.get(resourceKey); + if (headers['If-Match'] !== (data ? data.ref : '0')) { + return this.toResponse(412); + } + const ref = `${parseInt(data?.ref || '0') + 1}`; + this.data.set(resourceKey, { ref, content }); + return this.toResponse(200, { etag: ref }); + } + return this.toResponse(204); + } + + private async clear(headers?: IHeaders): Promise { + this.data.clear(); + this.session = null; + return this.toResponse(204); + } + + private toResponse(statusCode: number, headers?: IHeaders, data?: string): IRequestContext { + return { + res: { + headers: headers || {}, + statusCode + }, + stream: bufferToStream(VSBuffer.fromString(data || '')) + }; + } +} + +export class TestUserDataSyncUtilService implements IUserDataSyncUtilService { + + _serviceBrand: any; + + async resolveUserBindings(userbindings: string[]): Promise> { + const keys: IStringDictionary = {}; + for (const keybinding of userbindings) { + keys[keybinding] = keybinding; + } + return keys; + } + + async resolveFormattingOptions(file?: URI): Promise { + return { eol: '\n', insertSpaces: false, tabSize: 4 }; + } + +} + diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts new file mode 100644 index 00000000000..d2c35f09d00 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IUserDataSyncService, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { VSBuffer } from 'vs/base/common/buffer'; + +suite('UserDataSyncService', () => { + + const disposableStore = new DisposableStore(); + + teardown(() => disposableStore.clear()); + + test('test first time sync ever', async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncService); + + // Sync for first time + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } } + ]); + + }); + + test('test first time sync from the client with no changes - pull', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the first client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Setup the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + + // Sync (pull) from the test client + target.reset(); + await testObject.isFirstTimeSyncWithMerge(); + await testObject.pull(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + ]); + + }); + + test('test first time sync from the client with changes - pull', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the first client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Setup the test client with changes + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + const fileService = testClient.instantiationService.get(IFileService); + const environmentService = testClient.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + + // Sync (pull) from the test client + target.reset(); + await testObject.isFirstTimeSyncWithMerge(); + await testObject.pull(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + ]); + + }); + + test('test first time sync from the client with no changes - merge', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the first client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Setup the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + + // Sync (merge) from the test client + target.reset(); + await testObject.isFirstTimeSyncWithMerge(); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + ]); + + }); + + test('test first time sync from the client with changes - merge', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the first client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Setup the test client with changes + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const fileService = testClient.instantiationService.get(IFileService); + const environmentService = testClient.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + + // Sync (merge) from the test client + target.reset(); + await testObject.isFirstTimeSyncWithMerge(); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + ]); + + }); + + test('test sync when there are no changes', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + + // sync from the client again + target.reset(); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + ]); + }); + + test('test sync when there are local changes', async () => { + const target = new UserDataSyncTestServer(); + + // Setup and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject = client.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + target.reset(); + + // Do changes in the client + const fileService = client.instantiationService.get(IFileService); + const environmentService = client.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + + // Sync from the client + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, + // Keybindings + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } }, + // Global state + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + ]); + }); + + test('test sync when there are remote changes', async () => { + const target = new UserDataSyncTestServer(); + + // Sync from first client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Sync from test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + + // Do changes in first client and sync + const fileService = client.instantiationService.get(IFileService); + const environmentService = client.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Sync from test client + target.reset(); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: { 'If-None-Match': '1' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: { 'If-None-Match': '1' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } }, + ]); + + }); + + test('test delete', async () => { + const target = new UserDataSyncTestServer(); + + // Sync from the client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + + // Reset from the client + target.reset(); + await testObject.reset(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'DELETE', url: `${target.url}/v1/resource`, headers: {} }, + ]); + + }); + + test('test delete and sync', async () => { + const target = new UserDataSyncTestServer(); + + // Sync from the client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + + // Reset from the client + await testObject.reset(); + + // Sync again + target.reset(); + await testObject.sync(); + + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } } + ]); + + }); + + test('test delete on one client throws error on other client while syncing', async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await client.instantiationService.get(IUserDataSyncService).sync(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject = testClient.instantiationService.get(IUserDataSyncService); + await testObject.sync(); + + // Reset from the first client + await client.instantiationService.get(IUserDataSyncService).reset(); + + // Sync from the test client + target.reset(); + try { + await testObject.sync(); + } catch (e) { + assert.ok(e instanceof UserDataSyncError); + assert.deepEqual((e).code, UserDataSyncErrorCode.TurnedOff); + assert.deepEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + ]); + return; + } + throw assert.fail('Should fail with turned off error'); + }); + +}); From 97142839814e043bde4b2e15732f27968066d197 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 18 Feb 2020 09:42:24 +0100 Subject: [PATCH 189/192] Attempt to fix debug test failures --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 556180f6144..8af3f1ee82f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -88,10 +88,12 @@ export class DebugSession implements IDebugSession { const toDispose: IDisposable[] = []; toDispose.push(this.repl.onDidChangeElements(() => this._onDidChangeREPLElements.fire())); - toDispose.push(lifecycleService.onShutdown(() => { - this.shutdown(); - dispose(toDispose); - })); + if (lifecycleService) { + toDispose.push(lifecycleService.onShutdown(() => { + this.shutdown(); + dispose(toDispose); + })); + } } getId(): string { From 8451f8bd913914aac17b6b3273b6ed0db137a217 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 18 Feb 2020 09:47:00 +0100 Subject: [PATCH 190/192] fix standalone editor compilation --- src/vs/platform/files/common/inMemoryFilesystemProvider.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/platform/files/common/inMemoryFilesystemProvider.ts b/src/vs/platform/files/common/inMemoryFilesystemProvider.ts index 847aae72b9a..ad689bb57fc 100644 --- a/src/vs/platform/files/common/inMemoryFilesystemProvider.ts +++ b/src/vs/platform/files/common/inMemoryFilesystemProvider.ts @@ -66,9 +66,7 @@ export class InMemoryFileSystemProvider extends Disposable implements IFileSyste async readdir(resource: URI): Promise<[string, FileType][]> { const entry = this._lookupAsDirectory(resource, false); let result: [string, FileType][] = []; - for (const [name, child] of entry.entries) { - result.push([name, child.type]); - } + entry.entries.forEach((child, name) => result.push([name, child.type])); return result; } From 8ddce0b693e5db162b8fe06529067c4e949cc7ec Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 18 Feb 2020 09:55:31 +0100 Subject: [PATCH 191/192] Fix for strictFunctionTypes (#81574) --- src/vs/base/common/glob.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 78fc5e97ce1..6da220436f6 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -251,14 +251,14 @@ export interface IGlobOptions { } interface ParsedStringPattern { - (path: string, basename: string): string | null | Promise /* the matching pattern */; + (path: string, basename?: string): string | null | Promise /* the matching pattern */; basenames?: string[]; patterns?: string[]; allBasenames?: string[]; allPaths?: string[]; } interface ParsedExpressionPattern { - (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; + (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; requiresSiblings?: boolean; allBasenames?: string[]; allPaths?: string[]; @@ -374,7 +374,7 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern { if (n === 1) { return parsedPatterns[0]; } - const parsedPattern: ParsedStringPattern = function (path: string, basename: string) { + const parsedPattern: ParsedStringPattern = function (path: string, basename?: string) { for (let i = 0, n = parsedPatterns.length; i < n; i++) { if ((parsedPatterns[i])(path, basename)) { return pattern; @@ -409,7 +409,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par function toRegExp(pattern: string): ParsedStringPattern { try { const regExp = new RegExp(`^${parseRegExp(pattern)}$`); - return function (path: string, basename: string) { + return function (path: string) { regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it! return typeof path === 'string' && regExp.test(path) ? pattern : null; }; @@ -457,7 +457,7 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG if (parsedPattern === NULL) { return FALSE; } - const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename: string) { + const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename?: string) { return !!parsedPattern(path, basename); }; if (parsedPattern.allBasenames) { @@ -540,7 +540,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return parsedPatterns[0]; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string) { + const resultExpression: ParsedStringPattern = function (path: string, basename?: string) { for (let i = 0, n = parsedPatterns.length; i < n; i++) { // Pattern matches path const result = (parsedPatterns[i])(path, basename); @@ -565,7 +565,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise) { + const resultExpression: ParsedStringPattern = function (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise) { let name: string | undefined = undefined; for (let i = 0, n = parsedPatterns.length; i < n; i++) { @@ -620,12 +620,12 @@ function parseExpressionPattern(pattern: string, value: boolean | SiblingClause, if (value) { const when = (value).when; if (typeof when === 'string') { - const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise) => { + const result: ParsedExpressionPattern = (path: string, basename?: string, name?: string, hasSibling?: (name: string) => boolean | Promise) => { if (!hasSibling || !parsedPattern(path, basename)) { return null; } - const clausePattern = when.replace('$(basename)', name); + const clausePattern = when.replace('$(basename)', name!); const matched = hasSibling(clausePattern); return isThenable(matched) ? matched.then(m => m ? pattern : null) : From 2f06f00edcc2c92bf1dbc57270de14b28d27066b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 18 Feb 2020 10:01:21 +0100 Subject: [PATCH 192/192] Fixes #90785 --- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 16f5e3bca7a..0adf5382b3b 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -194,7 +194,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } catch (err) { // TODO: write to log once we have one } - await allPromises; + await Promise.all(allPromises); } public isActivated(extensionId: ExtensionIdentifier): boolean {