From 587ebc5d2ee5e56a7949aa305603f54940dd48f9 Mon Sep 17 00:00:00 2001 From: susiwen8 Date: Fri, 5 Feb 2021 18:46:54 +0800 Subject: [PATCH 001/176] Fix: wrong event for `onDidSaveNotebookDocument` --- src/vs/workbench/api/common/extHostNotebook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 49f35f11f9d..daa276d8c86 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -255,7 +255,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private _onDidCloseNotebookDocument = new Emitter(); onDidCloseNotebookDocument: Event = this._onDidCloseNotebookDocument.event; private _onDidSaveNotebookDocument = new Emitter(); - onDidSaveNotebookDocument: Event = this._onDidCloseNotebookDocument.event; + onDidSaveNotebookDocument: Event = this._onDidSaveNotebookDocument.event; visibleNotebookEditors: ExtHostNotebookEditor[] = []; private _onDidChangeActiveNotebookKernel = new Emitter<{ document: vscode.NotebookDocument, kernel: vscode.NotebookKernel | undefined; }>(); onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event; From 6d9611747ea032060ec3733bc295e01a6f4b0ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 12 Feb 2021 11:01:57 +0100 Subject: [PATCH 002/176] wip: update list ux - focus is now outline - expanding folders on twistie click only - clicking twistie now sets focus - list empty selection outline matches focus outline --- src/vs/base/browser/ui/list/listWidget.ts | 7 +++- src/vs/base/browser/ui/tree/abstractTree.ts | 3 +- src/vs/base/browser/ui/tree/media/tree.css | 4 ++ src/vs/platform/list/browser/listService.ts | 2 +- src/vs/platform/theme/common/colorRegistry.ts | 5 ++- src/vs/platform/theme/common/styler.ts | 39 ++++++++++--------- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 81caae42707..398ace4b213 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -776,7 +776,11 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); + } + + if (styles.listTwistieHoverBackground) { + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-tl-twistie:hover::before { background-color: ${styles.listTwistieHoverBackground}; }`); } if (styles.listHoverForeground) { @@ -870,6 +874,7 @@ export interface IListStyles { listInactiveFocusBackground?: Color; listHoverBackground?: Color; listHoverForeground?: Color; + listTwistieHoverBackground?: Color; listDropBackground?: Color; listFocusOutline?: Color; listInactiveFocusOutline?: Color; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 4df19745cfe..3e135b3c8a8 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1129,6 +1129,7 @@ class TreeNodeListMouseController extends MouseController< const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); const recursive = e.browserEvent.altKey; + this.tree.setFocus([location]); model.setCollapsed(location, undefined, recursive); if (expandOnlyOnTwistieClick && onTwistie) { @@ -1263,7 +1264,7 @@ export abstract class AbstractTree implements IDisposable get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; } - get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } + get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); readonly onDidUpdateOptions: Event> = this._onDidUpdateOptions.event; diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 06f775d6246..2701b559d5e 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -56,6 +56,10 @@ overflow: hidden; } +.monaco-tl-twistie::before { + border-radius: 20px; +} + .monaco-tl-twistie.collapsed::before { transform: rotate(-90deg); } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 1d515e5bf6a..127f5ad81bf 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1065,7 +1065,7 @@ configurationRegistry.registerConfiguration({ [treeExpandMode]: { type: 'string', enum: ['singleClick', 'doubleClick'], - default: 'singleClick', + default: 'doubleClick', description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names."), } } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 1256437a0d7..8ed02eb2756 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -362,15 +362,18 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: /** * List and tree colors */ -export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listFocusBackground = registerColor('list.focusBackground', { dark: null, light: null, hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listFocusOutline = registerColor('list.focusOutline', { dark: focusBorder, light: focusBorder, hc: activeContrastBorder }, nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusOutline', "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); +export const listTwistieHoverBackground = registerColor('list.twistieHoverBackground', { dark: transparent(lighten(listHoverBackground, 0.5), 0.7), light: transparent(darken(listHoverBackground, 0.1), 0.7), hc: null }, nls.localize('twistieHoverBackground', "List/Tree background when hovering over a twistie using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#0097fb', light: '#0066BF', hc: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hc: '#B89500' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 90bb201972c..68d7d175e40 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listTwistieHoverBackground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { IThemable, styleFn } from 'vs/base/common/styler'; @@ -210,6 +210,7 @@ export interface IListStyleOverrides extends IStyleOverrides { listBackground?: ColorIdentifier; listFocusBackground?: ColorIdentifier; listFocusForeground?: ColorIdentifier; + listFocusOutline?: ColorIdentifier; listActiveSelectionBackground?: ColorIdentifier; listActiveSelectionForeground?: ColorIdentifier; listFocusAndSelectionBackground?: ColorIdentifier; @@ -217,11 +218,11 @@ export interface IListStyleOverrides extends IStyleOverrides { listInactiveSelectionBackground?: ColorIdentifier; listInactiveSelectionForeground?: ColorIdentifier; listInactiveFocusBackground?: ColorIdentifier; + listInactiveFocusOutline?: ColorIdentifier; listHoverBackground?: ColorIdentifier; listHoverForeground?: ColorIdentifier; + listTwistieHoverBackground?: ColorIdentifier; listDropBackground?: ColorIdentifier; - listFocusOutline?: ColorIdentifier; - listInactiveFocusOutline?: ColorIdentifier; listSelectionOutline?: ColorIdentifier; listHoverOutline?: ColorIdentifier; listFilterWidgetBackground?: ColorIdentifier; @@ -236,26 +237,28 @@ export function attachListStyler(widget: IThemable, themeService: IThemeService, } export const defaultListStyles: IColorMapping = { - listFocusBackground: listFocusBackground, - listFocusForeground: listFocusForeground, - listActiveSelectionBackground: darken(listActiveSelectionBackground, 0.1), - listActiveSelectionForeground: listActiveSelectionForeground, + listFocusBackground, + listFocusForeground, + listFocusOutline, + listActiveSelectionBackground, + listActiveSelectionForeground, listFocusAndSelectionBackground: listActiveSelectionBackground, listFocusAndSelectionForeground: listActiveSelectionForeground, - listInactiveSelectionBackground: listInactiveSelectionBackground, - listInactiveSelectionForeground: listInactiveSelectionForeground, - listInactiveFocusBackground: listInactiveFocusBackground, - listHoverBackground: listHoverBackground, - listHoverForeground: listHoverForeground, - listDropBackground: listDropBackground, - listFocusOutline: activeContrastBorder, + listInactiveSelectionBackground, + listInactiveSelectionForeground, + listInactiveFocusBackground, + listInactiveFocusOutline, + listHoverBackground, + listHoverForeground, + listTwistieHoverBackground, + listDropBackground, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, - listFilterWidgetBackground: listFilterWidgetBackground, - listFilterWidgetOutline: listFilterWidgetOutline, - listFilterWidgetNoMatchesOutline: listFilterWidgetNoMatchesOutline, + listFilterWidgetBackground, + listFilterWidgetOutline, + listFilterWidgetNoMatchesOutline, listMatchesShadow: widgetShadow, - treeIndentGuidesStroke: treeIndentGuidesStroke + treeIndentGuidesStroke }; export interface IButtonStyleOverrides extends IStyleOverrides { From 13d51e7c21e42cf03638e1b2075443f595ca4b8e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 12 Feb 2021 15:34:11 +0100 Subject: [PATCH 003/176] actionBar: use up/down as well to move focus --- src/vs/base/browser/ui/actionbar/actionbar.ts | 18 +++++++++--------- src/vs/base/browser/ui/menu/menu.ts | 1 + .../parts/activitybar/activitybarPart.ts | 3 +-- src/vs/workbench/browser/parts/compositeBar.ts | 1 - 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 4ba146296b9..45afd63c40a 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -35,7 +35,7 @@ export interface IActionBarOptions { readonly triggerKeys?: ActionTrigger; readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; - readonly ignoreOrientationForPreviousAndNextKey?: boolean; + readonly respectOrientationForPreviousAndNextKey?: boolean; } export interface IActionOptions extends IActionViewItemOptions { @@ -119,22 +119,22 @@ export class ActionBar extends Disposable implements IActionRunner { switch (this._orientation) { case ActionsOrientation.HORIZONTAL: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; + previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; + nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; break; case ActionsOrientation.HORIZONTAL_REVERSE: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow]; + previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; + nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; this.domNode.className += ' reverse'; break; case ActionsOrientation.VERTICAL: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; + previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; + nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; this.domNode.className += ' vertical'; break; case ActionsOrientation.VERTICAL_REVERSE: - previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow]; - nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow]; + previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; + nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; this.domNode.className += ' vertical reverse'; break; } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 947ba2708b0..b94c9c99ac2 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -86,6 +86,7 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, + respectOrientationForPreviousAndNextKey: true, triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true } }); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index ba37e0b668f..f9ee130df8c 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -534,8 +534,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { orientation: ActionsOrientation.VERTICAL, ariaLabel: localize('manage', "Manage"), animated: false, - preventLoopNavigation: true, - ignoreOrientationForPreviousAndNextKey: true + preventLoopNavigation: true })); this.globalActivityAction = this._register(new ActivityAction({ diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 70d629fb2c0..94fccfcb646 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -227,7 +227,6 @@ export class CompositeBar extends Widget implements ICompositeBar { ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), animated: false, preventLoopNavigation: this.options.preventLoopNavigation, - ignoreOrientationForPreviousAndNextKey: true, triggerKeys: { keyDown: true } })); From 6a698d7d435466f13dff1b9852e95954a91c87f4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 12 Feb 2021 15:42:52 +0100 Subject: [PATCH 004/176] tabs - no need to lookup editor index fyi @isidorn --- .../browser/parts/editor/tabsTitleControl.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 7b657de07f4..850294ee6a0 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -509,8 +509,8 @@ export class TabsTitleControl extends TitleControl { setActive(isGroupActive: boolean): void { // Activity has an impact on each tab's active indication - this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel) => { - this.redrawTabActiveAndDirty(isGroupActive, editor, tabContainer, tabLabelWidget); + this.forEachTab((editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => { + this.redrawTabActiveAndDirty(isGroupActive, editor, tabContainer, tabActionBar); }); // Activity has an impact on the toolbar, so we need to update and layout @@ -545,7 +545,7 @@ export class TabsTitleControl extends TitleControl { } updateEditorDirty(editor: IEditorInput): void { - this.withTab(editor, (editor, index, tabContainer, tabLabelWidget) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget)); + this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabActionBar)); } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { @@ -1119,7 +1119,7 @@ export class TabsTitleControl extends TitleControl { this.redrawTabBorders(index, tabContainer); // Active / dirty state - this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabLabelWidget); + this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabActionBar); } private redrawTabLabel(editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel): void { @@ -1177,26 +1177,23 @@ export class TabsTitleControl extends TitleControl { } } - private redrawTabActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { + private redrawTabActiveAndDirty(isGroupActive: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabActionBar: ActionBar): void { const isTabActive = this.group.isActive(editor); const hasModifiedBorderTop = this.doRedrawTabDirty(isGroupActive, isTabActive, editor, tabContainer); - this.doRedrawTabActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabLabelWidget); + this.doRedrawTabActive(isGroupActive, !hasModifiedBorderTop, editor, tabContainer, tabActionBar); } - private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel): void { - const index = this.group.getIndexOfEditor(editor); - const actionBar = this.tabActionBars[index]; + private doRedrawTabActive(isGroupActive: boolean, allowBorderTop: boolean, editor: IEditorInput, tabContainer: HTMLElement, tabActionBar: ActionBar): void { + // Tab is active if (this.group.isActive(editor)) { // Container tabContainer.classList.add('active'); tabContainer.setAttribute('aria-selected', 'true'); - // Only active tab can be focused into - tabContainer.tabIndex = 0; - actionBar.setFocusable(true); + tabContainer.tabIndex = 0; // Only active tab can be focused into tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND) || ''; const activeTabBorderColorBottom = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER); @@ -1219,6 +1216,9 @@ export class TabsTitleControl extends TitleControl { // Label tabContainer.style.color = this.getColor(isGroupActive ? TAB_ACTIVE_FOREGROUND : TAB_UNFOCUSED_ACTIVE_FOREGROUND) || ''; + + // Actions + tabActionBar.setFocusable(true); } // Tab is inactive @@ -1227,13 +1227,15 @@ export class TabsTitleControl extends TitleControl { // Container tabContainer.classList.remove('active'); tabContainer.setAttribute('aria-selected', 'false'); - tabContainer.tabIndex = -1; - actionBar.setFocusable(false); + tabContainer.tabIndex = -1; // Only active tab can be focused into tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_INACTIVE_BACKGROUND : TAB_UNFOCUSED_INACTIVE_BACKGROUND) || ''; tabContainer.style.boxShadow = ''; // Label tabContainer.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND) || ''; + + // Actions + tabActionBar.setFocusable(false); } } From 41339cc2a0844f97315a746b89022d7b51477432 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 12 Feb 2021 15:49:15 +0100 Subject: [PATCH 005/176] Make user data path a AMD/CommonJS compatible thing (#116530) * debt - move paths.js into base * add a test * get rid of @ts-ignore * trigger a change * isolate scripts with self invoking function --- build/gulpfile.vscode.js | 2 +- src/main.js | 12 +- src/paths.js | 48 -- src/vs/base/node/languagePacks.js | 453 +++++++++--------- src/vs/base/node/userDataPath.d.ts | 9 + src/vs/base/node/userDataPath.js | 72 +++ .../node/userDataPath.test.ts} | 12 +- .../sharedProcess/sharedProcess.js | 5 +- .../electron-browser/workbench/workbench.js | 5 +- .../electron-sandbox/issue/issueReporter.js | 4 +- .../processExplorer/processExplorer.js | 4 +- .../electron-sandbox/workbench/workbench.js | 6 +- .../environment/node/environmentService.ts | 68 +-- .../platform/product/common/productService.ts | 2 +- 14 files changed, 369 insertions(+), 333 deletions(-) delete mode 100644 src/paths.js create mode 100644 src/vs/base/node/userDataPath.d.ts create mode 100644 src/vs/base/node/userDataPath.js rename src/vs/base/{node/paths.ts => test/node/userDataPath.test.ts} (56%) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 6d3a369082a..33af8a45375 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -52,13 +52,13 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', - 'out-build/paths.js', 'out-build/vs/**/*.{svg,png,html,jpg}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', + 'out-build/vs/base/node/userDataPath.js', 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/workbench/browser/media/*-theme.css', diff --git a/src/main.js b/src/main.js index 0541fb4c4a0..2584223e1d6 100644 --- a/src/main.js +++ b/src/main.js @@ -9,14 +9,14 @@ const perf = require('./vs/base/common/performance'); perf.mark('code/didStartMain'); -const lp = require('./vs/base/node/languagePacks'); const path = require('path'); const fs = require('fs'); const os = require('os'); +const { getNLSConfiguration } = require('./vs/base/node/languagePacks'); const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); -const paths = require('./paths'); -/** @type {Partial & { applicationName: string}} */ +const { getDefaultUserDataPath } = require('./vs/base/node/userDataPath'); +/** @type {Partial} */ const product = require('../product.json'); const { app, protocol, crashReporter } = require('electron'); @@ -84,7 +84,7 @@ let nlsConfigurationPromise = undefined; const metaDataFile = path.join(__dirname, 'nls.metadata.json'); const locale = getUserDefinedLocale(argvConfig); if (locale) { - nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); + nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); } // Load our code once ready @@ -405,7 +405,7 @@ function getUserDataPath(cliArgs) { return path.join(portable.portableDataPath, 'user-data'); } - return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath()); + return path.resolve(cliArgs['user-data-dir'] || getDefaultUserDataPath()); } /** @@ -560,7 +560,7 @@ async function resolveNlsConfiguration() { // See above the comment about the loader and case sensitiviness appLocale = appLocale.toLowerCase(); - nlsConfiguration = await lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale); + nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale); if (!nlsConfiguration) { nlsConfiguration = { locale: appLocale, availableLanguages: {} }; } diff --git a/src/paths.js b/src/paths.js deleted file mode 100644 index 2042123d726..00000000000 --- a/src/paths.js +++ /dev/null @@ -1,48 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -const pkg = require('../package.json'); -const path = require('path'); -const os = require('os'); - -/** - * @returns {string} - */ -function getDefaultUserDataPath() { - - // Support global VSCODE_APPDATA environment variable - let appDataPath = process.env['VSCODE_APPDATA']; - - // Otherwise check per platform - if (!appDataPath) { - switch (process.platform) { - case 'win32': - appDataPath = process.env['APPDATA']; - if (!appDataPath) { - const userProfile = process.env['USERPROFILE']; - if (typeof userProfile !== 'string') { - throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); - } - appDataPath = path.join(userProfile, 'AppData', 'Roaming'); - } - break; - case 'darwin': - appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); - break; - case 'linux': - appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); - break; - default: - throw new Error('Platform not supported'); - } - } - - return path.join(appDataPath, pkg.name); -} - -exports.getDefaultUserDataPath = getDefaultUserDataPath; diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js index f47191157c1..5c14fade878 100644 --- a/src/vs/base/node/languagePacks.js +++ b/src/vs/base/node/languagePacks.js @@ -3,256 +3,257 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check -'use strict'; - -/** - * @param {NodeRequire} nodeRequire - * @param {typeof import('path')} path - * @param {typeof import('fs')} fs - * @param {typeof import('../common/performance')} perf - */ -function factory(nodeRequire, path, fs, perf) { +(function () { + 'use strict'; /** - * @param {string} file - * @returns {Promise} + * @param {NodeRequire} nodeRequire + * @param {typeof import('path')} path + * @param {typeof import('fs')} fs + * @param {typeof import('../common/performance')} perf */ - function exists(file) { - return new Promise(c => fs.exists(file, c)); - } + function factory(nodeRequire, path, fs, perf) { - /** - * @param {string} file - * @returns {Promise} - */ - function touch(file) { - return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); - } - - /** - * @param {string} dir - * @returns {Promise} - */ - function mkdirp(dir) { - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); - } - - /** - * @param {string} location - * @returns {Promise} - */ - function rimraf(location) { - return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c())); - } - - /** - * @param {string} file - * @returns {Promise} - */ - function readFile(file) { - return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); - } - - /** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ - function writeFile(file, content) { - return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); - } - - /** - * @param {string} userDataPath - * @returns {object} - */ - function getLanguagePackConfigurations(userDataPath) { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { - return nodeRequire(configFile); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. + /** + * @param {string} file + * @returns {Promise} + */ + function exists(file) { + return new Promise(c => fs.exists(file, c)); } - return undefined; - } - /** - * @param {object} config - * @param {string} locale - */ - function resolveLanguagePackLocale(config, locale) { - try { - while (locale) { - if (config[locale]) { - return locale; - } else { - const index = locale.lastIndexOf('-'); - if (index > 0) { - locale = locale.substring(0, index); + /** + * @param {string} file + * @returns {Promise} + */ + function touch(file) { + return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function mkdirp(dir) { + return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); + } + + /** + * @param {string} location + * @returns {Promise} + */ + function rimraf(location) { + return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c())); + } + + /** + * @param {string} file + * @returns {Promise} + */ + function readFile(file) { + return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); + } + + /** + * @param {string} file + * @param {string} content + * @returns {Promise} + */ + function writeFile(file, content) { + return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); + } + + /** + * @param {string} userDataPath + * @returns {object} + */ + function getLanguagePackConfigurations(userDataPath) { + const configFile = path.join(userDataPath, 'languagepacks.json'); + try { + return nodeRequire(configFile); + } catch (err) { + // Do nothing. If we can't read the file we have no + // language pack config. + } + return undefined; + } + + /** + * @param {object} config + * @param {string} locale + */ + function resolveLanguagePackLocale(config, locale) { + try { + while (locale) { + if (config[locale]) { + return locale; } else { - return undefined; + const index = locale.lastIndexOf('-'); + if (index > 0) { + locale = locale.substring(0, index); + } else { + return undefined; + } } } + } catch (err) { + console.error('Resolving language pack configuration failed.', err); } - } catch (err) { - console.error('Resolving language pack configuration failed.', err); - } - return undefined; - } - - /** - * @param {string} commit - * @param {string} userDataPath - * @param {string} metaDataFile - * @param {string} locale - */ - function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) { - if (locale === 'pseudo') { - return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); + return undefined; } - if (process.env['VSCODE_DEV']) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - // We have a built version so we have extracted nls file. Try to find - // the right file to use. - - // Check if we have an English or English US locale. If so fall to default since that is our - // English translation (we don't ship *.nls.en.json files) - if (locale && (locale === 'en' || locale === 'en-us')) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - const initialLocale = locale; - - perf.mark('code/willGenerateNls'); - - const defaultResult = function (locale) { - perf.mark('code/didGenerateNls'); - return Promise.resolve({ locale: locale, availableLanguages: {} }); - }; - try { - if (!commit) { - return defaultResult(initialLocale); + /** + * @param {string} commit + * @param {string} userDataPath + * @param {string} metaDataFile + * @param {string} locale + */ + function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) { + if (locale === 'pseudo') { + return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); } - const configs = getLanguagePackConfigurations(userDataPath); - if (!configs) { - return defaultResult(initialLocale); + + if (process.env['VSCODE_DEV']) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); } - locale = resolveLanguagePackLocale(configs, locale); - if (!locale) { - return defaultResult(initialLocale); + + // We have a built version so we have extracted nls file. Try to find + // the right file to use. + + // Check if we have an English or English US locale. If so fall to default since that is our + // English translation (we don't ship *.nls.en.json files) + if (locale && (locale === 'en' || locale === 'en-us')) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); } - const packConfig = configs[locale]; - let mainPack; - if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { - return defaultResult(initialLocale); - } - return exists(mainPack).then(fileExists => { - if (!fileExists) { + + const initialLocale = locale; + + perf.mark('code/willGenerateNls'); + + const defaultResult = function (locale) { + perf.mark('code/didGenerateNls'); + return Promise.resolve({ locale: locale, availableLanguages: {} }); + }; + try { + if (!commit) { return defaultResult(initialLocale); } - const packId = packConfig.hash + '.' + locale; - const cacheRoot = path.join(userDataPath, 'clp', packId); - const coreLocation = path.join(cacheRoot, commit); - const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); - const corruptedFile = path.join(cacheRoot, 'corrupted.info'); - const result = { - locale: initialLocale, - availableLanguages: { '*': locale }, - _languagePackId: packId, - _translationsConfigFile: translationsConfigFile, - _cacheRoot: cacheRoot, - _resolvedLanguagePackCoreLocation: coreLocation, - _corruptedFile: corruptedFile - }; - return exists(corruptedFile).then(corrupted => { - // The nls cache directory is corrupted. - let toDelete; - if (corrupted) { - toDelete = rimraf(cacheRoot); - } else { - toDelete = Promise.resolve(undefined); + const configs = getLanguagePackConfigurations(userDataPath); + if (!configs) { + return defaultResult(initialLocale); + } + locale = resolveLanguagePackLocale(configs, locale); + if (!locale) { + return defaultResult(initialLocale); + } + const packConfig = configs[locale]; + let mainPack; + if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { + return defaultResult(initialLocale); + } + return exists(mainPack).then(fileExists => { + if (!fileExists) { + return defaultResult(initialLocale); } - return toDelete.then(() => { - return exists(coreLocation).then(fileExists => { - if (fileExists) { - // We don't wait for this. No big harm if we can't touch - touch(coreLocation).catch(() => { }); - perf.mark('code/didGenerateNls'); - return result; - } - return mkdirp(coreLocation).then(() => { - return Promise.all([readFile(metaDataFile), readFile(mainPack)]); - }).then(values => { - const metadata = JSON.parse(values[0]); - const packData = JSON.parse(values[1]).contents; - const bundles = Object.keys(metadata.bundles); - const writes = []; - for (const bundle of bundles) { - const modules = metadata.bundles[bundle]; - const target = Object.create(null); - for (const module of modules) { - const keys = metadata.keys[module]; - const defaultMessages = metadata.messages[module]; - const translations = packData[module]; - let targetStrings; - if (translations) { - targetStrings = []; - for (let i = 0; i < keys.length; i++) { - const elem = keys[i]; - const key = typeof elem === 'string' ? elem : elem.key; - let translatedMessage = translations[key]; - if (translatedMessage === undefined) { - translatedMessage = defaultMessages[i]; - } - targetStrings.push(translatedMessage); - } - } else { - targetStrings = defaultMessages; - } - target[module] = targetStrings; - } - writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); + const packId = packConfig.hash + '.' + locale; + const cacheRoot = path.join(userDataPath, 'clp', packId); + const coreLocation = path.join(cacheRoot, commit); + const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); + const corruptedFile = path.join(cacheRoot, 'corrupted.info'); + const result = { + locale: initialLocale, + availableLanguages: { '*': locale }, + _languagePackId: packId, + _translationsConfigFile: translationsConfigFile, + _cacheRoot: cacheRoot, + _resolvedLanguagePackCoreLocation: coreLocation, + _corruptedFile: corruptedFile + }; + return exists(corruptedFile).then(corrupted => { + // The nls cache directory is corrupted. + let toDelete; + if (corrupted) { + toDelete = rimraf(cacheRoot); + } else { + toDelete = Promise.resolve(undefined); + } + return toDelete.then(() => { + return exists(coreLocation).then(fileExists => { + if (fileExists) { + // We don't wait for this. No big harm if we can't touch + touch(coreLocation).catch(() => { }); + perf.mark('code/didGenerateNls'); + return result; } - writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); - return Promise.all(writes); - }).then(() => { - perf.mark('code/didGenerateNls'); - return result; - }).catch(err => { - console.error('Generating translation files failed.', err); - return defaultResult(locale); + return mkdirp(coreLocation).then(() => { + return Promise.all([readFile(metaDataFile), readFile(mainPack)]); + }).then(values => { + const metadata = JSON.parse(values[0]); + const packData = JSON.parse(values[1]).contents; + const bundles = Object.keys(metadata.bundles); + const writes = []; + for (const bundle of bundles) { + const modules = metadata.bundles[bundle]; + const target = Object.create(null); + for (const module of modules) { + const keys = metadata.keys[module]; + const defaultMessages = metadata.messages[module]; + const translations = packData[module]; + let targetStrings; + if (translations) { + targetStrings = []; + for (let i = 0; i < keys.length; i++) { + const elem = keys[i]; + const key = typeof elem === 'string' ? elem : elem.key; + let translatedMessage = translations[key]; + if (translatedMessage === undefined) { + translatedMessage = defaultMessages[i]; + } + targetStrings.push(translatedMessage); + } + } else { + targetStrings = defaultMessages; + } + target[module] = targetStrings; + } + writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); + } + writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); + return Promise.all(writes); + }).then(() => { + perf.mark('code/didGenerateNls'); + return result; + }).catch(err => { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + }); }); }); }); }); - }); - } catch (err) { - console.error('Generating translation files failed.', err); - return defaultResult(locale); + } catch (err) { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + } } + + return { + getNLSConfiguration + }; } - return { - getNLSConfiguration - }; -} - - -// @ts-ignore -if (typeof define === 'function') { - // amd - // @ts-ignore - define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); }); -} else if (typeof module === 'object' && typeof module.exports === 'object') { - const path = require('path'); - const fs = require('fs'); - const perf = require('../common/performance'); - module.exports = factory(require, path, fs, perf); -} else { - throw new Error('Unknown context'); -} + if (typeof define === 'function') { + // amd + define(['require', 'path', 'fs', 'vs/base/common/performance'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(require.__$__nodeRequire, path, fs, perf); }); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + const path = require('path'); + const fs = require('fs'); + const perf = require('../common/performance'); + module.exports = factory(require, path, fs, perf); + } else { + throw new Error('Unknown context'); + } +}()); diff --git a/src/vs/base/node/userDataPath.d.ts b/src/vs/base/node/userDataPath.d.ts new file mode 100644 index 00000000000..bc09b834a7e --- /dev/null +++ b/src/vs/base/node/userDataPath.d.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Returns the user data path to use. + */ +export function getDefaultUserDataPath(): string; diff --git a/src/vs/base/node/userDataPath.js b/src/vs/base/node/userDataPath.js new file mode 100644 index 00000000000..93384cb8f4e --- /dev/null +++ b/src/vs/base/node/userDataPath.js @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +//@ts-check +(function () { + 'use strict'; + + /** + * @param {typeof import('path')} path + * @param {typeof import('os')} os + * @param {string} productName + */ + function factory(path, os, productName) { + + function getDefaultUserDataPath() { + + // Support global VSCODE_APPDATA environment variable + let appDataPath = process.env['VSCODE_APPDATA']; + + // Otherwise check per platform + if (!appDataPath) { + switch (process.platform) { + case 'win32': + appDataPath = process.env['APPDATA']; + if (!appDataPath) { + const userProfile = process.env['USERPROFILE']; + if (typeof userProfile !== 'string') { + throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable'); + } + appDataPath = path.join(userProfile, 'AppData', 'Roaming'); + } + break; + case 'darwin': + appDataPath = path.join(os.homedir(), 'Library', 'Application Support'); + break; + case 'linux': + appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config'); + break; + default: + throw new Error('Platform not supported'); + } + } + + return path.join(appDataPath, productName); + } + + return { + getDefaultUserDataPath + }; + } + + if (typeof define === 'function') { + define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../common/network')} */ network, /** @type {typeof import("../common/resources")} */ resources) { + const rootPath = resources.dirname(network.FileAccess.asFileUri('', require)); + const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath); + + return factory(path, os, pkg.name); + }); // amd + } else if (typeof module === 'object' && typeof module.exports === 'object') { + const pkg = require('../../../../package.json'); + const path = require('path'); + const os = require('os'); + + module.exports = factory(path, os, pkg.name); // commonjs + } else { + throw new Error('Unknown context'); + } +}()); diff --git a/src/vs/base/node/paths.ts b/src/vs/base/test/node/userDataPath.test.ts similarity index 56% rename from src/vs/base/node/paths.ts rename to src/vs/base/test/node/userDataPath.test.ts index eaf03e6e400..74d0ae4f26e 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/test/node/userDataPath.test.ts @@ -3,9 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FileAccess } from 'vs/base/common/network'; +import * as assert from 'assert'; +import { getDefaultUserDataPath } from 'vs/base/node/userDataPath'; -const pathsPath = FileAccess.asFileUri('paths', require).fsPath; -const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath); +suite('User data path', () => { -export const getDefaultUserDataPath = paths.getDefaultUserDataPath; + test('getDefaultUserDataPath', () => { + const path = getDefaultUserDataPath(); + assert.ok(path.length > 0); + }); +}); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js index 2bb0a0bb730..50d8d3f7f8f 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrap = bootstrapLib(); const bootstrapWindow = bootstrapWindowLib(); @@ -38,5 +38,4 @@ } //#endregion - }()); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 64082146a6d..0c52f3cda25 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -6,9 +6,9 @@ /// //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top @@ -179,5 +179,4 @@ } //#endregion - }()); diff --git a/src/vs/code/electron-sandbox/issue/issueReporter.js b/src/vs/code/electron-sandbox/issue/issueReporter.js index 2c3529754b3..a51159e580e 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporter.js +++ b/src/vs/code/electron-sandbox/issue/issueReporter.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Load issue reporter into window diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js index 3b84f3acf06..36fb6ee5530 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ //@ts-check -'use strict'; - (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Load process explorer into window diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index c5cb1debb26..06edaee9432 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -5,10 +5,10 @@ /// -//@ts-check -'use strict'; - +// @ts-check (function () { + 'use strict'; + const bootstrapWindow = bootstrapWindowLib(); // Add a perf entry right from the top diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index f5c79cc4f0d..b9bf96a83e4 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; +import { homedir, tmpdir } from 'os'; +import product from 'vs/platform/product/common/product'; import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import * as paths from 'vs/base/node/paths'; -import * as path from 'vs/base/common/path'; -import * as resources from 'vs/base/common/resources'; +import { getDefaultUserDataPath } from 'vs/base/node/userDataPath'; +import { dirname, join, normalize, resolve } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; -import product from 'vs/platform/product/common/product'; import { toLocalISOString } from 'vs/base/common/date'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; @@ -22,46 +22,46 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get args(): NativeParsedArgs { return this._args; } @memoize - get appRoot(): string { return path.dirname(FileAccess.asFileUri('', require).fsPath); } + get appRoot(): string { return dirname(FileAccess.asFileUri('', require).fsPath); } readonly logsPath: string; @memoize - get userHome(): URI { return URI.file(os.homedir()); } + get userHome(): URI { return URI.file(homedir()); } @memoize get userDataPath(): string { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return path.join(vscodePortable, 'user-data'); + return join(vscodePortable, 'user-data'); } return parseUserDataDir(this._args, process); } @memoize - get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); } + get appSettingsHome(): URI { return URI.file(join(this.userDataPath, 'User')); } @memoize - get tmpDir(): URI { return URI.file(os.tmpdir()); } + get tmpDir(): URI { return URI.file(tmpdir()); } @memoize get userRoamingDataHome(): URI { return this.appSettingsHome; } @memoize - get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); } + get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } @memoize - get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); } + get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); } @memoize - get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); } + get userDataSyncLogResource(): URI { return URI.file(join(this.logsPath, 'userDataSync.log')); } @memoize get sync(): 'on' | 'off' | undefined { return this.args.sync; } @memoize - get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); } + get machineSettingsResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'Machine')), 'settings.json'); } @memoize get globalStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'globalStorage'); } @@ -70,32 +70,32 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get workspaceStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'workspaceStorage'); } @memoize - get keybindingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keybindings.json'); } + get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); } @memoize - get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } + get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } @memoize get argvResource(): URI { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return URI.file(path.join(vscodePortable, 'argv.json')); + return URI.file(join(vscodePortable, 'argv.json')); } - return resources.joinPath(this.userHome, product.dataFolderName, 'argv.json'); + return joinPath(this.userHome, product.dataFolderName, 'argv.json'); } @memoize - get snippetsHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'snippets'); } + get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); } @memoize get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; } @memoize - get untitledWorkspacesHome(): URI { return URI.file(path.join(this.userDataPath, 'Workspaces')); } + get untitledWorkspacesHome(): URI { return URI.file(join(this.userDataPath, 'Workspaces')); } @memoize - get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); } + get installSourcePath(): string { return join(this.userDataPath, 'installSource'); } @memoize get builtinExtensionsPath(): string { @@ -103,7 +103,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (fromArgs) { return fromArgs; } else { - return path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); + return normalize(join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); } } @@ -112,7 +112,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (fromArgs) { return fromArgs; } else { - return path.join(this.userDataPath, 'CachedExtensionVSIXs'); + return join(this.userDataPath, 'CachedExtensionVSIXs'); } } @@ -131,10 +131,10 @@ export class NativeEnvironmentService implements INativeEnvironmentService { const vscodePortable = process.env['VSCODE_PORTABLE']; if (vscodePortable) { - return path.join(vscodePortable, 'extensions'); + return join(vscodePortable, 'extensions'); } - return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; + return joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; } @memoize @@ -145,7 +145,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (/^[^:/?#]+?:\/\//.test(p)) { return URI.parse(p); } - return URI.file(path.normalize(p)); + return URI.file(normalize(p)); }); } return undefined; @@ -158,7 +158,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (/^[^:/?#]+?:\/\//.test(s)) { return URI.parse(s); } - return URI.file(path.normalize(s)); + return URI.file(normalize(s)); } return undefined; } @@ -188,7 +188,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get logLevel(): string | undefined { return this._args.log; } @memoize - get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); } + get serviceMachineIdResource(): URI { return joinPath(URI.file(this.userDataPath), 'machineid'); } get crashReporterId(): string | undefined { return this._args['crash-reporter-id']; } get crashReporterDirectory(): string | undefined { return this._args['crash-reporter-directory']; } @@ -196,13 +196,13 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get driverHandle(): string | undefined { return this._args['driver']; } @memoize - get telemetryLogResource(): URI { return URI.file(path.join(this.logsPath, 'telemetry.log')); } + get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } constructor(protected _args: NativeParsedArgs) { if (!_args.logsPath) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); - _args.logsPath = path.join(this.userDataPath, 'logs', key); + _args.logsPath = join(this.userDataPath, 'logs', key); } this.logsPath = _args.logsPath; } @@ -231,15 +231,15 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process): // Determine if the arg is relative or absolute, if relative use the original CWD // (VSCODE_CWD), not the potentially overridden one (process.cwd()). - const resolved = path.resolve(arg); + const resolved = resolve(arg); - if (path.normalize(arg) === resolved) { + if (normalize(arg) === resolved) { return resolved; } - return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg); + return resolve(process.env['VSCODE_CWD'] || process.cwd(), arg); } export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string { - return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath()); + return parsePathArg(args['user-data-dir'], process) || resolve(getDefaultUserDataPath()); } diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 92b8c1a6d52..3f6ace923a6 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -45,7 +45,7 @@ export interface IProductConfiguration { readonly applicationName: string; readonly urlProtocol: string; - readonly dataFolderName: string; + readonly dataFolderName: string; // location for extensions (e.g. ~/.vscode-insiders) readonly builtInExtensions?: IBuiltInExtension[]; From 815694184dd38f4b02747c9ee311509febbb30ff Mon Sep 17 00:00:00 2001 From: Kelvin Schoofs Date: Fri, 12 Feb 2021 16:18:47 +0100 Subject: [PATCH 006/176] Unrecognized variables with arguments getting truncated (#114474) This PR fixes #114473 --- .../services/configurationResolver/common/variableResolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 2d39c5bcd34..507dd8595b9 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -326,7 +326,8 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe default: try { - return this.resolveFromMap(match, variable, commandValueMapping, undefined); + const key = argument ? `${variable}:${argument}` : variable; + return this.resolveFromMap(match, key, commandValueMapping, undefined); } catch (error) { return match; } From ac1ba3312740d5327fecebcecd09280961031ba5 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 09:03:02 -0800 Subject: [PATCH 007/176] testing: add tests for and fix bugs in test result service --- .../testing/common/testResultService.ts | 45 +++-- .../contrib/testing/common/testServiceImpl.ts | 2 +- .../test/common/ownedTestCollection.ts | 14 ++ .../test/common/testResultService.test.ts | 187 ++++++++++++++++++ 4 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/test/common/testResultService.test.ts diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index bc2cbda0ca8..a0262052764 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -50,7 +50,7 @@ export interface ITestResult { toJSON(): ISerializedResults; } -const makeEmptyCounts = () => { +export const makeEmptyCounts = () => { const o: Partial = {}; for (const state of statesInOrder) { o[state] = 0; @@ -148,11 +148,10 @@ const makeNodeAndChildren = ( interface ISerializedResults { id: string; - counts: TestStateCount; - items: Iterable<[extId: string, item: TestResultItem]>; + items: (Omit & { children: string[], retired: undefined })[]; } -interface TestResultItem extends IncrementalTestCollectionItem { +export interface TestResultItem extends IncrementalTestCollectionItem { state: ITestState; computedState: TestRunState; retired: boolean; @@ -273,7 +272,7 @@ export class LiveTestResult implements ITestResult { public setAllToState(state: ITestState, when: (_t: TestResultItem) => boolean) { for (const test of this.testByInternalId.values()) { if (when(test)) { - this.counts[state.state]--; + this.counts[test.state.state]--; test.state = state; this.counts[state.state]++; refreshComputedState(this.computedStateAccessor, test, t => this.changeEmitter.fire(t)); @@ -336,7 +335,11 @@ export class LiveTestResult implements ITestResult { for (const collection of this.collections) { let test = collection.getNodeById(testId); if (test) { - return makeNodeAndChildren(collection, test, this.testByExtId, this.testByInternalId); + const originalSize = this.testByExtId.size; + makeParents(collection, test, this.testByExtId, this.testByInternalId); + const node = makeNodeAndChildren(collection, test, this.testByExtId, this.testByInternalId); + this.counts[TestRunState.Unset] += this.testByExtId.size - originalSize; + return node; } } @@ -361,7 +364,14 @@ export class LiveTestResult implements ITestResult { * @inheritdoc */ public toJSON(): ISerializedResults { - return { id: this.id, counts: this.counts, items: [...this.testByExtId.entries()] }; + return { + id: this.id, + items: [...this.testByExtId.values()].map(entry => ({ + ...entry, + retired: undefined, + children: [...entry.children], + })), + }; } } @@ -372,30 +382,33 @@ class HydratedTestResult implements ITestResult { /** * @inheritdoc */ - public readonly counts = this.serialized.counts; + public readonly counts = makeEmptyCounts(); /** * @inheritdoc */ - public readonly id = this.serialized.id; + public readonly id: string; /** * @inheritdoc */ public readonly isComplete = true; - private readonly map = new Map(); + private readonly byExtId = new Map(); constructor(private readonly serialized: ISerializedResults) { - for (const [key, value] of serialized.items) { - this.map.set(key, value); + this.id = serialized.id; - value.retired = true; - for (const message of value.state.messages) { + for (const item of serialized.items) { + const cast: TestResultItem = { ...item, retired: true, children: new Set(item.children) }; + for (const message of cast.state.messages) { if (message.location) { message.location.uri = URI.revive(message.location.uri); } } + + this.counts[item.state.state]++; + this.byExtId.set(item.item.extId, cast); } } @@ -403,7 +416,7 @@ class HydratedTestResult implements ITestResult { * @inheritdoc */ public getStateByExtId(extTestId: string) { - return this.map.get(extTestId); + return this.byExtId.get(extTestId); } /** @@ -568,7 +581,7 @@ export class TestResultService implements ITestResultService { private onComplete(result: LiveTestResult) { // move the complete test run down behind any still-running ones - for (let i = 0; i < this.results.length - 2; i++) { + for (let i = 0; i < this.results.length - 1; i++) { if (this.results[i].isComplete && !this.results[i + 1].isComplete) { [this.results[i], this.results[i + 1]] = [this.results[i + 1], this.results[i]]; } diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 8a18a20daa5..58787aa18f2 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -242,7 +242,7 @@ export class TestService extends Disposable implements ITestService { } } -class MainThreadTestCollection extends AbstractIncrementalTestCollection implements IMainThreadTestCollection { +export class MainThreadTestCollection extends AbstractIncrementalTestCollection implements IMainThreadTestCollection { private pendingRootChangeEmitter = new Emitter(); private busyProvidersChangeEmitter = new Emitter(); private _busyProviders = 0; diff --git a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts index a0f841e4b3e..8df8bb684ff 100644 --- a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts +++ b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts @@ -5,6 +5,8 @@ import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection'; import { TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { MainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testServiceImpl'; +import { testStubs } from 'vs/workbench/contrib/testing/common/testStubs'; export class TestSingleUseCollection extends SingleUseTestCollection { private idCounter = 0; @@ -35,3 +37,15 @@ export class TestOwnedTestCollection extends OwnedTestCollection { return new TestSingleUseCollection(this.testIdToInternal, publishDiff); } } + +/** + * Gets a main thread test collection initialized with the given set of + * roots/stubs. + */ +export const getInitializedMainTestCollection = (root = testStubs.nested()) => { + const c = new MainThreadTestCollection(0); + const singleUse = new TestSingleUseCollection(new Map(), () => undefined); + singleUse.addRoot(root, 'provider'); + c.apply(singleUse.collectDiff()); + return c; +}; diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts new file mode 100644 index 00000000000..c18dab77484 --- /dev/null +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testCollection'; +import { LiveTestResult, makeEmptyCounts, TestResultItem, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ReExportedTestRunState as TestRunState } from 'vs/workbench/contrib/testing/common/testStubs'; +import { getInitializedMainTestCollection } from 'vs/workbench/contrib/testing/test/common/ownedTestCollection'; +import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; + +suite('Workbench - Test Results Service', () => { + const getLabelsIn = (it: Iterable) => [...it].map(t => t.item.label).sort(); + + let r: LiveTestResult; + let changed = new Set(); + let retired = new Set(); + + setup(() => { + changed = new Set(); + retired = new Set(); + r = LiveTestResult.from( + [getInitializedMainTestCollection()], + [{ providerId: 'provider', testId: '1' }] + ); + + r.onChange(e => changed.add(e)); + r.onRetired(e => retired.add(e)); + }); + + suite('LiveTestResult', () => { + test('is empty if no tests are requesteed', () => { + const r = LiveTestResult.from([getInitializedMainTestCollection()], []); + assert.deepStrictEqual(getLabelsIn(r.tests), []); + }); + + test('does not change or retire initially', () => { + assert.deepStrictEqual(0, changed.size); + assert.deepStrictEqual(0, retired.size); + }); + + test('initializes with the subtree of requested tests', () => { + assert.deepStrictEqual(getLabelsIn(r.tests), ['a', 'aa', 'ab', 'root']); + }); + + test('initializes with valid counts', () => { + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Unset]: 4 + }); + }); + + test('setAllToState', () => { + r.setAllToState({ state: TestRunState.Queued, duration: 0, messages: [] }, t => t.item.label !== 'root'); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Unset]: 1, + [TestRunState.Queued]: 3, + }); + + assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Queued); + assert.deepStrictEqual(getLabelsIn(changed), ['a', 'aa', 'ab', 'root']); + }); + + test('updateState', () => { + r.updateState('1', { state: TestRunState.Running, duration: 0, messages: [] }); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Running]: 1, + [TestRunState.Unset]: 3, + }); + assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Running); + // update computed state: + assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running); + assert.deepStrictEqual(getLabelsIn(changed), ['a', 'root']); + }); + + test('retire', () => { + r.retire('root\0a'); + assert.deepStrictEqual(getLabelsIn(changed), ['a', 'aa', 'ab']); + assert.deepStrictEqual(getLabelsIn(retired), ['a']); + + retired.clear(); + changed.clear(); + + r.retire('root\0a'); + assert.deepStrictEqual(getLabelsIn(changed), []); + assert.deepStrictEqual(getLabelsIn(retired), []); + }); + + test('addTestToRun', () => { + r.updateState('4', { state: TestRunState.Running, duration: 0, messages: [] }); + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Running]: 1, + [TestRunState.Unset]: 4, + }); + assert.deepStrictEqual(r.getStateByExtId('root\0b')?.state.state, TestRunState.Running); + // update computed state: + assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running); + }); + + test('markComplete', () => { + r.setAllToState({ state: TestRunState.Queued, duration: 0, messages: [] }, t => true); + r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] }); + changed.clear(); + + r.markComplete(); + + assert.deepStrictEqual(r.counts, { + ...makeEmptyCounts(), + [TestRunState.Passed]: 1, + [TestRunState.Unset]: 3, + }); + + assert.deepStrictEqual(r.getStateByExtId('root')?.state.state, TestRunState.Unset); + assert.deepStrictEqual(r.getStateByExtId('root\0a\0aa')?.state.state, TestRunState.Passed); + }); + }); + + suite('service', () => { + let storage: TestStorageService; + let results: TestResultService; + + setup(() => { + storage = new TestStorageService(); + results = new TestResultService( + new MockContextKeyService(), + storage, + ); + }); + + test('pushes new result', () => { + results.push(r); + assert.deepStrictEqual(results.results, [r]); + }); + + test('serializes and re-hydrates', () => { + results.push(r); + r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] }); + r.markComplete(); + + results = new TestResultService( + new MockContextKeyService(), + storage, + ); + + const [rehydrated, actual] = results.getStateByExtId('root')!; + const expected = r.getStateByExtId('root')!; + delete expected.state.duration; // delete undefined props that don't survive serialization + delete expected.item.location; + + assert.deepStrictEqual(actual, { ...expected, retired: true }); + assert.deepStrictEqual(rehydrated.counts, r.counts); + assert.strictEqual(rehydrated.isComplete, true); + }); + + test('clears results but keeps ongoing tests', () => { + results.push(r); + r.markComplete(); + + const r2 = results.push(LiveTestResult.from( + [getInitializedMainTestCollection()], + [{ providerId: 'provider', testId: '1' }] + )); + results.clear(); + + assert.deepStrictEqual(results.results, [r2]); + }); + + test('keeps ongoing tests on top', () => { + results.push(r); + const r2 = results.push(LiveTestResult.from( + [getInitializedMainTestCollection()], + [{ providerId: 'provider', testId: '1' }] + )); + + assert.deepStrictEqual(results.results, [r2, r]); + r2.markComplete(); + assert.deepStrictEqual(results.results, [r, r2]); + r.markComplete(); + assert.deepStrictEqual(results.results, [r, r2]); + }); + }); +}); From a16f5d2c4c198cc887c814ccde54a1abe7a60eab Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 12 Feb 2021 18:11:50 +0100 Subject: [PATCH 008/176] fixes #116395 --- src/vs/workbench/services/label/common/labelService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index c9fc28409c0..550b36e9726 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -21,7 +21,6 @@ import { match } from 'vs/base/common/glob'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { hasDriveLetter } from 'vs/base/common/extpath'; const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', @@ -74,6 +73,10 @@ const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoin const sepRegexp = /\//g; const labelMatchingRegexp = /\$\{(scheme|authority|path|(query)\.(.+?))\}/g; +function hasDriveLetterIgnorePlatform(path: string): boolean { + return !!(path && path[2] === ':'); +} + class ResourceLabelFormattersHandler implements IWorkbenchContribution { private formattersDisposables = new Map(); @@ -283,7 +286,7 @@ export class LabelService extends Disposable implements ILabelService { }); // convert \c:\something => C:\something - if (formatting.normalizeDriveLetter && hasDriveLetter(label.substr(1))) { + if (formatting.normalizeDriveLetter && hasDriveLetterIgnorePlatform(label)) { label = label.charAt(1).toUpperCase() + label.substr(2); } From 14669c2e45731995833b9598ee087020fa006972 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 12 Feb 2021 09:05:31 -0800 Subject: [PATCH 009/176] Make scopes parameter optional to getSessions and remove getAllSessions --- .../github-authentication/src/extension.ts | 3 +-- .../github-authentication/src/github.ts | 6 ++++-- .../microsoft-authentication/src/AADHelper.ts | 6 +++++- .../microsoft-authentication/src/extension.ts | 1 - src/vs/vscode.proposed.d.ts | 20 ++++++++----------- .../api/browser/mainThreadAuthentication.ts | 6 +----- .../workbench/api/common/extHost.protocol.ts | 3 +-- .../api/common/extHostAuthentication.ts | 11 +--------- .../parts/activitybar/activitybarActions.ts | 2 +- .../issue/electron-sandbox/issueService.ts | 2 +- .../contrib/url/browser/trustedDomains.ts | 2 +- .../browser/authenticationService.ts | 14 ++----------- .../browser/userDataSyncWorkbenchService.ts | 2 +- 13 files changed, 27 insertions(+), 51 deletions(-) diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index c9b76d81670..df80913cd22 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -24,8 +24,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', { onDidChangeSessions: onDidChangeSessions.event, - getAllSessions: () => Promise.resolve(loginService.sessions), - getSessions: (scopes: string[]) => loginService.getSessions(scopes), + getSessions: (scopes?: string[]) => loginService.getSessions(scopes), createSession: async (scopeList: string[]) => { try { /* __GDPR__ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 89893c94ccd..bf6cee27132 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -44,8 +44,10 @@ export class GitHubAuthenticationProvider { context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates())); } - async getSessions(scopes: string[]): Promise { - return this._sessions.filter(session => arrayEquals(session.scopes, scopes)); + async getSessions(scopes?: string[]): Promise { + return scopes + ? this._sessions.filter(session => arrayEquals(session.scopes, scopes)) + : this._sessions; } private async verifySessions(): Promise { diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index fbb4e10a0e8..b0631344346 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -300,7 +300,11 @@ export class AzureActiveDirectoryService { return Promise.all(this._tokens.map(token => this.convertToSession(token))); } - async getSessions(scopes: string[]): Promise { + async getSessions(scopes?: string[]): Promise { + if (!scopes) { + return this.sessions; + } + const orderedScopes = scopes.sort().join(' '); const matchingTokens = this._tokens.filter(token => token.scope === orderedScopes); return Promise.all(matchingTokens.map(token => this.convertToSession(token))); diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 584a4027b64..41b8690f344 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -20,7 +20,6 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('microsoft', 'Microsoft', { onDidChangeSessions: onDidChangeSessions.event, - getAllSessions: () => Promise.resolve(loginService.sessions), getSessions: (scopes: string[]) => loginService.getSessions(scopes), createSession: async (scopes: string[]) => { try { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index febf42232e6..dbad76952cb 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -64,29 +64,25 @@ declare module 'vscode' { readonly onDidChangeSessions: Event; /** - * Returns an array of current sessions. - * - * TODO @RMacfarlane finish deprecating this and remove it + * Get a list of sessions. + * @param scopes An optional list of scopes. If provided, the sessions returned should match + * these permissions, otherwise all sessions should be returned. + * @returns A promise that resolves to an array of authentication sessions. */ // eslint-disable-next-line vscode-dts-provider-naming - getAllSessions(): Thenable>; - - - /** - * Returns an array of current sessions. - */ - // eslint-disable-next-line vscode-dts-provider-naming - getSessions(scopes: string[]): Thenable>; + getSessions(scopes?: string[]): Thenable>; /** * Prompts a user to login. + * @param scopes A list of scopes, permissions, that the new session should be created with. + * @returns A promise that resolves to an authentication session. */ // eslint-disable-next-line vscode-dts-provider-naming createSession(scopes: string[]): Thenable; /** * Removes the session corresponding to session id. - * @param sessionId The session id to log out of + * @param sessionId The id of the session to remove. */ // eslint-disable-next-line vscode-dts-provider-naming removeSession(sessionId: string): Thenable; diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index dfad0388642..64f7d14880f 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -89,14 +89,10 @@ export class MainThreadAuthenticationProvider extends Disposable { } } - async getSessions(scopes: string[]) { + async getSessions(scopes?: string[]) { return this._proxy.$getSessions(this.id, scopes); } - async getAllSessions(): Promise> { - return this._proxy.$getAllSessions(this.id); - } - createSession(scopes: string[]): Promise { return this._proxy.$createSession(this.id, scopes); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9e121b2c822..a55a0c041a1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1125,8 +1125,7 @@ export interface ExtHostLabelServiceShape { } export interface ExtHostAuthenticationShape { - $getAllSessions(id: string): Promise>; - $getSessions(id: string, scopes: string[]): Promise>; + $getSessions(id: string, scopes?: string[]): Promise>; $createSession(id: string, scopes: string[]): Promise; $removeSession(id: string, sessionId: string): Promise; $onDidChangeAuthenticationSessions(id: string, label: string, event: modes.AuthenticationSessionsChangeEvent): Promise; diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 988f93a5c9f..b4445864a7c 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -147,16 +147,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $getAllSessions(providerId: string): Promise> { - const providerData = this._authenticationProviders.get(providerId); - if (providerData) { - return Promise.resolve(providerData.provider.getAllSessions()); - } - - throw new Error(`Unable to find authentication provider with handle: ${providerId}`); - } - - $getSessions(providerId: string, scopes: string[]): Promise> { + $getSessions(providerId: string, scopes?: string[]): Promise> { const providerData = this._authenticationProviders.get(providerId); if (providerData) { return Promise.resolve(providerData.provider.getSessions(scopes)); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index c3f7817a47f..8b62c5a88de 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -210,7 +210,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { const providers = this.authenticationService.getProviderIds(); const allSessions = providers.map(async providerId => { try { - const sessions = await this.authenticationService.getAllSessions(providerId); + const sessions = await this.authenticationService.getSessions(providerId); const groupedSessions: { [label: string]: AuthenticationSession[]; } = {}; sessions.forEach(session => { diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index 1deb2d639b0..94beb1646ae 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -54,7 +54,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { }; }); const experiments = await this.experimentService.getCurrentExperiments(); - const githubSessions = await this.authenticationService.getAllSessions('github'); + const githubSessions = await this.authenticationService.getSessions('github'); const potentialSessions = githubSessions.filter(session => session.scopes.includes('repo')); const theme = this.themeService.getColorTheme(); const issueReporterData: IssueReporterData = Object.assign({ diff --git a/src/vs/workbench/contrib/url/browser/trustedDomains.ts b/src/vs/workbench/contrib/url/browser/trustedDomains.ts index b5188bfb036..145afdf032c 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomains.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomains.ts @@ -206,7 +206,7 @@ export async function readWorkspaceTrustedDomains(accessor: ServicesAccessor): P export async function readAuthenticationTrustedDomains(accessor: ServicesAccessor): Promise { const authenticationService = accessor.get(IAuthenticationService); - return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getAllSessions('github')) ?? []).length > 0 + return authenticationService.isAuthenticationProviderRegistered('github') && ((await authenticationService.getSessions('github')) ?? []).length > 0 ? [`https://github.com`] : []; } diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 5d6de318988..6c1efc704be 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -124,8 +124,7 @@ export interface IAuthenticationService { declaredProviders: AuthenticationProviderInformation[]; readonly onDidChangeDeclaredProviders: Event; - getSessions(id: string, scopes: string[], activateImmediate?: boolean): Promise>; - getAllSessions(providerId: string, activateImmediate?: boolean): Promise>; + getSessions(id: string, scopes?: string[], activateImmediate?: boolean): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; createSession(providerId: string, scopes: string[], activateImmediate?: boolean): Promise; @@ -694,16 +693,7 @@ export class AuthenticationService extends Disposable implements IAuthentication return Promise.race([didRegister, didTimeout]); } - async getAllSessions(id: string, activateImmediate: boolean = false): Promise> { - try { - const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate); - return await authProvider.getAllSessions(); - } catch (_) { - throw new Error(`No authentication provider '${id}' is currently registered.`); - } - } - - async getSessions(id: string, scopes: string[], activateImmediate: boolean = false): Promise> { + async getSessions(id: string, scopes?: string[], activateImmediate: boolean = false): Promise> { try { const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate); return await authProvider.getSessions(scopes); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 32228c4c3b0..81afe84f97c 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -202,7 +202,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat let accounts: Map = new Map(); let currentAccount: UserDataSyncAccount | null = null; - const sessions = await this.authenticationService.getAllSessions(authenticationProviderId) || []; + const sessions = await this.authenticationService.getSessions(authenticationProviderId) || []; for (const session of sessions) { const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); accounts.set(account.accountName, account); From c83064b9825429ee327a9f9b3cf8928b735b5350 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 12 Feb 2021 09:18:27 -0800 Subject: [PATCH 010/176] Update freshExecArgv comment, fix #116422 --- .../services/search/electron-browser/searchService.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/services/search/electron-browser/searchService.ts b/src/vs/workbench/services/search/electron-browser/searchService.ts index 09160857d0f..9e1f8f3f913 100644 --- a/src/vs/workbench/services/search/electron-browser/searchService.ts +++ b/src/vs/workbench/services/search/electron-browser/searchService.ts @@ -61,10 +61,7 @@ export class DiskSearch implements ISearchResultProvider { serverName: 'Search', timeout, args: ['--type=searchService'], - // See https://github.com/microsoft/vscode/issues/27665 // Pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`. - // e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host - // results in the forked process inheriting `--inspect-brk=xxx`. freshExecArgv: true, env: { VSCODE_AMD_ENTRYPOINT: 'vs/workbench/services/search/node/searchApp', From 52f633d19ce5479f4082df3e512d5489f054f5e4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Feb 2021 17:54:26 +0100 Subject: [PATCH 011/176] some :lipstick:, some API todos --- extensions/vscode-api-tests/src/utils.ts | 18 +++++++++++++++++- src/vs/vscode.proposed.d.ts | 4 ++++ .../api/browser/mainThreadNotebook.ts | 1 - .../notebook/browser/notebookServiceImpl.ts | 3 +++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index f7923bbc9f7..a102e7d2329 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -17,7 +17,7 @@ vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensi export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, ext = ''): Promise { let fakeFile: vscode.Uri; if (dir) { - assert.equal(dir.scheme, testFs.scheme); + assert.strictEqual(dir.scheme, testFs.scheme); fakeFile = dir.with({ path: dir.path + '/' + rndName() + ext }); } else { fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${rndName() + ext}`); @@ -117,3 +117,19 @@ export function assertNoRpcFromEntry(entry: [obj: any, name: string]) { assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n')); assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // happens... } + +export async function asPromise(event: vscode.Event, timeout = 1000): Promise { + return new Promise((resolve, reject) => { + + const handle = setTimeout(() => { + sub.dispose(); + reject(new Error('asPromise TIMEOUT reached')); + }, timeout); + + const sub = event(e => { + clearTimeout(handle); + sub.dispose(); + resolve(e); + }); + }); +} diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index dbad76952cb..0122b4e2b2a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1105,6 +1105,7 @@ declare module 'vscode' { readonly uri: Uri; readonly version: number; readonly fileName: string; + // todo@API should we really expose this? readonly viewType: string; readonly isDirty: boolean; readonly isUntitled: boolean; @@ -1305,6 +1306,8 @@ declare module 'vscode' { export namespace notebook { + // todo@API should we really support to pass the viewType? We do NOT support + // to open the same file with different viewTypes at the same time export function openNotebookDocument(uri: Uri, viewType?: string): Thenable; export const onDidOpenNotebookDocument: Event; export const onDidCloseNotebookDocument: Event; @@ -1330,6 +1333,7 @@ declare module 'vscode' { export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookEditorVisibleRanges: Event; + // TODO@API add overload for just a URI export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 812b2bed3f4..871641799f6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -501,7 +501,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const disposable = this._notebookService.registerNotebookController(viewType, extension, controller); this._notebookProviders.set(viewType, { controller, disposable }); - return; } async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 3e6484a7baa..3aabfc323aa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -680,6 +680,9 @@ export class NotebookService extends Disposable implements INotebookService, ICu } registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable { + if (this._notebookProviders.has(viewType)) { + throw new Error(`notebook controller for viewtype '${viewType}' already exists`); + } this._notebookProviders.set(viewType, { extensionData, controller }); if (controller.viewOptions && !this.notebookProviderInfoStore.get(viewType)) { From 1f1db8515ee160dc2453b85394167639849c9c4d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Feb 2021 18:29:49 +0100 Subject: [PATCH 012/176] while resolving an editor input it might get disposed --- .../notebook/browser/notebookEditorInput.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 80bb8e33897..cd68acce0ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -160,11 +160,12 @@ ${patterns} if (!this._textModel) { this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!); - - this._register(this._textModel.object.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(); - })); - + if (this.isDisposed()) { + this._textModel.dispose(); + this._textModel = null; + return null; + } + this._register(this._textModel.object.onDidChangeDirty(() => this._onDidChangeDirty.fire())); if (this._textModel.object.isDirty()) { this._onDidChangeDirty.fire(); } @@ -185,10 +186,8 @@ ${patterns} } dispose() { - if (this._textModel) { - this._textModel.dispose(); - this._textModel = null; - } + this._textModel?.dispose(); + this._textModel = null; super.dispose(); } } From 5f9f03e822621be9d96feb6f30f7f2899df6b5be Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Feb 2021 18:32:55 +0100 Subject: [PATCH 013/176] disable extensions when debugging API tests --- .vscode/launch.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 11c578b992d..b1db49c2549 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -110,7 +110,8 @@ // "${workspaceFolder}", // Uncomment for running out of sources. "${workspaceFolder}/extensions/vscode-api-tests/testWorkspace", "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests", - "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests" + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests", + "--disable-extensions" ], "outFiles": [ "${workspaceFolder}/out/**/*.js" From 2d6c2d0b2d793760c77cd8618d03248db259ae8c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Feb 2021 19:17:03 +0100 Subject: [PATCH 014/176] wip - move notebook integration test into our normal API test extension --- extensions/vscode-api-tests/package.json | 21 + .../notebook.document.test.ts | 235 ++++++++ .../src/singlefolder-tests}/notebook.test.ts | 531 ++++++++---------- extensions/vscode-notebook-tests/package.json | 10 - .../src/notebookTestMain.ts | 122 ---- 5 files changed, 487 insertions(+), 432 deletions(-) create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts rename extensions/{vscode-notebook-tests/src => vscode-api-tests/src/singlefolder-tests}/notebook.test.ts (81%) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 228a9681278..7e576805d5c 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -116,6 +116,27 @@ } ] } + ], + "notebookProvider": [ + { + "viewType": "notebookCoreTest", + "displayName": "Notebook Core Test", + "selector": [ + { + "filenamePattern": "*.vsctestnb", + "excludeFileNamePattern": "" + } + ] + }, + { + "viewType": "notebook.nbdtest", + "displayName": "notebook.nbdtest", + "selector": [ + { + "filenamePattern": "**/*.nbdtest" + } + ] + } ] }, "scripts": { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts new file mode 100644 index 00000000000..cf9bd87bd24 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -0,0 +1,235 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as vscode from 'vscode'; +import { createRandomFile, disposeAll, asPromise, closeAllEditors, assertNoRpc } from '../utils'; + +suite('Notebook Document', function () { + + const contentProvider = new class implements vscode.NotebookContentProvider { + async openNotebook(uri: vscode.Uri, _openContext: vscode.NotebookDocumentOpenContext): Promise { + return { + cells: [{ cellKind: vscode.NotebookCellKind.Code, source: uri.toString(), language: 'javascript', metadata: {}, outputs: [] }], + metadata: {} + }; + } + async resolveNotebook(_document: vscode.NotebookDocument, _webview: vscode.NotebookCommunication) { + // + } + async saveNotebook(_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { + // + } + async saveNotebookAs(_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { + // + } + async backupNotebook(_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) { + return { id: '', delete() { } }; + } + }; + + const disposables: vscode.Disposable[] = []; + + suiteTeardown(async function () { + assertNoRpc(); + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await closeAllEditors(); + disposeAll(disposables); + disposables.length = 0; + + for (let doc of vscode.notebook.notebookDocuments) { + assert.strictEqual(doc.isDirty, false) + } + }); + + suiteSetup(function () { + disposables.push(vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider)); + }); + + test('cannot register sample provider multiple times', function () { + assert.throws(() => { + vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider); + }); + }); + + test('cannot open unknown types', async function () { + try { + await vscode.notebook.openNotebookDocument(vscode.Uri.parse('some:///thing.notTypeKnown')); + assert.ok(false); + } catch { + assert.ok(true); + } + }); + + test('document basics', async function () { + const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const notebook = await vscode.notebook.openNotebookDocument(uri); + + assert.strictEqual(notebook.uri.toString(), uri.toString()); + assert.strictEqual(notebook.isDirty, false); + assert.strictEqual(notebook.isUntitled, false); + assert.strictEqual(notebook.cells.length, 1); + + assert.strictEqual(notebook.viewType, 'notebook.nbdtest'); + }); + + test('notebook open/close, notebook ready when cell-document open event is fired', async function () { + const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + let didHappen = false; + const p = asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + if (doc.uri.scheme !== 'vscode-notebook-cell') { + return; + } + const notebook = vscode.notebook.notebookDocuments.find(notebook => { + const cell = notebook.cells.find(cell => cell.document === doc); + return Boolean(cell); + }); + assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); + didHappen = true; + }); + + await vscode.notebook.openNotebookDocument(uri); + await p; + assert.strictEqual(didHappen, true); + }); + + test('notebook open/close, all cell-documents are ready', async function () { + const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + + const p = asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + for (let cell of notebook.cells) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); + assert.ok(doc); + assert.strictEqual(doc.notebook === notebook, true); + assert.strictEqual(doc === cell.document, true); + assert.strictEqual(doc?.languageId, cell.language); + assert.strictEqual(doc?.isDirty, false); + assert.strictEqual(doc?.isClosed, false); + } + }); + + await vscode.notebook.openNotebookDocument(uri); + await p; + }); + + + test('workspace edit API (replaceCells)', async function () { + const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + + const document = await vscode.notebook.openNotebookDocument(uri); + assert.strictEqual(document.cells.length, 1); + + // inserting two new cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // deleting cell 1 and 3 + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, []); + edit.replaceNotebookCells(document.uri, 2, 3, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + + assert.strictEqual(document.cells.length, 1); + assert.strictEqual(document.cells[0].document.getText(), 'new_code'); + + // replacing all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 1, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new2_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new2_code' + }]); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 2); + assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new2_code'); + + // remove all cells + { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, document.cells.length, []); + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + } + assert.strictEqual(document.cells.length, 0); + }); + + test('workspace edit API (replaceCells, event)', async function () { + const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const document = await vscode.notebook.openNotebookDocument(uri); + assert.strictEqual(document.cells.length, 1); + + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(document.uri, 0, 0, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const event = asPromise(vscode.notebook.onDidChangeNotebookCells); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + + const data = await event; + + // check document + assert.strictEqual(document.cells.length, 3); + assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); + assert.strictEqual(document.cells[1].document.getText(), 'new_code'); + + // check event data + assert.strictEqual(data.document === document, true); + assert.strictEqual(data.changes.length, 1); + assert.strictEqual(data.changes[0].deletedCount, 0); + assert.strictEqual(data.changes[0].deletedItems.length, 0); + assert.strictEqual(data.changes[0].items.length, 2); + assert.strictEqual(data.changes[0].items[0], document.cells[0]); + assert.strictEqual(data.changes[0].items[1], document.cells[1]); + }); +}); diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts similarity index 81% rename from extensions/vscode-notebook-tests/src/notebook.test.ts rename to extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 0117e41160a..7db8c7784d6 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -6,52 +6,13 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile } from './utils'; - -export function timeoutAsync(n: number): Promise { - return new Promise(resolve => { - setTimeout(() => { - resolve(); - }, n); - }); -} - -export function once(event: vscode.Event): vscode.Event { - return (listener: any, thisArgs = null, disposables?: any) => { - // we need this, in case the event fires during the listener call - let didFire = false; - let result: vscode.Disposable; - result = event(e => { - if (didFire) { - return; - } else if (result) { - result.dispose(); - } else { - didFire = true; - } - - return listener.call(thisArgs, e); - }, null, disposables); - - if (didFire) { - result.dispose(); - } - - return result; - }; -} - -async function getEventOncePromise(event: vscode.Event): Promise { - return new Promise((resolve, _reject) => { - once(event)((result: T) => resolve(result)); - }); -} +import { createRandomFile, asPromise, disposeAll, closeAllEditors } from '../utils'; // Since `workbench.action.splitEditor` command does await properly // Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves // The workaround here is waiting for the first visible notebook editor change event. async function splitEditor() { - const once = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const once = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('workbench.action.splitEditor'); await once; } @@ -100,7 +61,7 @@ async function updateNotebookMetadata(uri: vscode.Uri, newMetadata: vscode.Noteb } async function withEvent(event: vscode.Event, callback: (e: Promise) => Promise) { - const e = getEventOncePromise(event); + const e = asPromise(event); await callback(e); } @@ -112,7 +73,144 @@ function assertInitalState() { // assert.strictEqual(vscode.notebook.visibleNotebookEditors.length, 0); } -suite('Notebook API tests', () => { +suite('Notebook API tests', function () { + + const disposables: vscode.Disposable[] = []; + + suiteTeardown(async function () { + disposeAll(disposables); + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await closeAllEditors(); + }); + + suiteSetup(function () { + disposables.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', { + openNotebook: async (_resource: vscode.Uri): Promise => { + if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { + return { + metadata: {}, + cells: [] + }; + } + + const dto: vscode.NotebookData = { + metadata: { + custom: { testMetadata: false } + }, + cells: [ + { + source: 'test', + language: 'typescript', + cellKind: vscode.NotebookCellKind.Code, + outputs: [], + metadata: { + custom: { testCellMetadata: 123 } + } + } + ] + }; + return dto; + }, + resolveNotebook: async (_document: vscode.NotebookDocument) => { + return; + }, + saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { + return; + }, + backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { + return { + id: '1', + delete: () => { } + }; + } + })); + + + const kernel: vscode.NotebookKernel = { + id: 'mainKernel', + label: 'Notebook Test Kernel', + isPreferred: true, + supportedLanguages: ['typescript'], + executeAllCells: async (_document: vscode.NotebookDocument) => { + const edit = new vscode.WorkspaceEdit(); + + edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) + ])]); + return vscode.workspace.applyEdit(edit); + }, + cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, + executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { + if (!cell) { + cell = document.cells[0]; + } + + if (document.uri.path.endsWith('customRenderer.vsctestnb')) { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + } + + const edit = new vscode.WorkspaceEdit(); + // const previousOutputs = cell.outputs; + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + }, + cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + const kernel2: vscode.NotebookKernel = { + id: 'secondaryKernel', + label: 'Notebook Secondary Test Kernel', + isPreferred: false, + supportedLanguages: ['typescript'], + executeAllCells: async (_document: vscode.NotebookDocument) => { + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) + ])]); + + return vscode.workspace.applyEdit(edit); + }, + cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, + executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { + if (!cell) { + cell = document.cells[0]; + } + + const edit = new vscode.WorkspaceEdit(); + + if (document.uri.path.endsWith('customRenderer.vsctestnb')) { + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined) + ])]); + } else { + edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) + ])]); + } + + return vscode.workspace.applyEdit(edit); + }, + cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } + }; + + disposables.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { + provideKernels: async () => { + return [kernel, kernel2]; + } + })); + }); + // test.only('crash', async function () { // for (let i = 0; i < 200; i++) { // let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb')); @@ -141,60 +239,20 @@ suite('Notebook API tests', () => { test('document open/close event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstDocumentOpen = asPromise(vscode.notebook.onDidOpenNotebookDocument); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstDocumentOpen; - const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument); + const firstDocumentClose = asPromise(vscode.notebook.onDidCloseNotebookDocument); await vscode.commands.executeCommand('workbench.action.closeAllEditors'); await firstDocumentClose; }); - test('notebook open/close, all cell-documents are ready', async function () { - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - - const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { - for (let cell of notebook.cells) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); - assert.ok(doc); - assert.strictEqual(doc === cell.document, true); - assert.strictEqual(doc?.languageId, cell.language); - assert.strictEqual(doc?.isDirty, false); - assert.strictEqual(doc?.isClosed, false); - } - }); - - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await p; - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - }); - - test('notebook open/close, notebook ready when cell-document open event is fired', async function () { - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - let didHappen = false; - const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => { - if (doc.uri.scheme !== 'vscode-notebook-cell') { - return; - } - const notebook = vscode.notebook.notebookDocuments.find(notebook => { - const cell = notebook.cells.find(cell => cell.document === doc); - return Boolean(cell); - }); - assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`); - didHappen = true; - }); - - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - await p; - assert.strictEqual(didHappen, true); - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); - }); - test('shared document in notebook editors', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let counter = 0; const disposables: vscode.Disposable[] = []; disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => { @@ -217,12 +275,12 @@ suite('Notebook API tests', () => { test('editor open/close event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstEditorOpen = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstEditorOpen; - const firstEditorClose = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors); + const firstEditorClose = asPromise(vscode.window.onDidChangeVisibleNotebookEditors); await vscode.commands.executeCommand('workbench.action.closeAllEditors'); await firstEditorClose; }); @@ -230,7 +288,7 @@ suite('Notebook API tests', () => { test('editor open/close event 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let count = 0; const disposables: vscode.Disposable[] = []; disposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => { @@ -250,10 +308,10 @@ suite('Notebook API tests', () => { test('editor editing event 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); const cellChangeEventRet = await cellsChangeEvent; assert.strictEqual(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document); @@ -269,7 +327,7 @@ suite('Notebook API tests', () => { const secondCell = vscode.window.activeNotebookEditor!.document.cells[1]; - const moveCellEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const moveCellEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.moveUp'); const moveCellEventRet = await moveCellEvent; assert.deepStrictEqual(moveCellEventRet, { @@ -290,7 +348,7 @@ suite('Notebook API tests', () => { ] }); - const cellOutputChange = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const cellOutputChange = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.execute'); const cellOutputsAddedRet = await cellOutputChange; assert.deepStrictEqual(cellOutputsAddedRet, { @@ -299,7 +357,7 @@ suite('Notebook API tests', () => { }); assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 1); - const cellOutputClear = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const cellOutputClear = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.clearOutputs'); const cellOutputsCleardRet = await cellOutputClear; assert.deepStrictEqual(cellOutputsCleardRet, { @@ -323,7 +381,7 @@ suite('Notebook API tests', () => { test('editor move cell event', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); @@ -331,7 +389,7 @@ suite('Notebook API tests', () => { const activeCell = vscode.window.activeNotebookEditor!.selection; assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); - const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const moveChange = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.moveDown'); const ret = await moveChange; assert.deepStrictEqual(ret, { @@ -365,7 +423,7 @@ suite('Notebook API tests', () => { test('notebook editor active/visible', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstEditor = vscode.window.activeNotebookEditor; assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); @@ -377,7 +435,7 @@ suite('Notebook API tests', () => { assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2); - const untitledEditorChange = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const untitledEditorChange = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.files.newUntitledFile'); await untitledEditorChange; assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true); @@ -386,7 +444,7 @@ suite('Notebook API tests', () => { assert.notStrictEqual(secondEditor, vscode.window.activeNotebookEditor); assert.strictEqual(vscode.window.visibleNotebookEditors.length, 1); - const activeEditorClose = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const activeEditorClose = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); await activeEditorClose; assert.strictEqual(secondEditor, vscode.window.activeNotebookEditor); @@ -399,12 +457,12 @@ suite('Notebook API tests', () => { test('notebook active editor change', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + const firstEditorOpen = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await firstEditorOpen; - const firstEditorDeactivate = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor); + const firstEditorDeactivate = asPromise(vscode.window.onDidChangeActiveNotebookEditor); await vscode.commands.executeCommand('workbench.action.splitEditor'); await firstEditorDeactivate; @@ -413,10 +471,10 @@ suite('Notebook API tests', () => { test('edit API (replaceCells)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); }); @@ -434,7 +492,7 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput, USE NotebookCellOutput-type)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { @@ -463,7 +521,7 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { @@ -485,10 +543,10 @@ suite('Notebook API tests', () => { test('edit API (replaceOutput, event)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const outputChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const outputChangeEvent = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([ new vscode.NotebookCellOutputItem('foo', 'bar') @@ -511,7 +569,7 @@ suite('Notebook API tests', () => { test('edit API (replaceMetadata)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { @@ -530,10 +588,10 @@ suite('Notebook API tests', () => { test('edit API (replaceMetadata, event)', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const event = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const event = asPromise(vscode.notebook.onDidChangeCellMetadata); await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); @@ -548,141 +606,13 @@ suite('Notebook API tests', () => { await saveFileAndCloseAll(resource); }); - test('workspace edit API (replaceCells)', async function () { - - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const { document } = vscode.window.activeNotebookEditor!; - assert.strictEqual(document.cells.length, 1); - - // inserting two new cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 0, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new_code' - }]); - - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - - assert.strictEqual(document.cells.length, 3); - assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new_code'); - - // deleting cell 1 and 3 - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 1, []); - edit.replaceNotebookCells(document.uri, 2, 3, []); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - - assert.strictEqual(document.cells.length, 1); - assert.strictEqual(document.cells[0].document.getText(), 'new_code'); - - // replacing all cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 1, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new2_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new2_code' - }]); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - assert.strictEqual(document.cells.length, 2); - assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new2_code'); - - // remove all cells - { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, document.cells.length, []); - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - } - assert.strictEqual(document.cells.length, 0); - - await saveFileAndCloseAll(resource); - }); - - test('workspace edit API (replaceCells, event)', async function () { - - assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const { document } = vscode.window.activeNotebookEditor!; - assert.strictEqual(document.cells.length, 1); - - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCells(document.uri, 0, 0, [{ - cellKind: vscode.NotebookCellKind.Markdown, - language: 'markdown', - metadata: undefined, - outputs: [], - source: 'new_markdown' - }, { - cellKind: vscode.NotebookCellKind.Code, - language: 'fooLang', - metadata: undefined, - outputs: [], - source: 'new_code' - }]); - - const event = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - - const success = await vscode.workspace.applyEdit(edit); - assert.strictEqual(success, true); - - const data = await event; - - // check document - assert.strictEqual(document.cells.length, 3); - assert.strictEqual(document.cells[0].document.getText(), 'new_markdown'); - assert.strictEqual(document.cells[1].document.getText(), 'new_code'); - - // check event data - assert.strictEqual(data.document === document, true); - assert.strictEqual(data.changes.length, 1); - assert.strictEqual(data.changes[0].deletedCount, 0); - assert.strictEqual(data.changes[0].deletedItems.length, 0); - assert.strictEqual(data.changes[0].items.length, 2); - assert.strictEqual(data.changes[0].items[0], document.cells[0]); - assert.strictEqual(data.changes[0].items[1], document.cells[1]); - await saveFileAndCloseAll(resource); - }); - test('edit API batch edits', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); @@ -697,11 +627,11 @@ suite('Notebook API tests', () => { test('edit API batch edits undo/redo', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); - const cellMetadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + const cellMetadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); @@ -724,7 +654,7 @@ suite('Notebook API tests', () => { test('initialzation should not emit cell change events.', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); let count = 0; const disposables: vscode.Disposable[] = []; @@ -739,12 +669,13 @@ suite('Notebook API tests', () => { await saveFileAndCloseAll(resource); }); -}); + // }); + + // suite('notebook workflow', () => { -suite('notebook workflow', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -766,7 +697,7 @@ suite('notebook workflow', () => { test('notebook cell actions', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -840,7 +771,7 @@ suite('notebook workflow', () => { test('notebook join cells', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -852,7 +783,7 @@ suite('notebook workflow', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); await vscode.commands.executeCommand('notebook.cell.joinAbove'); await cellsChangeEvent; @@ -864,7 +795,7 @@ suite('notebook workflow', () => { test('move cells will not recreate cells in ExtHost', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); @@ -884,7 +815,7 @@ suite('notebook workflow', () => { }); // test.only('document metadata is respected', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); @@ -909,7 +840,7 @@ suite('notebook workflow', () => { test('cell runnable metadata is respected', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -918,14 +849,14 @@ suite('notebook workflow', () => { const cell = editor.document.cells[0]; assert.strictEqual(cell.outputs.length, 0); - let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + let metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: false }); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work - metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); + metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: true }); await metadataChangeEvent; @@ -938,7 +869,7 @@ suite('notebook workflow', () => { test('document runnable metadata is respected', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -973,7 +904,7 @@ suite('notebook workflow', () => { // TODO@rebornix this is wrong, `await vscode.commands.executeCommand('notebook.execute');` doesn't wait until the workspace edit is applied test.skip('cell execute command takes arguments', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -988,7 +919,7 @@ suite('notebook workflow', () => { test('cell execute command takes arguments 2', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; @@ -1011,7 +942,7 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 0, 'should clear'); }); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await withEvent(vscode.notebook.onDidChangeCellOutputs, async (event) => { @@ -1029,13 +960,13 @@ suite('notebook workflow', () => { test('document execute command takes arguments', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cells[0]; - const metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); await metadataChangeEvent; assert.strictEqual(editor.document.metadata.runnable, true); @@ -1046,12 +977,12 @@ suite('notebook workflow', () => { assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked }); - const clearChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellOutputs); + const clearChangeEvent = asPromise(vscode.notebook.onDidChangeCellOutputs); await vscode.commands.executeCommand('notebook.cell.clearOutputs'); await clearChangeEvent; assert.strictEqual(cell.outputs.length, 0, 'should clear'); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await withEvent(vscode.notebook.onDidChangeCellOutputs, async (event) => { @@ -1069,13 +1000,13 @@ suite('notebook workflow', () => { test('cell execute and select kernel', async () => { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cells[0]; - const metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); + const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); await metadataChangeEvent; @@ -1087,7 +1018,7 @@ suite('notebook workflow', () => { 'my output' ]); - await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-notebook-tests', id: 'secondaryKernel' }); + await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: 'secondaryKernel' }); await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked assert.strictEqual(cell.outputs[0].outputs.length, 1); @@ -1099,12 +1030,12 @@ suite('notebook workflow', () => { await vscode.commands.executeCommand('workbench.action.files.save'); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); }); -}); + // }); -suite('notebook dirty state', () => { + // suite('notebook dirty state', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -1133,12 +1064,12 @@ suite('notebook dirty state', () => { await saveFileAndCloseAll(resource); }); -}); + // }); -suite('notebook undo redo', () => { + // suite('notebook undo redo', () => { test('notebook open', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test'); @@ -1181,7 +1112,7 @@ suite('notebook undo redo', () => { // test.skip('execute and then undo redo', async function () { // assertInitalState(); - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // const cellsChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells); @@ -1242,11 +1173,11 @@ suite('notebook undo redo', () => { // await saveFileAndCloseAll(resource); // }); -}); + // }); -suite('notebook working copy', () => { + // suite('notebook working copy', () => { // test('notebook revert on close', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1267,7 +1198,7 @@ suite('notebook working copy', () => { // }); // test('notebook revert', async function () { - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1288,7 +1219,7 @@ suite('notebook working copy', () => { test('multiple tabs: dirty + clean', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1298,7 +1229,7 @@ suite('notebook working copy', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); @@ -1314,7 +1245,7 @@ suite('notebook working copy', () => { test('multiple tabs: two dirty tabs and switching', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1324,7 +1255,7 @@ suite('notebook working copy', () => { edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); await vscode.workspace.applyEdit(edit); - const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb'); + const secondResource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), ''); @@ -1353,7 +1284,7 @@ suite('notebook working copy', () => { test('multiple tabs: different editors with same document', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstNotebookEditor = vscode.window.activeNotebookEditor; assert.strictEqual(firstNotebookEditor !== undefined, true, 'notebook first'); @@ -1375,12 +1306,12 @@ suite('notebook working copy', () => { // await vscode.commands.executeCommand('workbench.action.files.saveAll'); // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); -}); + // }); -suite('metadata', () => { + // suite('metadata', () => { test('custom metadata should be supported', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); @@ -1394,7 +1325,7 @@ suite('metadata', () => { // TODO@rebornix skip as it crashes the process all the time test.skip('custom metadata should be supported 2', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); @@ -1409,9 +1340,9 @@ suite('metadata', () => { await saveFileAndCloseAll(resource); }); -}); + // }); -suite('regression', () => { + // suite('regression', () => { // test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () { // assertInitalState(); // await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" }); @@ -1423,7 +1354,7 @@ suite('regression', () => { test('#106657. Opening a notebook from markers view is broken ', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const document = vscode.window.activeNotebookEditor?.document!; @@ -1441,7 +1372,7 @@ suite('regression', () => { test.skip('Cannot open notebook from cell-uri with vscode.open-command', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const document = vscode.window.activeNotebookEditor?.document!; @@ -1459,7 +1390,7 @@ suite('regression', () => { test('#97830, #97764. Support switch to other editor types', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); const edit = new vscode.WorkspaceEdit(); @@ -1485,7 +1416,7 @@ suite('regression', () => { // open text editor, pin, and then open a notebook test('#96105 - dirty editors', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); const edit = new vscode.WorkspaceEdit(); edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;'); @@ -1509,7 +1440,7 @@ suite('regression', () => { test('#102423 - copy/paste shares the same text buffer', async function () { assertInitalState(); - const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); let activeCell = vscode.window.activeNotebookEditor!.selection; @@ -1530,16 +1461,16 @@ suite('regression', () => { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); -}); + // }); -suite('webview', () => { + // suite('webview', () => { // for web, `asWebUri` gets `https`? // test('asWebviewUri', async function () { // if (vscode.env.uiKind === vscode.UIKind.Web) { // return; // } - // const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + // const resource = await createRandomFile('', undefined, '.vsctestnb'); // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); // const uri = vscode.window.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png')); diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 0451ebf9283..153ce130e24 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -34,16 +34,6 @@ } ], "notebookProvider": [ - { - "viewType": "notebookCoreTest", - "displayName": "Notebook Core Test", - "selector": [ - { - "filenamePattern": "*.vsctestnb", - "excludeFileNamePattern": "" - } - ] - }, { "viewType": "notebookSmokeTest", "displayName": "Notebook Smoke Test", diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts index c856d19e46c..8cf19b430be 100644 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts @@ -9,128 +9,6 @@ import { smokeTestActivate } from './notebookSmokeTestMain'; export function activate(context: vscode.ExtensionContext): any { smokeTestActivate(context); - context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', { - openNotebook: async (_resource: vscode.Uri): Promise => { - if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { - return { - metadata: {}, - cells: [] - }; - } - const dto: vscode.NotebookData = { - metadata: { - custom: { testMetadata: false } - }, - cells: [ - { - source: 'test', - language: 'typescript', - cellKind: vscode.NotebookCellKind.Code, - outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } - } - ] - }; - return dto; - }, - resolveNotebook: async (_document: vscode.NotebookDocument) => { - return; - }, - saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { - return; - }, - saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => { - return; - }, - backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => { - return { - id: '1', - delete: () => { } - }; - } - })); - const kernel: vscode.NotebookKernel = { - id: 'mainKernel', - label: 'Notebook Test Kernel', - isPreferred: true, - supportedLanguages: ['typescript'], - executeAllCells: async (_document: vscode.NotebookDocument) => { - const edit = new vscode.WorkspaceEdit(); - - edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) - ])]); - return vscode.workspace.applyEdit(edit); - }, - cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, - executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { - if (!cell) { - cell = document.cells[0]; - } - - if (document.uri.path.endsWith('customRenderer.vsctestnb')) { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - } - - const edit = new vscode.WorkspaceEdit(); - // const previousOutputs = cell.outputs; - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - }, - cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } - }; - - const kernel2: vscode.NotebookKernel = { - id: 'secondaryKernel', - label: 'Notebook Secondary Test Kernel', - isPreferred: false, - supportedLanguages: ['typescript'], - executeAllCells: async (_document: vscode.NotebookDocument) => { - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) - ])]); - - return vscode.workspace.applyEdit(edit); - }, - cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { }, - executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => { - if (!cell) { - cell = document.cells[0]; - } - - const edit = new vscode.WorkspaceEdit(); - - if (document.uri.path.endsWith('customRenderer.vsctestnb')) { - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined) - ])]); - } else { - edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined) - ])]); - } - - return vscode.workspace.applyEdit(edit); - }, - cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { } - }; - - context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, { - provideKernels: async () => { - return [kernel, kernel2]; - } - })); } From f1202da4a00a64d6fd685c22ec3b3de717a28673 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 12 Feb 2021 10:30:38 -0800 Subject: [PATCH 015/176] Simplify search view tree height calculation Fix #116182 --- .../contrib/search/browser/media/searchview.css | 11 +++++++++++ .../workbench/contrib/search/browser/searchView.ts | 12 +----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 896a356acac..057347da4d8 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -3,6 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.search-view { + display: flex; + flex-direction: column; + height: 100%; +} + +.search-view .results { + flex-grow: 1; + min-height: 0; /* Allow it to be smaller than its contents */ +} + .search-view .search-widgets-container { margin: 0px 12px 0 2px; padding-top: 6px; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 40f36f73b52..8350e27ee2d 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1038,17 +1038,7 @@ export class SearchView extends ViewPane { this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */); this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */); - const messagesSize = this.messagesElement.style.display === 'none' ? - 0 : - dom.getTotalHeight(this.messagesElement); - - const searchResultContainerHeight = this.size.height - - messagesSize - - dom.getTotalHeight(this.searchWidgetsContainerElement); - - this.resultsElement.style.height = searchResultContainerHeight + 'px'; - - this.tree.layout(searchResultContainerHeight, this.size.width); + this.tree.layout(); // The tree will measure its container } protected layoutBody(height: number, width: number): void { From 33d715555c15b70885222fa0726851b58f5f4987 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 12 Feb 2021 10:37:14 -0800 Subject: [PATCH 016/176] Remeove "Skip" link, ref #114964. --- .../contrib/welcome/gettingStarted/browser/gettingStarted.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index cb7bcf8f3c9..ef8331f915d 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -324,7 +324,7 @@ export class GettingStartedPage extends EditorPane { const showOnStartupCheckbox = $('input.checkbox', { id: 'showOnStartup', type: 'checkbox' }) as HTMLInputElement; categoryScrollContainer.appendChild( $('.footer', {}, - $('button.skip.button-link', { 'x-dispatch': 'skip' }, localize('gettingStarted.skip', "Skip")), + // $('button.skip.button-link', { 'x-dispatch': 'skip' }, localize('gettingStarted.skip', "Skip")), $('p.showOnStartup', {}, showOnStartupCheckbox, $('label.caption', { for: 'showOnStartup' }, localize('welcomePage.showOnStartup', "Show Getting Started page on startup"))) From 0568d26fad687f580ff53350e8fd3bbf932b732f Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Fri, 12 Feb 2021 18:54:11 +0000 Subject: [PATCH 017/176] Revert "Support the new 4.0 definition of isArray (#102413)" (#116571) This reverts commit d47ddb670db0aaa025feba46f99d3bb84e6e25e1. --- src/vs/base/common/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index da93a17e08a..a91a3cfd174 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; /** * @returns whether the provided parameter is a JavaScript Array or not. */ -export function isArray(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] { +export function isArray(array: any): array is any[] { return Array.isArray(array); } From 2e1166cb0c204c048de89eab009aaa8dec616ef2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 11 Feb 2021 20:07:38 -0800 Subject: [PATCH 018/176] Make sure webview editor has not been disposed before claiming webview Fixes #115743 Also added an exception to make sure we don't call `show` on a disposed of webview editor overlay --- .../contrib/webview/browser/dynamicWebviewEditorOverlay.ts | 7 +++++++ .../contrib/webviewPanel/browser/webviewEditor.ts | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index d67cf68e348..102f7132e9c 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -60,10 +60,13 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv return !!this._webview.value?.isFocused; } + private _isDisposed = false; + private readonly _onDidDispose = this._register(new Emitter()); public onDidDispose = this._onDidDispose.event; dispose() { + this._isDisposed = true; this.container.remove(); this._onDidDispose.fire(); super.dispose(); @@ -138,6 +141,10 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv } private show() { + if (this._isDisposed) { + throw new Error('Webview overlay is disposed'); + } + if (!this._webview.value) { const webview = this._webviewService.createWebviewElement(this.id, this._options, this._contentOptions, this.extension); this._webview.value = webview; diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 1eebb76e499..5a6f5253013 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -31,6 +31,7 @@ export class WebviewEditor extends EditorPane { private _element?: HTMLElement; private _dimension?: DOM.Dimension; private _visible = false; + private _isDisposed = false; private readonly _webviewVisibleDisposables = this._register(new DisposableStore()); private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); @@ -71,6 +72,8 @@ export class WebviewEditor extends EditorPane { } public dispose(): void { + this._isDisposed = true; + if (this._element) { this._element.remove(); this._element = undefined; @@ -133,7 +136,7 @@ export class WebviewEditor extends EditorPane { await super.setInput(input, options, context, token); await input.resolve(); - if (token.isCancellationRequested) { + if (token.isCancellationRequested || this._isDisposed) { return; } From 2cb4179ec6ab930510e2595e1a982b333a224dc8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 11 Feb 2021 20:25:39 -0800 Subject: [PATCH 019/176] Make sure the webview gets focused even if the actual webview content does not yet exist Fixes #112677 --- src/vs/workbench/contrib/webview/browser/pre/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index b2f093ee195..cc3bc25eab0 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -446,6 +446,8 @@ host.onMessage('focus', () => { const activeFrame = getActiveFrame(); if (!activeFrame || !activeFrame.contentWindow) { + // Focus the top level webview instead + window.focus(); return; } From 27e1db98c128f99c52b5c93705ed064068642544 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 11 Feb 2021 20:25:59 -0800 Subject: [PATCH 020/176] Move some additional focus fixed from electron webviews to iframe webviews --- .../electron-sandbox/iframeWebviewElement.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts index 672018e2ce4..5321866d575 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement.ts @@ -128,8 +128,16 @@ export class ElectronIframeWebview extends IFrameWebview { return; } + // Clear the existing focus first if not already on the webview. + // This is required because the next part where we set the focus is async. + if (document.activeElement && document.activeElement instanceof HTMLElement && document.activeElement !== this.element) { + // Don't blur if on the webview because this will also happen async and may unset the focus + // after the focus trigger fires below. + document.activeElement.blur(); + } + // Workaround for https://github.com/microsoft/vscode/issues/75209 - // .focus is async for imframes so for a sequence of actions such as: + // Electron's webview.focus is async so for a sequence of actions such as: // // 1. Open webview // 1. Show quick pick from command palette @@ -143,8 +151,7 @@ export class ElectronIframeWebview extends IFrameWebview { if (!this.isFocused || !this.element) { return; } - - if (document.activeElement?.tagName === 'INPUT') { + if (document.activeElement && document.activeElement?.tagName !== 'BODY') { return; } try { From fab9533c5ce01fc32f44a53e75e789acac3796ea Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 12 Feb 2021 11:37:20 -0800 Subject: [PATCH 021/176] Add some aria roles ref #115896 --- .../gettingStarted/browser/gettingStarted.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index ef8331f915d..d17e7d5c275 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -302,7 +302,9 @@ export class GettingStartedPage extends EditorPane { $('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description), $('.category-progress', { 'x-data-category-id': category.id, }, $('.message'), - $('.progress-bar-outer', {}, + $('.progress-bar-outer', { + 'role': 'progressbar' + }, $('.progress-bar-inner')))) : $('.category-description-container', {}, @@ -310,12 +312,15 @@ export class GettingStartedPage extends EditorPane { $('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description)); return $('button.getting-started-category', - { 'x-dispatch': 'selectCategory:' + category.id, }, + { + 'x-dispatch': 'selectCategory:' + category.id, + 'role': 'listitem', + }, $(ThemeIcon.asCSSSelector(category.icon), {}), categoryDescriptionElement); }); const categoryScrollContainer = $('.getting-started-categories-scrolling-container'); - const categoriesContainer = $('.getting-started-categories-container'); + const categoriesContainer = $('.getting-started-categories-container', { 'role': 'list' }); categoryElements.forEach(element => { categoriesContainer.appendChild(element); }); @@ -384,6 +389,10 @@ export class GettingStartedPage extends EditorPane { const message = assertIsDefined(element.firstChild); const bar = assertIsDefined(element.querySelector('.progress-bar-inner')) as HTMLDivElement; + bar.setAttribute('aria-valuemin', '0'); + bar.setAttribute('aria-valuenow', '' + numDone); + bar.setAttribute('aria-valuemax', '' + numTotal); + bar.style.width = `${(numDone / numTotal) * 100}%`; if (numTotal === numDone) { @@ -429,6 +438,7 @@ export class GettingStartedPage extends EditorPane { 'x-dispatch': 'selectTask:' + task.id, 'data-task-id': task.id, 'aria-expanded': 'false', + 'role': 'listitem', }, $('.codicon' + (task.done ? '.complete.codicon-pass-filled' : '.codicon-circle-large-outline'), { 'data-done-task-id': task.id }), $('.task-description-container', {}, @@ -451,7 +461,7 @@ export class GettingStartedPage extends EditorPane { )) ))); - const detailContainer = $('.getting-started-detail-container'); + const detailContainer = $('.getting-started-detail-container', { 'role': 'list' }); if (this.detailsScrollbar) { this.detailsScrollbar.getDomNode().remove(); this.detailsScrollbar.dispose(); } this.detailsScrollbar = this._register(new DomScrollableElement(detailContainer, { className: 'full-height-scrollable' })); categoryElements.forEach(element => detailContainer.appendChild(element)); From 653f025dfdb664ecee854454c472e4db6b36781e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 12 Feb 2021 12:08:57 -0800 Subject: [PATCH 022/176] Enable ts check for service-worker Fixes #116533 --- .../webview/browser/pre/service-worker.js | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index 1030c047421..f6c93682fea 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -2,13 +2,18 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// @ts-check + +/// /// +const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {any} */ (self)); + const VERSION = 1; const resourceCacheName = `vscode-resource-cache-${VERSION}`; -const rootPath = self.location.pathname.replace(/\/service-worker.js$/, ''); +const rootPath = sw.location.pathname.replace(/\/service-worker.js$/, ''); /** * Root path for resources @@ -101,11 +106,12 @@ const localhostRequestStore = new RequestStore(); const notFound = () => new Response('Not Found', { status: 404, }); -self.addEventListener('message', async (event) => { +sw.addEventListener('message', async (event) => { switch (event.data.channel) { case 'version': { - self.clients.get(event.source.id).then(client => { + const source = /** @type {Client} */ (event.source); + sw.clients.get(source.id).then(client => { if (client) { client.postMessage({ channel: 'version', @@ -153,26 +159,26 @@ self.addEventListener('message', async (event) => { console.log('Unknown message'); }); -self.addEventListener('fetch', (event) => { +sw.addEventListener('fetch', (event) => { const requestUrl = new URL(event.request.url); // See if it's a resource request - if (requestUrl.origin === self.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { + if (requestUrl.origin === sw.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { return event.respondWith(processResourceRequest(event, requestUrl)); } // See if it's a localhost request - if (requestUrl.origin !== self.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { + if (requestUrl.origin !== sw.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { return event.respondWith(processLocalhostRequest(event, requestUrl)); } }); -self.addEventListener('install', (event) => { - event.waitUntil(self.skipWaiting()); // Activate worker immediately +sw.addEventListener('install', (event) => { + event.waitUntil(sw.skipWaiting()); // Activate worker immediately }); -self.addEventListener('activate', (event) => { - event.waitUntil(self.clients.claim()); // Become available to all pages +sw.addEventListener('activate', (event) => { + event.waitUntil(sw.clients.claim()); // Become available to all pages }); /** @@ -180,7 +186,7 @@ self.addEventListener('activate', (event) => { * @param {URL} requestUrl */ async function processResourceRequest(event, requestUrl) { - const client = await self.clients.get(event.clientId); + const client = await sw.clients.get(event.clientId); if (!client) { console.log('Could not find inner client for request'); return notFound(); @@ -252,7 +258,7 @@ async function processResourceRequest(event, requestUrl) { * @param {URL} requestUrl */ async function processLocalhostRequest(event, requestUrl) { - const client = await self.clients.get(event.clientId); + const client = await sw.clients.get(event.clientId); if (!client) { // This is expected when requesting resources on other localhost ports // that are not spawned by vs code @@ -299,7 +305,7 @@ function getWebviewIdForClient(client) { } async function getOuterIframeClient(webviewId) { - const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + const allClients = await sw.clients.matchAll({ includeUncontrolled: true }); return allClients.find(client => { const clientUrl = new URL(client.url); return (clientUrl.pathname === `${rootPath}/` || clientUrl.pathname === `${rootPath}/index.html`) && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); From 7661c8e35cfed8fddf98adf8191659b0f0f88b0e Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Fri, 12 Feb 2021 20:10:52 +0000 Subject: [PATCH 023/176] Move emmet to emmetio/emmet npm dependency, fixes #110697 --- extensions/emmet/package.json | 5 ++--- extensions/emmet/yarn.lock | 33 +++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index c786c715cde..dea1a8fae08 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -430,8 +430,7 @@ "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { - "@types/node": "^12.19.9", - "emmet": "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" + "@types/node": "^12.19.9" }, "dependencies": { "@emmetio/abbreviation": "^2.2.0", @@ -439,7 +438,7 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^1.0.4", "image-size": "^0.5.2", - "vscode-emmet-helper": "2.2.4", + "vscode-emmet-helper": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1" } } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index bebce02cf48..e9ac076c3f0 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@emmetio/abbreviation@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.0.tgz#9f8dedbdb00e3136d6d37c6415375c82c0bb477f" - integrity sha512-NPGVUmnr7cLj4i6MKS4c8NjuoIIJROrruJl/8nXsp2MdbDRHvtfq25foySvv/NbfqTQm+P9JzVLDD9JxGIpvkQ== +"@emmetio/abbreviation@^2.2.0", "@emmetio/abbreviation@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.1.tgz#d9458fe1f09fe042f019c48aa681165ba613a48d" + integrity sha512-uUNwNgbH0JPlrdXhy8VQbNPLLG7abMvOaLVMblx22i68Rl9r+2N235ALgIYFUty1yXC9DkVw6xMbz/D4QVARcQ== dependencies: "@emmetio/scanner" "^1.0.0" @@ -54,15 +54,16 @@ integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@^12.19.9": - version "12.19.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.15.tgz#0de7e978fb43db62da369db18ea088a63673c182" - integrity sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw== + version "12.20.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.0.tgz#692dfdecd6c97f5380c42dd50f19261f9f604deb" + integrity sha512-0/41wHcurotvSOTHQUFkgL702c3pyWR1mToSrrX3pGPvGfpHTv3Ksx0M4UVuU5VJfjVb62Eyr1eKO1tWNUCg2Q== -"emmet@https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a": - version "2.3.0" - resolved "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" +emmet@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.1.tgz#77614d949d1d01e5c248d08043a13a7f4d539e47" + integrity sha512-u8h++9u3y9QWhn0imUXfQO+s80To5MGD97zd/00wGC39CfNGBPe//ZKepJz9I1LQ2FDRXHrn+e3JaN/53Y5z6A== dependencies: - "@emmetio/abbreviation" "^2.2.0" + "@emmetio/abbreviation" "^2.2.1" "@emmetio/css-abbreviation" "^2.1.2" image-size@^0.5.2: @@ -75,12 +76,12 @@ jsonc-parser@^2.3.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== -vscode-emmet-helper@2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.2.4.tgz#8ab86d2b7fe9e6270b4c77c9fd8d1eb8f3f4c401" - integrity sha512-1N6bMzP1ZzkDGzamvsKxQ/lOmBc4+OQdj0dA2C9A5PSeYV9gh5xbJ061sm+VyFHOGZE+VyUQq5m/WFmFsLbKnA== +vscode-emmet-helper@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.3.0.tgz#a98357ad5ac9c71d7c00396f22b7963b1a74cc5c" + integrity sha512-QhU8+HlynMuUkqBYgA3wIDTSsUNkw8GWxLR214Hjvwr0lkFZa0CRqW/PzAI1CFREjSrTxJYQvkVnbfatZzKAuA== dependencies: - emmet "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a" + emmet "^2.3.0" jsonc-parser "^2.3.0" vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" From 1170b3158e38b516f78514ec7b4e8a288bd69a37 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Fri, 12 Feb 2021 15:54:57 -0500 Subject: [PATCH 024/176] Emmet: add output.reverseAttributes option (#116088) Fixes #110251 --- extensions/emmet/package.json | 5 +++++ extensions/emmet/package.nls.json | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index dea1a8fae08..0766cda7bc4 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -212,6 +212,11 @@ "type": "number", "default": 0.3, "description": "%emmetPreferencesCssFuzzySearchMinScore%" + }, + "output.reverseAttributes": { + "type": "boolean", + "default": false, + "description": "%emmetPreferencesOutputReverseAttributes%" } } }, diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 2a1add8935e..8e168ec4c4e 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -55,5 +55,6 @@ "emmetPreferencesCssOProperties": "Comma separated CSS properties that get the 'o' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'o' prefix.", "emmetPreferencesCssMsProperties": "Comma separated CSS properties that get the 'ms' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'ms' prefix.", "emmetPreferencesCssFuzzySearchMinScore": "The minimum score (from 0 to 1) that fuzzy-matched abbreviation should achieve. Lower values may produce many false-positive matches, higher values may reduce possible matches.", - "emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed." + "emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed.", + "emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets." } From 53c2e1b23c77d850ab4109c7bfcf201909fe294c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 10:11:14 -0800 Subject: [PATCH 025/176] testing: close peek view when associate test state changes Fixes https://github.com/microsoft/vscode/issues/115003 --- .../hierarchalByLocation.ts | 10 +-- .../testing/browser/testingDecorations.ts | 6 +- .../testing/browser/testingExplorerView.ts | 2 +- .../testing/browser/testingOutputPeek.ts | 41 ++++++++++- .../testing/common/testResultService.ts | 70 ++++++++++--------- .../contrib/testing/common/testingAutoRun.ts | 13 ++-- .../testing/common/testingContentProvider.ts | 2 +- .../contrib/testing/common/testingUri.ts | 14 ++-- .../test/common/testResultService.test.ts | 35 ++++++---- .../testing/test/common/testingUri.test.ts | 6 +- 10 files changed, 127 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index d785bf0de24..98d7a14b0dc 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -81,12 +81,12 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes // when test states change, reflect in the tree // todo: optimize this to avoid needing to iterate - this._register(results.onTestChanged(([, { item, state, retired, computedState }]) => { + this._register(results.onTestChanged(({ result }) => { for (const i of this.items.values()) { - if (i.test.item.extId === item.extId) { - i.ownState = state.state; - i.retired = retired; - refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState); + if (i.test.item.extId === result.item.extId) { + i.ownState = result.state.state; + i.retired = result.retired; + refreshComputedState(computedStateAccessor, i, this.addUpdated, result.computedState); this.addUpdated(i); this.updateEmitter.fire(); return; diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 01d809934ca..0bb37457281 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -63,8 +63,8 @@ export class TestingDecorations extends Disposable implements IEditorContributio } this.collection.value = this.testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, uri, () => this.setDecorations(uri)); - this._register(this.results.onTestChanged(([, changed]) => { - if (changed.item.location?.uri.toString() === uri.toString()) { + this._register(this.results.onTestChanged(({ result }) => { + if (result.item.location?.uri.toString() === uri.toString()) { this.setDecorations(uri); } })); @@ -102,7 +102,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio type: TestUriType.ResultActualOutput, messageIndex: i, resultId: result.id, - testId: stateItem.item.extId, + testExtId: stateItem.item.extId, }); newDecorations.push(this.instantiationService.createInstance(TestMessageDecoration, m, uri, m.location, this.editor)); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b1a3c35be43..b9792a16dc4 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -411,7 +411,7 @@ export class TestingExplorerViewModel extends Disposable { type: TestUriType.ResultMessage, messageIndex: index, resultId: result.id, - testId: item.test!.item.extId, + testExtId: item.test!.item.extId, })); return true; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 081c8893ad9..89361e1f09d 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -30,7 +30,7 @@ import { Testing } from 'vs/workbench/contrib/testing/common/constants'; import { ITestItem, ITestMessage, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { buildTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResultService, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; interface ITestDto { test: ITestItem, @@ -71,6 +71,18 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo super(); this.visible = TestingContextKeys.isPeekVisible.bindTo(contextKeyService); this._register(editor.onDidChangeModel(() => this.peek.clear())); + this._register(testResults.onTestChanged((evt) => { + // if the test we're currently showing has its state change to something + // else, then clear the peek + if (evt.reason !== TestResultItemChangeReason.OwnStateChange || evt.previous.state === evt.result.state.state) { + return; + } + + const displayed = this.peek.value?.currentTest(); + if (displayed?.extId === evt.result.item.extId) { + this.peek.clear(); + } + })); } /** @@ -113,13 +125,13 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.clear(); } - private async retrieveTest(uri: URI): Promise { + private retrieveTest(uri: URI): ITestDto | undefined { const parts = parseTestUri(uri); if (!parts) { return undefined; } - const test = this.testResults.getResult(parts.resultId)?.getStateByExtId(parts.testId); + const test = this.testResults.getResult(parts.resultId)?.getStateByExtId(parts.testExtId); return test && { test: test.item, state: test.state, @@ -168,6 +180,11 @@ abstract class TestingOutputPeek extends PeekViewWidget { */ public abstract setModel(dto: ITestDto): Promise; + /** + * Returns the test whose data is currently shown in the peek view. + */ + public abstract currentTest(): ITestItem | undefined; + /** * @override */ @@ -205,6 +222,7 @@ const diffEditorOptions: IDiffEditorOptions = { class TestingDiffOutputPeek extends TestingOutputPeek { private readonly diff = this._disposables.add(new MutableDisposable()); + private test: ITestItem | undefined; /** * @override @@ -227,6 +245,7 @@ class TestingDiffOutputPeek extends TestingOutputPeek { return; } + this.test = test; this.show(message.location.range, hintDiffPeekHeight(message)); this.setTitle(message.message.toString(), test.label); @@ -243,6 +262,13 @@ class TestingDiffOutputPeek extends TestingOutputPeek { } } + /** + * @override + */ + public currentTest() { + return this.test; + } + /** * @override */ @@ -254,6 +280,7 @@ class TestingDiffOutputPeek extends TestingOutputPeek { class TestingMessageOutputPeek extends TestingOutputPeek { private readonly preview = this._disposables.add(new MutableDisposable()); + private test: ITestItem | undefined; /** * @override @@ -276,6 +303,7 @@ class TestingMessageOutputPeek extends TestingOutputPeek { return; } + this.test = test; this.show(message.location.range, hintPeekStrHeight(message.message.toString())); this.setTitle(message.message.toString(), test.label); @@ -287,6 +315,13 @@ class TestingMessageOutputPeek extends TestingOutputPeek { } } + /** + * @override + */ + public currentTest() { + return this.test; + } + /** * @override */ diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index a0262052764..5ce95376f8b 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -22,6 +22,18 @@ import { IMainThreadTestCollection } from 'vs/workbench/contrib/testing/common/t */ export type TestStateCount = { [K in TestRunState]: number }; +export const enum TestResultItemChangeReason { + Retired, + ParentRetired, + ComputedStateChange, + OwnStateChange, +} + +export type TestResultItemChange = { result: TestResultItem; } & ( + | { reason: TestResultItemChangeReason.Retired | TestResultItemChangeReason.ParentRetired | TestResultItemChangeReason.ComputedStateChange } + | { reason: TestResultItemChangeReason.OwnStateChange; previous: ITestState } +); + export interface ITestResult { /** * Count of the number of tests in each run state. @@ -188,11 +200,9 @@ export class LiveTestResult implements ITestResult { } private readonly completeEmitter = new Emitter(); - private readonly retireEmitter = new Emitter(); - private readonly changeEmitter = new Emitter(); + private readonly changeEmitter = new Emitter(); private _complete = false; - public readonly onRetired = this.retireEmitter.event; public readonly onChange = this.changeEmitter.event; public readonly onComplete = this.completeEmitter.event; @@ -272,10 +282,7 @@ export class LiveTestResult implements ITestResult { public setAllToState(state: ITestState, when: (_t: TestResultItem) => boolean) { for (const test of this.testByInternalId.values()) { if (when(test)) { - this.counts[test.state.state]--; - test.state = state; - this.counts[state.state]++; - refreshComputedState(this.computedStateAccessor, test, t => this.changeEmitter.fire(t)); + this.fireUpdateAndRefresh(test, state); } } } @@ -292,15 +299,22 @@ export class LiveTestResult implements ITestResult { return; } - if (state.state === entry.state.state) { - entry.state = state; - this.changeEmitter.fire(entry); - } else { - this.counts[entry.state.state]--; - entry.state = state; - this.counts[entry.state.state]++; - refreshComputedState(this.computedStateAccessor, entry, t => this.changeEmitter.fire(t)); + this.fireUpdateAndRefresh(entry, state); + } + + private fireUpdateAndRefresh(entry: TestResultItem, newState: ITestState) { + const previous = entry.state; + entry.state = newState; + + if (newState.state !== previous.state) { + this.counts[previous.state]--; + this.counts[newState.state]++; + refreshComputedState(this.computedStateAccessor, entry, t => ( + t !== entry && this.changeEmitter.fire({ result: t, reason: TestResultItemChangeReason.ComputedStateChange }) + )); } + + this.changeEmitter.fire({ result: entry, reason: TestResultItemChangeReason.OwnStateChange, previous }); } /** @@ -312,7 +326,6 @@ export class LiveTestResult implements ITestResult { return; } - this.retireEmitter.fire(root); const queue: Iterable[] = [[root.id]]; while (queue.length) { for (const id of queue.pop()!) { @@ -320,7 +333,12 @@ export class LiveTestResult implements ITestResult { if (entry && !entry.retired) { entry.retired = true; queue.push(entry.children); - this.changeEmitter.fire(entry); + this.changeEmitter.fire({ + result: entry, + reason: entry === root + ? TestResultItemChangeReason.Retired + : TestResultItemChangeReason.ParentRetired + }); } } } @@ -442,12 +460,7 @@ export interface ITestResultService { /** * Fired when a test changed it state, or its computed state is updated. */ - readonly onTestChanged: Event<[results: ITestResult, item: TestResultItem]>; - - /** - * Fired when a test is retired, in addition to `onTestChanged`. - */ - readonly onTestRetired: Event; + readonly onTestChanged: Event; /** * List of known test results. @@ -482,8 +495,7 @@ const RETAIN_LAST_RESULTS = 64; export class TestResultService implements ITestResultService { declare _serviceBrand: undefined; private changeResultEmitter = new Emitter(); - private testRetiredEmitter = new Emitter(); - private testChangeEmitter = new Emitter<[results: ITestResult, item: TestResultItem]>(); + private testChangeEmitter = new Emitter(); /** * @inheritdoc @@ -500,11 +512,6 @@ export class TestResultService implements ITestResultService { */ public readonly onTestChanged = this.testChangeEmitter.event; - /** - * @inheritdoc - */ - public readonly onTestRetired = this.testRetiredEmitter.event; - private readonly isRunning: IContextKey; private readonly serializedResults: StoredValue; @@ -545,8 +552,7 @@ export class TestResultService implements ITestResultService { } result.onComplete(() => this.onComplete(result)); - result.onChange(t => this.testChangeEmitter.fire([result, t]), this.testChangeEmitter); - result.onRetired(this.testRetiredEmitter.fire, this.testRetiredEmitter); + result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter); this.isRunning.set(true); this.changeResultEmitter.fire({ started: result }); result.setAllToState(queuedState, () => true); diff --git a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts index 360bbf09c31..33ea8045ba4 100644 --- a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts +++ b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts @@ -13,7 +13,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResultService, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; @@ -90,10 +90,15 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun { } }, delay)); - store.add(this.results.onTestRetired(test => { + store.add(this.results.onTestChanged(evt => { + if (evt.reason !== TestResultItemChangeReason.Retired) { + return; + } + + const { extId } = evt.result.item; const workspaceTest = mapFind(workspaceTests.workspaceFolderCollections, - ([, c]) => c.getNodeById(test.id) ?? Iterable.find(c.all, t => t.item.extId === test.item.extId)); - const subject = workspaceTest ?? test; + ([, c]) => c.getNodeById(evt.result.id) ?? Iterable.find(c.all, t => t.item.extId === extId)); + const subject = workspaceTest ?? evt.result; rerunIds.set(subject.id, ({ testId: subject.id, providerId: subject.providerId })); diff --git a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts index c63a507d15f..b3e91103ce4 100644 --- a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts +++ b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts @@ -38,7 +38,7 @@ export class TestingContentProvider implements IWorkbenchContribution, ITextMode return null; } - const test = this.resultService.getResult(parsed.resultId)?.getStateByExtId(parsed.testId); + const test = this.resultService.getResult(parsed.resultId)?.getStateByExtId(parsed.testExtId); if (!test) { return null; diff --git a/src/vs/workbench/contrib/testing/common/testingUri.ts b/src/vs/workbench/contrib/testing/common/testingUri.ts index 5f99df72ba0..9503c99e152 100644 --- a/src/vs/workbench/contrib/testing/common/testingUri.ts +++ b/src/vs/workbench/contrib/testing/common/testingUri.ts @@ -15,7 +15,7 @@ export const enum TestUriType { interface IResultTestUri { resultId: string; - testId: string; + testExtId: string; } interface IResultTestMessageReference extends IResultTestUri { @@ -43,19 +43,20 @@ const enum TestUriParts { export const parseTestUri = (uri: URI): ParsedTestUri | undefined => { const type = uri.authority; - const [locationId, testId, ...request] = uri.path.slice(1).split('/'); + const [locationId, ...request] = uri.path.slice(1).split('/'); if (request[0] === TestUriParts.Messages) { const index = Number(request[1]); const part = request[2]; + const testExtId = uri.query; if (type === TestUriParts.Results) { switch (part) { case TestUriParts.Text: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultMessage }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultMessage }; case TestUriParts.ActualOutput: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultActualOutput }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultActualOutput }; case TestUriParts.ExpectedOutput: - return { resultId: locationId, testId, messageIndex: index, type: TestUriType.ResultExpectedOutput }; + return { resultId: locationId, testExtId, messageIndex: index, type: TestUriType.ResultExpectedOutput }; } } } @@ -71,7 +72,8 @@ export const buildTestUri = (parsed: ParsedTestUri): URI => { const msgRef = (locationId: string, index: number, ...remaining: string[]) => URI.from({ ...uriParts, - path: ['', locationId, parsed.testId, TestUriParts.Messages, index, ...remaining].join('/'), + query: parsed.testExtId, + path: ['', locationId, TestUriParts.Messages, index, ...remaining].join('/'), }); switch (parsed.type) { diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index c18dab77484..5e243770b86 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -6,28 +6,28 @@ import * as assert from 'assert'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testCollection'; -import { LiveTestResult, makeEmptyCounts, TestResultItem, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { LiveTestResult, makeEmptyCounts, TestResultItemChange, TestResultItemChangeReason, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ReExportedTestRunState as TestRunState } from 'vs/workbench/contrib/testing/common/testStubs'; import { getInitializedMainTestCollection } from 'vs/workbench/contrib/testing/test/common/ownedTestCollection'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Workbench - Test Results Service', () => { const getLabelsIn = (it: Iterable) => [...it].map(t => t.item.label).sort(); + const getChangeSummary = () => [...changed] + .map(c => ({ reason: c.reason, label: c.result.item.label })) + .sort((a, b) => a.label.localeCompare(b.label)); let r: LiveTestResult; - let changed = new Set(); - let retired = new Set(); + let changed = new Set(); setup(() => { changed = new Set(); - retired = new Set(); r = LiveTestResult.from( [getInitializedMainTestCollection()], [{ providerId: 'provider', testId: '1' }] ); r.onChange(e => changed.add(e)); - r.onRetired(e => retired.add(e)); }); suite('LiveTestResult', () => { @@ -38,7 +38,6 @@ suite('Workbench - Test Results Service', () => { test('does not change or retire initially', () => { assert.deepStrictEqual(0, changed.size); - assert.deepStrictEqual(0, retired.size); }); test('initializes with the subtree of requested tests', () => { @@ -61,7 +60,12 @@ suite('Workbench - Test Results Service', () => { }); assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Queued); - assert.deepStrictEqual(getLabelsIn(changed), ['a', 'aa', 'ab', 'root']); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'aa', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'ab', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'root', reason: TestResultItemChangeReason.ComputedStateChange }, + ]); }); test('updateState', () => { @@ -74,20 +78,23 @@ suite('Workbench - Test Results Service', () => { assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Running); // update computed state: assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running); - assert.deepStrictEqual(getLabelsIn(changed), ['a', 'root']); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.OwnStateChange }, + { label: 'root', reason: TestResultItemChangeReason.ComputedStateChange }, + ]); }); test('retire', () => { r.retire('root\0a'); - assert.deepStrictEqual(getLabelsIn(changed), ['a', 'aa', 'ab']); - assert.deepStrictEqual(getLabelsIn(retired), ['a']); + assert.deepStrictEqual(getChangeSummary(), [ + { label: 'a', reason: TestResultItemChangeReason.Retired }, + { label: 'aa', reason: TestResultItemChangeReason.ParentRetired }, + { label: 'ab', reason: TestResultItemChangeReason.ParentRetired }, + ]); - retired.clear(); changed.clear(); - r.retire('root\0a'); - assert.deepStrictEqual(getLabelsIn(changed), []); - assert.deepStrictEqual(getLabelsIn(retired), []); + assert.strictEqual(changed.size, 0); }); test('addTestToRun', () => { diff --git a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts index 41d534e5d69..e9c1d60412e 100644 --- a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts @@ -9,9 +9,9 @@ import { buildTestUri, ParsedTestUri, parseTestUri, TestUriType } from 'vs/workb suite('Workbench - Testing URIs', () => { test('round trip', () => { const uris: ParsedTestUri[] = [ - { type: TestUriType.ResultActualOutput, messageIndex: 42, resultId: 'r', testId: 't' }, - { type: TestUriType.ResultExpectedOutput, messageIndex: 42, resultId: 'r', testId: 't' }, - { type: TestUriType.ResultMessage, messageIndex: 42, resultId: 'r', testId: 't' }, + { type: TestUriType.ResultActualOutput, messageIndex: 42, resultId: 'r', testExtId: 't' }, + { type: TestUriType.ResultExpectedOutput, messageIndex: 42, resultId: 'r', testExtId: 't' }, + { type: TestUriType.ResultMessage, messageIndex: 42, resultId: 'r', testExtId: 't' }, ]; for (const uri of uris) { From 5c449afc04d2fc758636729d712510e2eea1e3d5 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 12:40:53 -0800 Subject: [PATCH 026/176] testing: automatically open peek view on failures Fixes https://github.com/microsoft/vscode/issues/115769 --- .../hierarchalByLocation.ts | 2 +- .../testing/browser/testing.contribution.ts | 3 +- .../testing/browser/testingDecorations.ts | 2 +- .../testing/browser/testingExplorerView.ts | 36 +---- .../testing/browser/testingOutputPeek.ts | 133 ++++++++++++++++-- .../contrib/testing/common/configuration.ts | 28 +++- .../contrib/testing/common/testCollection.ts | 3 +- .../testing/common/testResultService.ts | 31 ++-- .../contrib/testing/common/testServiceImpl.ts | 2 +- .../contrib/testing/common/testingAutoRun.ts | 8 +- .../test/common/testResultService.test.ts | 10 +- 11 files changed, 187 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index 98d7a14b0dc..47cbf47ad77 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -81,7 +81,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes // when test states change, reflect in the tree // todo: optimize this to avoid needing to iterate - this._register(results.onTestChanged(({ result }) => { + this._register(results.onTestChanged(({ item: result }) => { for (const i of this.items.values()) { if (i.test.item.extId === result.item.extId) { i.ownState = result.state.state; diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 3cbf194564e..295f2c0fa17 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -19,7 +19,7 @@ import { testingViewIcon } from 'vs/workbench/contrib/testing/browser/icons'; import { TestingDecorations } from 'vs/workbench/contrib/testing/browser/testingDecorations'; import { ITestExplorerFilterState, TestExplorerFilterState } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter'; import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView'; -import { CloseTestPeek, TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { CloseTestPeek, ITestingPeekOpener, TestingOutputPeekController, TestingPeekOpener } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer'; import { testingConfiguation } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; @@ -38,6 +38,7 @@ registerSingleton(ITestService, TestService); registerSingleton(ITestResultService, TestResultService); registerSingleton(ITestExplorerFilterState, TestExplorerFilterState); registerSingleton(ITestingAutoRun, TestingAutoRun, true); +registerSingleton(ITestingPeekOpener, TestingPeekOpener); registerSingleton(IWorkspaceTestCollectionService, WorkspaceTestCollectionService); const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 0bb37457281..341c7b70818 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -63,7 +63,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio } this.collection.value = this.testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, uri, () => this.setDecorations(uri)); - this._register(this.results.onTestChanged(({ result }) => { + this._register(this.results.onTestChanged(({ item: result }) => { if (result.item.location?.uri.toString() === uri.toString()) { this.setDecorations(uri); } diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b9792a16dc4..444e96c7b1c 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -50,11 +50,10 @@ import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/b import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; import { testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons'; import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter'; -import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { ITestingPeekOpener, TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; import { TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; -import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; import { ITestResultService, sumCounts, TestStateCount } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService, TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; @@ -230,6 +229,7 @@ export class TestingExplorerViewModel extends Disposable { @IStorageService private readonly storageService: IStorageService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @ITestResultService private readonly testResults: ITestResultService, + @ITestingPeekOpener private readonly peekOpener: ITestingPeekOpener, ) { super(); @@ -386,35 +386,9 @@ export class TestingExplorerViewModel extends Disposable { */ private async tryPeekError(item: ITestTreeElement) { const lookup = item.test && this.testResults.getStateByExtId(item.test.item.extId); - if (!lookup || !isFailedState(lookup[1].state.state)) { - return false; - } - - const [result, test] = lookup; - const index = test.state.messages.findIndex(m => !!m.location); - if (index === -1) { - return; - } - - const message = test.state.messages[index]; - const pane = await this.editorService.openEditor({ - resource: message.location!.uri, - options: { selection: message.location!.range, preserveFocus: true } - }); - - const control = pane?.getControl(); - if (!isCodeEditor(control)) { - return false; - } - - TestingOutputPeekController.get(control).show(buildTestUri({ - type: TestUriType.ResultMessage, - messageIndex: index, - resultId: result.id, - testExtId: item.test!.item.extId, - })); - - return true; + return lookup && isFailedState(lookup[1].state.state) + ? this.peekOpener.tryPeekFirstError(lookup[0], lookup[1], { preserveFocus: true }) + : false; } private updatePreferredProjection() { diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 89361e1f09d..3ee275e166a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -11,7 +11,7 @@ import { Disposable, IReference, MutableDisposable } from 'vs/base/common/lifecy import { clamp } from 'vs/base/common/numbers'; import { count } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; @@ -20,17 +20,22 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { getOuterEditor, IPeekViewService, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from 'vs/editor/contrib/peekView/peekView'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorModel } from 'vs/workbench/common/editor'; import { testingPeekBorder } from 'vs/workbench/contrib/testing/browser/theme'; +import { AutoOpenPeekViewWhen, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; import { ITestItem, ITestMessage, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { buildTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; -import { ITestResultService, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResult, ITestResultService, TestResultItem, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; interface ITestDto { test: ITestItem, @@ -41,6 +46,93 @@ interface ITestDto { messageUri: URI; } +export interface ITestingPeekOpener { + _serviceBrand: undefined; + + /** + * Tries to peek the first test error, if the item is in a failed state. + * @returns a boolean indicating whether a peek was opened + */ + tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial): Promise; +} + +export const ITestingPeekOpener = createDecorator('testingPeekOpener'); + +export class TestingPeekOpener extends Disposable implements ITestingPeekOpener { + declare _serviceBrand: undefined; + + constructor( + @IConfigurationService private readonly configuration: IConfigurationService, + @IEditorService private readonly editorService: IEditorService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @ITestResultService testResults: ITestResultService, + ) { + super(); + this._register(testResults.onTestChanged(this.openPeekOnFailure, this)); + } + + /** + * Tries to peek the first test error, if the item is in a failed state. + * @returns a boolean if a peek was opened + */ + public async tryPeekFirstError(result: ITestResult, test: TestResultItem, options?: Partial) { + const index = test.state.messages.findIndex(m => !!m.location); + if (index === -1) { + return false; + } + + const message = test.state.messages[index]; + const pane = await this.editorService.openEditor({ + resource: message.location!.uri, + options: { selection: message.location!.range, revealIfOpened: true, ...options } + }); + + const control = pane?.getControl(); + if (!isCodeEditor(control)) { + return false; + } + + TestingOutputPeekController.get(control).show(buildTestUri({ + type: TestUriType.ResultMessage, + messageIndex: index, + resultId: result.id, + testExtId: test.item.extId, + })); + + return true; + } + + /** + * Opens the peek view on a test failure, based on user preferences. + */ + private openPeekOnFailure(evt: TestResultItemChange) { + if (!isFailedState(evt.item.state.state) || !evt.item.state.messages.length) { + return; + } + + if (evt.result.isAutoRun && !getTestingConfiguration(this.configuration, TestingConfigKeys.AutoOpenPeekViewDuringAutoRun)) { + return; + } + + const editors = this.codeEditorService.listCodeEditors(); + const cfg = getTestingConfiguration(this.configuration, TestingConfigKeys.AutoOpenPeekView); + + // don't show the peek if the user asked to only auto-open peeks for visible tests, + // and this test is not in any of the editors' models. + const testUri = evt.item.item.location?.uri.toString(); + if (cfg === AutoOpenPeekViewWhen.FailureVisible && (!testUri || !editors.some(e => e.getModel()?.uri.toString() === testUri))) { + return; + } + + const controllers = editors.map(TestingOutputPeekController.get); + if (controllers.some(c => c?.isVisible)) { + return; + } + + this.tryPeekFirstError(evt.result, evt.item); + } +} + /** * Adds output/message peek functionality to code editors. */ @@ -62,6 +154,13 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo */ private readonly visible: IContextKey; + /** + * Gets whether a peek is currently shown in the associated editor. + */ + public get isVisible() { + return this.peek.value; + } + constructor( private readonly editor: ICodeEditor, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -71,18 +170,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo super(); this.visible = TestingContextKeys.isPeekVisible.bindTo(contextKeyService); this._register(editor.onDidChangeModel(() => this.peek.clear())); - this._register(testResults.onTestChanged((evt) => { - // if the test we're currently showing has its state change to something - // else, then clear the peek - if (evt.reason !== TestResultItemChangeReason.OwnStateChange || evt.previous.state === evt.result.state.state) { - return; - } - - const displayed = this.peek.value?.currentTest(); - if (displayed?.extId === evt.result.item.extId) { - this.peek.clear(); - } - })); + this._register(testResults.onTestChanged((evt) => this.closePeekOnTestChange(evt))); } /** @@ -125,6 +213,21 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.clear(); } + /** + * If the test we're currently showing has its state change to something + * else, then clear the peek. + */ + private closePeekOnTestChange(evt: TestResultItemChange) { + if (evt.reason !== TestResultItemChangeReason.OwnStateChange || evt.previous.state === evt.item.state.state) { + return; + } + + const displayed = this.peek.value?.currentTest(); + if (displayed?.extId === evt.item.item.extId) { + this.peek.clear(); + } + } + private retrieveTest(uri: URI): ITestDto | undefined { const parts = parseTestUri(uri); if (!parts) { diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index 0630b3f40ac..546ec5ff9da 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -8,7 +8,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; export const enum TestingConfigKeys { - AutoRunDelay = 'testing.autoRun.delay' + AutoRunDelay = 'testing.autoRun.delay', + AutoOpenPeekView = 'testing.automaticallyOpenPeekView', + AutoOpenPeekViewDuringAutoRun = 'testing.automaticallyOpenPeekViewDuringAutoRun', +} + +export const enum AutoOpenPeekViewWhen { + FailureVisible = 'failureInVisibleDocument', + FailureAnywhere = 'failureAnywhere', } export const testingConfiguation: IConfigurationNode = { @@ -23,11 +30,30 @@ export const testingConfiguation: IConfigurationNode = { description: localize('testing.autoRun.delay', "How long to wait, in milliseconds, after a test is marked as outdated and starting a new run."), default: 1000, }, + [TestingConfigKeys.AutoOpenPeekView]: { + description: localize('testing.automaticallyOpenPeekView', "Configures when the error peek view is automatically opened."), + enum: [ + AutoOpenPeekViewWhen.FailureAnywhere, + AutoOpenPeekViewWhen.FailureVisible, + ], + default: AutoOpenPeekViewWhen.FailureVisible, + enumDescriptions: [ + localize('testing.automaticallyOpenPeekView.failureAnywhere', "Open automatically no matter where the failure is."), + localize('testing.automaticallyOpenPeekView.failureInVisibleDocument', "Open automatically when a test fails in a visible document.") + ], + }, + [TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: { + description: localize('testing.automaticallyOpenPeekViewDuringAutoRun', "Controls whether to automatically open the peek view during auto-run mode."), + type: 'boolean', + default: false, + } } }; export interface ITestingConfiguration { [TestingConfigKeys.AutoRunDelay]: number; + [TestingConfigKeys.AutoOpenPeekView]: AutoOpenPeekViewWhen; + [TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: boolean; } export const getTestingConfiguration = (config: IConfigurationService, key: K) => config.getValue(key); diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts index 8ceb490a2dc..b90a86c393e 100644 --- a/src/vs/workbench/contrib/testing/common/testCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -15,11 +15,12 @@ export interface TestIdWithProvider { } /** - * Request to them main thread to run a set of tests. + * Request to the main thread to run a set of tests. */ export interface RunTestsRequest { tests: TestIdWithProvider[]; debug: boolean; + isAutoRun?: boolean; } /** diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index 5ce95376f8b..371d312c767 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -12,7 +12,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { TestRunState } from 'vs/workbench/api/common/extHostTypes'; import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contrib/testing/common/getComputedState'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { IncrementalTestCollectionItem, ITestState, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; +import { IncrementalTestCollectionItem, ITestState, RunTestsRequest } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates'; import { IMainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testService'; @@ -29,7 +29,7 @@ export const enum TestResultItemChangeReason { OwnStateChange, } -export type TestResultItemChange = { result: TestResultItem; } & ( +export type TestResultItemChange = { item: TestResultItem; result: ITestResult } & ( | { reason: TestResultItemChangeReason.Retired | TestResultItemChangeReason.ParentRetired | TestResultItemChangeReason.ComputedStateChange } | { reason: TestResultItemChangeReason.OwnStateChange; previous: ITestState } ); @@ -50,6 +50,11 @@ export interface ITestResult { */ readonly isComplete: boolean; + /** + * Whether this test result is triggered from an auto run. + */ + readonly isAutoRun?: boolean; + /** * Gets the state of the test by its extension-assigned ID. */ @@ -180,11 +185,11 @@ export class LiveTestResult implements ITestResult { */ public static from( collections: ReadonlyArray, - tests: ReadonlyArray, + req: RunTestsRequest, ) { const testByExtId = new Map(); const testByInternalId = new Map(); - for (const test of tests) { + for (const test of req.tests) { for (const collection of collections) { const node = collection.getNodeById(test.testId); if (!node) { @@ -196,7 +201,7 @@ export class LiveTestResult implements ITestResult { } } - return new LiveTestResult(collections, testByExtId, testByInternalId); + return new LiveTestResult(collections, testByExtId, testByInternalId, !!req.isAutoRun); } private readonly completeEmitter = new Emitter(); @@ -265,6 +270,7 @@ export class LiveTestResult implements ITestResult { private readonly collections: ReadonlyArray, private readonly testByExtId: Map, private readonly testByInternalId: Map, + public readonly isAutoRun: boolean, ) { this.counts[TestRunState.Unset] = testByInternalId.size; } @@ -310,11 +316,11 @@ export class LiveTestResult implements ITestResult { this.counts[previous.state]--; this.counts[newState.state]++; refreshComputedState(this.computedStateAccessor, entry, t => ( - t !== entry && this.changeEmitter.fire({ result: t, reason: TestResultItemChangeReason.ComputedStateChange }) + t !== entry && this.changeEmitter.fire({ item: t, result: this, reason: TestResultItemChangeReason.ComputedStateChange }) )); } - this.changeEmitter.fire({ result: entry, reason: TestResultItemChangeReason.OwnStateChange, previous }); + this.changeEmitter.fire({ item: entry, result: this, reason: TestResultItemChangeReason.OwnStateChange, previous }); } /** @@ -334,7 +340,8 @@ export class LiveTestResult implements ITestResult { entry.retired = true; queue.push(entry.children); this.changeEmitter.fire({ - result: entry, + result: this, + item: entry, reason: entry === root ? TestResultItemChangeReason.Retired : TestResultItemChangeReason.ParentRetired @@ -523,8 +530,12 @@ export class TestResultService implements ITestResultService { target: StorageTarget.MACHINE }, storage); - for (const value of this.serializedResults.get([])) { - this.results.push(new HydratedTestResult(value)); + try { + for (const value of this.serializedResults.get([])) { + this.results.push(new HydratedTestResult(value)); + } + } catch (e) { + // outdated structure } } diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 58787aa18f2..95d4a9e7bc8 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -124,7 +124,7 @@ export class TestService extends Disposable implements ITestService { const subscriptions = [...this.testSubscriptions.values()] .filter(v => req.tests.some(t => v.collection.getNodeById(t.testId))) .map(s => this.subscribeToDiffs(s.ident.resource, s.ident.uri)); - const result = this.testResults.push(LiveTestResult.from(subscriptions.map(s => s.object), req.tests)); + const result = this.testResults.push(LiveTestResult.from(subscriptions.map(s => s.object), req)); try { const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1); diff --git a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts index 33ea8045ba4..a1eae07dd53 100644 --- a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts +++ b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts @@ -82,7 +82,7 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun { isRunning = true; rerunIds.clear(); - await this.testService.runTests({ debug: false, tests }); + await this.testService.runTests({ debug: false, tests, isAutoRun: true }); isRunning = false; if (rerunIds.size > 0) { @@ -95,10 +95,10 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun { return; } - const { extId } = evt.result.item; + const { extId } = evt.item.item; const workspaceTest = mapFind(workspaceTests.workspaceFolderCollections, - ([, c]) => c.getNodeById(evt.result.id) ?? Iterable.find(c.all, t => t.item.extId === extId)); - const subject = workspaceTest ?? evt.result; + ([, c]) => c.getNodeById(evt.item.id) ?? Iterable.find(c.all, t => t.item.extId === extId)); + const subject = workspaceTest ?? evt.item; rerunIds.set(subject.id, ({ testId: subject.id, providerId: subject.providerId })); diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 5e243770b86..86851a62f51 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -14,7 +14,7 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic suite('Workbench - Test Results Service', () => { const getLabelsIn = (it: Iterable) => [...it].map(t => t.item.label).sort(); const getChangeSummary = () => [...changed] - .map(c => ({ reason: c.reason, label: c.result.item.label })) + .map(c => ({ reason: c.reason, label: c.item.item.label })) .sort((a, b) => a.label.localeCompare(b.label)); let r: LiveTestResult; @@ -24,7 +24,7 @@ suite('Workbench - Test Results Service', () => { changed = new Set(); r = LiveTestResult.from( [getInitializedMainTestCollection()], - [{ providerId: 'provider', testId: '1' }] + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } ); r.onChange(e => changed.add(e)); @@ -32,7 +32,7 @@ suite('Workbench - Test Results Service', () => { suite('LiveTestResult', () => { test('is empty if no tests are requesteed', () => { - const r = LiveTestResult.from([getInitializedMainTestCollection()], []); + const r = LiveTestResult.from([getInitializedMainTestCollection()], { tests: [], debug: false }); assert.deepStrictEqual(getLabelsIn(r.tests), []); }); @@ -170,7 +170,7 @@ suite('Workbench - Test Results Service', () => { const r2 = results.push(LiveTestResult.from( [getInitializedMainTestCollection()], - [{ providerId: 'provider', testId: '1' }] + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } )); results.clear(); @@ -181,7 +181,7 @@ suite('Workbench - Test Results Service', () => { results.push(r); const r2 = results.push(LiveTestResult.from( [getInitializedMainTestCollection()], - [{ providerId: 'provider', testId: '1' }] + { tests: [{ providerId: 'provider', testId: '1' }], debug: false } )); assert.deepStrictEqual(results.results, [r2, r]); From 6693b29b19cc279578c45f8bda222eec24b7e14e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 14:17:01 -0800 Subject: [PATCH 027/176] testing: show peek link in hover Fixes https://github.com/microsoft/vscode/issues/115770 --- .../editor/contrib/hover/modesContentHover.ts | 4 ++++ .../testing/browser/testing.contribution.ts | 10 ++++++++++ .../testing/browser/testingDecorations.ts | 17 ++++++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index ee10d9b8003..7b401019e06 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -118,6 +118,10 @@ class ModesContentComputer implements IHoverComputer { const maxColumn = model.getLineMaxColumn(lineNumber); const lineDecorations = this._editor.getLineDecorations(lineNumber).filter((d) => { + if (d.options.isWholeLine) { + return true; + } + const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1; const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn; if (startColumn > hoverRange.startColumn || hoverRange.endColumn > endColumn) { diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 295f2c0fa17..8a9600b6b63 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -128,4 +128,14 @@ CommandsRegistry.registerCommand({ } }); +CommandsRegistry.registerCommand({ + id: 'vscode.peekTestError', + handler: async (accessor: ServicesAccessor, extId: string) => { + const lookup = accessor.get(ITestResultService).getStateByExtId(extId); + if (lookup) { + accessor.get(ITestingPeekOpener).tryPeekFirstError(lookup[0], lookup[1]); + } + } +}); + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration(testingConfiguation); diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 341c7b70818..6006946567f 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -29,7 +29,7 @@ import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browse import { testMessageSeverityColors } from 'vs/workbench/contrib/testing/browser/theme'; import { IncrementalTestCollectionItem, ITestMessage } from 'vs/workbench/contrib/testing/common/testCollection'; import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestResultService, TestResultItem } from 'vs/workbench/contrib/testing/common/testResultService'; import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/testing/common/testService'; export class TestingDecorations extends Disposable implements IEditorContribution { @@ -87,7 +87,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio const stateLookup = this.results.getStateByExtId(test.item.extId); if (hasValidLocation(uri, test.item)) { newDecorations.push(this.instantiationService.createInstance( - RunTestDecoration, test, ref.object, test.item.location, this.editor, stateLookup?.[1].computedState)); + RunTestDecoration, test, ref.object, test.item.location, this.editor, stateLookup?.[1])); } if (!stateLookup) { @@ -172,7 +172,7 @@ class RunTestDecoration extends Disposable implements ITestDecoration { private readonly collection: IMainThreadTestCollection, private readonly location: ModeLocation, private readonly editor: ICodeEditor, - computedState: TestRunState | undefined, + stateItem: TestResultItem | undefined, @ITestService private readonly testService: ITestService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @ICommandService private readonly commandService: ICommandService, @@ -180,14 +180,21 @@ class RunTestDecoration extends Disposable implements ITestDecoration { super(); this.line = location.range.startLineNumber; - const icon = computedState !== undefined && computedState !== TestRunState.Unset - ? testingStatesToIcons.get(computedState)! + const icon = stateItem?.computedState !== undefined && stateItem.computedState !== TestRunState.Unset + ? testingStatesToIcons.get(stateItem.computedState)! : test.children.size > 0 ? testingRunAllIcon : testingRunIcon; + const hoverMessage = new MarkdownString('', true).appendText(localize('failedHoverMessage', '{0} has failed. ', test.item.label)); + if (stateItem?.state.messages.length) { + const args = encodeURIComponent(JSON.stringify([test.item.extId])); + hoverMessage.appendMarkdown(`[${localize('failedPeekAction', 'Peek Error')}](command:vscode.peekTestError?${args})`); + } + this.editorDecoration = { range: firstLineRange(this.location.range), options: { isWholeLine: true, + hoverMessage, glyphMarginClassName: ThemeIcon.asClassName(icon) + ' testing-run-glyph', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, glyphMarginHoverMessage: new MarkdownString().appendText(localize('testing.clickToRun', 'Click to run tests, right click for more options')), From 778ce3d6af25be81e88efe74ec2d88585408e5f2 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 12 Feb 2021 15:18:31 -0800 Subject: [PATCH 028/176] Add potential top level command entries --- .../common/gettingStartedContent.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts index f96301a0577..64e10c2eb92 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedContent.ts @@ -40,6 +40,26 @@ type GettingStartedCategory = { type GettingStartedContent = GettingStartedCategory[]; export const content: GettingStartedContent = [ + // { + // id: 'topLevelCommandPalette', + // title: localize('gettingStarted.commandPalette.title', "Command Palette"), + // description: localize('gettingStarted.commandPalette.description', "The one keybinding to show you everything VS Code can do."), + // icon: Codicon.symbolColor, + // content: { + // type: 'command', + // command: 'workbench.action.showCommands', + // } + // }, + // { + // id: 'topLevelSeeExtensions', + // title: localize('gettingStarted.languageSupport.title', "Install Language Support"), + // description: localize('gettingStarted.languageSupport.description', "Want even more features? Install extensions to add support for languages like Python, C, or Java."), + // icon: Codicon.extensions, + // content: { + // type: 'command', + // command: 'workbench.extensions.action.showPopularExtensions', + // } + // }, { id: 'Codespaces', title: localize('gettingStarted.codespaces.title', "Primer on Codespaces"), From e9ecaca14fed4b874f7090faba66f99fca893258 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 12 Feb 2021 15:22:34 -0800 Subject: [PATCH 029/176] Rescan task scroll container after task selection animation complete Fixes #116521 --- .../contrib/welcome/gettingStarted/browser/gettingStarted.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index d17e7d5c275..f890c8f273e 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -242,6 +242,11 @@ export class GettingStartedPage extends EditorPane { mediaElement.setAttribute('src', ''); mediaElement.setAttribute('alt', ''); } + setTimeout(() => { + // rescan after animation finishes + this.detailsScrollbar?.scanDomNode(); + this.detailImageScrollbar?.scanDomNode(); + }, 100); this.detailsScrollbar?.scanDomNode(); this.detailImageScrollbar?.scanDomNode(); } From 1a4c9ac9f2a68e1394af401a77c91b81965f5fb9 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 12 Feb 2021 15:25:07 -0800 Subject: [PATCH 030/176] fix classic menubar safari --- src/vs/base/browser/ui/menu/menubar.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index bfa79a1c523..e6aa5f4ddbe 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -42,7 +42,7 @@ } .menubar .menubar-menu-items-holder { - position: absolute; + position: fixed; left: 0px; opacity: 1; z-index: 2000; From 2f277cb41eff4d30e09b17a72abb9c8afcb2b9de Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 12 Feb 2021 15:38:41 -0800 Subject: [PATCH 031/176] Add github session telemetry event --- .../github-authentication/src/extension.ts | 2 +- .../github-authentication/src/github.ts | 8 +++-- .../github-authentication/src/githubServer.ts | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index df80913cd22..7ae1e1d2110 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -14,7 +14,7 @@ export async function activate(context: vscode.ExtensionContext) { const telemetryReporter = new TelemetryReporter(name, version, aiKey); context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); - const loginService = new GitHubAuthenticationProvider(context); + const loginService = new GitHubAuthenticationProvider(context, telemetryReporter); await loginService.initialize(context); diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index bf6cee27132..595d13147a1 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -9,6 +9,7 @@ import { Keychain } from './common/keychain'; import { GitHubServer, NETWORK_ERROR } from './githubServer'; import Logger from './common/logger'; import { arrayEquals } from './common/utils'; +import TelemetryReporter from 'vscode-extension-telemetry'; export const onDidChangeSessions = new vscode.EventEmitter(); @@ -25,12 +26,13 @@ interface SessionData { export class GitHubAuthenticationProvider { private _sessions: vscode.AuthenticationSession[] = []; - private _githubServer = new GitHubServer(); + private _githubServer: GitHubServer; private _keychain: Keychain; - constructor(context: vscode.ExtensionContext) { + constructor(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) { this._keychain = new Keychain(context); + this._githubServer = new GitHubServer(telemetryReporter); } public async initialize(context: vscode.ExtensionContext): Promise { @@ -55,6 +57,7 @@ export class GitHubAuthenticationProvider { const verificationPromises = this._sessions.map(async session => { try { await this._githubServer.getUserInfo(session.accessToken); + this._githubServer.checkIsEdu(session.accessToken); verifiedSessions.push(session); } catch (e) { // Remove sessions that return unauthorized response @@ -163,6 +166,7 @@ export class GitHubAuthenticationProvider { public async createSession(scopes: string): Promise { const token = await this._githubServer.login(scopes); const session = await this.tokenToSession(token, scopes.split(' ')); + this._githubServer.checkIsEdu(token); await this.setToken(session); return session; } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index b50285c9ab2..13ec13b735b 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -9,6 +9,7 @@ import fetch, { Response } from 'node-fetch'; import { v4 as uuid } from 'uuid'; import { PromiseAdapter, promiseFromEvent } from './common/utils'; import Logger from './common/logger'; +import TelemetryReporter from 'vscode-extension-telemetry'; const localize = nls.loadMessageBundle(); @@ -41,6 +42,8 @@ export class GitHubServer { private _pendingStates = new Map(); private _codeExchangePromises = new Map>(); + constructor(private readonly telemetryReporter: TelemetryReporter) { } + private isTestEnvironment(url: vscode.Uri): boolean { return url.authority === 'vscode-web-test-playground.azurewebsites.net' || url.authority.startsWith('localhost:'); } @@ -210,4 +213,36 @@ export class GitHubServer { throw new Error(result.statusText); } } + + public async checkIsEdu(token: string): Promise { + try { + const result = await fetch('https://education.github.com/api/user', { + headers: { + Authorization: `token ${token}`, + 'faculty-check-preview': 'true', + 'User-Agent': 'Visual-Studio-Code' + } + }); + + if (result.ok) { + const json: { student: boolean, faculty: boolean } = await result.json(); + + /* __GDPR__ + "session" : { + "isEdu": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryReporter.sendTelemetryEvent('session', { + isEdu: json.student + ? 'student' + : json.faculty + ? 'faculty' + : 'none' + }); + } + } catch (e) { + // No-op + } + + } } From be7cf6f2e9e43da36e26086a81feaf104b87949c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 12 Feb 2021 15:41:37 -0800 Subject: [PATCH 032/176] use checkmark instead of toggle lang 4 diff editor --- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 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 003527d67e0..78d51af076a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -464,7 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCo MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_RIGHT, title: nls.localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); // Editor Title Menu -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('toggleInlineView', "Toggle Inline View") }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_DIFF_SIDE_BY_SIDE, title: nls.localize('inlineView', "Inline View"), toggled: ContextKeyExpr.equals('config.diffEditor.renderSideBySide', false) }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); From 07e3bcf7eacfb5a3f9c6e426fc977d176e40a726 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 15:01:11 -0800 Subject: [PATCH 033/176] testing: allow filtering to only executed or failed tests --- .../testing/browser/testingExplorerFilter.ts | 29 +++++++++++++++++-- .../testing/browser/testingExplorerView.ts | 21 ++++++++++++-- .../contrib/testing/common/constants.ts | 6 ++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index eae8ec3d55d..3c102a91fd5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -9,7 +9,7 @@ import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; +import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -24,7 +24,7 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { testingFilterIcon } from 'vs/workbench/contrib/testing/browser/icons'; -import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { TestExplorerStateFilter, Testing } from 'vs/workbench/contrib/testing/common/constants'; import { ObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; @@ -34,6 +34,7 @@ export interface ITestExplorerFilterState { readonly text: ObservableValue; /** Reveal request, the extId of the test to reveal */ readonly reveal: ObservableValue; + readonly stateFilter: ObservableValue; readonly currentDocumentOnly: ObservableValue; readonly onDidRequestInputFocus: Event; @@ -46,6 +47,11 @@ export class TestExplorerFilterState implements ITestExplorerFilterState { declare _serviceBrand: undefined; private readonly focusEmitter = new Emitter(); public readonly text = new ObservableValue(''); + public readonly stateFilter = ObservableValue.stored(new StoredValue({ + key: 'testStateFilter', + scope: StorageScope.WORKSPACE, + target: StorageTarget.USER + }, this.storage), TestExplorerStateFilter.All); public readonly currentDocumentOnly = ObservableValue.stored(new StoredValue({ key: 'testsByCurrentDocumentOnly', scope: StorageScope.WORKSPACE, @@ -83,6 +89,7 @@ export class TestingExplorerFilter extends BaseActionViewItem { super(null, action); this.updateFilterActiveState(); this._register(state.currentDocumentOnly.onDidChange(this.updateFilterActiveState, this)); + this._register(state.stateFilter.onDidChange(this.updateFilterActiveState, this)); } /** @@ -166,7 +173,8 @@ export class TestingExplorerFilter extends BaseActionViewItem { * Updates the 'checked' state of the filter submenu. */ private updateFilterActiveState() { - this.filtersAction.checked = this.state.currentDocumentOnly.value; + this.filtersAction.checked = this.state.currentDocumentOnly.value + || this.state.stateFilter.value !== TestExplorerStateFilter.All; } } @@ -198,6 +206,21 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { private getActions(): IAction[] { return [ + ...[ + { v: TestExplorerStateFilter.OnlyFailed, label: localize('testing.filters.showOnlyFailed', "Show Only Failed Tests") }, + { v: TestExplorerStateFilter.OnlyExecuted, label: localize('testing.filters.showOnlyExecuted', "Show Only Executed Tests") }, + { v: TestExplorerStateFilter.All, label: localize('testing.filters.showAll', "Show All Tests") }, + ].map(({ v, label }) => ({ + checked: this.filters.stateFilter.value === v, + class: undefined, + enabled: true, + id: v, + label, + run: async () => this.filters.stateFilter.value = v, + tooltip: '', + dispose: () => null + })), + new Separator(), { checked: this.filters.currentDocumentOnly.value, class: undefined, diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 444e96c7b1c..aa396f4e514 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -51,7 +51,7 @@ import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/brows import { testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons'; import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter'; import { ITestingPeekOpener, TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; -import { TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants'; +import { TestExplorerStateFilter, TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { ITestResultService, sumCounts, TestStateCount } from 'vs/workbench/contrib/testing/common/testResultService'; @@ -257,7 +257,11 @@ export class TestingExplorerViewModel extends Disposable { filter: this.filter, }) as WorkbenchObjectTree; - this._register(Event.any(filterState.currentDocumentOnly.onDidChange, filterState.text.onDidChange)(this.tree.refilter, this.tree)); + this._register(Event.any( + filterState.currentDocumentOnly.onDidChange, + filterState.text.onDidChange, + filterState.stateFilter.onDidChange, + )(this.tree.refilter, this.tree)); this._register(editorService.onDidActiveEditorChange(() => { if (filterState.currentDocumentOnly.value && editorService.activeEditor?.resource) { if (this.projection.hasTestInDocument(editorService.activeEditor.resource)) { @@ -523,7 +527,7 @@ class TestsFilter implements ITreeFilter { this.setFilter(this.state.text.value); } - switch (Math.min(this.testFilterText(element), this.testLocation(element))) { + switch (Math.min(this.testFilterText(element), this.testLocation(element), this.testState(element))) { case FilterResult.Exclude: return TreeVisibility.Hidden; case FilterResult.Include: @@ -533,6 +537,17 @@ class TestsFilter implements ITreeFilter { } } + private testState(element: ITestTreeElement): FilterResult { + switch (this.state.stateFilter.value) { + case TestExplorerStateFilter.All: + return FilterResult.Include; + case TestExplorerStateFilter.OnlyExecuted: + return element.ownState !== TestRunState.Unset ? FilterResult.Include : FilterResult.Inherit; + case TestExplorerStateFilter.OnlyFailed: + return isFailedState(element.ownState) ? FilterResult.Include : FilterResult.Inherit; + } + } + private testLocation(element: ITestTreeElement): FilterResult { if (!this._filterToUri || !this.state.currentDocumentOnly.value) { return FilterResult.Include; diff --git a/src/vs/workbench/contrib/testing/common/constants.ts b/src/vs/workbench/contrib/testing/common/constants.ts index 817cef54b7e..0e6ebdae3ba 100644 --- a/src/vs/workbench/contrib/testing/common/constants.ts +++ b/src/vs/workbench/contrib/testing/common/constants.ts @@ -25,6 +25,12 @@ export const enum TestExplorerViewSorting { ByName = 'name', } +export const enum TestExplorerStateFilter { + OnlyFailed = 'failed', + OnlyExecuted = 'excuted', + All = 'all', +} + export const testStateNames: { [K in TestRunState]: string } = { [TestRunState.Errored]: localize('testState.errored', 'Errored'), [TestRunState.Failed]: localize('testState.failed', 'Failed'), From a0e0324a8daad1b7f5a9e2ed7f174ded9186a3aa Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 12 Feb 2021 16:08:57 -0800 Subject: [PATCH 034/176] testing: commands to run tests at current cursor and in file Refs https://github.com/microsoft/vscode/issues/116589 --- .../api/browser/mainThreadTesting.ts | 3 + .../api/common/extHostTypeConverters.ts | 4 +- .../explorerProjections/locationStore.ts | 20 +- .../testing/browser/testExplorerActions.ts | 173 +++++++++++++++++- .../testing/browser/testing.contribution.ts | 4 + .../testing/browser/testingDecorations.ts | 9 +- .../contrib/testing/common/testCollection.ts | 14 +- .../testing/common/testResultService.ts | 7 + .../contrib/testing/common/testService.ts | 17 ++ 9 files changed, 223 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index 95fc3e9ae7f..110becb9583 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -6,6 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { getTestSubscriptionKey, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService'; @@ -18,6 +19,7 @@ const reviveDiff = (diff: TestsDiff) => { const item = entry[1]; if (item.item.location) { item.item.location.uri = URI.revive(item.item.location.uri); + item.item.location.range = Range.lift(item.item.location.range); } } } @@ -71,6 +73,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh for (const message of state.messages) { if (message.location) { message.location.uri = URI.revive(message.location.uri); + message.location.range = Range.lift(message.location.range); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9799bddf7bd..e838b45bfb9 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1507,7 +1507,7 @@ export namespace TestState { severity: message.severity, expectedOutput: message.expectedOutput, actualOutput: message.actualOutput, - location: message.location ? location.from(message.location) : undefined, + location: message.location ? location.from(message.location) as any : undefined, })) ?? [], }; } @@ -1536,7 +1536,7 @@ export namespace TestItem { return { extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label), label: item.label, - location: item.location ? location.from(item.location) : undefined, + location: item.location ? location.from(item.location) as any : undefined, debuggable: item.debuggable ?? false, description: item.description, runnable: item.runnable ?? true, diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts index 9e23c907a81..f604a164eec 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/locationStore.ts @@ -6,25 +6,22 @@ import { findFirstInSorted } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { Range } from 'vs/editor/common/core/range'; +import { IRichLocation } from 'vs/workbench/contrib/testing/common/testCollection'; -export const locationsEqual = (a: ModeLocation | undefined, b: ModeLocation | undefined) => { +export const locationsEqual = (a: IRichLocation | undefined, b: IRichLocation | undefined) => { if (a === undefined || b === undefined) { return b === a; } - return a.uri.toString() === b.uri.toString() - && a.range.startLineNumber === b.range.startLineNumber - && a.range.startColumn === b.range.startColumn - && a.range.endLineNumber === b.range.endLineNumber - && a.range.endColumn === b.range.endColumn; + return a.uri.toString() === b.uri.toString() && a.range.equalsRange(b.range); }; /** * Stores and looks up test-item-like-objects by their uri/range. Used to * implement the 'reveal' action efficiently. */ -export class TestLocationStore { +export class TestLocationStore { private readonly itemsByUri = new Map(); public hasTestInDocument(uri: URI) { @@ -39,12 +36,7 @@ export class TestLocationStore { const range = test.location?.range; - return range - && new Position(range.startLineNumber, range.startColumn).isBeforeOrEqual(position) - && position.isBeforeOrEqual(new Position( - range.endLineNumber ?? range.startLineNumber, - range.endColumn ?? range.startColumn, - )); + return range && Range.lift(range).containsPosition(position); }); } diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 60ffcfc38c0..2dac0056fb6 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -26,8 +26,9 @@ import { InternalTestItem, TestIdWithProvider } from 'vs/workbench/contrib/testi import { ITestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { ITestResult, ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestService, waitForAllRoots } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestService, waitForAllRoots, waitForAllTests } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; const category = localize('testing.category', 'Test'); @@ -94,7 +95,7 @@ export class RunAction extends Action { } } -abstract class RunOrDebugAction extends ViewAction { +abstract class RunOrDebugSelectedAction extends ViewAction { constructor(id: string, title: string, icon: ThemeIcon, private readonly debug: boolean) { super({ id, @@ -103,6 +104,7 @@ abstract class RunOrDebugAction extends ViewAction { viewId: Testing.ExplorerViewId, f1: true, category, + precondition: FocusedViewContext.isEqualTo(Testing.ExplorerViewId), }); } @@ -143,7 +145,7 @@ abstract class RunOrDebugAction extends ViewAction { protected abstract filter(item: InternalTestItem): boolean; } -export class RunSelectedAction extends RunOrDebugAction { +export class RunSelectedAction extends RunOrDebugSelectedAction { constructor( ) { super( @@ -162,7 +164,7 @@ export class RunSelectedAction extends RunOrDebugAction { } } -export class DebugSelectedAction extends RunOrDebugAction { +export class DebugSelectedAction extends RunOrDebugSelectedAction { constructor() { super( 'testing.debugSelected', @@ -502,3 +504,166 @@ export class ToggleAutoRun extends Action2 { accessor.get(ITestingAutoRun).toggle(); } } + +abstract class RunOrDebugAtCursor extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const control = accessor.get(IEditorService).activeTextEditorControl; + const position = control?.getPosition(); + const model = control?.getModel(); + if (!position || !model || !('uri' in model)) { + return; + } + + + const testService = accessor.get(ITestService); + const collection = testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, model.uri); + + let bestDepth = -1; + let bestNode: InternalTestItem | undefined; + + try { + await waitForAllTests(collection.object); + const queue: [depth: number, nodes: Iterable][] = [[0, collection.object.rootIds]]; + while (queue.length > 0) { + const [depth, candidates] = queue.pop()!; + for (const id of candidates) { + const candidate = collection.object.getNodeById(id); + if (candidate) { + if (depth > bestDepth && this.filter(candidate) && candidate.item.location?.range.containsPosition(position)) { + bestDepth = depth; + bestNode = candidate; + } + + queue.push([depth + 1, candidate.children]); + } + } + } + + if (bestNode) { + await this.runTest(testService, bestNode); + } + } finally { + collection.dispose(); + } + } + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem): Promise; +} + +export class RunAtCursor extends RunOrDebugAtCursor { + constructor() { + super({ + id: 'testing.runAtCursor', + title: localize('testing.runAtCursor', "Run Test at Cursor"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, node: InternalTestItem): Promise { + return service.runTests({ debug: false, tests: [{ testId: node.id, providerId: node.providerId }] }); + } +} + +export class DebugAtCursor extends RunOrDebugAtCursor { + constructor() { + super({ + id: 'testing.debugAtCursor', + title: localize('testing.debugAtCursor', "Debug Test at Cursor"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, node: InternalTestItem): Promise { + return service.runTests({ debug: true, tests: [{ testId: node.id, providerId: node.providerId }] }); + } +} + + +abstract class RunOrDebugCurrentFile extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const control = accessor.get(IEditorService).activeTextEditorControl; + const position = control?.getPosition(); + const model = control?.getModel(); + if (!position || !model || !('uri' in model)) { + return; + } + + const testService = accessor.get(ITestService); + const collection = testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, model.uri); + + try { + await waitForAllTests(collection.object); + + const roots = [...collection.object.rootIds] + .map(r => collection.object.getNodeById(r)) + .filter(isDefined) + .filter(n => this.filter(n)); + + if (roots.length) { + await this.runTest(testService, roots); + } + } finally { + collection.dispose(); + } + } + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise; +} + +export class RunCurrentFile extends RunOrDebugCurrentFile { + constructor() { + super({ + id: 'testing.runCurrentFile', + title: localize('testing.runCurrentFile', "Run Tests in Current File"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugCurrentFile extends RunOrDebugCurrentFile { + constructor() { + super({ + id: 'testing.debugCurrentFile', + title: localize('testing.debugCurrentFile', "Debug Tests in Current File"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 8a9600b6b63..6ef650fcedf 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -97,6 +97,10 @@ registerAction2(Action.DebugAllAction); registerAction2(Action.EditFocusedTest); registerAction2(Action.ClearTestResultsAction); registerAction2(Action.ToggleAutoRun); +registerAction2(Action.DebugAtCursor); +registerAction2(Action.RunAtCursor); +registerAction2(Action.DebugCurrentFile); +registerAction2(Action.RunCurrentFile); registerAction2(CloseTestPeek); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 6006946567f..567258a3597 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -14,7 +14,6 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -27,7 +26,7 @@ import { BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from import { testingRunAllIcon, testingRunIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons'; import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; import { testMessageSeverityColors } from 'vs/workbench/contrib/testing/browser/theme'; -import { IncrementalTestCollectionItem, ITestMessage } from 'vs/workbench/contrib/testing/common/testCollection'; +import { IncrementalTestCollectionItem, IRichLocation, ITestMessage } from 'vs/workbench/contrib/testing/common/testCollection'; import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; import { ITestResultService, TestResultItem } from 'vs/workbench/contrib/testing/common/testResultService'; import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/testing/common/testService'; @@ -144,7 +143,7 @@ interface ITestDecoration extends IDisposable { click(e: IEditorMouseEvent): boolean; } -const hasValidLocation = (editorUri: URI, t: T): t is T & { location: ModeLocation } => +const hasValidLocation = (editorUri: URI, t: T): t is T & { location: IRichLocation } => t.location?.uri.toString() === editorUri.toString(); const firstLineRange = (originalRange: IRange) => ({ @@ -170,7 +169,7 @@ class RunTestDecoration extends Disposable implements ITestDecoration { constructor( private readonly test: IncrementalTestCollectionItem, private readonly collection: IMainThreadTestCollection, - private readonly location: ModeLocation, + private readonly location: IRichLocation, private readonly editor: ICodeEditor, stateItem: TestResultItem | undefined, @ITestService private readonly testService: ITestService, @@ -282,7 +281,7 @@ class TestMessageDecoration implements ITestDecoration { constructor( { message, severity }: ITestMessage, private readonly messageUri: URI, - location: ModeLocation, + location: IRichLocation, private readonly editor: ICodeEditor, @ICodeEditorService private readonly editorService: ICodeEditorService, @IThemeService themeService: IThemeService, diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts index b90a86c393e..e42f8a207a9 100644 --- a/src/vs/workbench/contrib/testing/common/testCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -5,7 +5,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; -import { Location as ModeLocation } from 'vs/editor/common/modes'; +import { Range } from 'vs/editor/common/core/range'; import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol'; import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; @@ -33,12 +33,20 @@ export interface RunTestForProviderRequest { debug: boolean; } +/** + * Location with a fully-instantiated Range and URI. + */ +export interface IRichLocation { + range: Range; + uri: URI; +} + export interface ITestMessage { message: string | IMarkdownString; severity: TestMessageSeverity | undefined; expectedOutput: string | undefined; actualOutput: string | undefined; - location: ModeLocation | undefined; + location: IRichLocation | undefined; } export interface ITestState { @@ -55,7 +63,7 @@ export interface ITestItem { extId: string; label: string; children?: never; - location: ModeLocation | undefined; + location: IRichLocation | undefined; description: string | undefined; runnable: boolean; debuggable: boolean; diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index 371d312c767..c380e53708b 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -6,6 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import { Range } from 'vs/editor/common/core/range'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -426,9 +427,15 @@ class HydratedTestResult implements ITestResult { for (const item of serialized.items) { const cast: TestResultItem = { ...item, retired: true, children: new Set(item.children) }; + if (cast.item.location) { + cast.item.location.uri = URI.revive(cast.item.location.uri); + cast.item.location.range = Range.lift(cast.item.location.range); + } + for (const message of cast.state.messages) { if (message.location) { message.location.uri = URI.revive(message.location.uri); + message.location.range = Range.lift(message.location.range); } } diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index 61639ba1874..8d1e5221dde 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -74,6 +74,23 @@ export const waitForAllRoots = (collection: IMainThreadTestCollection, timeout = }).finally(() => listener.dispose()); }; +export const waitForAllTests = (collection: IMainThreadTestCollection, timeout = 3000) => { + if (collection.busyProviders === 0) { + return Promise.resolve(); + } + + let listener: IDisposable; + return new Promise(resolve => { + listener = collection.onBusyProvidersChange(count => { + if (count === 0) { + resolve(); + } + }); + + setTimeout(resolve, timeout); + }).finally(() => listener.dispose()); +}; + export interface ITestService { readonly _serviceBrand: undefined; readonly onShouldSubscribe: Event<{ resource: ExtHostTestingResource, uri: URI; }>; From 647a4b044a2947a5043c67915b1e785400abfa22 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 12 Feb 2021 18:24:17 -0800 Subject: [PATCH 035/176] Make sure we focus inner iframe contents if the webview itself is already focused --- src/vs/workbench/contrib/webview/browser/pre/main.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index cc3bc25eab0..2d4ac83084a 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -591,6 +591,10 @@ contentWindow.addEventListener('scroll', handleInnerScroll); contentWindow.addEventListener('wheel', handleWheel); + if (document.hasFocus()) { + contentWindow.focus(); + } + pendingMessages.forEach((data) => { contentWindow.postMessage(data, '*'); }); From 992cf6bd44d89f85b50199d52ff34ca14fd5358f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sat, 13 Feb 2021 10:28:08 +0100 Subject: [PATCH 036/176] Update node-fetch (#116560) --- extensions/github-authentication/package.json | 2 +- extensions/github-authentication/yarn.lock | 8 ++++---- extensions/github/yarn.lock | 6 +++--- extensions/microsoft-authentication/package.json | 2 +- extensions/microsoft-authentication/yarn.lock | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 0fe917632fe..726949d02e0 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -53,7 +53,7 @@ "vscode:prepublish": "npm run compile" }, "dependencies": { - "node-fetch": "2.6.0", + "node-fetch": "2.6.1", "uuid": "8.1.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.2" diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 1da6beed526..5220f268c51 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -84,10 +84,10 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== semver@^5.3.0: version "5.7.1" diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index 5e790ac2903..05a0b4cf6f9 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -125,9 +125,9 @@ is-plain-object@^3.0.0: integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== node-fetch@^2.3.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== once@^1.4.0: version "1.4.0" diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index e6416410be3..54405b6939f 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "buffer": "^5.6.0", - "node-fetch": "^2.6.0", + "node-fetch": "2.6.1", "randombytes": "github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971", "sha.js": "2.4.11", "stream": "0.0.2", diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 58e34ee843a..b58b488d970 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -126,10 +126,10 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== "randombytes@github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971": version "2.1.0" From 05922f8a260c3731abdaedfcab1b546c6c691986 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 13 Feb 2021 10:44:21 +0100 Subject: [PATCH 037/176] storage - add tests for all services --- .eslintrc.json | 1 + .../storageService.test.ts} | 56 ++++--- .../test/common/storageService.test.ts | 149 ++++++++++-------- .../storage/test/node/storageService.test.ts | 54 ++++--- 4 files changed, 149 insertions(+), 111 deletions(-) rename src/vs/platform/storage/test/{electron-browser/storage.test.ts => browser/storageService.test.ts} (54%) diff --git a/.eslintrc.json b/.eslintrc.json index cf4c1418a11..09fa5a59cde 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -278,6 +278,7 @@ "sinon", "vs/nls", "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", "**/vs/platform/*/{common,browser}/**", "**/vs/platform/*/test/{common,browser}/**" ] diff --git a/src/vs/platform/storage/test/electron-browser/storage.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts similarity index 54% rename from src/vs/platform/storage/test/electron-browser/storage.test.ts rename to src/vs/platform/storage/test/browser/storageService.test.ts index 836f287788c..fdeb01f61c6 100644 --- a/src/vs/platform/storage/test/electron-browser/storage.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -4,25 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import { strictEqual } from 'assert'; -import { FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; -import { join } from 'vs/base/common/path'; -import { tmpdir } from 'os'; -import { rimraf } from 'vs/base/node/pfs'; +import { BrowserStorageService, FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { NullLogService } from 'vs/platform/log/common/log'; import { Storage } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { FileService } from 'vs/platform/files/common/fileService'; -import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -suite('Storage', () => { +suite('StorageService (browser)', function () { - let testDir: string; + const disposables = new DisposableStore(); + + createSuite({ + setup: async () => { + const logService = new NullLogService(); + + const fileService = disposables.add(new FileService(logService)); + + const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); + disposables.add(fileService.registerProvider(Schemas.userData, userDataProvider)); + + const storageService = disposables.add(new BrowserStorageService({ userRoamingDataHome: URI.file('/User').with({ scheme: Schemas.userData }) } as unknown as IEnvironmentService, fileService)); + + await storageService.initialize({ id: String(Date.now()) }); + + return storageService; + }, + teardown: async storage => { + disposables.clear(); + } + }); +}); + +suite('FileStorageDatabase (browser)', () => { let fileService: FileService; - let fileProvider: DiskFileSystemProvider; const disposables = new DisposableStore(); @@ -31,20 +51,18 @@ suite('Storage', () => { fileService = disposables.add(new FileService(logService)); - fileProvider = disposables.add(new DiskFileSystemProvider(logService)); - disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - - testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); + const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); + disposables.add(fileService.registerProvider(Schemas.userData, userDataProvider)); }); teardown(() => { disposables.clear(); - - return rimraf(testDir); }); - test('File Based Storage', async () => { - let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + test('Basics', async () => { + const testDir = URI.file('/User/storage.json').with({ scheme: Schemas.userData }); + + let storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); @@ -58,7 +76,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); @@ -76,7 +94,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); + storage = new Storage(new FileStorageDatabase(testDir, false, fileService)); await storage.init(); diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 182ff12af03..fd115f627de 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -4,98 +4,102 @@ *--------------------------------------------------------------------------------------------*/ import { strictEqual, ok } from 'assert'; -import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent } from 'vs/platform/storage/common/storage'; +import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent, IStorageService } from 'vs/platform/storage/common/storage'; -suite('StorageService', function () { +export function createSuite(params: { setup: () => Promise, teardown: (service: T) => Promise }): void { - test('Get Data, Integer, Boolean (global, in-memory)', () => { + let storageService: T; + + setup(async () => { + storageService = await params.setup(); + }); + + teardown(() => { + return params.teardown(storageService); + }); + + test('Get Data, Integer, Boolean (global)', () => { storeData(StorageScope.GLOBAL); }); - test('Get Data, Integer, Boolean (workspace, in-memory)', () => { + test('Get Data, Integer, Boolean (workspace)', () => { storeData(StorageScope.WORKSPACE); }); function storeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(e => storageValueChangeEvents.push(e)); - strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar'); - strictEqual(storage.get('test.get', scope, ''), ''); - strictEqual(storage.getNumber('test.getNumber', scope, 5), 5); - strictEqual(storage.getNumber('test.getNumber', scope, 0), 0); - strictEqual(storage.getBoolean('test.getBoolean', scope, true), true); - strictEqual(storage.getBoolean('test.getBoolean', scope, false), false); + strictEqual(storageService.get('test.get', scope, 'foobar'), 'foobar'); + strictEqual(storageService.get('test.get', scope, ''), ''); + strictEqual(storageService.getNumber('test.getNumber', scope, 5), 5); + strictEqual(storageService.getNumber('test.getNumber', scope, 0), 0); + strictEqual(storageService.getBoolean('test.getBoolean', scope, true), true); + strictEqual(storageService.getBoolean('test.getBoolean', scope, false), false); - storage.store('test.get', 'foobar', scope, StorageTarget.MACHINE); - strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar'); + storageService.store('test.get', 'foobar', scope, StorageTarget.MACHINE); + strictEqual(storageService.get('test.get', scope, (undefined)!), 'foobar'); let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.get'); storageValueChangeEvents = []; - storage.store('test.get', '', scope, StorageTarget.MACHINE); - strictEqual(storage.get('test.get', scope, (undefined)!), ''); + storageService.store('test.get', '', scope, StorageTarget.MACHINE); + strictEqual(storageService.get('test.get', scope, (undefined)!), ''); storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get'); strictEqual(storageValueChangeEvent!.scope, scope); strictEqual(storageValueChangeEvent!.key, 'test.get'); - storage.store('test.getNumber', 5, scope, StorageTarget.MACHINE); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5); + storageService.store('test.getNumber', 5, scope, StorageTarget.MACHINE); + strictEqual(storageService.getNumber('test.getNumber', scope, (undefined)!), 5); - storage.store('test.getNumber', 0, scope, StorageTarget.MACHINE); - strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0); + storageService.store('test.getNumber', 0, scope, StorageTarget.MACHINE); + strictEqual(storageService.getNumber('test.getNumber', scope, (undefined)!), 0); - storage.store('test.getBoolean', true, scope, StorageTarget.MACHINE); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true); + storageService.store('test.getBoolean', true, scope, StorageTarget.MACHINE); + strictEqual(storageService.getBoolean('test.getBoolean', scope, (undefined)!), true); - storage.store('test.getBoolean', false, scope, StorageTarget.MACHINE); - strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false); + storageService.store('test.getBoolean', false, scope, StorageTarget.MACHINE); + strictEqual(storageService.getBoolean('test.getBoolean', scope, (undefined)!), false); - strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault'); - strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5); - strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true); + strictEqual(storageService.get('test.getDefault', scope, 'getDefault'), 'getDefault'); + strictEqual(storageService.getNumber('test.getNumberDefault', scope, 5), 5); + strictEqual(storageService.getBoolean('test.getBooleanDefault', scope, true), true); } - test('Remove Data (global, in-memory)', () => { + test('Remove Data (global)', () => { removeData(StorageScope.GLOBAL); }); - test('Remove Data (workspace, in-memory)', () => { + test('Remove Data (workspace)', () => { removeData(StorageScope.WORKSPACE); }); function removeData(scope: StorageScope): void { - const storage = new InMemoryStorageService(); - let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storage.onDidChangeValue(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(e => storageValueChangeEvents.push(e)); - storage.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); - strictEqual('foobar', storage.get('test.remove', scope, (undefined)!)); + storageService.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); + strictEqual('foobar', storageService.get('test.remove', scope, (undefined)!)); - storage.remove('test.remove', scope); - ok(!storage.get('test.remove', scope, (undefined)!)); + storageService.remove('test.remove', scope); + ok(!storageService.get('test.remove', scope, (undefined)!)); let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.remove'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.remove'); } test('Keys (in-memory)', () => { - const storage = new InMemoryStorageService(); - let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; - storage.onDidChangeTarget(e => storageTargetEvent = e); + storageService.onDidChangeTarget(e => storageTargetEvent = e); let storageValueChangeEvent: IStorageValueChangeEvent | undefined = undefined; - storage.onDidChangeValue(e => storageValueChangeEvent = e); + storageService.onDidChangeValue(e => storageValueChangeEvent = e); // Empty for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - strictEqual(storage.keys(scope, target).length, 0); + strictEqual(storageService.keys(scope, target).length, 0); } } @@ -105,8 +109,8 @@ suite('StorageService', function () { storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); - storage.store('test.target1', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); strictEqual(storageTargetEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.target1'); strictEqual(storageValueChangeEvent?.scope, scope); @@ -115,33 +119,33 @@ suite('StorageService', function () { storageTargetEvent = undefined; storageValueChangeEvent = Object.create(null); - storage.store('test.target1', 'otherValue1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'otherValue1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); strictEqual(storageTargetEvent, undefined); strictEqual(storageValueChangeEvent?.key, 'test.target1'); strictEqual(storageValueChangeEvent?.scope, scope); strictEqual(storageValueChangeEvent?.target, target); - storage.store('test.target2', 'value2', scope, target); - storage.store('test.target3', 'value3', scope, target); + storageService.store('test.target2', 'value2', scope, target); + storageService.store('test.target3', 'value3', scope, target); - strictEqual(storage.keys(scope, target).length, 3); + strictEqual(storageService.keys(scope, target).length, 3); } } // Remove values for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - const keysLength = storage.keys(scope, target).length; + const keysLength = storageService.keys(scope, target).length; - storage.store('test.target4', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, keysLength + 1); + storageService.store('test.target4', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, keysLength + 1); storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); - storage.remove('test.target4', scope); - strictEqual(storage.keys(scope, target).length, keysLength); + storageService.remove('test.target4', scope); + strictEqual(storageService.keys(scope, target).length, keysLength); strictEqual(storageTargetEvent?.scope, scope); strictEqual(storageValueChangeEvent?.key, 'test.target4'); strictEqual(storageValueChangeEvent?.scope, scope); @@ -151,48 +155,55 @@ suite('StorageService', function () { // Remove all for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - const keys = storage.keys(scope, target); + const keys = storageService.keys(scope, target); for (const key of keys) { - storage.remove(key, scope); + storageService.remove(key, scope); } - strictEqual(storage.keys(scope, target).length, 0); + strictEqual(storageService.keys(scope, target).length, 0); } } // Adding undefined or null removes value for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { - storage.store('test.target1', 'value1', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', 'value1', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); storageTargetEvent = Object.create(null); - storage.store('test.target1', undefined, scope, target); - strictEqual(storage.keys(scope, target).length, 0); + storageService.store('test.target1', undefined, scope, target); + strictEqual(storageService.keys(scope, target).length, 0); strictEqual(storageTargetEvent?.scope, scope); - storage.store('test.target1', '', scope, target); - strictEqual(storage.keys(scope, target).length, 1); + storageService.store('test.target1', '', scope, target); + strictEqual(storageService.keys(scope, target).length, 1); - storage.store('test.target1', null, scope, target); - strictEqual(storage.keys(scope, target).length, 0); + storageService.store('test.target1', null, scope, target); + strictEqual(storageService.keys(scope, target).length, 0); } } // Target change storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(storageTargetEvent); storageTargetEvent = undefined; - storage.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); ok(!storageTargetEvent); // no change in target }); +} + +suite('StorageService (in-memory)', function () { + createSuite({ + setup: async () => new InMemoryStorageService(), + teardown: async () => { } + }); }); diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index fed89b3eda6..ce8eb321aac 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -15,38 +15,46 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; -flakySuite('NativeStorageService', function () { +flakySuite('StorageService (native)', function () { + + class StorageTestEnvironmentService extends NativeEnvironmentService { + + constructor(private workspaceStorageFolderPath: URI, private _extensionsPath: string) { + super(parseArgs(process.argv, OPTIONS)); + } + + get workspaceStorageHome(): URI { + return this.workspaceStorageFolderPath; + } + + get extensionsPath(): string { + return this._extensionsPath; + } + } let testDir: string; - setup(() => { - testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); + createSuite({ + setup: async () => { + testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageservice'); - return promises.mkdir(testDir, { recursive: true }); - }); + await promises.mkdir(testDir, { recursive: true }); - teardown(() => { - return rimraf(testDir); + const storageService = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + await storageService.initialize({ id: String(Date.now()) }); + + return storageService; + }, + teardown: async storageService => { + await storageService.close(); + + return rimraf(testDir); + } }); test('Migrate Data', async function () { - - class StorageTestEnvironmentService extends NativeEnvironmentService { - - constructor(private workspaceStorageFolderPath: URI, private _extensionsPath: string) { - super(parseArgs(process.argv, OPTIONS)); - } - - get workspaceStorageHome(): URI { - return this.workspaceStorageFolderPath; - } - - get extensionsPath(): string { - return this._extensionsPath; - } - } - const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); await storage.initialize({ id: String(Date.now()) }); From 3a442c6939b2515fc8109f90886ee6fcae193137 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 13 Feb 2021 15:40:29 -0800 Subject: [PATCH 038/176] Fix broken markdown cells The editor wants to create a scoped CKS which you can't do with an Overlay --- .../contrib/notebook/browser/view/renderers/markdownCell.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 03a42b55550..b45001f687b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -308,9 +308,8 @@ export class StatefulMarkdownCell extends Disposable { this.templateData.editorContainer.innerText = ''; // create a special context key service that set the inCompositeEditor-contextkey - const editorContextKeyService = this.contextKeyService.createOverlay([ - [EditorContextKeys.inCompositeEditor.key, true] - ]); + const editorContextKeyService = this.contextKeyService.createScoped(this.templateData.editorPart); + EditorContextKeys.inCompositeEditor.bindTo(editorContextKeyService).set(true); const editorInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, editorContextKeyService])); this.editor = editorInstaService.createInstance(CodeEditorWidget, this.templateData.editorContainer, { From a699ffaee62010c4634d301da2bbdb7646b8d1da Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 13 Feb 2021 15:48:37 -0800 Subject: [PATCH 039/176] Reenable notebook smoke test Fix #116535 --- test/smoke/src/areas/notebook/notebook.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 27f8269954b..1d70ee93a89 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -34,7 +34,7 @@ export function setup() { await app.workbench.notebook.stopEditingCell(); }); - it.skip('inserts/edits markdown cell', async function () { + it('inserts/edits markdown cell', async function () { const app = this.app as Application; await app.workbench.notebook.openNotebook(); await app.workbench.notebook.focusNextCell(); From 1f8bb6d33b3095c2152eb5bfd666b3fb41afeaa2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 14 Feb 2021 12:01:50 +0100 Subject: [PATCH 040/176] storage - change main storage service to support global and workspace storage (stubs) --- .../sharedProcess/sharedProcessMain.ts | 7 +- src/vs/code/electron-main/app.ts | 6 +- src/vs/platform/storage/node/storageIpc.ts | 204 +++++----- src/vs/platform/storage/node/storageMain.ts | 370 ++++++++++++++++++ .../storage/node/storageMainService.ts | 186 ++------- .../platform/storage/node/storageService2.ts | 158 ++++++++ src/vs/platform/windows/common/windows.ts | 3 + .../platform/windows/electron-main/window.ts | 91 +++-- .../electron-browser/desktop.main.ts | 21 +- .../electron-sandbox/desktop.contribution.ts | 6 + .../electron-browser/workbenchTestServices.ts | 1 + 11 files changed, 753 insertions(+), 300 deletions(-) create mode 100644 src/vs/platform/storage/node/storageMain.ts create mode 100644 src/vs/platform/storage/node/storageService2.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 1e4f3c62849..0e4264134c1 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -58,8 +58,8 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; -import { NativeStorageService } from 'vs/platform/storage/node/storageService'; -import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { NativeStorageService2 } from 'vs/platform/storage/node/storageService2'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; @@ -173,7 +173,8 @@ class SharedProcessMain extends Disposable { await configurationService.initialize(); // Storage - const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService); + const storageDatabase = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), undefined /* no workspace access for shared process */); + const storageService = new NativeStorageService2(storageDatabase.globalStorage, storageDatabase.workspaceStorage, environmentService); services.set(IStorageService, storageService); await storageService.initialize(); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index ef4909c7d6b..9646be97a2b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -59,7 +59,7 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; +import { StorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; @@ -582,7 +582,7 @@ export class CodeApplication extends Disposable { // Storage const storageMainService = new StorageMainService(this.logService, this.environmentService); services.set(IStorageMainService, storageMainService); - this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.close())); + this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.globalStorage.close())); // Backups const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); @@ -666,7 +666,7 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('webview', webviewChannel); // Storage (main & shared process) - const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, accessor.get(IStorageMainService))); + const storageChannel = this._register(new StorageDatabaseChannel(this.logService, accessor.get(IStorageMainService))); mainProcessElectronServer.registerChannel('storage', storageChannel); sharedProcessClient.then(client => client.registerChannel('storage', storageChannel)); diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index 67195995c28..e4a95707a57 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -3,20 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; -import { generateUuid } from 'vs/base/common/uuid'; -import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey, currentSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/node/storageMain'; +import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; type Key = string; type Value = string; type Item = [Key, Value]; -interface ISerializableUpdateRequest { +interface IWorkspaceArgument { + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined +} + +interface ISerializableUpdateRequest extends IWorkspaceArgument { insert?: Item[]; delete?: Key[]; } @@ -26,60 +30,34 @@ interface ISerializableItemsChangeEvent { readonly deleted?: Key[]; } -export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel { +//#region --- Storage Server + +export class StorageDatabaseChannel extends Disposable implements IServerChannel { private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; - private readonly _onDidChangeItems = this._register(new Emitter()); - readonly onDidChangeItems = this._onDidChangeItems.event; - - private readonly whenReady = this.init(); + private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); + private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; constructor( private logService: ILogService, private storageMainService: IStorageMainService ) { super(); + + // Trigger init of global storage directly from ctor + this.withStorageInitialized(undefined); + + this.registerGlobalStorageListeners(); } - private async init(): Promise { - try { - await this.storageMainService.initialize(); - } catch (error) { - this.logService.error(`[storage] init(): Unable to init global storage due to ${error}`); - } + //#region Global Storage Change Events - // Apply global telemetry values as part of the initialization - // These are global across all windows and thereby should be - // written from the main process once. - this.initTelemetry(); - - // Setup storage change listeners - this.registerListeners(); - } - - private initTelemetry(): void { - const instanceId = this.storageMainService.get(instanceStorageKey, undefined); - if (instanceId === undefined) { - this.storageMainService.store(instanceStorageKey, generateUuid()); - } - - const firstSessionDate = this.storageMainService.get(firstSessionDateStorageKey, undefined); - if (firstSessionDate === undefined) { - this.storageMainService.store(firstSessionDateStorageKey, new Date().toUTCString()); - } - - const lastSessionDate = this.storageMainService.get(currentSessionDateStorageKey, undefined); // previous session date was the "current" one at that time - const currentSessionDate = new Date().toUTCString(); // current session date is "now" - this.storageMainService.store(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); - this.storageMainService.store(currentSessionDateStorageKey, currentSessionDate); - } - - private registerListeners(): void { + private registerGlobalStorageListeners(): void { // Listen for changes in global storage to send to listeners // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { + this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { if (!prev) { prev = [cur]; } else { @@ -87,18 +65,18 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC } return prev; - }, GlobalStorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { + }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { if (events.length) { - this._onDidChangeItems.fire(this.serializeEvents(events)); + this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); } })); } - private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { + private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { const changed = new Map(); const deleted = new Set(); events.forEach(event => { - const existing = this.storageMainService.get(event.key); + const existing = this.storageMainService.globalStorage.get(event.key); if (typeof existing === 'string') { changed.set(event.key, existing); } else { @@ -114,33 +92,35 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC listen(_: unknown, event: string): Event { switch (event) { - case 'onDidChangeItems': return this.onDidChangeItems; + case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; } throw new Error(`Event not found: ${event}`); } - async call(_: unknown, command: string, arg?: any): Promise { + //#endregion - // ensure to always wait for ready - await this.whenReady; + async call(_: unknown, command: string, arg: IWorkspaceArgument): Promise { + + // Get storage to be ready + const storage = await this.withStorageInitialized(arg.workspace); // handle call switch (command) { case 'getItems': { - return Array.from(this.storageMainService.items.entries()); + return Array.from(storage.items.entries()); } case 'updateItems': { const items: ISerializableUpdateRequest = arg; if (items.insert) { for (const [key, value] of items.insert) { - this.storageMainService.store(key, value); + storage.store(key, value); } } if (items.delete) { - items.delete.forEach(key => this.storageMainService.remove(key)); + items.delete.forEach(key => storage.remove(key)); } break; @@ -150,44 +130,41 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC throw new Error(`Call not found: ${command}`); } } + + private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined): Promise { + const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; + + try { + await storage.initialize(); + } catch (error) { + this.logService.error(`[storage] init(): Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + } + + return storage; + } } -export class GlobalStorageDatabaseChannelClient extends Disposable implements IStorageDatabase { +//#endregion - declare readonly _serviceBrand: undefined; +//#region --- Storage Client - private readonly _onDidChangeItemsExternal = this._register(new Emitter()); - readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; +abstract class BaseStorageDatabase extends Disposable implements IStorageDatabase { - private onDidChangeItemsOnMainListener: IDisposable | undefined; + abstract onDidChangeItemsExternal: Event; - constructor(private channel: IChannel) { + constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) { super(); - - this.registerListeners(); - } - - private registerListeners(): void { - this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e)); - } - - private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void { - if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { - this._onDidChangeItemsExternal.fire({ - changed: e.changed ? new Map(e.changed) : undefined, - deleted: e.deleted ? new Set(e.deleted) : undefined - }); - } } async getItems(): Promise> { - const items: Item[] = await this.channel.call('getItems'); + const serializableRequest: IWorkspaceArgument = { workspace: this.workspace }; + const items: Item[] = await this.channel.call('getItems', serializableRequest); return new Map(items); } updateItems(request: IUpdateRequest): Promise { - const serializableRequest: ISerializableUpdateRequest = Object.create(null); + const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; if (request.insert) { serializableRequest.insert = Array.from(request.insert.entries()); @@ -200,15 +177,66 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS return this.channel.call('updateItems', serializableRequest); } + abstract close(recovery?: () => Map): Promise; +} + +class GlobalStorageDatabase extends BaseStorageDatabase implements IStorageDatabase { + + private readonly _onDidChangeItemsExternal = this._register(new Emitter()); + readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; + + private onDidChangeGlobalStorageListener: IDisposable | undefined; + + constructor(channel: IChannel) { + super(channel, undefined); + + this.registerListeners(); + } + + private registerListeners(): void { + this.onDidChangeGlobalStorageListener = this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + } + + private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { + if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { + this._onDidChangeItemsExternal.fire({ + changed: e.changed ? new Map(e.changed) : undefined, + deleted: e.deleted ? new Set(e.deleted) : undefined + }); + } + } + async close(): Promise { - // when we are about to close, we start to ignore main-side changes since we close anyway - dispose(this.onDidChangeItemsOnMainListener); - } - - dispose(): void { - super.dispose(); - - dispose(this.onDidChangeItemsOnMainListener); + // when we are about to close, we start to ignore global storage changes since we close anyway + dispose(this.onDidChangeGlobalStorageListener); } } + +class WorkspaceStorageDatabase extends BaseStorageDatabase implements IStorageDatabase { + + readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window + + constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { + super(channel, workspace); + } + + async close(): Promise { + // TODO@bpasero close workspace storage? + } +} + +export class StorageDatabaseChannelClient extends Disposable { + + readonly globalStorage = new GlobalStorageDatabase(this.channel); + readonly workspaceStorage = this.workspace ? new WorkspaceStorageDatabase(this.channel, this.workspace) : undefined; + + constructor( + private channel: IChannel, + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined + ) { + super(); + } +} + +//#endregion diff --git a/src/vs/platform/storage/node/storageMain.ts b/src/vs/platform/storage/node/storageMain.ts new file mode 100644 index 00000000000..4fd2e233149 --- /dev/null +++ b/src/vs/platform/storage/node/storageMain.ts @@ -0,0 +1,370 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; +import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; +import { join } from 'vs/base/common/path'; +import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { generateUuid } from 'vs/base/common/uuid'; + +/** + * Provides access to global and workspace storage from the + * electron-main side that is the owner of all storage connections. + */ +export interface IStorageMain { + + /** + * Emitted whenever data is updated or deleted. + */ + readonly onDidChangeStorage: Event; + + /** + * Emitted when the storage is about to persist. This is the right time + * to persist data to ensure it is stored before the application shuts + * down. + * + * Note: this event may be fired many times, not only on shutdown to prevent + * loss of state in situations where the shutdown is not sufficient to + * persist the data properly. + */ + readonly onWillSaveState: Event; + + /** + * Access to all cached items of this storage service. + */ + readonly items: Map; + + /** + * Required call to ensure the service can be used. + */ + initialize(): Promise; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. + */ + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a boolean. + */ + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a number using parseInt with a base of 10. + */ + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + + /** + * Store a string value under the given key to storage. The value will + * be converted to a string. + */ + store(key: string, value: string | boolean | number | undefined | null): void; + + /** + * Delete an element stored under the provided key from storage. + */ + remove(key: string): void; + + /** + * Close the storage connection. + */ + close(): Promise; +} + +export interface IStorageChangeEvent { + key: string; +} + +export class GlobalStorageMain extends Disposable implements IStorageMain { + + private static readonly STORAGE_NAME = 'state.vscdb'; + + private readonly _onDidChangeStorage = this._register(new Emitter()); + readonly onDidChangeStorage = this._onDidChangeStorage.event; + + private readonly _onWillSaveState = this._register(new Emitter()); + readonly onWillSaveState = this._onWillSaveState.event; + + get items(): Map { return this.storage.items; } + + private storage: IStorage; + + private initializePromise: Promise | undefined; + + constructor( + @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: IEnvironmentService + ) { + super(); + + // Until the storage has been initialized, it can only be in memory + this.storage = new Storage(new InMemoryStorageDatabase()); + } + + private get storagePath(): string { + if (!!this.environmentService.extensionTestsLocationURI) { + return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! + } + + return join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + } + + private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { + return { + logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, + logError: error => this.logService.error(error) + }; + } + + initialize(): Promise { + if (!this.initializePromise) { + this.initializePromise = this.doInitialize(); + } + + return this.initializePromise; + } + + private async doInitialize(): Promise { + this.storage.dispose(); + this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, { + logging: this.createLogginOptions() + })); + + this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + + await this.storage.init(); + + // Check to see if this is the first time we are "opening" the application + const firstOpen = this.storage.getBoolean(IS_NEW_KEY); + if (firstOpen === undefined) { + this.storage.set(IS_NEW_KEY, true); + } else if (firstOpen) { + this.storage.set(IS_NEW_KEY, false); + } + + // Apply global telemetry values as part of the initialization + this.storeTelemetryStateOnce(); + } + + private storeTelemetryStateOnce(): void { + const instanceId = this.get(instanceStorageKey, undefined); + if (instanceId === undefined) { + this.store(instanceStorageKey, generateUuid()); + } + + const firstSessionDate = this.get(firstSessionDateStorageKey, undefined); + if (firstSessionDate === undefined) { + this.store(firstSessionDateStorageKey, new Date().toUTCString()); + } + + const lastSessionDate = this.get(currentSessionDateStorageKey, undefined); // previous session date was the "current" one at that time + const currentSessionDate = new Date().toUTCString(); // current session date is "now" + this.store(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); + this.store(currentSessionDateStorageKey, currentSessionDate); + } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + return this.storage.get(key, fallbackValue); + } + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { + return this.storage.getBoolean(key, fallbackValue); + } + + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { + return this.storage.getNumber(key, fallbackValue); + } + + store(key: string, value: string | boolean | number | undefined | null): Promise { + return this.storage.set(key, value); + } + + remove(key: string): Promise { + return this.storage.delete(key); + } + + close(): Promise { + + // Signal as event so that clients can still store data + this._onWillSaveState.fire(); + + // Do it + return this.storage.close(); + } +} + +export class WorkspaceStorageMain extends Disposable implements IStorageMain { + + readonly onDidChangeStorage = Event.None; + readonly onWillSaveState = Event.None; + + get items(): Map { return this.storage.items; } + + private storage: IStorage; + + private initializePromise: Promise | undefined; + + constructor( + ) { + super(); + + // Until the storage has been initialized, it can only be in memory + this.storage = new Storage(new InMemoryStorageDatabase()); + } + + async initialize(): Promise { + if (!this.initializePromise) { + this.initializePromise = this.doInitialize(); + } + + return this.initializePromise; + } + + private async doInitialize(): Promise { + // private async initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise { + + // // Prepare workspace storage folder for DB + // try { + // const result = await this.prepareWorkspaceStorageFolder(payload); + + // const useInMemoryStorage = !!this.environmentService.extensionTestsLocationURI; // no storage during extension tests! + + // // Create workspace storage and initialize + // mark('code/willInitWorkspaceStorage'); + // try { + // const workspaceStorage = this.createWorkspaceStorage( + // useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME), + // result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined + // ); + // await workspaceStorage.init(); + + // // Check to see if this is the first time we are "opening" this workspace + // const firstWorkspaceOpen = workspaceStorage.getBoolean(IS_NEW_KEY); + // if (firstWorkspaceOpen === undefined) { + // workspaceStorage.set(IS_NEW_KEY, result.wasCreated); + // } else if (firstWorkspaceOpen) { + // workspaceStorage.set(IS_NEW_KEY, false); + // } + // } finally { + // mark('code/didInitWorkspaceStorage'); + // } + // } catch (error) { + // this.logService.error(`[storage] initializeWorkspaceStorage(): Unable to init workspace storage due to ${error}`); + // } + // } + + // private createWorkspaceStorage(workspaceStoragePath: string, hint?: StorageHint): IStorage { + + // // Logger for workspace storage + // const workspaceLoggingOptions: ISQLiteStorageDatabaseLoggingOptions = { + // logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, + // logError: error => this.logService.error(error) + // }; + + // // Dispose old (if any) + // dispose(this.workspaceStorage); + // dispose(this.workspaceStorageListener); + + // // Create new + // this.workspaceStoragePath = workspaceStoragePath; + // this.workspaceStorage = new Storage(new SQLiteStorageDatabase(workspaceStoragePath, { logging: workspaceLoggingOptions }), { hint }); + // this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + + // return this.workspaceStorage; + // } + + // private getWorkspaceStorageFolderPath(payload: IWorkspaceInitializationPayload): string { + // return join(this.environmentService.workspaceStorageHome.fsPath, payload.id); // workspace home + workspace id; + // } + + // private async prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload): Promise<{ path: string, wasCreated: boolean }> { + // const workspaceStorageFolderPath = this.getWorkspaceStorageFolderPath(payload); + + // const storageExists = await exists(workspaceStorageFolderPath); + // if (storageExists) { + // return { path: workspaceStorageFolderPath, wasCreated: false }; + // } + + // await promises.mkdir(workspaceStorageFolderPath, { recursive: true }); + + // // Write metadata into folder + // this.ensureWorkspaceStorageFolderMeta(payload); + + // return { path: workspaceStorageFolderPath, wasCreated: true }; + // } + + // private ensureWorkspaceStorageFolderMeta(payload: IWorkspaceInitializationPayload): void { + // let meta: object | undefined = undefined; + // if (isSingleFolderWorkspaceIdentifier(payload)) { + // meta = { folder: payload.uri.toString() }; + // } else if (isWorkspaceIdentifier(payload)) { + // meta = { workspace: payload.configPath.toString() }; + // } + + // if (meta) { + // (async () => { + // try { + // const workspaceStorageMetaPath = join(this.getWorkspaceStorageFolderPath(payload), NativeStorageService.WORKSPACE_META_NAME); + // const storageExists = await exists(workspaceStorageMetaPath); + // if (!storageExists) { + // await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); + // } + // } catch (error) { + // this.logService.error(error); + // } + // })(); + // } + // } + } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + return this.storage.get(key, fallbackValue); + } + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { + return this.storage.getBoolean(key, fallbackValue); + } + + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { + return this.storage.getNumber(key, fallbackValue); + } + + store(key: string, value: string | boolean | number | undefined | null): Promise { + return this.storage.set(key, value); + } + + remove(key: string): Promise { + return this.storage.delete(key); + } + + close(): Promise { + return this.storage.close(); + } +} diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index 0d25d4e6554..0e4920b6fd0 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -3,15 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; -import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; -import { join } from 'vs/base/common/path'; -import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/node/storageMain'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IStorageMainService = createDecorator('storageMainService'); @@ -20,172 +16,50 @@ export interface IStorageMainService { readonly _serviceBrand: undefined; /** - * Emitted whenever data is updated or deleted. + * Provides access to the global storage shared across all windows. */ - readonly onDidChangeStorage: Event; + readonly globalStorage: IStorageMain; /** - * Emitted when the storage is about to persist. This is the right time - * to persist data to ensure it is stored before the application shuts - * down. - * - * Note: this event may be fired many times, not only on shutdown to prevent - * loss of state in situations where the shutdown is not sufficient to - * persist the data properly. + * Provides access to the workspace storage specific to a single window. */ - readonly onWillSaveState: Event; - - /** - * Access to all cached items of this storage service. - */ - readonly items: Map; - - /** - * Required call to ensure the service can be used. - */ - initialize(): Promise; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. - */ - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. - */ - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. - */ - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - - /** - * Store a string value under the given key to storage. The value will - * be converted to a string. - */ - store(key: string, value: string | boolean | number | undefined | null): void; - - /** - * Delete an element stored under the provided key from storage. - */ - remove(key: string): void; + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain; } -export interface IStorageChangeEvent { - key: string; -} - -export class StorageMainService extends Disposable implements IStorageMainService { +export class StorageMainService implements IStorageMainService { declare readonly _serviceBrand: undefined; - private static readonly STORAGE_NAME = 'state.vscdb'; + readonly globalStorage = this.createGlobalStorage(); - private readonly _onDidChangeStorage = this._register(new Emitter()); - readonly onDidChangeStorage = this._onDidChangeStorage.event; - - private readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; - - get items(): Map { return this.storage.items; } - - private storage: IStorage; - - private initializePromise: Promise | undefined; + private readonly mapWorkspaceToStorage = new Map(); constructor( @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { - super(); - - // Until the storage has been initialized, it can only be in memory - this.storage = new Storage(new InMemoryStorageDatabase()); } - private get storagePath(): string { - if (!!this.environmentService.extensionTestsLocationURI) { - return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! + private createGlobalStorage(): IStorageMain { + const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); + + return globalStorage; + } + + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { + const workspaceStorage = new WorkspaceStorageMain(); + // TODO@bpasero lifecycle like global storage? window events? crashes? + + return workspaceStorage; + } + + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { + let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); + if (!workspaceStorage) { + workspaceStorage = this.createWorkspaceStorage(workspace); + this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); } - return join(this.environmentService.globalStorageHome.fsPath, StorageMainService.STORAGE_NAME); - } - - private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { - return { - logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => this.logService.error(error) - }; - } - - initialize(): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(); - } - - return this.initializePromise; - } - - private async doInitialize(): Promise { - this.storage.dispose(); - this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, { - logging: this.createLogginOptions() - })); - - this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); - - await this.storage.init(); - - // Check to see if this is the first time we are "opening" the application - const firstOpen = this.storage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - this.storage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - this.storage.set(IS_NEW_KEY, false); - } - } - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - get(key: string, fallbackValue?: string): string | undefined { - return this.storage.get(key, fallbackValue); - } - - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - return this.storage.getBoolean(key, fallbackValue); - } - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - getNumber(key: string, fallbackValue?: number): number | undefined { - return this.storage.getNumber(key, fallbackValue); - } - - store(key: string, value: string | boolean | number | undefined | null): Promise { - return this.storage.set(key, value); - } - - remove(key: string): Promise { - return this.storage.delete(key); - } - - close(): Promise { - - // Signal as event so that clients can still store data - this._onWillSaveState.fire(); - - // Do it - return this.storage.close(); + return workspaceStorage; } } diff --git a/src/vs/platform/storage/node/storageService2.ts b/src/vs/platform/storage/node/storageService2.ts new file mode 100644 index 00000000000..e5748a183f0 --- /dev/null +++ b/src/vs/platform/storage/node/storageService2.ts @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { StorageScope, WillSaveStateReason, logStorage, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { Storage, IStorageDatabase, IStorage } from 'vs/base/parts/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { assertIsDefined } from 'vs/base/common/types'; +import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; + +export class NativeStorageService2 extends AbstractStorageService { + + private readonly globalStorage = new Storage(this.globalStorageDatabase); + private readonly workspaceStorage = this.workspaceStorageDatabase ? new Storage(this.workspaceStorageDatabase) : undefined; + + private initializePromise: Promise | undefined; + + private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60000 /* every minute */)); + private runWhenIdleDisposable: IDisposable | undefined = undefined; + + constructor( + private globalStorageDatabase: IStorageDatabase, + private workspaceStorageDatabase: IStorageDatabase | undefined, + @IEnvironmentService private readonly environmentService: IEnvironmentService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this._register(this.workspaceStorage?.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)) ?? Disposable.None); + } + + initialize(): Promise { + if (!this.initializePromise) { + this.initializePromise = this.doInitialize(); + } + + return this.initializePromise; + } + + private async doInitialize(): Promise { + + // Init all storage locations + await Promises.settled([ + this.globalStorage.init(), + this.workspaceStorage?.init() ?? Promise.resolve() + ]); + + // On some OS we do not get enough time to persist state on shutdown (e.g. when + // Windows restarts after applying updates). In other cases, VSCode might crash, + // so we periodically save state to reduce the chance of loosing any state. + this.periodicFlushScheduler.schedule(); + } + + get(key: string, scope: StorageScope, fallbackValue: string): string; + get(key: string, scope: StorageScope): string | undefined; + get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { + return this.getStorage(scope).get(key, fallbackValue); + } + + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope): boolean | undefined; + getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { + return this.getStorage(scope).getBoolean(key, fallbackValue); + } + + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope): number | undefined; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + return this.getStorage(scope).getNumber(key, fallbackValue); + } + + protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { + this.getStorage(scope).set(key, value); + } + + protected doRemove(key: string, scope: StorageScope): void { + this.getStorage(scope).delete(key); + } + + private getStorage(scope: StorageScope): IStorage { + return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); + } + + protected async doFlush(): Promise { + await Promises.settled([ + this.globalStorage.whenFlushed(), + this.workspaceStorage?.whenFlushed() ?? Promise.resolve() + ]); + } + + private doFlushWhenIdle(): void { + + // Dispose any previous idle runner + dispose(this.runWhenIdleDisposable); + + // Run when idle + this.runWhenIdleDisposable = runWhenIdle(() => { + + // send event to collect state + this.flush(); + + // repeat + this.periodicFlushScheduler.schedule(); + }); + } + + async close(): Promise { + + // Stop periodic scheduler and idle runner as we now collect state normally + this.periodicFlushScheduler.dispose(); + dispose(this.runWhenIdleDisposable); + this.runWhenIdleDisposable = undefined; + + // Signal as event so that clients can still store data + this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); + + // Do it + await Promises.settled([ + this.globalStorage.close(), + this.workspaceStorage?.close() ?? Promise.resolve() + ]); + } + + async logStorage(): Promise { + return logStorage( + this.globalStorage.items, + this.workspaceStorage ? this.workspaceStorage.items : new Map(), + this.environmentService.globalStorageHome.fsPath, + /* this.workspaceStoragePath || */ ''); + } + + async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { + // if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { + // return; // no migration needed if running in memory + // } + + // // Close workspace DB to be able to copy + // await this.getStorage(StorageScope.WORKSPACE).close(); + + // // Prepare new workspace storage folder + // const result = await this.prepareWorkspaceStorageFolder(toWorkspace); + + // const newWorkspaceStoragePath = join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME); + + // // Copy current storage over to new workspace storage + // await copy(assertIsDefined(this.workspaceStoragePath), newWorkspaceStoragePath, { preserveSymlinks: false }); + + // // Recreate and init workspace storage + // return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); + } +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 2ae70d36bce..3ca7fa2b745 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -114,6 +114,7 @@ export interface IWindowSettings { readonly enableMenuBarMnemonics: boolean; readonly closeWhenEmpty: boolean; readonly clickThroughInactive: boolean; + readonly enableExperimentalMainProcessWorkspaceStorage: boolean; } export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { @@ -253,6 +254,8 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native filesToWait?: IPathsToWaitFor; os: IOSConfiguration; + + enableExperimentalMainProcessWorkspaceStorage: boolean; } /** diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 7fc86f531bd..2d60e42471c 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -114,9 +114,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IFileService private readonly fileService: IFileService, - @IStorageMainService storageService: IStorageMainService, + @IStorageMainService storageMainService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @@ -158,7 +158,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { nativeWindowOpen: true, webviewTag: true, zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel), - ...this.environmentService.sandbox ? + ...this.environmentMainService.sandbox ? // Sandbox { @@ -181,9 +181,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Linux: always // Windows: only when running out of sources, otherwise an icon is set by us on the executable if (isLinux) { - options.icon = join(this.environmentService.appRoot, 'resources/linux/code.png'); - } else if (isWindows && !this.environmentService.isBuilt) { - options.icon = join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); + options.icon = join(this.environmentMainService.appRoot, 'resources/linux/code.png'); + } else if (isWindows && !this.environmentMainService.isBuilt) { + options.icon = join(this.environmentMainService.appRoot, 'resources/win32/code_150x150.png'); } if (isMacintosh && !this.useNativeFullScreen()) { @@ -217,7 +217,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._id = this._win.id; // Open devtools if instructed from command line args - if (this.environmentService.args['open-devtools'] === true) { + if (this.environmentMainService.args['open-devtools'] === true) { this._win.webContents.openDevTools(); } @@ -267,9 +267,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.createTouchBar(); // Request handling - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService, { - get(key) { return storageService.get(key); }, - store(key, value) { storageService.store(key, value); } + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentMainService, this.fileService, { + get: key => storageMainService.globalStorage.get(key), + store: (key, value) => storageMainService.globalStorage.store(key, value) }); // Eventing @@ -535,20 +535,23 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); // Handle configuration changes - this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated())); + this._register(this.configurationService.onDidChangeConfiguration(() => this.onConfigurationUpdated())); // Handle Workspace events this._register(this.workspacesManagementMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; - this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => - this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }))); + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, async (details, cb) => { + const headers = await this.marketplaceHeadersPromise; + + cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }); + }); } - private onWindowError(error: WindowError.UNRESPONSIVE): void; - private onWindowError(error: WindowError.CRASHED, details: RenderProcessGoneDetails): void; - private onWindowError(error: WindowError, details?: RenderProcessGoneDetails): void { + private async onWindowError(error: WindowError.UNRESPONSIVE): Promise; + private async onWindowError(error: WindowError.CRASHED, details: RenderProcessGoneDetails): Promise; + private async onWindowError(error: WindowError, details?: RenderProcessGoneDetails): Promise { this.logService.error(error === WindowError.CRASHED ? `Main: renderer process crashed (detail: ${details?.reason})` : 'Main: detected unresponsive'); // If we run extension tests from CLI, showing a dialog is not @@ -581,25 +584,25 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Show Dialog - this.dialogMainService.showMessageBox({ + const result = await this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message: localize('appStalled', "The window is no longer responding"), detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true - }, this._win).then(result => { - if (!this._win) { - return; // Return early if the window has been going down already - } + }, this._win); - if (result.response === 0) { - this._win.webContents.forcefullyCrashRenderer(); // Calling reload() immediately after calling this method will force the reload to occur in a new process - this.reload(); - } else if (result.response === 2) { - this.destroyWindow(); - } - }); + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this._win.webContents.forcefullyCrashRenderer(); // Calling reload() immediately after calling this method will force the reload to occur in a new process + this.reload(); + } else if (result.response === 2) { + this.destroyWindow(); + } } // Crashed @@ -611,24 +614,24 @@ export class CodeWindow extends Disposable implements ICodeWindow { message = localize('appCrashed', "The window has crashed", details?.reason); } - this.dialogMainService.showMessageBox({ + const result = await this.dialogMainService.showMessageBox({ title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message, detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true - }, this._win).then(result => { - if (!this._win) { - return; // Return early if the window has been going down already - } + }, this._win); - if (result.response === 0) { - this.reload(); - } else if (result.response === 1) { - this.destroyWindow(); - } - }); + if (!this._win) { + return; // Return early if the window has been going down already + } + + if (result.response === 0) { + this.reload(); + } else if (result.response === 1) { + this.destroyWindow(); + } } } @@ -747,7 +750,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Make window visible if it did not open in N seconds because this indicates an error // Only do this when running out of sources and not when running tests - if (!this.environmentService.isBuilt && !this.environmentService.extensionTestsLocationURI) { + if (!this.environmentMainService.isBuilt && !this.environmentMainService.extensionTestsLocationURI) { this.showTimeoutHandle = setTimeout(() => { if (this._win && !this._win.isVisible() && !this._win.isMinimized()) { this._win.show(); @@ -824,7 +827,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.windowId = this._win.id; windowConfiguration.sessionId = `window:${this._win.id}`; windowConfiguration.logLevel = this.logService.getLevel(); - windowConfiguration.logsPath = this.environmentService.logsPath; + windowConfiguration.logsPath = this.environmentMainService.logsPath; // Set zoomlevel const windowConfig = this.configurationService.getValue('window'); @@ -851,13 +854,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { windowConfiguration.perfMarks = getMarks(); // Parts splash - windowConfiguration.partsSplashPath = join(this.environmentService.userDataPath, 'rapid_render.json'); + windowConfiguration.partsSplashPath = join(this.environmentMainService.userDataPath, 'rapid_render.json'); // OS Info windowConfiguration.os = { release: release() }; + windowConfiguration.enableExperimentalMainProcessWorkspaceStorage = !!(windowConfig?.enableExperimentalMainProcessWorkspaceStorage); + // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; @@ -887,7 +892,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private doGetUrl(config: object): string { let workbench: string; - if (this.environmentService.sandbox) { + if (this.environmentMainService.sandbox) { workbench = 'vs/code/electron-sandbox/workbench/workbench.html'; } else { workbench = 'vs/code/electron-browser/workbench/workbench.html'; diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index a6b22ab4def..24a5f7361c7 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -18,11 +18,12 @@ import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { NativeStorageService } from 'vs/platform/storage/node/storageService'; +import { NativeStorageService2 } from 'vs/platform/storage/node/storageService2'; import { Schemas } from 'vs/base/common/network'; -import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -130,14 +131,14 @@ class DesktopMain extends Disposable { services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } - private registerListeners(workbench: Workbench, storageService: NativeStorageService): void { + private registerListeners(workbench: Workbench, storageService: NativeStorageService | NativeStorageService2): void { // Workbench Lifecycle this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); } - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService | NativeStorageService2 }> { const serviceCollection = new ServiceCollection(); @@ -319,9 +320,15 @@ class DesktopMain extends Disposable { } } - private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { - const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')); - const storageService = new NativeStorageService(globalStorageDatabase, logService, this.environmentService); + private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { + const storageDataBase = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); + + let storageService: NativeStorageService | NativeStorageService2; + if (this.configuration.enableExperimentalMainProcessWorkspaceStorage) { + storageService = new NativeStorageService2(storageDataBase.globalStorage, storageDataBase.workspaceStorage, this.environmentService); + } else { + storageService = new NativeStorageService(storageDataBase.globalStorage, logService, this.environmentService); + } try { await storageService.initialize(payload); diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 299561eb5e1..1b1971fd14e 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -273,6 +273,12 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.clickThroughInactive', "If enabled, clicking on an inactive window will both activate the window and trigger the element under the mouse if it is clickable. If disabled, clicking anywhere on an inactive window will activate it only and a second click is required on the element."), 'included': isMacintosh + }, + 'window.enableExperimentalMainProcessWorkspaceStorage': { + 'type': 'boolean', + 'default': false, + 'scope': ConfigurationScope.APPLICATION, + 'description': localize('window.localize', "Enables workspace storage access from the main process. Requires a restart to take effect."), } } }); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index f2c26043db4..6dbb50fa818 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -55,6 +55,7 @@ export const TestWorkbenchConfiguration: INativeWorkbenchConfiguration = { perfMarks: [], colorScheme: { dark: true, highContrast: false }, os: { release: release() }, + enableExperimentalMainProcessWorkspaceStorage: false, ...parseArgs(process.argv, OPTIONS) }; From f49d78c4afc16c4725f439be610986c9a8202b1b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 14 Feb 2021 12:24:55 +0100 Subject: [PATCH 041/176] storage - fix layers to be able to use native storage service in sandbox --- .../sharedProcess/sharedProcessMain.ts | 4 +- src/vs/code/electron-main/app.ts | 4 +- src/vs/platform/storage/common/storageIpc.ts | 119 +++++++++ .../storage/electron-main/storageIpc.ts | 125 +++++++++ .../{node => electron-main}/storageMain.ts | 0 .../storageMainService.ts | 2 +- .../storageService2.ts | 0 src/vs/platform/storage/node/storageIpc.ts | 242 ------------------ .../platform/windows/electron-main/window.ts | 2 +- .../electron-browser/desktop.main.ts | 10 +- .../electron-sandbox/desktop.main.ts | 91 ++++++- .../sandbox.simpleservices.ts | 19 +- 12 files changed, 335 insertions(+), 283 deletions(-) create mode 100644 src/vs/platform/storage/common/storageIpc.ts create mode 100644 src/vs/platform/storage/electron-main/storageIpc.ts rename src/vs/platform/storage/{node => electron-main}/storageMain.ts (100%) rename src/vs/platform/storage/{node => electron-main}/storageMainService.ts (97%) rename src/vs/platform/storage/{node => electron-sandbox}/storageService2.ts (100%) delete mode 100644 src/vs/platform/storage/node/storageIpc.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 0e4264134c1..22620bc9345 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -58,8 +58,8 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; -import { NativeStorageService2 } from 'vs/platform/storage/node/storageService2'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 9646be97a2b..918d2fb505d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -58,8 +58,8 @@ import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; -import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { StorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; +import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts new file mode 100644 index 00000000000..b885e0ce33a --- /dev/null +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export type Key = string; +export type Value = string; +export type Item = [Key, Value]; + +export interface IWorkspaceArgument { + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined +} + +export interface ISerializableUpdateRequest extends IWorkspaceArgument { + insert?: Item[]; + delete?: Key[]; +} + +export interface ISerializableItemsChangeEvent { + readonly changed?: Item[]; + readonly deleted?: Key[]; +} + +abstract class BaseStorageDatabaseClient extends Disposable implements IStorageDatabase { + + abstract onDidChangeItemsExternal: Event; + + constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) { + super(); + } + + async getItems(): Promise> { + const serializableRequest: IWorkspaceArgument = { workspace: this.workspace }; + const items: Item[] = await this.channel.call('getItems', serializableRequest); + + return new Map(items); + } + + updateItems(request: IUpdateRequest): Promise { + const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + + if (request.insert) { + serializableRequest.insert = Array.from(request.insert.entries()); + } + + if (request.delete) { + serializableRequest.delete = Array.from(request.delete.values()); + } + + return this.channel.call('updateItems', serializableRequest); + } + + abstract close(recovery?: () => Map): Promise; +} + +class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { + + private readonly _onDidChangeItemsExternal = this._register(new Emitter()); + readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; + + private onDidChangeGlobalStorageListener: IDisposable | undefined; + + constructor(channel: IChannel) { + super(channel, undefined); + + this.registerListeners(); + } + + private registerListeners(): void { + this.onDidChangeGlobalStorageListener = this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + } + + private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { + if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { + this._onDidChangeItemsExternal.fire({ + changed: e.changed ? new Map(e.changed) : undefined, + deleted: e.deleted ? new Set(e.deleted) : undefined + }); + } + } + + async close(): Promise { + + // when we are about to close, we start to ignore global storage changes since we close anyway + dispose(this.onDidChangeGlobalStorageListener); + } +} + +class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { + + readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window + + constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { + super(channel, workspace); + } + + async close(): Promise { + // TODO@bpasero close workspace storage? + } +} + +export class StorageDatabaseChannelClient extends Disposable { + + readonly globalStorage = new GlobalStorageDatabaseClient(this.channel); + readonly workspaceStorage = this.workspace ? new WorkspaceStorageDatabaseClient(this.channel, this.workspace) : undefined; + + constructor( + private channel: IChannel, + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined + ) { + super(); + } +} diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts new file mode 100644 index 00000000000..b413e57c1f0 --- /dev/null +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, IWorkspaceArgument, Key, Value } from 'vs/platform/storage/common/storageIpc'; +import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +export class StorageDatabaseChannel extends Disposable implements IServerChannel { + + private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; + + private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); + private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; + + constructor( + private logService: ILogService, + private storageMainService: IStorageMainService + ) { + super(); + + // Trigger init of global storage directly from ctor + this.withStorageInitialized(undefined); + + this.registerGlobalStorageListeners(); + } + + //#region Global Storage Change Events + + private registerGlobalStorageListeners(): void { + + // Listen for changes in global storage to send to listeners + // that are listening. Use a debouncer to reduce IPC traffic. + this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { + if (!prev) { + prev = [cur]; + } else { + prev.push(cur); + } + + return prev; + }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { + if (events.length) { + this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); + } + })); + } + + private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { + const changed = new Map(); + const deleted = new Set(); + events.forEach(event => { + const existing = this.storageMainService.globalStorage.get(event.key); + if (typeof existing === 'string') { + changed.set(event.key, existing); + } else { + deleted.add(event.key); + } + }); + + return { + changed: Array.from(changed.entries()), + deleted: Array.from(deleted.values()) + }; + } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; + } + + throw new Error(`Event not found: ${event}`); + } + + //#endregion + + async call(_: unknown, command: string, arg: IWorkspaceArgument): Promise { + + // Get storage to be ready + const storage = await this.withStorageInitialized(arg.workspace); + + // handle call + switch (command) { + case 'getItems': { + return Array.from(storage.items.entries()); + } + + case 'updateItems': { + const items: ISerializableUpdateRequest = arg; + if (items.insert) { + for (const [key, value] of items.insert) { + storage.store(key, value); + } + } + + if (items.delete) { + items.delete.forEach(key => storage.remove(key)); + } + + break; + } + + default: + throw new Error(`Call not found: ${command}`); + } + } + + private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined): Promise { + const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; + + try { + await storage.initialize(); + } catch (error) { + this.logService.error(`[storage] init(): Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + } + + return storage; + } +} diff --git a/src/vs/platform/storage/node/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts similarity index 100% rename from src/vs/platform/storage/node/storageMain.ts rename to src/vs/platform/storage/electron-main/storageMain.ts diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts similarity index 97% rename from src/vs/platform/storage/node/storageMainService.ts rename to src/vs/platform/storage/electron-main/storageMainService.ts index 0e4920b6fd0..c9fc335fad4 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -6,7 +6,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/node/storageMain'; +import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IStorageMainService = createDecorator('storageMainService'); diff --git a/src/vs/platform/storage/node/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts similarity index 100% rename from src/vs/platform/storage/node/storageService2.ts rename to src/vs/platform/storage/electron-sandbox/storageService2.ts diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts deleted file mode 100644 index e4a95707a57..00000000000 --- a/src/vs/platform/storage/node/storageIpc.ts +++ /dev/null @@ -1,242 +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 { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/node/storageMain'; -import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; - -type Key = string; -type Value = string; -type Item = [Key, Value]; - -interface IWorkspaceArgument { - workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined -} - -interface ISerializableUpdateRequest extends IWorkspaceArgument { - insert?: Item[]; - delete?: Key[]; -} - -interface ISerializableItemsChangeEvent { - readonly changed?: Item[]; - readonly deleted?: Key[]; -} - -//#region --- Storage Server - -export class StorageDatabaseChannel extends Disposable implements IServerChannel { - - private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; - - private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); - private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; - - constructor( - private logService: ILogService, - private storageMainService: IStorageMainService - ) { - super(); - - // Trigger init of global storage directly from ctor - this.withStorageInitialized(undefined); - - this.registerGlobalStorageListeners(); - } - - //#region Global Storage Change Events - - private registerGlobalStorageListeners(): void { - - // Listen for changes in global storage to send to listeners - // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { - if (!prev) { - prev = [cur]; - } else { - prev.push(cur); - } - - return prev; - }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { - if (events.length) { - this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); - } - })); - } - - private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { - const changed = new Map(); - const deleted = new Set(); - events.forEach(event => { - const existing = this.storageMainService.globalStorage.get(event.key); - if (typeof existing === 'string') { - changed.set(event.key, existing); - } else { - deleted.add(event.key); - } - }); - - return { - changed: Array.from(changed.entries()), - deleted: Array.from(deleted.values()) - }; - } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; - } - - throw new Error(`Event not found: ${event}`); - } - - //#endregion - - async call(_: unknown, command: string, arg: IWorkspaceArgument): Promise { - - // Get storage to be ready - const storage = await this.withStorageInitialized(arg.workspace); - - // handle call - switch (command) { - case 'getItems': { - return Array.from(storage.items.entries()); - } - - case 'updateItems': { - const items: ISerializableUpdateRequest = arg; - if (items.insert) { - for (const [key, value] of items.insert) { - storage.store(key, value); - } - } - - if (items.delete) { - items.delete.forEach(key => storage.remove(key)); - } - - break; - } - - default: - throw new Error(`Call not found: ${command}`); - } - } - - private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined): Promise { - const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; - - try { - await storage.initialize(); - } catch (error) { - this.logService.error(`[storage] init(): Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); - } - - return storage; - } -} - -//#endregion - -//#region --- Storage Client - -abstract class BaseStorageDatabase extends Disposable implements IStorageDatabase { - - abstract onDidChangeItemsExternal: Event; - - constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) { - super(); - } - - async getItems(): Promise> { - const serializableRequest: IWorkspaceArgument = { workspace: this.workspace }; - const items: Item[] = await this.channel.call('getItems', serializableRequest); - - return new Map(items); - } - - updateItems(request: IUpdateRequest): Promise { - const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; - - if (request.insert) { - serializableRequest.insert = Array.from(request.insert.entries()); - } - - if (request.delete) { - serializableRequest.delete = Array.from(request.delete.values()); - } - - return this.channel.call('updateItems', serializableRequest); - } - - abstract close(recovery?: () => Map): Promise; -} - -class GlobalStorageDatabase extends BaseStorageDatabase implements IStorageDatabase { - - private readonly _onDidChangeItemsExternal = this._register(new Emitter()); - readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - - private onDidChangeGlobalStorageListener: IDisposable | undefined; - - constructor(channel: IChannel) { - super(channel, undefined); - - this.registerListeners(); - } - - private registerListeners(): void { - this.onDidChangeGlobalStorageListener = this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); - } - - private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { - if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { - this._onDidChangeItemsExternal.fire({ - changed: e.changed ? new Map(e.changed) : undefined, - deleted: e.deleted ? new Set(e.deleted) : undefined - }); - } - } - - async close(): Promise { - - // when we are about to close, we start to ignore global storage changes since we close anyway - dispose(this.onDidChangeGlobalStorageListener); - } -} - -class WorkspaceStorageDatabase extends BaseStorageDatabase implements IStorageDatabase { - - readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window - - constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { - super(channel, workspace); - } - - async close(): Promise { - // TODO@bpasero close workspace storage? - } -} - -export class StorageDatabaseChannelClient extends Disposable { - - readonly globalStorage = new GlobalStorageDatabase(this.channel); - readonly workspaceStorage = this.workspace ? new WorkspaceStorageDatabase(this.channel, this.workspace) : undefined; - - constructor( - private channel: IChannel, - private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined - ) { - super(); - } -} - -//#endregion diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 2d60e42471c..7f42a6c6ae8 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -32,7 +32,7 @@ import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMain import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ByteSize, IFileService } from 'vs/platform/files/common/files'; import { FileAccess, Schemas } from 'vs/base/common/network'; import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 24a5f7361c7..8b6823290e0 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -21,9 +21,9 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { NativeStorageService } from 'vs/platform/storage/node/storageService'; -import { NativeStorageService2 } from 'vs/platform/storage/node/storageService2'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { Schemas } from 'vs/base/common/network'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -321,13 +321,13 @@ class DesktopMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { - const storageDataBase = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); let storageService: NativeStorageService | NativeStorageService2; if (this.configuration.enableExperimentalMainProcessWorkspaceStorage) { - storageService = new NativeStorageService2(storageDataBase.globalStorage, storageDataBase.workspaceStorage, this.environmentService); + storageService = new NativeStorageService2(storageDataBaseClient.globalStorage, storageDataBaseClient.workspaceStorage, this.environmentService); } else { - storageService = new NativeStorageService(storageDataBase.globalStorage, logService, this.environmentService); + storageService = new NativeStorageService(storageDataBaseClient.globalStorage, logService, this.environmentService); } try { diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index d72468c610b..a28d6151bf8 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -9,35 +9,43 @@ import { Workbench } from 'vs/workbench/browser/workbench'; import { NativeWindow } from 'vs/workbench/electron-sandbox/window'; import { setZoomLevel, setZoomFactor, setFullscreen } from 'vs/base/browser/browser'; import { domContentLoaded } from 'vs/base/browser/dom'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ILogService } from 'vs/platform/log/common/log'; +import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; +import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { Schemas } from 'vs/base/common/network'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Disposable } from 'vs/base/common/lifecycle'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-sandbox/remoteAgentServiceImpl'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { basename } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import product from 'vs/platform/product/common/product'; +import { NativeLogService } from 'vs/workbench/services/log/electron-sandbox/logService'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; -import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; -import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; -import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService'; +import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout'; +import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout'; +import { LoggerService } from 'vs/workbench/services/log/electron-sandbox/loggerService'; import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; +import { SimpleConfigurationService, simpleFileSystemProvider, SimpleSignService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices'; class DesktopMain extends Disposable { @@ -107,14 +115,14 @@ class DesktopMain extends Disposable { services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } - private registerListeners(workbench: Workbench, storageService: SimpleStorageService): void { + private registerListeners(workbench: Workbench, storageService: NativeStorageService2): void { // Workbench Lifecycle this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); } - private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: SimpleStorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService2 }> { const serviceCollection = new ServiceCollection(); @@ -142,8 +150,12 @@ class DesktopMain extends Disposable { // Product serviceCollection.set(IProductService, this.productService); + // Logger + const loggerService = new LoggerService(mainProcessService); + serviceCollection.set(ILoggerService, loggerService); + // Log - const logService = new SimpleLogService(); + const logService = this._register(new NativeLogService(`renderer${this.configuration.windowId}`, loggerService, mainProcessService, this.environmentService)); serviceCollection.set(ILogService, logService); // Remote @@ -209,6 +221,8 @@ class DesktopMain extends Disposable { fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); } + const payload = this.resolveWorkspaceInitializationPayload(); + const services = await Promise.all([ this.createWorkspaceService().then(service => { @@ -221,11 +235,19 @@ class DesktopMain extends Disposable { return service; }), - this.createStorageService().then(service => { + this.createStorageService(payload, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); + return service; + }), + + this.createKeyboardLayoutService(mainProcessService).then(service => { + + // KeyboardLayout + serviceCollection.set(IKeyboardLayoutService, service); + return service; }) ]); @@ -247,12 +269,57 @@ class DesktopMain extends Disposable { return { serviceCollection, logService, storageService: services[1] }; } + private resolveWorkspaceInitializationPayload(): IWorkspaceInitializationPayload { + let workspaceInitializationPayload: IWorkspaceInitializationPayload | undefined = this.configuration.workspace; + + // Fallback to empty workspace if we have no payload yet. + if (!workspaceInitializationPayload) { + let id: string; + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (this.environmentService.isExtensionDevelopment) { + id = 'ext-dev'; // extension development window never stores backups and is a singleton + } else { + throw new Error('Unexpected window configuration without backupPath'); + } + + workspaceInitializationPayload = { id }; + } + + return workspaceInitializationPayload; + } + private async createWorkspaceService(): Promise { return new SimpleWorkspaceService(); } - private async createStorageService(): Promise { - return new SimpleStorageService(); + private async createStorageService(payload: IWorkspaceInitializationPayload, mainProcessService: IMainProcessService): Promise { + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); + const storageService = new NativeStorageService2(storageDataBaseClient.globalStorage, storageDataBaseClient.workspaceStorage, this.environmentService); + + try { + await storageService.initialize(); + + return storageService; + } catch (error) { + onUnexpectedError(error); + + return storageService; + } + } + + private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise { + const keyboardLayoutService = new KeyboardLayoutService(mainProcessService); + + try { + await keyboardLayoutService.initialize(); + + return keyboardLayoutService; + } catch (error) { + onUnexpectedError(error); + + return keyboardLayoutService; + } } } diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 4a2f05f35f9..7b7312d9575 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -6,7 +6,6 @@ /* eslint-disable code-no-standalone-editor */ /* eslint-disable code-import-patterns */ -import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; import { ISignService } from 'vs/platform/sign/common/sign'; import { URI } from 'vs/base/common/uri'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; @@ -15,7 +14,6 @@ import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnectio import { ITelemetryData, ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { SimpleConfigurationService as BaseSimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices'; -import { InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { ITextSnapshot } from 'vs/editor/common/model'; @@ -177,13 +175,6 @@ export class SimpleWorkspaceService implements IWorkspaceContextService { //#endregion -//#region Configuration - -export class SimpleStorageService extends InMemoryStorageService { } - -//#endregion - - //#region Configuration export class SimpleConfigurationService extends BaseSimpleConfigurationService implements IWorkbenchConfigurationService { @@ -193,15 +184,7 @@ export class SimpleConfigurationService extends BaseSimpleConfigurationService i //#endregion -//#region Logger - -export class SimpleLogService extends LogService { - - constructor() { - super(new ConsoleLogger()); - } - -} +//#region Signing export class SimpleSignService implements ISignService { From 76fecd9855407c017119b63c1c0672bfe5360294 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 14 Feb 2021 12:45:36 +0100 Subject: [PATCH 042/176] storage - add test for storage main service --- .../electron-main/storageMainService.test.ts | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/vs/platform/storage/test/electron-main/storageMainService.test.ts diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts new file mode 100644 index 00000000000..71ce669617e --- /dev/null +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { promises } from 'fs'; +import { tmpdir } from 'os'; +import { strictEqual } from 'assert'; +import { URI } from 'vs/base/common/uri'; +import { rimraf } from 'vs/base/node/pfs'; +import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { generateUuid } from 'vs/base/common/uuid'; +import { isWindows } from 'vs/base/common/platform'; + +flakySuite('StorageMainService (native)', function () { + + class StorageTestEnvironmentService extends NativeEnvironmentService { + + constructor(private globalStorageFolderPath: URI, private _extensionsPath: string) { + super(parseArgs(process.argv, OPTIONS)); + } + + get globalStorageHome(): URI { + return this.globalStorageFolderPath; + } + + get extensionsPath(): string { + return this._extensionsPath; + } + } + + let testDir: string; + let storageMainService: IStorageMainService; + + setup(async () => { + testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageMainService'); + + await promises.mkdir(testDir, { recursive: true }); + + storageMainService = new StorageMainService(new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + }); + + teardown(async () => { + await storageMainService.globalStorage.close(); + + return rimraf(testDir); + }); + + async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { + + // Telemetry: added after init + if (isGlobal) { + strictEqual(storageMainService.globalStorage.items.size, 0); + strictEqual(storageMainService.globalStorage.get(instanceStorageKey), undefined); + await storageMainService.globalStorage.initialize(); + strictEqual(typeof storageMainService.globalStorage.get(instanceStorageKey), 'string'); + } + + let storageChangeEvent: IStorageChangeEvent | undefined = undefined; + const listener = storageMainService.globalStorage.onDidChangeStorage(e => { + storageChangeEvent = e; + }); + + // Basic store/get/remove + const size = storageMainService.globalStorage.items.size; + + storageMainService.globalStorage.store('bar', 'foo'); + strictEqual(storageChangeEvent!.key, 'bar'); + storageMainService.globalStorage.store('barNumber', 55); + storageMainService.globalStorage.store('barBoolean', true); + + strictEqual(storageMainService.globalStorage.get('bar'), 'foo'); + strictEqual(storageMainService.globalStorage.getNumber('barNumber'), 55); + strictEqual(storageMainService.globalStorage.getBoolean('barBoolean'), true); + + strictEqual(storageMainService.globalStorage.items.size, size + 3); + + storageMainService.globalStorage.remove('bar'); + strictEqual(storageMainService.globalStorage.get('bar'), undefined); + + strictEqual(storageMainService.globalStorage.items.size, size + 2); + + listener.dispose(); + } + + test('basics (global)', async function () { + testStorage(storageMainService.globalStorage, true); + }); + + test('basics (workspace)', async function () { + testStorage(storageMainService.workspaceStorage({ id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }), false); + }); +}); From a508b135382b435dcf0b5f7a050ed625436d09ab Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 14 Feb 2021 16:23:18 +0100 Subject: [PATCH 043/176] storage - wire in close() --- src/vs/platform/storage/common/storageIpc.ts | 21 +++++++++---------- .../storage/electron-main/storageIpc.ts | 10 +++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index b885e0ce33a..8196401c0f4 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -56,7 +56,9 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD return this.channel.call('updateItems', serializableRequest); } - abstract close(recovery?: () => Map): Promise; + async close(): Promise { + return this.channel.call('close', { workspace: this.workspace }); + } } class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { @@ -64,8 +66,6 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I private readonly _onDidChangeItemsExternal = this._register(new Emitter()); readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - private onDidChangeGlobalStorageListener: IDisposable | undefined; - constructor(channel: IChannel) { super(channel, undefined); @@ -73,7 +73,7 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I } private registerListeners(): void { - this.onDidChangeGlobalStorageListener = this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); } private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { @@ -87,8 +87,11 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I async close(): Promise { - // when we are about to close, we start to ignore global storage changes since we close anyway - dispose(this.onDidChangeGlobalStorageListener); + // Remove our listeners on `close` because we are no longer + // interested in change events from the global database + this.dispose(); + + return super.close(); } } @@ -99,10 +102,6 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { super(channel, workspace); } - - async close(): Promise { - // TODO@bpasero close workspace storage? - } } export class StorageDatabaseChannelClient extends Disposable { diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index b413e57c1f0..6719120089d 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -106,6 +106,16 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel break; } + case 'close': { + if (arg.workspace) { + // Only allow to close workspace storage databases but not + // the global database that is shared across multiple connections + return storage.close(); + } + + break; + } + default: throw new Error(`Call not found: ${command}`); } From 980becda8d52cc341ddf349021bbda0788912b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Sun, 14 Feb 2021 20:10:24 +0100 Subject: [PATCH 044/176] main rename fixes #116341 --- .devcontainer/cache/build-cache-image.sh | 2 +- .devcontainer/devcontainer.json | 2 +- .github/pull_request_template.md | 2 +- .github/workflows/build-chat.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/devcontainer-cache.yml | 6 +++--- .github/workflows/rich-navigation.yml | 2 +- .vscode/notebooks/inbox.github-issues | 2 +- README.md | 2 +- build/azure-pipelines/distro-build.yml | 8 ++++---- build/azure-pipelines/exploration-build.yml | 4 ++-- build/azure-pipelines/product-build.yml | 2 +- build/azure-pipelines/publish-types/publish-types.yml | 2 +- build/azure-pipelines/publish-types/update-types.ts | 2 +- build/gulpfile.editor.js | 2 +- build/gulpfile.vscode.js | 8 ++++---- build/monaco/README-npm.md | 2 +- product.json | 2 +- scripts/generate-definitelytyped.sh | 2 +- .../test/browser/services/decorationRenderOptions.test.ts | 4 ++-- src/vs/platform/product/common/product.ts | 2 +- src/vs/workbench/common/actions.ts | 2 +- test/ui/tree/public/compressed.json | 6 +++--- 23 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.devcontainer/cache/build-cache-image.sh b/.devcontainer/cache/build-cache-image.sh index 78d0fbfdf0c..42e143d7af4 100755 --- a/.devcontainer/cache/build-cache-image.sh +++ b/.devcontainer/cache/build-cache-image.sh @@ -8,7 +8,7 @@ set -e SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)" CONTAINER_IMAGE_REPOSITORY="$1" -BRANCH="${2:-"master"}" +BRANCH="${2:-"main"}" if [ "${CONTAINER_IMAGE_REPOSITORY}" = "" ]; then echo "Container repository not specified!" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cd632e134ef..3b82cd9028d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ "name": "Code - OSS", // Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile - "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-master", + "image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-main", "workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached", "workspaceFolder": "/home/node/workspace/vscode", diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 45f9ee8f340..19314029215 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ * Read our Pull Request guidelines: https://github.com/microsoft/vscode/wiki/How-to-Contribute#pull-requests * Associate an issue with the Pull Request. -* Ensure that the code is up-to-date with the `master` branch. +* Ensure that the code is up-to-date with the `main` branch. * Include a description of the proposed changes and how to test them. --> diff --git a/.github/workflows/build-chat.yml b/.github/workflows/build-chat.yml index f9f146164ba..03f3bd42caf 100644 --- a/.github/workflows/build-chat.yml +++ b/.github/workflows/build-chat.yml @@ -7,7 +7,7 @@ on: types: - completed branches: - - master + - main - release/* jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d53283f0cf4..f1324b59bc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,11 +3,11 @@ name: CI on: push: branches: - - master + - main - release/* pull_request: branches: - - master + - main - release/* jobs: diff --git a/.github/workflows/devcontainer-cache.yml b/.github/workflows/devcontainer-cache.yml index a250b56cd7a..4670186bdda 100644 --- a/.github/workflows/devcontainer-cache.yml +++ b/.github/workflows/devcontainer-cache.yml @@ -2,9 +2,9 @@ name: VS Code Repo Dev Container Cache Image Generation on: push: - # Currently doing this for master, but could be done for PRs as well + # Currently doing this for main, but could be done for PRs as well branches: - - "master" + - "main" # Only updates to these files result in changes to installed packages, so skip otherwise paths: @@ -35,6 +35,6 @@ jobs: az acr login --name $ACR_REGISTRY_NAME GIT_BRANCH=$(echo "${{ github.ref }}" | grep -oP 'refs/(heads|tags)/\K(.+)') - if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi + if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=main; fi .devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}" diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index aee0796fa28..71824cab83d 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -3,7 +3,7 @@ on: pull_request: push: branches: - - master + - main jobs: richnav: diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index f14b59f1483..be0a2609cb2 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -30,7 +30,7 @@ { "kind": 1, "language": "markdown", - "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", + "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/main/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", "editable": true }, { diff --git a/README.md b/README.md index ec206f09460..541c6e3773c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## The Repository -This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/master/LICENSE.txt). +This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/main/LICENSE.txt). ## Visual Studio Code diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 331fbf9675e..22d6983e7f8 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,9 +1,9 @@ trigger: branches: - include: ["master", "release/*"] + include: ["main", "release/*"] pr: branches: - include: ["master", "release/*"] + include: ["main", "release/*"] steps: - task: NodeTool@0 @@ -31,8 +31,8 @@ steps: git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" git fetch distro - # Push master branch into oss/master - git push distro origin/master:refs/heads/oss/master + # Push main branch into oss/main + git push distro origin/main:refs/heads/oss/main # Push every release branch into oss/release git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 22e2602d0aa..719e6e469cb 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -28,9 +28,9 @@ steps: git config user.name "VSCode" git checkout origin/electron-11.x.y - git merge origin/master + git merge origin/main - # Push master branch into exploration branch + # Push main branch into exploration branch git push origin HEAD:electron-11.x.y displayName: Sync & Merge Exploration diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 4d376246944..011f8d4f7d8 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -5,7 +5,7 @@ schedules: displayName: Mon-Fri at 7:00 branches: include: - - master + - main parameters: - name: VSCODE_QUALITY diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 0e3f4e4daa4..09964dc6ad0 100644 --- a/build/azure-pipelines/publish-types/publish-types.yml +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -61,7 +61,7 @@ steps: TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) CHANNEL="G1C14HJ2F" - MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" + MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame champion, please open this link, examine changes and create a PR:" LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index bbce67221da..eae002e23a7 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -72,7 +72,7 @@ function getNewFileHeader(tag: string) { `/*---------------------------------------------------------------------------------------------`, ` * Copyright (c) Microsoft Corporation. All rights reserved.`, ` * Licensed under the MIT License.`, - ` * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information.`, + ` * See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information.`, ` *--------------------------------------------------------------------------------------------*/`, ``, `/**`, diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 7ef97f15be3..230082e4ad9 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -49,7 +49,7 @@ let BUNDLED_FILE_HEADER = [ ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Version: ' + headerVersion, ' * Released under the MIT license', - ' * https://github.com/microsoft/vscode/blob/master/LICENSE.txt', + ' * https://github.com/microsoft/vscode/blob/main/LICENSE.txt', ' *-----------------------------------------------------------*/', '' ].join('\n'); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 33af8a45375..7f52a5d99a2 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -526,7 +526,7 @@ gulp.task(task.define( if (!shouldSetupSettingsSearch()) { const branch = process.env.BUILD_SOURCEBRANCH; - console.log(`Only runs on master and release branches, not ${branch}`); + console.log(`Only runs on main and release branches, not ${branch}`); return; } @@ -552,21 +552,21 @@ gulp.task(task.define( function shouldSetupSettingsSearch() { const branch = process.env.BUILD_SOURCEBRANCH; - return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); + return branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0); } function getSettingsSearchBuildId(packageJson) { try { const branch = process.env.BUILD_SOURCEBRANCH; const branchId = branch.indexOf('/release/') >= 0 ? 0 : - /\/master$/.test(branch) ? 1 : + /\/main$/.test(branch) ? 1 : 2; // Some unexpected branch const out = cp.execSync(`git rev-list HEAD --count`); const count = parseInt(out.toString()); // - // 1.25.1, 1,234,567 commits, master = 1250112345671 + // 1.25.1, 1,234,567 commits, main = 1250112345671 return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; } catch (e) { throw new Error('Could not determine build number: ' + e.toString()); diff --git a/build/monaco/README-npm.md b/build/monaco/README-npm.md index ee0ffc6e95c..737e06bbc5c 100644 --- a/build/monaco/README-npm.md +++ b/build/monaco/README-npm.md @@ -11,4 +11,4 @@ a good page describing the code editor's features is [here](https://code.visuals This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/microsoft/vscode). ## License -[MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) +[MIT](https://github.com/microsoft/vscode/blob/main/LICENSE.txt) diff --git a/product.json b/product.json index 47bb865de2e..e9561e59ee2 100644 --- a/product.json +++ b/product.json @@ -5,7 +5,7 @@ "dataFolderName": ".vscode-oss", "win32MutexName": "vscodeoss", "licenseName": "MIT", - "licenseUrl": "https://github.com/microsoft/vscode/blob/master/LICENSE.txt", + "licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt", "win32DirName": "Microsoft Code OSS", "win32NameVersion": "Microsoft Code OSS", "win32RegValueName": "CodeOSS", diff --git a/scripts/generate-definitelytyped.sh b/scripts/generate-definitelytyped.sh index 118401b43cb..1b139ebbf79 100755 --- a/scripts/generate-definitelytyped.sh +++ b/scripts/generate-definitelytyped.sh @@ -14,7 +14,7 @@ header="// Type definitions for Visual Studio Code ${1} /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. - * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information. + * See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information. *--------------------------------------------------------------------------------------------*/ /** diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 37055a85416..a4286aa63af 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -52,7 +52,7 @@ class TestGlobalStyleSheet extends GlobalStyleSheet { suite('Decoration Render Options', () => { let options: IDecorationRenderOptions = { - gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png'), + gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png'), gutterIconSize: 'contain', backgroundColor: 'red', borderColor: 'yellow' @@ -79,7 +79,7 @@ suite('Decoration Render Options', () => { const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); s.registerDecorationType('example', options); const sheet = readStyleSheet(styleSheet); - assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); + assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0); }); diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 71bb6ae4cd6..2730ee7e8c6 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -28,7 +28,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! urlProtocol: 'code-oss', reportIssueUrl: 'https://github.com/microsoft/vscode/issues/new', licenseName: 'MIT', - licenseUrl: 'https://github.com/microsoft/vscode/blob/master/LICENSE.txt', + licenseUrl: 'https://github.com/microsoft/vscode/blob/main/LICENSE.txt', extensionAllowedProposedApi: [ 'ms-vscode.vscode-js-profile-flame', 'ms-vscode.vscode-js-profile-table', diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index dfe61debb2e..4640272f1ae 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -59,7 +59,7 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR // menu item // TODO@Rob slightly weird if-check required because of - // https://github.com/microsoft/vscode/blob/master/src/vs/workbench/contrib/search/electron-browser/search.contribution.ts#L266 + // https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/search/electron-browser/search.contribution.ts#L266 if (descriptor.label) { let idx = alias.indexOf(': '); diff --git a/test/ui/tree/public/compressed.json b/test/ui/tree/public/compressed.json index c0b5d4d7161..939a0a29145 100644 --- a/test/ui/tree/public/compressed.json +++ b/test/ui/tree/public/compressed.json @@ -148,7 +148,7 @@ "children": [ { "element": { - "name": "master" + "name": "main" }, "incompressible": true } @@ -228,7 +228,7 @@ "children": [ { "element": { - "name": "master" + "name": "main" }, "incompressible": true } @@ -15617,4 +15617,4 @@ } ] } -] \ No newline at end of file +] From 23a780dbe731c66630579c5aed5a941c410d6721 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 07:44:43 +0100 Subject: [PATCH 045/176] storage - introduce shared class for storage in main --- src/vs/platform/storage/common/storageIpc.ts | 14 +- .../storage/electron-main/storageIpc.ts | 13 +- .../storage/electron-main/storageMain.ts | 238 +++++++++--------- .../electron-main/storageMainService.ts | 31 ++- .../electron-main/storageMainService.test.ts | 51 ++-- .../platform/workspaces/common/workspaces.ts | 8 +- 6 files changed, 192 insertions(+), 163 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 8196401c0f4..cd5550464c6 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -7,17 +7,17 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ISerializedWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export type Key = string; export type Value = string; export type Item = [Key, Value]; -export interface IWorkspaceArgument { - workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined +export interface ISerializableWorkspaceArgument { + workspace: ISerializedWorkspaceIdentifier | undefined } -export interface ISerializableUpdateRequest extends IWorkspaceArgument { +export interface ISerializableUpdateRequest extends ISerializableWorkspaceArgument { insert?: Item[]; delete?: Key[]; } @@ -36,7 +36,7 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD } async getItems(): Promise> { - const serializableRequest: IWorkspaceArgument = { workspace: this.workspace }; + const serializableRequest: ISerializableWorkspaceArgument = { workspace: this.workspace }; const items: Item[] = await this.channel.call('getItems', serializableRequest); return new Map(items); @@ -57,7 +57,9 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD } async close(): Promise { - return this.channel.call('close', { workspace: this.workspace }); + const serializableRequest: ISerializableWorkspaceArgument = { workspace: this.workspace }; + + return this.channel.call('close', serializableRequest); } } diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 6719120089d..7eed5eff59e 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -7,10 +7,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/platform/log/common/log'; -import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, IWorkspaceArgument, Key, Value } from 'vs/platform/storage/common/storageIpc'; +import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, ISerializableWorkspaceArgument, Key, Value } from 'vs/platform/storage/common/storageIpc'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; export class StorageDatabaseChannel extends Disposable implements IServerChannel { @@ -80,10 +80,11 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel //#endregion - async call(_: unknown, command: string, arg: IWorkspaceArgument): Promise { + async call(_: unknown, command: string, arg: ISerializableWorkspaceArgument): Promise { + const workspace = reviveIdentifier(arg.workspace); // Get storage to be ready - const storage = await this.withStorageInitialized(arg.workspace); + const storage = await this.withStorageInitialized(workspace); // handle call switch (command) { @@ -107,7 +108,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } case 'close': { - if (arg.workspace) { + if (workspace) { // Only allow to close workspace storage databases but not // the global database that is shared across multiple connections return storage.close(); @@ -127,7 +128,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel try { await storage.initialize(); } catch (error) { - this.logService.error(`[storage] init(): Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); } return storage; diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 4fd2e233149..37df63ece3c 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -8,11 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; -import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; +import { Storage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { join } from 'vs/base/common/path'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; /** * Provides access to global and workspace storage from the @@ -36,6 +37,11 @@ export interface IStorageMain { */ readonly onWillSaveState: Event; + /** + * Emitted when the storage is closed. + */ + readonly onDidCloseStorage: Event; + /** * Access to all cached items of this storage service. */ @@ -90,93 +96,43 @@ export interface IStorageChangeEvent { key: string; } -export class GlobalStorageMain extends Disposable implements IStorageMain { +abstract class BaseStorageMain extends Disposable implements IStorageMain { - private static readonly STORAGE_NAME = 'state.vscdb'; - - private readonly _onDidChangeStorage = this._register(new Emitter()); + protected readonly _onDidChangeStorage = this._register(new Emitter()); readonly onDidChangeStorage = this._onDidChangeStorage.event; - private readonly _onWillSaveState = this._register(new Emitter()); + protected readonly _onWillSaveState = this._register(new Emitter()); readonly onWillSaveState = this._onWillSaveState.event; - get items(): Map { return this.storage.items; } + private readonly _onDidCloseStorage = this._register(new Emitter()); + readonly onDidCloseStorage = this._onDidCloseStorage.event; - private storage: IStorage; + private storage = new Storage(new InMemoryStorageDatabase()); // storage is in-memory until initialized - private initializePromise: Promise | undefined; + private initializePromise: Promise | undefined = undefined; - constructor( - @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService - ) { + constructor() { super(); - - // Until the storage has been initialized, it can only be in memory - this.storage = new Storage(new InMemoryStorageDatabase()); - } - - private get storagePath(): string { - if (!!this.environmentService.extensionTestsLocationURI) { - return SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! - } - - return join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); - } - - private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { - return { - logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => this.logService.error(error) - }; } initialize(): Promise { if (!this.initializePromise) { - this.initializePromise = this.doInitialize(); + this.initializePromise = (async () => { + const storage = await this.doInitialize(); + + // Replace our in-memory storage with the initialized + // one once that is finished and use it from then on + this.storage.dispose(); + this.storage = storage; + })(); } return this.initializePromise; } - private async doInitialize(): Promise { - this.storage.dispose(); - this.storage = new Storage(new SQLiteStorageDatabase(this.storagePath, { - logging: this.createLogginOptions() - })); + protected abstract doInitialize(): Promise; - this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); - - await this.storage.init(); - - // Check to see if this is the first time we are "opening" the application - const firstOpen = this.storage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - this.storage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - this.storage.set(IS_NEW_KEY, false); - } - - // Apply global telemetry values as part of the initialization - this.storeTelemetryStateOnce(); - } - - private storeTelemetryStateOnce(): void { - const instanceId = this.get(instanceStorageKey, undefined); - if (instanceId === undefined) { - this.store(instanceStorageKey, generateUuid()); - } - - const firstSessionDate = this.get(firstSessionDateStorageKey, undefined); - if (firstSessionDate === undefined) { - this.store(firstSessionDateStorageKey, new Date().toUTCString()); - } - - const lastSessionDate = this.get(currentSessionDateStorageKey, undefined); // previous session date was the "current" one at that time - const currentSessionDate = new Date().toUTCString(); // current session date is "now" - this.store(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); - this.store(currentSessionDateStorageKey, currentSessionDate); - } + get items(): Map { return this.storage.items; } get(key: string, fallbackValue: string): string; get(key: string, fallbackValue?: string): string | undefined; @@ -204,44 +160,112 @@ export class GlobalStorageMain extends Disposable implements IStorageMain { return this.storage.delete(key); } + async close(): Promise { + + // Propagate to storage lib + await this.storage.close(); + + // Signal as event + this._onDidCloseStorage.fire(); + } +} + +export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { + + private static readonly STORAGE_NAME = 'state.vscdb'; + + constructor( + @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: IEnvironmentService + ) { + super(); + } + + private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { + return { + logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, + logError: error => this.logService.error(error) + }; + } + + protected async doInitialize(): Promise { + let storagePath: string; + if (!!this.environmentService.extensionTestsLocationURI) { + storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! + } else { + storagePath = join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + } + + const storage = new Storage(new SQLiteStorageDatabase(storagePath, { + logging: this.createLogginOptions() + })); + + // Re-emit storage changes via event + this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + + await storage.init(); + + // Check to see if this is the first time we are "opening" the application + const firstOpen = storage.getBoolean(IS_NEW_KEY); + if (firstOpen === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (firstOpen) { + storage.set(IS_NEW_KEY, false); + } + + // Apply global telemetry values as part of the initialization + this.updateTelemetryState(storage); + + return storage; + } + + private updateTelemetryState(storage: Storage): void { + + // Instance UUID (once) + const instanceId = storage.get(instanceStorageKey, undefined); + if (instanceId === undefined) { + storage.set(instanceStorageKey, generateUuid()); + } + + // First session date (once) + const firstSessionDate = storage.get(firstSessionDateStorageKey, undefined); + if (firstSessionDate === undefined) { + storage.set(firstSessionDateStorageKey, new Date().toUTCString()); + } + + // Last / current session (always) + // previous session date was the "current" one at that time + // current session date is "now" + const lastSessionDate = storage.get(currentSessionDateStorageKey, undefined); + const currentSessionDate = new Date().toUTCString(); + storage.set(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); + storage.set(currentSessionDateStorageKey, currentSessionDate); + } + close(): Promise { // Signal as event so that clients can still store data this._onWillSaveState.fire(); // Do it - return this.storage.close(); + return super.close(); } } -export class WorkspaceStorageMain extends Disposable implements IStorageMain { +export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { - readonly onDidChangeStorage = Event.None; - readonly onWillSaveState = Event.None; - - get items(): Map { return this.storage.items; } - - private storage: IStorage; - - private initializePromise: Promise | undefined; - - constructor( - ) { + constructor(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { super(); - - // Until the storage has been initialized, it can only be in memory - this.storage = new Storage(new InMemoryStorageDatabase()); } - async initialize(): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(); - } + protected async doInitialize(): Promise { + const storage = new Storage(new InMemoryStorageDatabase()); - return this.initializePromise; - } + // Re-emit storage changes via event + this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + + return storage; - private async doInitialize(): Promise { // private async initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise { // // Prepare workspace storage folder for DB @@ -337,34 +361,4 @@ export class WorkspaceStorageMain extends Disposable implements IStorageMain { // } // } } - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - get(key: string, fallbackValue?: string): string | undefined { - return this.storage.get(key, fallbackValue); - } - - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - return this.storage.getBoolean(key, fallbackValue); - } - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - getNumber(key: string, fallbackValue?: number): number | undefined { - return this.storage.getNumber(key, fallbackValue); - } - - store(key: string, value: string | boolean | number | undefined | null): Promise { - return this.storage.set(key, value); - } - - remove(key: string): Promise { - return this.storage.delete(key); - } - - close(): Promise { - return this.storage.close(); - } } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index c9fc335fad4..5647a4ea36c 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { once } from 'vs/base/common/functional'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IStorageMainService = createDecorator('storageMainService'); @@ -30,25 +31,31 @@ export class StorageMainService implements IStorageMainService { declare readonly _serviceBrand: undefined; - readonly globalStorage = this.createGlobalStorage(); - - private readonly mapWorkspaceToStorage = new Map(); - constructor( @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { } + //#region Global Storage + + readonly globalStorage = this.createGlobalStorage(); + private createGlobalStorage(): IStorageMain { const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); return globalStorage; } + //#endregion + + + //#region Workspace Storage + + private readonly mapWorkspaceToStorage = new Map(); + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { - const workspaceStorage = new WorkspaceStorageMain(); - // TODO@bpasero lifecycle like global storage? window events? crashes? + const workspaceStorage = new WorkspaceStorageMain(workspace); return workspaceStorage; } @@ -56,10 +63,20 @@ export class StorageMainService implements IStorageMainService { workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); if (!workspaceStorage) { + this.logService.info(`StorageMainService: creating workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + workspaceStorage = this.createWorkspaceStorage(workspace); this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); + + once(workspaceStorage.onDidCloseStorage)(() => { + this.logService.info(`StorageMainService: closed workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + + this.mapWorkspaceToStorage.delete(workspace.id); + }); } return workspaceStorage; } + + //#endregion } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 71ce669617e..0383a7264f5 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -56,44 +56,53 @@ flakySuite('StorageMainService (native)', function () { // Telemetry: added after init if (isGlobal) { - strictEqual(storageMainService.globalStorage.items.size, 0); - strictEqual(storageMainService.globalStorage.get(instanceStorageKey), undefined); - await storageMainService.globalStorage.initialize(); - strictEqual(typeof storageMainService.globalStorage.get(instanceStorageKey), 'string'); + strictEqual(storage.items.size, 0); + strictEqual(storage.get(instanceStorageKey), undefined); + await storage.initialize(); + strictEqual(typeof storage.get(instanceStorageKey), 'string'); } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; - const listener = storageMainService.globalStorage.onDidChangeStorage(e => { + const storageChangeListener = storage.onDidChangeStorage(e => { storageChangeEvent = e; }); + let storageDidClose = false; + const storageCloseListener = storage.onDidCloseStorage(() => storageDidClose = true); + // Basic store/get/remove - const size = storageMainService.globalStorage.items.size; + const size = storage.items.size; - storageMainService.globalStorage.store('bar', 'foo'); + storage.store('bar', 'foo'); strictEqual(storageChangeEvent!.key, 'bar'); - storageMainService.globalStorage.store('barNumber', 55); - storageMainService.globalStorage.store('barBoolean', true); + storage.store('barNumber', 55); + storage.store('barBoolean', true); - strictEqual(storageMainService.globalStorage.get('bar'), 'foo'); - strictEqual(storageMainService.globalStorage.getNumber('barNumber'), 55); - strictEqual(storageMainService.globalStorage.getBoolean('barBoolean'), true); + strictEqual(storage.get('bar'), 'foo'); + strictEqual(storage.getNumber('barNumber'), 55); + strictEqual(storage.getBoolean('barBoolean'), true); - strictEqual(storageMainService.globalStorage.items.size, size + 3); + strictEqual(storage.items.size, size + 3); - storageMainService.globalStorage.remove('bar'); - strictEqual(storageMainService.globalStorage.get('bar'), undefined); + storage.remove('bar'); + strictEqual(storage.get('bar'), undefined); - strictEqual(storageMainService.globalStorage.items.size, size + 2); + strictEqual(storage.items.size, size + 2); - listener.dispose(); + // Close + await storage.close(); + + strictEqual(storageDidClose, true); + + storageChangeListener.dispose(); + storageCloseListener.dispose(); } - test('basics (global)', async function () { - testStorage(storageMainService.globalStorage, true); + test('basics (global)', function () { + return testStorage(storageMainService.globalStorage, true); }); - test('basics (workspace)', async function () { - testStorage(storageMainService.workspaceStorage({ id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }), false); + test('basics (workspace)', function () { + return testStorage(storageMainService.workspaceStorage({ id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }), false); }); }); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 66413f2e4e3..491630b4764 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -161,7 +161,13 @@ export function isWorkspaceIdentifier(obj: unknown): obj is IWorkspaceIdentifier return typeof workspaceIdentifier?.id === 'string' && URI.isUri(workspaceIdentifier.configPath); } -export function reviveIdentifier(identifier: { id: string, uri?: UriComponents, configPath?: UriComponents } | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { +export interface ISerializedWorkspaceIdentifier { + id: string; + uri?: UriComponents; + configPath?: UriComponents; +} + +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { if (identifier?.uri) { return { id: identifier.id, uri: URI.revive(identifier.uri) }; } From 5eca02fd99e4970a55363469b3fede1a998d3f64 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 08:18:33 +0100 Subject: [PATCH 046/176] storage - some :lipstick: --- .../sharedProcess/sharedProcessMain.ts | 6 ++--- .../electron-sandbox/workbench/workbench.js | 3 +-- src/vs/platform/storage/common/storageIpc.ts | 4 +-- .../storage/electron-main/storageIpc.ts | 3 --- .../storage/electron-main/storageMain.ts | 25 +++++++------------ .../electron-main/storageMainService.ts | 13 ++++++++-- .../electron-main/storageMainService.test.ts | 8 +++++- 7 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 22620bc9345..fe2cb6adcae 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -172,9 +172,9 @@ class SharedProcessMain extends Disposable { await configurationService.initialize(); - // Storage - const storageDatabase = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), undefined /* no workspace access for shared process */); - const storageService = new NativeStorageService2(storageDatabase.globalStorage, storageDatabase.workspaceStorage, environmentService); + // Storage (global access only) + const storageDatabaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), undefined); + const storageService = new NativeStorageService2(storageDatabaseClient.globalStorage, undefined, environmentService); services.set(IStorageService, storageService); await storageService.initialize(); diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index 06edaee9432..1946d7f5e3b 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -5,7 +5,7 @@ /// -// @ts-check +//@ts-check (function () { 'use strict'; @@ -76,5 +76,4 @@ } //#endregion - }()); diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index cd5550464c6..105f15b1c3a 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -14,7 +14,7 @@ export type Value = string; export type Item = [Key, Value]; export interface ISerializableWorkspaceArgument { - workspace: ISerializedWorkspaceIdentifier | undefined + readonly workspace: ISerializedWorkspaceIdentifier | undefined } export interface ISerializableUpdateRequest extends ISerializableWorkspaceArgument { @@ -87,7 +87,7 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I } } - async close(): Promise { + close(): Promise { // Remove our listeners on `close` because we are no longer // interested in change events from the global database diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 7eed5eff59e..486a64c06ab 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -25,9 +25,6 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel ) { super(); - // Trigger init of global storage directly from ctor - this.withStorageInitialized(undefined); - this.registerGlobalStorageListeners(); } diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 37df63ece3c..3f75df7deb5 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -124,6 +124,14 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { // one once that is finished and use it from then on this.storage.dispose(); this.storage = storage; + + // Ensure we track wether storage is new or not + const isNewStorage = storage.getBoolean(IS_NEW_KEY); + if (isNewStorage === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (isNewStorage) { + storage.set(IS_NEW_KEY, false); + } })(); } @@ -203,16 +211,9 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { // Re-emit storage changes via event this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + // Forward init to SQLite DB await storage.init(); - // Check to see if this is the first time we are "opening" the application - const firstOpen = storage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - storage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - storage.set(IS_NEW_KEY, false); - } - // Apply global telemetry values as part of the initialization this.updateTelemetryState(storage); @@ -282,14 +283,6 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai // result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined // ); // await workspaceStorage.init(); - - // // Check to see if this is the first time we are "opening" this workspace - // const firstWorkspaceOpen = workspaceStorage.getBoolean(IS_NEW_KEY); - // if (firstWorkspaceOpen === undefined) { - // workspaceStorage.set(IS_NEW_KEY, result.wasCreated); - // } else if (firstWorkspaceOpen) { - // workspaceStorage.set(IS_NEW_KEY, false); - // } // } finally { // mark('code/didInitWorkspaceStorage'); // } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 5647a4ea36c..3e4c232099d 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -42,8 +42,17 @@ export class StorageMainService implements IStorageMainService { readonly globalStorage = this.createGlobalStorage(); private createGlobalStorage(): IStorageMain { + if (this.globalStorage) { + return this.globalStorage; // only once + } + const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); + // Trigger init of global storage directly from here + // so that we can be ready for access when the window + // needs it (prevents waterfall of initialization) + globalStorage.initialize(); + return globalStorage; } @@ -63,13 +72,13 @@ export class StorageMainService implements IStorageMainService { workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); if (!workspaceStorage) { - this.logService.info(`StorageMainService: creating workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + this.logService.trace(`StorageMainService: creating workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); workspaceStorage = this.createWorkspaceStorage(workspace); this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); once(workspaceStorage.onDidCloseStorage)(() => { - this.logService.info(`StorageMainService: closed workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + this.logService.trace(`StorageMainService: closed workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); this.mapWorkspaceToStorage.delete(workspace.id); }); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 0383a7264f5..99067897869 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -13,10 +13,11 @@ import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { NullLogService } from 'vs/platform/log/common/log'; import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; -import { instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; +import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; flakySuite('StorageMainService (native)', function () { @@ -60,6 +61,8 @@ flakySuite('StorageMainService (native)', function () { strictEqual(storage.get(instanceStorageKey), undefined); await storage.initialize(); strictEqual(typeof storage.get(instanceStorageKey), 'string'); + strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); + strictEqual(typeof storage.get(currentSessionDateStorageKey), 'string'); } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; @@ -89,6 +92,9 @@ flakySuite('StorageMainService (native)', function () { strictEqual(storage.items.size, size + 2); + // IS_NEW + strictEqual(storage.get(IS_NEW_KEY), true); + // Close await storage.close(); From 02613ef2b41e5802df83cf248d47428a37352a47 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 11:14:14 +0100 Subject: [PATCH 047/176] storage - implement workspace storage and fix tests --- src/vs/platform/storage/common/storageIpc.ts | 18 +- .../storage/electron-main/storageIpc.ts | 8 +- .../storage/electron-main/storageMain.ts | 202 ++++++++---------- .../electron-main/storageMainService.ts | 14 +- .../electron-sandbox/storageService2.ts | 14 +- .../electron-main/storageMainService.test.ts | 52 ++++- .../platform/workspaces/common/workspaces.ts | 41 ++-- .../workspaces/test/common/workspaces.test.ts | 17 +- .../performance/browser/perfviewEditor.ts | 1 + .../electron-browser/desktop.main.ts | 7 +- .../electron-sandbox/desktop.main.ts | 7 +- .../browser/configurationService.ts | 6 +- .../services/timer/browser/timerService.ts | 10 + 13 files changed, 233 insertions(+), 164 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 105f15b1c3a..5669ec1f22f 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -7,17 +7,17 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; -import { ISerializedWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEmptyWorkspaceIdentifier, ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export type Key = string; export type Value = string; export type Item = [Key, Value]; -export interface ISerializableWorkspaceArgument { - readonly workspace: ISerializedWorkspaceIdentifier | undefined +export interface IBaseSerializableStorageRequest { + readonly workspace: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined } -export interface ISerializableUpdateRequest extends ISerializableWorkspaceArgument { +export interface ISerializableUpdateRequest extends IBaseSerializableStorageRequest { insert?: Item[]; delete?: Key[]; } @@ -31,12 +31,12 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD abstract onDidChangeItemsExternal: Event; - constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) { + constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { super(); } async getItems(): Promise> { - const serializableRequest: ISerializableWorkspaceArgument = { workspace: this.workspace }; + const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; const items: Item[] = await this.channel.call('getItems', serializableRequest); return new Map(items); @@ -57,7 +57,7 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD } async close(): Promise { - const serializableRequest: ISerializableWorkspaceArgument = { workspace: this.workspace }; + const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; return this.channel.call('close', serializableRequest); } @@ -101,7 +101,7 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window - constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { + constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { super(channel, workspace); } } @@ -113,7 +113,7 @@ export class StorageDatabaseChannelClient extends Disposable { constructor( private channel: IChannel, - private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined ) { super(); } diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 486a64c06ab..27084af2ee6 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -7,10 +7,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/platform/log/common/log'; -import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, ISerializableWorkspaceArgument, Key, Value } from 'vs/platform/storage/common/storageIpc'; +import { ISerializableItemsChangeEvent, ISerializableUpdateRequest, IBaseSerializableStorageRequest, Key, Value } from 'vs/platform/storage/common/storageIpc'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, reviveIdentifier } from 'vs/platform/workspaces/common/workspaces'; export class StorageDatabaseChannel extends Disposable implements IServerChannel { @@ -77,7 +77,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel //#endregion - async call(_: unknown, command: string, arg: ISerializableWorkspaceArgument): Promise { + async call(_: unknown, command: string, arg: IBaseSerializableStorageRequest): Promise { const workspace = reviveIdentifier(arg.workspace); // Get storage to be ready @@ -119,7 +119,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } } - private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined): Promise { + private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; try { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 3f75df7deb5..18494b1b00b 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -3,17 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { promises } from 'fs'; +import { exists, writeFile } from 'vs/base/node/pfs'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; -import { Storage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; +import { Storage, InMemoryStorageDatabase, StorageHint, IStorage } from 'vs/base/parts/storage/common/storage'; import { join } from 'vs/base/common/path'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; /** * Provides access to global and workspace storage from the @@ -107,30 +109,34 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { private readonly _onDidCloseStorage = this._register(new Emitter()); readonly onDidCloseStorage = this._onDidCloseStorage.event; - private storage = new Storage(new InMemoryStorageDatabase()); // storage is in-memory until initialized + private storage: IStorage = new Storage(new InMemoryStorageDatabase()); // storage is in-memory until initialized private initializePromise: Promise | undefined = undefined; - constructor() { + constructor(protected readonly logService: ILogService) { super(); } initialize(): Promise { if (!this.initializePromise) { this.initializePromise = (async () => { - const storage = await this.doInitialize(); + try { + const storage = await this.doInitialize(); - // Replace our in-memory storage with the initialized - // one once that is finished and use it from then on - this.storage.dispose(); - this.storage = storage; + // Replace our in-memory storage with the initialized + // one once that is finished and use it from then on + this.storage.dispose(); + this.storage = storage; - // Ensure we track wether storage is new or not - const isNewStorage = storage.getBoolean(IS_NEW_KEY); - if (isNewStorage === undefined) { - storage.set(IS_NEW_KEY, true); - } else if (isNewStorage) { - storage.set(IS_NEW_KEY, false); + // Ensure we track wether storage is new or not + const isNewStorage = storage.getBoolean(IS_NEW_KEY); + if (isNewStorage === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (isNewStorage) { + storage.set(IS_NEW_KEY, false); + } + } catch (error) { + this.logService.error(`StorageMain#initialize(): Unable to init storage due to ${error}`); } })(); } @@ -138,7 +144,14 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { return this.initializePromise; } - protected abstract doInitialize(): Promise; + protected createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { + return { + logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, + logError: error => this.logService.error(error) + }; + } + + protected abstract doInitialize(): Promise; get items(): Map { return this.storage.items; } @@ -183,20 +196,13 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { private static readonly STORAGE_NAME = 'state.vscdb'; constructor( - @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + logService: ILogService, + private readonly environmentService: IEnvironmentService ) { - super(); + super(logService); } - private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { - return { - logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => this.logService.error(error) - }; - } - - protected async doInitialize(): Promise { + protected async doInitialize(): Promise { let storagePath: string; if (!!this.environmentService.extensionTestsLocationURI) { storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! @@ -204,6 +210,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { storagePath = join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); } + // Create Storage const storage = new Storage(new SQLiteStorageDatabase(storagePath, { logging: this.createLogginOptions() })); @@ -255,103 +262,80 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { - constructor(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier) { - super(); + private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; + private static readonly WORKSPACE_META_NAME = 'workspace.json'; + + constructor( + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier, + logService: ILogService, + private readonly environmentService: IEnvironmentService + ) { + super(logService); } - protected async doInitialize(): Promise { - const storage = new Storage(new InMemoryStorageDatabase()); + protected async doInitialize(): Promise { + + // Prepare workspace storage folder for DB + const { storageFilePath, wasCreated } = await this.prepareWorkspaceStorageFolder(); + + // Create Storage + const storage = new Storage(new SQLiteStorageDatabase(storageFilePath, { + logging: this.createLogginOptions() + }), { hint: wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined }); // Re-emit storage changes via event this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + // Forward init to SQLite DB + await storage.init(); + return storage; + } - // private async initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise { + private async prepareWorkspaceStorageFolder(): Promise<{ storageFilePath: string, wasCreated: boolean }> { - // // Prepare workspace storage folder for DB - // try { - // const result = await this.prepareWorkspaceStorageFolder(payload); + // Return early with in-memory when running extension tests + if (!!this.environmentService.extensionTestsLocationURI) { + return { storageFilePath: SQLiteStorageDatabase.IN_MEMORY_PATH, wasCreated: true }; + } - // const useInMemoryStorage = !!this.environmentService.extensionTestsLocationURI; // no storage during extension tests! + // Otherwise, ensure the storage folder exists on disk + const workspaceStorageFolderPath = join(this.environmentService.workspaceStorageHome.fsPath, this.workspace.id); + const workspaceStorageDatabasePath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_STORAGE_NAME); - // // Create workspace storage and initialize - // mark('code/willInitWorkspaceStorage'); - // try { - // const workspaceStorage = this.createWorkspaceStorage( - // useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME), - // result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined - // ); - // await workspaceStorage.init(); - // } finally { - // mark('code/didInitWorkspaceStorage'); - // } - // } catch (error) { - // this.logService.error(`[storage] initializeWorkspaceStorage(): Unable to init workspace storage due to ${error}`); - // } - // } + const storageExists = await exists(workspaceStorageFolderPath); + if (storageExists) { + return { storageFilePath: workspaceStorageDatabasePath, wasCreated: false }; + } - // private createWorkspaceStorage(workspaceStoragePath: string, hint?: StorageHint): IStorage { + await promises.mkdir(workspaceStorageFolderPath, { recursive: true }); - // // Logger for workspace storage - // const workspaceLoggingOptions: ISQLiteStorageDatabaseLoggingOptions = { - // logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - // logError: error => this.logService.error(error) - // }; + // Write metadata into folder + this.ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath); - // // Dispose old (if any) - // dispose(this.workspaceStorage); - // dispose(this.workspaceStorageListener); + return { storageFilePath: workspaceStorageDatabasePath, wasCreated: true }; + } - // // Create new - // this.workspaceStoragePath = workspaceStoragePath; - // this.workspaceStorage = new Storage(new SQLiteStorageDatabase(workspaceStoragePath, { logging: workspaceLoggingOptions }), { hint }); - // this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + private ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath: string): void { + let meta: object | undefined = undefined; + if (isSingleFolderWorkspaceIdentifier(this.workspace)) { + meta = { folder: this.workspace.uri.toString() }; + } else if (isWorkspaceIdentifier(this.workspace)) { + meta = { workspace: this.workspace.configPath.toString() }; + } - // return this.workspaceStorage; - // } - - // private getWorkspaceStorageFolderPath(payload: IWorkspaceInitializationPayload): string { - // return join(this.environmentService.workspaceStorageHome.fsPath, payload.id); // workspace home + workspace id; - // } - - // private async prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload): Promise<{ path: string, wasCreated: boolean }> { - // const workspaceStorageFolderPath = this.getWorkspaceStorageFolderPath(payload); - - // const storageExists = await exists(workspaceStorageFolderPath); - // if (storageExists) { - // return { path: workspaceStorageFolderPath, wasCreated: false }; - // } - - // await promises.mkdir(workspaceStorageFolderPath, { recursive: true }); - - // // Write metadata into folder - // this.ensureWorkspaceStorageFolderMeta(payload); - - // return { path: workspaceStorageFolderPath, wasCreated: true }; - // } - - // private ensureWorkspaceStorageFolderMeta(payload: IWorkspaceInitializationPayload): void { - // let meta: object | undefined = undefined; - // if (isSingleFolderWorkspaceIdentifier(payload)) { - // meta = { folder: payload.uri.toString() }; - // } else if (isWorkspaceIdentifier(payload)) { - // meta = { workspace: payload.configPath.toString() }; - // } - - // if (meta) { - // (async () => { - // try { - // const workspaceStorageMetaPath = join(this.getWorkspaceStorageFolderPath(payload), NativeStorageService.WORKSPACE_META_NAME); - // const storageExists = await exists(workspaceStorageMetaPath); - // if (!storageExists) { - // await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); - // } - // } catch (error) { - // this.logService.error(error); - // } - // })(); - // } - // } + if (meta) { + (async () => { + try { + const workspaceStorageMetaPath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_META_NAME); + const storageExists = await exists(workspaceStorageMetaPath); + if (!storageExists) { + await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); + } + } catch (error) { + this.logService.error(`StorageMain#ensureWorkspaceStorageFolderMeta(): Unable to create workspace storage metadata due to ${error}`); + } + })(); + } } } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 3e4c232099d..38e8d235a33 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -8,7 +8,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; -import { ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IStorageMainService = createDecorator('storageMainService'); @@ -24,7 +24,7 @@ export interface IStorageMainService { /** * Provides access to the workspace storage specific to a single window. */ - workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain; + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain; } export class StorageMainService implements IStorageMainService { @@ -63,22 +63,22 @@ export class StorageMainService implements IStorageMainService { private readonly mapWorkspaceToStorage = new Map(); - private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { - const workspaceStorage = new WorkspaceStorageMain(workspace); + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { + const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService); return workspaceStorage; } - workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): IStorageMain { + workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); if (!workspaceStorage) { - this.logService.trace(`StorageMainService: creating workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + this.logService.trace(`StorageMainService: creating workspace storage (${workspace.id})`); workspaceStorage = this.createWorkspaceStorage(workspace); this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); once(workspaceStorage.onDidCloseStorage)(() => { - this.logService.trace(`StorageMainService: closed workspace storage (${isWorkspaceIdentifier(workspace) ? workspace.configPath : workspace.uri})`); + this.logService.trace(`StorageMainService: closed workspace storage (${workspace.id})`); this.mapWorkspaceToStorage.delete(workspace.id); }); diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index e5748a183f0..97fc6da4e21 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -10,6 +10,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { assertIsDefined } from 'vs/base/common/types'; import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; +import { mark } from 'vs/base/common/performance'; export class NativeStorageService2 extends AbstractStorageService { @@ -47,10 +48,15 @@ export class NativeStorageService2 extends AbstractStorageService { private async doInitialize(): Promise { // Init all storage locations - await Promises.settled([ - this.globalStorage.init(), - this.workspaceStorage?.init() ?? Promise.resolve() - ]); + mark('code/willInitStorage'); + try { + await Promises.settled([ + this.globalStorage.init(), + this.workspaceStorage?.init() ?? Promise.resolve() + ]); + } finally { + mark('code/didInitStorage'); + } // On some OS we do not get enough time to persist state on shutdown (e.g. when // Windows restarts after applying updates). In other cases, VSCode might crash, diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 99067897869..025bbf6226c 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -12,18 +12,19 @@ import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { joinPath } from 'vs/base/common/resources'; flakySuite('StorageMainService (native)', function () { class StorageTestEnvironmentService extends NativeEnvironmentService { - constructor(private globalStorageFolderPath: URI, private _extensionsPath: string) { + constructor(private globalStorageFolderPath: URI, private workspaceStorageFolderPath: URI, private _extensionsPath: string) { super(parseArgs(process.argv, OPTIONS)); } @@ -31,29 +32,37 @@ flakySuite('StorageMainService (native)', function () { return this.globalStorageFolderPath; } + get workspaceStorageHome(): URI { + return this.workspaceStorageFolderPath; + } + get extensionsPath(): string { return this._extensionsPath; } } let testDir: string; - let storageMainService: IStorageMainService; + let environmentService: StorageTestEnvironmentService; setup(async () => { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageMainService'); await promises.mkdir(testDir, { recursive: true }); - storageMainService = new StorageMainService(new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + const globalStorageFolder = joinPath(URI.file(testDir), 'globalStorage'); + const workspaceStorageFolder = joinPath(URI.file(testDir), 'workspaceStorage'); + + await promises.mkdir(globalStorageFolder.fsPath, { recursive: true }); + + environmentService = new StorageTestEnvironmentService(globalStorageFolder, workspaceStorageFolder, testDir); }); - teardown(async () => { - await storageMainService.globalStorage.close(); - + teardown(() => { return rimraf(testDir); }); - async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { + async function testStorage(storageFn: () => IStorageMain, isGlobal: boolean): Promise { + let storage = storageFn(); // Telemetry: added after init if (isGlobal) { @@ -63,6 +72,8 @@ flakySuite('StorageMainService (native)', function () { strictEqual(typeof storage.get(instanceStorageKey), 'string'); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); strictEqual(typeof storage.get(currentSessionDateStorageKey), 'string'); + } else { + await storage.initialize(); } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; @@ -93,7 +104,7 @@ flakySuite('StorageMainService (native)', function () { strictEqual(storage.items.size, size + 2); // IS_NEW - strictEqual(storage.get(IS_NEW_KEY), true); + strictEqual(storage.getBoolean(IS_NEW_KEY), true); // Close await storage.close(); @@ -102,13 +113,32 @@ flakySuite('StorageMainService (native)', function () { storageChangeListener.dispose(); storageCloseListener.dispose(); + + // Reopen + storage = storageFn(); + await storage.initialize(); + + strictEqual(storage.getNumber('barNumber'), 55); + strictEqual(storage.getBoolean('barBoolean'), true); + + await storage.close(); } test('basics (global)', function () { - return testStorage(storageMainService.globalStorage, true); + return testStorage(() => { + const storageMainService = new StorageMainService(new NullLogService(), environmentService); + + return storageMainService.globalStorage; + }, true); }); test('basics (workspace)', function () { - return testStorage(storageMainService.workspaceStorage({ id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }), false); + const workspace = { id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }; + + return testStorage(() => { + const storageMainService = new StorageMainService(new NullLogService(), environmentService); + + return storageMainService.workspaceStorage(workspace); + }, false); }); }); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 491630b4764..897a204578e 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -116,6 +116,10 @@ export interface ISingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifi uri: URI; } +export interface ISerializedSingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifier { + uri: UriComponents; +} + export function isSingleFolderWorkspaceIdentifier(obj: unknown): obj is ISingleFolderWorkspaceIdentifier { const singleFolderIdentifier = obj as ISingleFolderWorkspaceIdentifier | undefined; @@ -133,6 +137,10 @@ export interface IWorkspaceIdentifier extends IBaseWorkspaceIdentifier { configPath: URI; } +export interface ISerializedWorkspaceIdentifier extends IBaseWorkspaceIdentifier { + configPath: UriComponents; +} + export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { // Multi root @@ -161,19 +169,28 @@ export function isWorkspaceIdentifier(obj: unknown): obj is IWorkspaceIdentifier return typeof workspaceIdentifier?.id === 'string' && URI.isUri(workspaceIdentifier.configPath); } -export interface ISerializedWorkspaceIdentifier { - id: string; - uri?: UriComponents; - configPath?: UriComponents; -} +export function reviveIdentifier(identifier: undefined): undefined; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier): IWorkspaceIdentifier; +export function reviveIdentifier(identifier: ISerializedSingleFolderWorkspaceIdentifier): ISingleFolderWorkspaceIdentifier; +export function reviveIdentifier(identifier: IEmptyWorkspaceIdentifier): IEmptyWorkspaceIdentifier; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined; +export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined { -export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | undefined): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { - if (identifier?.uri) { - return { id: identifier.id, uri: URI.revive(identifier.uri) }; + // Single Folder + const singleFolderIdentifierCandidate = identifier as ISerializedSingleFolderWorkspaceIdentifier | undefined; + if (singleFolderIdentifierCandidate?.uri) { + return { id: singleFolderIdentifierCandidate.id, uri: URI.revive(singleFolderIdentifierCandidate.uri) }; } - if (identifier?.configPath) { - return { id: identifier.id, configPath: URI.revive(identifier.configPath) }; + // Multi folder + const workspaceIdentifierCandidate = identifier as ISerializedWorkspaceIdentifier | undefined; + if (workspaceIdentifierCandidate?.configPath) { + return { id: workspaceIdentifierCandidate.id, configPath: URI.revive(workspaceIdentifierCandidate.configPath) }; + } + + // Empty + if (identifier?.id) { + return { id: identifier.id }; } return undefined; @@ -183,9 +200,9 @@ export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentS return extUriBiasedIgnorePathCase.isEqualOrParent(path, environmentService.untitledWorkspacesHome); } -export interface IEmptyWorkspaceInitializationPayload extends IBaseWorkspaceIdentifier { } +export interface IEmptyWorkspaceIdentifier extends IBaseWorkspaceIdentifier { } -export type IWorkspaceInitializationPayload = IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceInitializationPayload; +export type IWorkspaceInitializationPayload = IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier; //#endregion diff --git a/src/vs/platform/workspaces/test/common/workspaces.test.ts b/src/vs/platform/workspaces/test/common/workspaces.test.ts index 270eaee4899..cb8c60e8668 100644 --- a/src/vs/platform/workspaces/test/common/workspaces.test.ts +++ b/src/vs/platform/workspaces/test/common/workspaces.test.ts @@ -5,10 +5,25 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { hasWorkspaceFileExtension, toWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { hasWorkspaceFileExtension, toWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, ISerializedWorkspaceIdentifier, reviveIdentifier, ISerializedSingleFolderWorkspaceIdentifier, IEmptyWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; suite('Workspaces', () => { + test('reviveIdentifier', () => { + let serializedWorkspaceIdentifier: ISerializedWorkspaceIdentifier = { id: 'id', configPath: URI.file('foo').toJSON() }; + assert.strictEqual(isWorkspaceIdentifier(reviveIdentifier(serializedWorkspaceIdentifier)), true); + + let serializedSingleFolderWorkspaceIdentifier: ISerializedSingleFolderWorkspaceIdentifier = { id: 'id', uri: URI.file('foo').toJSON() }; + assert.strictEqual(isSingleFolderWorkspaceIdentifier(reviveIdentifier(serializedSingleFolderWorkspaceIdentifier)), true); + + let serializedEmptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier = { id: 'id' }; + assert.strictEqual(reviveIdentifier(serializedEmptyWorkspaceIdentifier).id, serializedEmptyWorkspaceIdentifier.id); + assert.strictEqual(isWorkspaceIdentifier(serializedEmptyWorkspaceIdentifier), false); + assert.strictEqual(isSingleFolderWorkspaceIdentifier(serializedEmptyWorkspaceIdentifier), false); + + assert.strictEqual(reviveIdentifier(undefined), undefined); + }); + test('hasWorkspaceFileExtension', () => { assert.strictEqual(hasWorkspaceFileExtension('something'), false); assert.strictEqual(hasWorkspaceFileExtension('something.code-workspace'), true); diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 93974d117c3..51d3d495873 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -174,6 +174,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { table.push(['window.loadUrl() => begin to require(workbench.desktop.main.js)', metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)]); table.push(['require(workbench.desktop.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]); table.push(['wait for shell environment', metrics.timers.ellapsedWaitForShellEnv, '[renderer]', undefined]); + table.push(['init storage (global & workspace)', metrics.timers.ellapsedStorageInit, '[renderer]', undefined]); table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); if (isWeb) { diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 8b6823290e0..411501667ec 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -84,7 +84,10 @@ class DesktopMain extends Disposable { private reviveUris() { // Workspace - this.configuration.workspace = reviveIdentifier(this.configuration.workspace); + const workspace = reviveIdentifier(this.configuration.workspace); + if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) { + this.configuration.workspace = workspace; + } // Files const filesToWait = this.configuration.filesToWait; @@ -321,7 +324,7 @@ class DesktopMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { - const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); let storageService: NativeStorageService | NativeStorageService2; if (this.configuration.enableExperimentalMainProcessWorkspaceStorage) { diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index a28d6151bf8..6edfbc6d1d4 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -73,7 +73,10 @@ class DesktopMain extends Disposable { private reviveUris() { // Workspace - this.configuration.workspace = reviveIdentifier(this.configuration.workspace); + const workspace = reviveIdentifier(this.configuration.workspace); + if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) { + this.configuration.workspace = workspace; + } // Files const filesToWait = this.configuration.filesToWait; @@ -294,7 +297,7 @@ class DesktopMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, mainProcessService: IMainProcessService): Promise { - const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : undefined); + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); const storageService = new NativeStorageService2(storageDataBaseClient.globalStorage, storageDataBaseClient.workspaceStorage, this.environmentService); try { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 5577ffbb2b0..fa4552e36f9 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -17,7 +17,7 @@ import { Configuration } from 'vs/workbench/services/configuration/common/config import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IWorkspaceInitializationPayload, IEmptyWorkspaceIdentifier, useSlashForPath, getStoredWorkspaceFolder, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditingService'; import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, UserConfiguration } from 'vs/workbench/services/configuration/browser/configuration'; @@ -418,8 +418,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat return Promise.resolve(workspace); } - private createEmptyWorkspace(emptyWorkspacePayload: IEmptyWorkspaceInitializationPayload): Promise { - const workspace = new Workspace(emptyWorkspacePayload.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); + private createEmptyWorkspace(emptyWorkspaceIdentifier: IEmptyWorkspaceIdentifier): Promise { + const workspace = new Workspace(emptyWorkspaceIdentifier.id, [], null, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); workspace.initialized = true; return Promise.resolve(workspace); } diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index f4bc25a6066..324de644b65 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -46,6 +46,7 @@ export interface IMemoryInfo { "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceServiceInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequiredUserDataInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, @@ -215,6 +216,14 @@ export interface IStartupMetrics { */ readonly ellapsedWorkspaceStorageInit: number; + /** + * The time it took to init the storage database connection from the workbench. + * + * * Happens in the renderer-process + * * Measured with the `code/willInitStorage` and `code/didInitStorage` performance marks. + */ + readonly ellapsedStorageInit: number; + /** * The time it took to initialize the workspace and configuration service. * @@ -514,6 +523,7 @@ export abstract class AbstractTimerService implements ITimerService { ellapsedWindowLoadToRequire: this._marks.getDuration('code/willOpenNewWindow', 'code/willLoadWorkbenchMain'), ellapsedRequire: this._marks.getDuration('code/willLoadWorkbenchMain', 'code/didLoadWorkbenchMain'), ellapsedWaitForShellEnv: this._marks.getDuration('code/willWaitForShellEnv', 'code/didWaitForShellEnv'), + ellapsedStorageInit: this._marks.getDuration('code/willInitStorage', 'code/didInitStorage'), ellapsedWorkspaceStorageInit: this._marks.getDuration('code/willInitWorkspaceStorage', 'code/didInitWorkspaceStorage'), ellapsedWorkspaceServiceInit: this._marks.getDuration('code/willInitWorkspaceService', 'code/didInitWorkspaceService'), ellapsedRequiredUserDataInit: this._marks.getDuration('code/willInitRequiredUserData', 'code/didInitRequiredUserData'), From 5236d3446048c8c08c9ceb46e1b63d438ab5bd75 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 11:40:21 +0100 Subject: [PATCH 048/176] run active editor test only when having focus --- .../vscode-api-tests/src/singlefolder-tests/window.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) 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 65fd89d3b96..615bb79ac6b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -150,6 +150,13 @@ suite('vscode API - window', () => { }); test('active editor not always correct... #49125', async function () { + + if (!window.state.focused) { + // no focus! + this.skip(); + return; + } + if (process.env['BUILD_SOURCEVERSION']) { this.skip(); return; From 196bf678a152024ff55edeb493733dd929adfdfd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 11:44:51 +0100 Subject: [PATCH 049/176] reset dirty state when reverting a notebook, update extension host when dirty state of a notebook (working copy) changes --- .../notebook.document.test.ts | 27 +++++++++---------- extensions/vscode-api-tests/src/utils.ts | 4 +++ .../api/browser/mainThreadNotebook.ts | 21 ++++++++++++++- .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostNotebook.ts | 9 ++++++- .../notebook/common/notebookEditorModel.ts | 1 + 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index cf9bd87bd24..4e126b0d475 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, disposeAll, asPromise, closeAllEditors, assertNoRpc } from '../utils'; +import * as utils from '../utils'; suite('Notebook Document', function () { @@ -33,14 +33,13 @@ suite('Notebook Document', function () { const disposables: vscode.Disposable[] = []; suiteTeardown(async function () { - assertNoRpc(); - await vscode.commands.executeCommand('workbench.action.files.saveAll'); - await closeAllEditors(); - disposeAll(disposables); + await utils.revertAllDirty(); + await utils.closeAllEditors(); + utils.disposeAll(disposables); disposables.length = 0; for (let doc of vscode.notebook.notebookDocuments) { - assert.strictEqual(doc.isDirty, false) + assert.strictEqual(doc.isDirty, false, doc.uri.toString()); } }); @@ -64,7 +63,7 @@ suite('Notebook Document', function () { }); test('document basics', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const notebook = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(notebook.uri.toString(), uri.toString()); @@ -76,9 +75,9 @@ suite('Notebook Document', function () { }); test('notebook open/close, notebook ready when cell-document open event is fired', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); let didHappen = false; - const p = asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + const p = utils.asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { if (doc.uri.scheme !== 'vscode-notebook-cell') { return; } @@ -96,9 +95,9 @@ suite('Notebook Document', function () { }); test('notebook open/close, all cell-documents are ready', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); - const p = asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { for (let cell of notebook.cells) { const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); assert.ok(doc); @@ -116,7 +115,7 @@ suite('Notebook Document', function () { test('workspace edit API (replaceCells)', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const document = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(document.cells.length, 1); @@ -192,7 +191,7 @@ suite('Notebook Document', function () { }); test('workspace edit API (replaceCells, event)', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const document = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(document.cells.length, 1); @@ -211,7 +210,7 @@ suite('Notebook Document', function () { source: 'new_code' }]); - const event = asPromise(vscode.notebook.onDidChangeNotebookCells); + const event = utils.asPromise(vscode.notebook.onDidChangeNotebookCells); const success = await vscode.workspace.applyEdit(edit); assert.strictEqual(success, true); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index a102e7d2329..4bddd45a5e3 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -48,6 +48,10 @@ export function closeAllEditors(): Thenable { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); } +export function saveAllEditors(): Thenable { + return vscode.commands.executeCommand('workbench.action.files.saveAll'); +} + export async function revertAllDirty(): Promise { return vscode.commands.executeCommand('_workbench.revertAllDirty'); } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 871641799f6..5866a81d96a 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -10,7 +10,8 @@ import { Emitter } from 'vs/base/common/event'; import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; -import { IExtUri } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; +import { IExtUri, isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -30,6 +31,7 @@ import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { @@ -121,6 +123,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo constructor( extHostContext: IExtHostContext, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @@ -202,6 +205,22 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } registerListeners() { + + // forward changes to dirty state + // todo@bpasero this seem way too complicated... is there an easy way to + // the actual resource from a working copy? + this._register(this._workingCopyService.onDidChangeDirty(e => { + if (e.resource.scheme !== Schemas.vscodeNotebook) { + return; + } + for (const notebook of this._notebookService.getNotebookTextModels()) { + if (isEqual(notebook.uri.with({ scheme: Schemas.vscodeNotebook }), e.resource)) { + this._proxy.$acceptDirtyStateChanged(notebook.uri, e.isDirty()); + break; + } + } + })); + this._notebookService.listNotebookEditors().forEach((e) => { this._addNotebookEditor(e); }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a55a0c041a1..40b4891d0a6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1803,6 +1803,7 @@ export interface ExtHostNotebookShape { $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void; $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void; + $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 40ff93d7dd5..2e76b5fe7c9 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -578,7 +578,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - public $acceptModelSaved(uriComponents: UriComponents): void { + $acceptDirtyStateChanged(resource: UriComponents, isDirty: boolean): void { + const document = this._documents.get(URI.revive(resource)); + if (document) { + document.acceptModelChanged({ rawEvents: [], versionId: document.notebookDocument.version }, isDirty); + } + } + + $acceptModelSaved(uriComponents: UriComponents): void { const document = this._documents.get(URI.revive(uriComponents)); if (document) { // this.$acceptDirtyStateChanged(uriComponents, false); diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 1ad9b60a8b6..583dad5a8cf 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -125,6 +125,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM async revert(options?: IRevertOptions | undefined): Promise { if (options?.soft) { await this._backupFileService.discardBackup(this.resource); + this.setDirty(false); return; } From cc4d7e8a11adf1c893fe151b07f595216d149219 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 11:52:14 +0100 Subject: [PATCH 050/176] update (restore) language test for new cells --- .../src/singlefolder-tests/notebook.test.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 7db8c7784d6..b437120c23c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -6,7 +6,7 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, asPromise, disposeAll, closeAllEditors } from '../utils'; +import { createRandomFile, asPromise, disposeAll, closeAllEditors, revertAllDirty } from '../utils'; // Since `workbench.action.splitEditor` command does await properly // Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves @@ -78,9 +78,11 @@ suite('Notebook API tests', function () { const disposables: vscode.Disposable[] = []; suiteTeardown(async function () { - disposeAll(disposables); - await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await revertAllDirty(); await closeAllEditors(); + + disposeAll(disposables); + disposables.length = 0; }); suiteSetup(function () { @@ -1400,12 +1402,9 @@ suite('Notebook API tests', function () { assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first'); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); - // todo@jrieken enforce a kernel (how) and test that its language is picked - // assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); - // no kernel -> no default language assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined); - assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'plaintext'); + assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); assert.strictEqual(vscode.window.activeTextEditor?.document.uri.path, resource.path); From 88089d3f204c8367376e3e0729e4382ff9146172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 12:04:31 +0100 Subject: [PATCH 051/176] fix terrapin failures not reporting --- build/azure-pipelines/darwin/product-build-darwin.yml | 1 + build/azure-pipelines/linux/product-build-alpine.yml | 1 + build/azure-pipelines/linux/product-build-linux.yml | 1 + build/azure-pipelines/product-compile.yml | 1 + build/azure-pipelines/web/product-build-web.yml | 1 + build/azure-pipelines/win32/product-build-win32.yml | 6 ++++-- 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index a34ae1c4247..1f3cba946bd 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -71,6 +71,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index a2bbb119bfb..7e3aea8d231 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -69,6 +69,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 9a42f058155..1d26cee26f4 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -67,6 +67,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64')) - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 08e3f694520..7855cde1663 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -50,6 +50,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 05aa68fe126..0a8e1c36a88 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -60,6 +60,7 @@ steps: displayName: Extract node_modules cache - script: | + set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 035580035b0..7076ebf13b0 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -65,8 +65,10 @@ steps: condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) displayName: Extract node_modules cache - - script: | - npx https://aka.ms/enablesecurefeed standAlone + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { npx https://aka.ms/enablesecurefeed standAlone } timeoutInMinutes: 5 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages From 50bb1cf1a4da4668ce650161b39cafc9cf8e8fb9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 12:18:19 +0100 Subject: [PATCH 052/176] storage - do not init at random (fix tests on windows) --- src/vs/platform/storage/electron-main/storageMainService.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 38e8d235a33..7ed969b0bd2 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -48,11 +48,6 @@ export class StorageMainService implements IStorageMainService { const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); - // Trigger init of global storage directly from here - // so that we can be ready for access when the window - // needs it (prevents waterfall of initialization) - globalStorage.initialize(); - return globalStorage; } From ac5b7117ec410194814ff92be79254ebc4389dfe Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 12:52:54 +0100 Subject: [PATCH 053/176] assert no rpc in notebook doc test --- .../src/singlefolder-tests/notebook.document.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index 4e126b0d475..3a2c081f1d5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -33,6 +33,7 @@ suite('Notebook Document', function () { const disposables: vscode.Disposable[] = []; suiteTeardown(async function () { + utils.assertNoRpc(); await utils.revertAllDirty(); await utils.closeAllEditors(); utils.disposeAll(disposables); From 114bac541eba442ef8b6c81e6d409337f8430ac2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 13:25:02 +0100 Subject: [PATCH 054/176] storage - bring back logging support --- .../sharedProcess/sharedProcessMain.ts | 4 +-- .../electron-sandbox/storageService2.ts | 26 ++++++++++++------- .../electron-browser/desktop.main.ts | 5 ++-- .../electron-sandbox/desktop.main.ts | 4 +-- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index fe2cb6adcae..f60f74463ca 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -59,7 +59,6 @@ import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService'; import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; @@ -173,8 +172,7 @@ class SharedProcessMain extends Disposable { await configurationService.initialize(); // Storage (global access only) - const storageDatabaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), undefined); - const storageService = new NativeStorageService2(storageDatabaseClient.globalStorage, undefined, environmentService); + const storageService = new NativeStorageService2(undefined, mainProcessService, environmentService); services.set(IStorageService, storageService); await storageService.initialize(); diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index 97fc6da4e21..b2dd29e21f2 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -5,30 +5,37 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { StorageScope, WillSaveStateReason, logStorage, AbstractStorageService } from 'vs/platform/storage/common/storage'; -import { Storage, IStorageDatabase, IStorage } from 'vs/base/parts/storage/common/storage'; +import { Storage, IStorage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { assertIsDefined } from 'vs/base/common/types'; import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; import { mark } from 'vs/base/common/performance'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; +import { joinPath } from 'vs/base/common/resources'; export class NativeStorageService2 extends AbstractStorageService { - private readonly globalStorage = new Storage(this.globalStorageDatabase); - private readonly workspaceStorage = this.workspaceStorageDatabase ? new Storage(this.workspaceStorageDatabase) : undefined; + private readonly globalStorage: IStorage; + private readonly workspaceStorage: IStorage | undefined; private initializePromise: Promise | undefined; - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60000 /* every minute */)); + private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60 * 1000 /* every minute */)); private runWhenIdleDisposable: IDisposable | undefined = undefined; constructor( - private globalStorageDatabase: IStorageDatabase, - private workspaceStorageDatabase: IStorageDatabase | undefined, - @IEnvironmentService private readonly environmentService: IEnvironmentService + private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, + mainProcessService: IMainProcessService, + private readonly environmentService: IEnvironmentService ) { super(); + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), workspace); + this.globalStorage = new Storage(storageDataBaseClient.globalStorage); + this.workspaceStorage = storageDataBaseClient.workspaceStorage ? new Storage(storageDataBaseClient.workspaceStorage) : undefined; + this.registerListeners(); } @@ -139,7 +146,8 @@ export class NativeStorageService2 extends AbstractStorageService { this.globalStorage.items, this.workspaceStorage ? this.workspaceStorage.items : new Map(), this.environmentService.globalStorageHome.fsPath, - /* this.workspaceStoragePath || */ ''); + this.workspace ? joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath : '' + ); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 411501667ec..057696c9420 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -324,12 +324,11 @@ class DesktopMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { - const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); - let storageService: NativeStorageService | NativeStorageService2; if (this.configuration.enableExperimentalMainProcessWorkspaceStorage) { - storageService = new NativeStorageService2(storageDataBaseClient.globalStorage, storageDataBaseClient.workspaceStorage, this.environmentService); + storageService = new NativeStorageService2(payload, mainProcessService, this.environmentService); } else { + const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); storageService = new NativeStorageService(storageDataBaseClient.globalStorage, logService, this.environmentService); } diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 6edfbc6d1d4..14d55ac5c31 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -18,7 +18,6 @@ import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIni import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2'; import { Schemas } from 'vs/base/common/network'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -297,8 +296,7 @@ class DesktopMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, mainProcessService: IMainProcessService): Promise { - const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); - const storageService = new NativeStorageService2(storageDataBaseClient.globalStorage, storageDataBaseClient.workspaceStorage, this.environmentService); + const storageService = new NativeStorageService2(payload, mainProcessService, this.environmentService); try { await storageService.initialize(); From 4d89ae10ff8b1de9342803d8ee088d1cef4f6438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 12:57:02 +0100 Subject: [PATCH 055/176] fix #116523 --- build/gulpfile.vscode.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 7f52a5d99a2..e3ca3514bb6 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -284,6 +284,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op let result = all .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()) + .pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523 .pipe(electron(_.extend({}, config, { platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true }))) .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true })); From 965518f9a98e35149842af4464c8715481f47ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 13:39:41 +0100 Subject: [PATCH 056/176] fixes #116558 --- src/vs/base/browser/ui/sash/sash.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index f82138b8626..2fe3fc2bb33 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -398,13 +398,21 @@ export class Sash extends Disposable { })); } - private static onMouseEnter(sash: Sash): void { + private static onMouseEnter(sash: Sash, fromLinkedSash: boolean = false): void { sash.hoverDelayer.trigger(() => sash.el.classList.add('hover')); + + if (!fromLinkedSash && sash.linkedSash) { + Sash.onMouseEnter(sash.linkedSash); + } } - private static onMouseLeave(sash: Sash): void { + private static onMouseLeave(sash: Sash, fromLinkedSash: boolean = false): void { sash.hoverDelayer.cancel(); sash.el.classList.remove('hover'); + + if (!fromLinkedSash && sash.linkedSash) { + Sash.onMouseLeave(sash.linkedSash); + } } layout(): void { From c9886c394626b45d1a890f987142486301ee9a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 13:40:42 +0100 Subject: [PATCH 057/176] missing recursion break --- src/vs/base/browser/ui/sash/sash.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 2fe3fc2bb33..bceaf6e4e9b 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -402,7 +402,7 @@ export class Sash extends Disposable { sash.hoverDelayer.trigger(() => sash.el.classList.add('hover')); if (!fromLinkedSash && sash.linkedSash) { - Sash.onMouseEnter(sash.linkedSash); + Sash.onMouseEnter(sash.linkedSash, true); } } @@ -411,7 +411,7 @@ export class Sash extends Disposable { sash.el.classList.remove('hover'); if (!fromLinkedSash && sash.linkedSash) { - Sash.onMouseLeave(sash.linkedSash); + Sash.onMouseLeave(sash.linkedSash, true); } } From 5c330b06d4df1e2c7b17b629b7b31df4b85efea5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 13:50:41 +0100 Subject: [PATCH 058/176] cells are editable --- .vscode/notebooks/my-work.github-issues | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index fb59e914e78..1502c29db16 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -8,7 +8,8 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\"", + "editable": true }, { "kind": 1, @@ -19,7 +20,8 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone assignee:@me is:open" + "value": "$repos $milestone assignee:@me is:open", + "editable": true }, { "kind": 1, @@ -36,7 +38,8 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:bug" + "value": "$repos assignee:@me is:open label:bug", + "editable": true }, { "kind": 1, @@ -47,7 +50,8 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering" + "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", + "editable": true }, { "kind": 1, @@ -58,7 +62,8 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak" + "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", + "editable": true }, { "kind": 1, @@ -69,12 +74,14 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc" + "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", + "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"" + "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", + "editable": true }, { "kind": 1, @@ -91,7 +98,8 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream" + "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream", + "editable": true }, { "kind": 1, @@ -102,6 +110,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"needs more info\"" + "value": "$repos assignee:@me is:open label:\"needs more info\"", + "editable": true } ] \ No newline at end of file From f3b4f3f6acf9addfcde71165b99315794ee839d8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 14:11:45 +0100 Subject: [PATCH 059/176] unescape characters that marked escaped, fixes https://github.com/microsoft/vscode/issues/115391 --- src/vs/base/browser/markdownRenderer.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 81457733d22..2feb2ab7f0c 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -383,5 +383,16 @@ export function renderMarkdownAsPlaintext(markdown: IMarkdownString) { if (value.length > 100_000) { value = `${value.substr(0, 100_000)}…`; } - return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString(); + + const unescapeInfo = new Map([ + ['"', ':'], + ['&', '&'], + [''', '\''], + ['<', '<'], + ['>', '>'], + ]); + + const html = marked.parse(value, { renderer }).replace(/&(#\d+|[a-zA-Z]+);/g, m => unescapeInfo.get(m) ?? m); + + return sanitizeRenderedMarkdown({ isTrusted: false }, html).toString(); } From 793371f05595dffe0885c94fc13f787dcc6a9b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 14:13:24 +0100 Subject: [PATCH 060/176] remove twistie hover feedback --- src/vs/base/browser/ui/list/listWidget.ts | 5 ----- src/vs/platform/theme/common/colorRegistry.ts | 1 - src/vs/platform/theme/common/styler.ts | 4 +--- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 398ace4b213..74b933a7e18 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -779,10 +779,6 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } - if (styles.listTwistieHoverBackground) { - content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-tl-twistie:hover::before { background-color: ${styles.listTwistieHoverBackground}; }`); - } - if (styles.listHoverForeground) { content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); } @@ -874,7 +870,6 @@ export interface IListStyles { listInactiveFocusBackground?: Color; listHoverBackground?: Color; listHoverForeground?: Color; - listTwistieHoverBackground?: Color; listDropBackground?: Color; listFocusOutline?: Color; listInactiveFocusOutline?: Color; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 8ed02eb2756..6098286d467 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -373,7 +373,6 @@ export const listInactiveFocusBackground = registerColor('list.inactiveFocusBack export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline', { dark: null, light: null, hc: null }, nls.localize('listInactiveFocusOutline', "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); -export const listTwistieHoverBackground = registerColor('list.twistieHoverBackground', { dark: transparent(lighten(listHoverBackground, 0.5), 0.7), light: transparent(darken(listHoverBackground, 0.1), 0.7), hc: null }, nls.localize('twistieHoverBackground', "List/Tree background when hovering over a twistie using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#0097fb', light: '#0066BF', hc: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hc: '#B89500' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 68d7d175e40..02c66cc4325 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listTwistieHoverBackground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground, problemsWarningIconForeground, problemsErrorIconForeground, problemsInfoIconForeground, buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground, listFocusOutline, listInactiveFocusOutline } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { IThemable, styleFn } from 'vs/base/common/styler'; @@ -221,7 +221,6 @@ export interface IListStyleOverrides extends IStyleOverrides { listInactiveFocusOutline?: ColorIdentifier; listHoverBackground?: ColorIdentifier; listHoverForeground?: ColorIdentifier; - listTwistieHoverBackground?: ColorIdentifier; listDropBackground?: ColorIdentifier; listSelectionOutline?: ColorIdentifier; listHoverOutline?: ColorIdentifier; @@ -250,7 +249,6 @@ export const defaultListStyles: IColorMapping = { listInactiveFocusOutline, listHoverBackground, listHoverForeground, - listTwistieHoverBackground, listDropBackground, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, From 9dca2bed0bb35ddc83e626bc616598ee4f9e2445 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 15 Feb 2021 14:15:57 +0100 Subject: [PATCH 061/176] Fix bug in tree view tests Fixes #113896 --- .../workbench/test/browser/api/extHostTreeViews.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 651add03080..22f2eeef016 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -604,7 +604,7 @@ suite('ExtHostTreeView', function () { const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { - runWithEventMerging((resolve) => { + return runWithEventMerging((resolve) => { tree = { 'a': { 'aa': {}, @@ -633,9 +633,9 @@ suite('ExtHostTreeView', function () { .then(() => { assert.ok(revealTarget.calledOnce); assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]); - assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1])); - assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); + assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1].item)); + assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][1].parentChain).map(arg => removeUnsetKeys(arg))); + assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]); }); }); }); From c4c044e20dba1434b61294b426712af47558e976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 14:29:23 +0100 Subject: [PATCH 062/176] fix quick input inactive focus background color --- src/vs/base/browser/ui/list/listWidget.ts | 6 ++++++ src/vs/base/parts/quickinput/browser/quickInput.ts | 6 +----- src/vs/platform/quickinput/browser/quickInput.ts | 4 ++-- src/vs/platform/theme/common/colorRegistry.ts | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 74b933a7e18..d399ef27bd7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -761,6 +761,11 @@ export class DefaultStyleController implements IStyleController { `); } + if (styles.listInactiveFocusForeground) { + content.push(`.monaco-list${suffix} .monaco-list-row.focused { color: ${styles.listInactiveFocusForeground}; }`); + content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { color: ${styles.listInactiveFocusForeground}; }`); // overwrite :hover style in this case! + } + if (styles.listInactiveFocusBackground) { content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`); content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case! @@ -867,6 +872,7 @@ export interface IListStyles { listFocusAndSelectionForeground?: Color; listInactiveSelectionBackground?: Color; listInactiveSelectionForeground?: Color; + listInactiveFocusForeground?: Color; listInactiveFocusBackground?: Color; listHoverBackground?: Color; listHoverForeground?: Color; diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 038816012f3..2ab5e7a478d 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -56,7 +56,7 @@ export interface IQuickInputStyles { countBadge: ICountBadgetyles; button: IButtonStyles; progressBar: IProgressBarStyles; - list: IListStyles & { listInactiveFocusForeground?: Color; pickerGroupBorder?: Color; pickerGroupForeground?: Color; }; + list: IListStyles & { pickerGroupBorder?: Color; pickerGroupForeground?: Color; }; } export interface IQuickInputWidgetStyles { @@ -1706,10 +1706,6 @@ export class QuickInputController extends Disposable { this.ui.list.style(this.styles.list); const content: string[] = []; - if (this.styles.list.listInactiveFocusForeground) { - content.push(`.monaco-list .monaco-list-row.focused { color: ${this.styles.list.listInactiveFocusForeground}; }`); - content.push(`.monaco-list .monaco-list-row.focused:hover { color: ${this.styles.list.listInactiveFocusForeground}; }`); // overwrite :hover style in this case! - } if (this.styles.list.pickerGroupBorder) { content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.list.pickerGroupBorder}; }`); } diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 86f5ddaf348..299eb02fd91 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -7,7 +7,7 @@ import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuick import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground } from 'vs/platform/theme/common/colorRegistry'; +import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, listFocusOutline, quickInputListFocusBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { computeStyles } from 'vs/platform/theme/common/styler'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -213,7 +213,7 @@ export class QuickInputService extends Themable implements IQuickInputService { listBackground: quickInputBackground, // Look like focused when inactive. listInactiveFocusForeground: listFocusForeground, - listInactiveFocusBackground: listFocusBackground, + listInactiveFocusBackground: quickInputListFocusBackground, listFocusOutline: activeContrastBorder, listInactiveFocusOutline: activeContrastBorder, pickerGroupBorder, diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 6098286d467..58b90b849ff 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -288,6 +288,7 @@ export const editorWidgetResizeBorder = registerColor('editorWidget.resizeBorder export const quickInputBackground = registerColor('quickInput.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('pickerBackground', "Quick picker background color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputForeground = registerColor('quickInput.foreground', { dark: editorWidgetForeground, light: editorWidgetForeground, hc: editorWidgetForeground }, nls.localize('pickerForeground', "Quick picker foreground color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputTitleBackground = registerColor('quickInputTitle.background', { dark: new Color(new RGBA(255, 255, 255, 0.105)), light: new Color(new RGBA(0, 0, 0, 0.06)), hc: '#000000' }, nls.localize('pickerTitleBackground', "Quick picker title background color. The quick picker widget is the container for pickers like the command palette.")); +export const quickInputListFocusBackground = registerColor('quickInput.list.focusBackground', { dark: '#062F4A', light: '#D6EBFF', hc: null }, nls.localize('quickInput.listFocusBackground', "Quick picker background color for the focused item.")); export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#0066BF', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); From 4077a67914bacb7d85c58f921812fcd01ea3a8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 14:29:36 +0100 Subject: [PATCH 063/176] adopt focus border across all core themes --- extensions/theme-abyss/themes/abyss-color-theme.json | 2 +- .../theme-kimbie-dark/themes/kimbie-dark-color-theme.json | 2 +- .../theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json | 2 +- extensions/theme-monokai/themes/monokai-color-theme.json | 2 +- extensions/theme-quietlight/themes/quietlight-color-theme.json | 2 +- extensions/theme-red/themes/Red-color-theme.json | 2 +- .../theme-solarized-dark/themes/solarized-dark-color-theme.json | 2 +- .../themes/solarized-light-color-theme.json | 2 +- .../themes/tomorrow-night-blue-color-theme.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 8e97bbb6dc1..391b5af61b7 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -303,7 +303,7 @@ "list.activeSelectionBackground": "#08286b", // "list.activeSelectionForeground": "", - "list.focusBackground": "#08286b", + "quickInput.list.focusBackground": "#08286b", "list.hoverBackground": "#061940", "list.inactiveSelectionBackground": "#152037", "list.dropBackground": "#041D52", diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 3453c53dc2b..24ba31854fe 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -10,7 +10,7 @@ "list.highlightForeground": "#e3b583", "list.activeSelectionBackground": "#7c5021", "list.hoverBackground": "#7c502166", - "list.focusBackground": "#7c5021AA", + "quickInput.list.focusBackground": "#7c5021AA", "list.inactiveSelectionBackground": "#645342", "pickerGroup.foreground": "#e3b583", "pickerGroup.border": "#e3b583", diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index bd8b896c011..9ca62cc15e3 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -3,7 +3,7 @@ "colors": { "dropdown.background": "#525252", "list.activeSelectionBackground": "#707070", - "list.focusBackground": "#707070", + "quickInput.list.focusBackground": "#707070", "list.inactiveSelectionBackground": "#4e4e4e", "list.hoverBackground": "#444444", "list.highlightForeground": "#e58520", diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 969455d01e3..100d291ddad 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -9,7 +9,7 @@ "colors": { "dropdown.background": "#414339", "list.activeSelectionBackground": "#75715E", - "list.focusBackground": "#414339", + "quickInput.list.focusBackground": "#414339", "dropdown.listBackground": "#1e1f1c", "list.inactiveSelectionBackground": "#414339", "list.hoverBackground": "#3e3d32", diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index e395b99e3eb..376c5fda7a3 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -473,7 +473,7 @@ "pickerGroup.foreground": "#A6B39B", "pickerGroup.border": "#749351", "list.activeSelectionForeground": "#6c6c6c", - "list.focusBackground": "#CADEB9", + "quickInput.list.focusBackground": "#CADEB9", "list.hoverBackground": "#e0e0e0", "list.activeSelectionBackground": "#c4d9b1", "list.inactiveSelectionBackground": "#d3dbcd", diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 8eeda13456e..c3e728b7f39 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -49,7 +49,7 @@ "list.activeSelectionBackground": "#880000", "list.inactiveSelectionBackground": "#770000", "list.dropBackground": "#662222", - "list.focusBackground": "#660000", + "quickInput.list.focusBackground": "#660000", "list.highlightForeground": "#ff4444", "pickerGroup.foreground": "#cc9999", "pickerGroup.border": "#ff000033", diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 8a9deb0cd4b..b887521b605 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -350,7 +350,7 @@ "list.activeSelectionBackground": "#005A6F", // "list.activeSelectionForeground": "", - "list.focusBackground": "#005A6F", + "quickInput.list.focusBackground": "#005A6F", "list.hoverBackground": "#004454AA", "list.inactiveSelectionBackground": "#00445488", "list.dropBackground": "#00445488", diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 427b7c3c359..1efe1e000e3 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -350,7 +350,7 @@ "list.activeSelectionBackground": "#DFCA88", "list.activeSelectionForeground": "#6C6C6C", - "list.focusBackground": "#DFCA8866", + "quickInput.list.focusBackground": "#DFCA8866", "list.hoverBackground": "#DFCA8844", "list.inactiveSelectionBackground": "#D1CBB8", "list.highlightForeground": "#B58900", diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index 91c135342f1..e4ae768d9f5 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -5,7 +5,7 @@ "errorForeground": "#a92049", "input.background": "#001733", "dropdown.background": "#001733", - "list.focusBackground": "#ffffff60", + "quickInput.list.focusBackground": "#ffffff60", "list.activeSelectionBackground": "#ffffff60", "list.inactiveSelectionBackground": "#ffffff40", "list.hoverBackground": "#ffffff30", From 250c2265459f3bb4b65022e1b678286f804e8fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 14:42:28 +0100 Subject: [PATCH 064/176] fix suggest widget styles --- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 884207922f2..94135e09b63 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -21,7 +21,7 @@ import { Context as SuggestContext, CompletionItem } from './suggest'; import { CompletionModel } from './completionModel'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorWidgetBackground, quickInputListFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -40,7 +40,7 @@ import { clamp } from 'vs/base/common/numbers'; export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); -export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); +export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hc: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); const enum State { From e2d1cfb64a1d1a85ba9f159d4abe7636bfaf1f22 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 15:01:34 +0100 Subject: [PATCH 065/176] make SubmenuItemAction dynamic again --- src/vs/platform/actions/common/actions.ts | 28 ++++++++++------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 9de9273c464..1e2d2df6c2f 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -321,34 +321,30 @@ export class ExecuteCommandAction extends Action { export class SubmenuItemAction extends SubmenuAction { - readonly item: ISubmenuItem; - constructor( - item: ISubmenuItem, - menuService: IMenuService, - contextKeyService: IContextKeyService, - options?: IMenuActionOptions + readonly item: ISubmenuItem, + private readonly _menuService: IMenuService, + private readonly _contextKeyService: IContextKeyService, + private readonly _options?: IMenuActionOptions ) { + super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, [], 'submenu'); + } + + get actions(): readonly IAction[] { const result: IAction[] = []; - const menu = menuService.createMenu(item.submenu, contextKeyService); - const groups = menu.getActions(options); + const menu = this._menuService.createMenu(this.item.submenu, this._contextKeyService); + const groups = menu.getActions(this._options); menu.dispose(); - - for (let group of groups) { - const [, actions] = group; - + for (const [, actions] of groups) { if (actions.length > 0) { result.push(...actions); result.push(new Separator()); } } - if (result.length) { result.pop(); // remove last separator } - - super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, result, 'submenu'); - this.item = item; + return result; } } From 59afea597bd88efcee15e93abaebe704d9dab889 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 15:02:39 +0100 Subject: [PATCH 066/176] explorer: click in empty area -> create a new file fixes #116676 --- .../contrib/files/browser/views/explorerView.ts | 10 +++++++++- 1 file changed, 9 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 59b05dd4866..451dcceccff 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -183,7 +183,8 @@ export class ExplorerView extends ViewPane { @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - @IOpenerService openerService: IOpenerService, + @ICommandService private readonly commandService: ICommandService, + @IOpenerService openerService: IOpenerService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -459,6 +460,13 @@ export class ExplorerView extends ViewPane { } })); + this._register(this.tree.onMouseDblClick(e => { + if (e.element === null) { + // click in empty area -> create a new file #116676 + this.commandService.executeCommand(NEW_FILE_COMMAND_ID); + } + })); + // save view state this._register(this.storageService.onWillSaveState(() => { this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE, StorageTarget.MACHINE); From 7dc11581e45058e8f9f43a7eec04ac901efab2f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 15:09:17 +0100 Subject: [PATCH 067/176] remove unused event --- .../api/common/extHostNotebookDocument.ts | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 21d2e99a563..0fe42bd9de4 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -8,7 +8,6 @@ import { hash } from 'vs/base/common/hash'; import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; -import { ISplice } from 'vs/base/common/sequence'; import { URI } from 'vs/base/common/uri'; import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -30,7 +29,7 @@ class RawContentChangeEvent { } } -export class ExtHostCell extends Disposable { +export class ExtHostCell { static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { return { @@ -47,11 +46,7 @@ export class ExtHostCell extends Disposable { private _onDidDispose = new Emitter(); readonly onDidDispose: Event = this._onDidDispose.event; - private _onDidChangeOutputs = new Emitter[]>(); - readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; - private _outputs: IOutputDto[]; - private _metadata: vscode.NotebookCellMetadata; readonly handle: number; @@ -65,18 +60,18 @@ export class ExtHostCell extends Disposable { private readonly _extHostDocument: ExtHostDocumentsAndEditors, private readonly _cellData: IMainCellDto, ) { - super(); - this.handle = _cellData.handle; this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; - this._outputs = _cellData.outputs; - - this._metadata = _cellData.metadata ?? {}; } + dispose() { + this._onDidDispose.fire(); + this._onDidDispose.dispose(); + } + get cell(): vscode.NotebookCell { if (!this._cell) { const that = this; @@ -100,11 +95,6 @@ export class ExtHostCell extends Disposable { return this._cell; } - dispose() { - super.dispose(); - this._onDidDispose.fire(); - } - setOutputs(newOutputs: IOutputDto[]): void { this._outputs = newOutputs; } @@ -137,10 +127,8 @@ export class ExtHostNotebookDocument extends Disposable { private _cellDisposableMapping = new Map(); private _notebook: vscode.NotebookDocument | undefined; - // private _metadata: Required; - // private _metadataChangeListener: IDisposable; private _versionId = 0; - private _isDirty: boolean = false; + private _isDirty = false; private _backupCounter = 1; private _backup?: vscode.NotebookDocumentBackup; private _disposed = false; From 62d027a713cc25b4d7224957f170aeb793a1b56b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 15:26:28 +0100 Subject: [PATCH 068/176] more API todos --- src/vs/vscode.proposed.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 0122b4e2b2a..7f26c9427ab 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1104,6 +1104,7 @@ declare module 'vscode' { export interface NotebookDocument { readonly uri: Uri; readonly version: number; + // todo@API don't have this... readonly fileName: string; // todo@API should we really expose this? readonly viewType: string; @@ -1322,6 +1323,9 @@ declare module 'vscode' { export const onDidChangeNotebookDocumentMetadata: Event; export const onDidChangeNotebookCells: Event; export const onDidChangeCellOutputs: Event; + + // todo@API we send document close and open events when the language of a document changes and + // I believe we should stick that for cells as well export const onDidChangeCellLanguage: Event; export const onDidChangeCellMetadata: Event; } From 8848ddd9c06aac2918d2bbd37ff3c8eefa7d76eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 15:27:19 +0100 Subject: [PATCH 069/176] use metadata classes inside NotebookCell and NotebookDocument --- .../workbench/api/common/extHostNotebook.ts | 2 +- .../api/common/extHostNotebookDocument.ts | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 2e76b5fe7c9..347418f4927 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -729,7 +729,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { that._onDidChangeNotebookDocumentMetadata.fire(event); } - }, viewType, modelData.contentOptions, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); + }, viewType, modelData.contentOptions, new extHostTypes.NotebookDocumentMetadata().with(modelData.metadata ?? {}), uri, storageRoot); document.acceptModelChanged({ versionId: modelData.versionId, diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 0fe42bd9de4..b8a9bc12073 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -12,7 +12,8 @@ import { URI } from 'vs/base/common/uri'; import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; +import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; class RawContentChangeEvent { @@ -47,7 +48,7 @@ export class ExtHostCell { readonly onDidDispose: Event = this._onDidDispose.event; private _outputs: IOutputDto[]; - private _metadata: vscode.NotebookCellMetadata; + private _metadata: extHostTypes.NotebookCellMetadata; readonly handle: number; readonly uri: URI; @@ -64,7 +65,7 @@ export class ExtHostCell { this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; this._outputs = _cellData.outputs; - this._metadata = _cellData.metadata ?? {}; + this._metadata = new extHostTypes.NotebookCellMetadata().with(_cellData.metadata ?? {}); } dispose() { @@ -99,8 +100,8 @@ export class ExtHostCell { this._outputs = newOutputs; } - setMetadata(newMetadata: vscode.NotebookCellMetadata): void { - this._metadata = newMetadata; + setMetadata(newMetadata: NotebookCellMetadata): void { + this._metadata = this._metadata.with(newMetadata); } } @@ -138,7 +139,7 @@ export class ExtHostNotebookDocument extends Disposable { private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, private readonly _contentOptions: vscode.NotebookDocumentContentOptions, - private _metadata: Required, + private _metadata: extHostTypes.NotebookDocumentMetadata, public readonly uri: URI, private readonly _storagePath: URI | undefined ) { @@ -190,11 +191,9 @@ export class ExtHostNotebookDocument extends Disposable { } acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) { - const newMetadata = { - ...notebookDocumentMetadataDefaults, - ...data.metadata - }; - this._metadata = newMetadata; + if (data.metadata) { + this._metadata = this._metadata.with(data.metadata); + } this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument }); } From 58b13a2fd1d32406d8c4b1e220eb9e7947f8af8d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 15 Feb 2021 15:34:18 +0100 Subject: [PATCH 070/176] Fix too many/wrong port notification Fixes microsoft/vscode-remote-release#4472 --- src/vs/workbench/contrib/remote/browser/remoteExplorer.ts | 3 +++ src/vs/workbench/contrib/remote/browser/urlFinder.ts | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index f3ba2a45148..a5325de6807 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -544,6 +544,9 @@ class ProcAutomaticPortForwarding extends Disposable { if (this.initialCandidates.has(address)) { return undefined; } + if (this.notifiedOnly.has(address) || this.autoForwarded.has(address)) { + return undefined; + } const alreadyForwarded = mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port); if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, value.host, value.port)) { return undefined; diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index cb3c96dc2f1..ba36c2b5414 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -104,7 +104,12 @@ export class UrlFinder extends Disposable { if (urlMatches && urlMatches.length > 0) { urlMatches.forEach((match) => { // check if valid url - const serverUrl = new URL(match); + let serverUrl; + try { + serverUrl = new URL(match); + } catch (e) { + // Not a valid URL + } if (serverUrl) { // check if the port is a valid integer value const portMatch = match.match(UrlFinder.extractPortRegex); From d56305b3f9a3244d36b5f16742f22974f39c4560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 15:40:37 +0100 Subject: [PATCH 071/176] cleanup imports --- src/vs/platform/quickinput/browser/quickInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 299eb02fd91..6c54bf00996 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -7,7 +7,7 @@ import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuick import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, listFocusOutline, quickInputListFocusBackground } from 'vs/platform/theme/common/colorRegistry'; +import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground, quickInputListFocusBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { computeStyles } from 'vs/platform/theme/common/styler'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; From 96640b6b875d650100297bdd68f9f362483c14b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 15:48:06 +0100 Subject: [PATCH 072/176] tree: remove bogus expandOnlyOnDoubleClick --- src/vs/base/browser/ui/tree/abstractTree.ts | 6 ------ src/vs/platform/list/browser/listService.ts | 4 ---- 2 files changed, 10 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3e135b3c8a8..6f644b9c940 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -961,7 +961,6 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly filterOnType?: boolean; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; - readonly expandOnlyOnDoubleClick?: boolean; readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } @@ -1121,10 +1120,6 @@ class TreeNodeListMouseController extends MouseController< return super.onViewPointer(e); } - if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) { - return super.onViewPointer(e); - } - if (node.collapsible) { const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); @@ -1263,7 +1258,6 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } - get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 127f5ad81bf..b421ee719c6 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -852,7 +852,6 @@ function workbenchTreeDataPreamble(treeExpandMode) === 'doubleClick') } as TOptions }; @@ -952,9 +951,6 @@ class WorkbenchTreeInternals { const horizontalScrolling = configurationService.getValue(horizontalScrollingKey); newOptions = { ...newOptions, horizontalScrolling }; } - if (e.affectsConfiguration(openModeSettingKey)) { - newOptions = { ...newOptions, expandOnlyOnDoubleClick: configurationService.getValue(openModeSettingKey) === 'doubleClick' }; - } if (e.affectsConfiguration(treeExpandMode) && options.expandOnlyOnTwistieClick === undefined) { newOptions = { ...newOptions, expandOnlyOnTwistieClick: configurationService.getValue<'singleClick' | 'doubleClick'>(treeExpandMode) === 'doubleClick' }; } From 2a9b2181ffe7305d10a29ff3d2049e08a995569c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 15:52:59 +0100 Subject: [PATCH 073/176] improve list settings docs --- src/vs/platform/list/browser/listService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index b421ee719c6..752fdb0e4e1 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1017,7 +1017,7 @@ configurationRegistry.registerConfiguration({ 'description': localize({ key: 'openModeModifier', comment: ['`singleClick` and `doubleClick` refers to a value the setting can take and should not be localized.'] - }, "Controls how to open items in trees and lists using the mouse (if supported). For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable. ") + }, "Controls how to open items in trees and lists using the mouse (if supported). For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable.") }, [horizontalScrollingKey]: { 'type': 'boolean', @@ -1062,7 +1062,7 @@ configurationRegistry.registerConfiguration({ type: 'string', enum: ['singleClick', 'doubleClick'], default: 'doubleClick', - description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names."), + description: localize('expand mode', "Controls how tree folders are expanded when clicking the folder names. Note that some trees and lists might choose to ignore this setting if it is not applicable."), } } }); From 7bf4a3d823d16138782b9aaf59c2794b1d582e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 15:53:42 +0100 Subject: [PATCH 074/176] fixes #115212 --- src/vs/base/browser/ui/tree/abstractTree.ts | 8 +++++++- src/vs/workbench/contrib/outline/browser/outlinePane.ts | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 6f644b9c940..af081547c85 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -961,6 +961,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly filterOnType?: boolean; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; + readonly expandOnDoubleClick?: boolean; readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T } @@ -1120,6 +1121,10 @@ class TreeNodeListMouseController extends MouseController< return super.onViewPointer(e); } + if (!this.tree.expandOnDoubleClick && e.browserEvent.detail === 2) { + return super.onViewPointer(e); + } + if (node.collapsible) { const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); @@ -1138,7 +1143,7 @@ class TreeNodeListMouseController extends MouseController< protected onDoubleClick(e: IListMouseEvent>): void { const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie'); - if (onTwistie) { + if (onTwistie || !this.tree.expandOnDoubleClick) { return; } @@ -1258,6 +1263,7 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } + get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 5a3c4048178..9ef31066da0 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -232,6 +232,7 @@ export class OutlinePane extends ViewPane { ...newOutline.config.options, sorter, openOnSingleClick: true, + expandOnDoubleClick: false, expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, hideTwistiesOfChildlessElements: true, From ebc30d6c92cfab9a0e9571a27490f634ed2853a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 15 Feb 2021 15:55:25 +0100 Subject: [PATCH 075/176] fixes #116700 --- src/vs/workbench/contrib/outline/browser/outlinePane.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 9ef31066da0..e988a1a3a9d 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -231,7 +231,6 @@ export class OutlinePane extends ViewPane { { ...newOutline.config.options, sorter, - openOnSingleClick: true, expandOnDoubleClick: false, expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, From 18c04a5716063d0d7d21adf3e09667db314b37f4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 15:47:53 +0100 Subject: [PATCH 076/176] make `vscode.NotebookCellRange` a class --- src/vs/vscode.proposed.d.ts | 5 ++-- .../workbench/api/common/extHost.api.impl.ts | 3 +++ .../workbench/api/common/extHost.protocol.ts | 7 +----- .../workbench/api/common/extHostNotebook.ts | 10 ++++---- .../api/common/extHostNotebookEditor.ts | 10 ++++---- .../api/common/extHostTypeConverters.ts | 13 +++++++++- src/vs/workbench/api/common/extHostTypes.ts | 25 +++++++++++++++++++ 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7f26c9427ab..6bd29a1c0f9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1116,13 +1116,14 @@ declare module 'vscode' { } // todo@API maybe have a NotebookCellPosition sibling - // todo@API should be a class - export interface NotebookCellRange { + export class NotebookCellRange { readonly start: number; /** * exclusive */ readonly end: number; + + constructor(start: number, end: number); } export enum NotebookEditorRevealType { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 09a44ef2b07..b9bacd2d7f6 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1253,6 +1253,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // checkProposedApiEnabled(extension); return extHostTypes.TimelineItem; }, + get NotebookCellRange() { + return extHostTypes.NotebookCellRange; + }, get NotebookCellKind() { // checkProposedApiEnabled(extension); return extHostTypes.NotebookCellKind; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 40b4891d0a6..d8dea657c6f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1731,13 +1731,8 @@ export interface INotebookSelectionChangeEvent { selections: number[]; } -export interface INotebookCellVisibleRange { - start: number; - end: number; -} - export interface INotebookVisibleRangesEvent { - ranges: INotebookCellVisibleRange[]; + ranges: ICellRange[]; } export interface INotebookEditorPropertiesChangeData { diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 347418f4927..8b5ff7fd411 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -403,7 +403,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN resolvedOptions = { position: typeConverters.ViewColumn.from(options.viewColumn), preserveFocus: options.preserveFocus, - selection: options.selection, + selection: options.selection && typeConverters.NotebookCellRange.from(options.selection), pinned: typeof options.preview === 'boolean' ? !options.preview : undefined }; } else { @@ -608,7 +608,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (data.visibleRanges) { - editor.editor._acceptVisibleRanges(data.visibleRanges.ranges); + editor.editor._acceptVisibleRanges(data.visibleRanges.ranges.map(typeConverters.NotebookCellRange.to)); this._onDidChangeNotebookEditorVisibleRanges.fire({ notebookEditor: editor.editor, visibleRanges: editor.editor.visibleRanges @@ -643,7 +643,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: vscode.NotebookCellRange[]) { + private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[], visibleRanges: extHostTypes.NotebookCellRange[]) { const revivedUri = document.uri; let webComm = this._webviewComm.get(editorId); @@ -753,7 +753,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN // create editor if populated if (modelData.attachedEditor) { - this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges); + this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections, modelData.attachedEditor.visibleRanges.map(typeConverters.NotebookCellRange.to)); editorChanged = true; } @@ -773,7 +773,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const document = this._documents.get(revivedUri); if (document) { - this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges); + this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges.map(typeConverters.NotebookCellRange.to)); editorChanged = true; } } diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 9afae7d5be3..7b3105cfb18 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -90,7 +90,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook //TODO@rebornix noop setter? selection?: vscode.NotebookCell; - private _visibleRanges: vscode.NotebookCellRange[] = []; + private _visibleRanges: extHostTypes.NotebookCellRange[] = []; private _viewColumn?: vscode.ViewColumn; private _active: boolean = false; private _visible: boolean = false; @@ -153,11 +153,11 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return this._visibleRanges; } - set visibleRanges(_range: vscode.NotebookCellRange[]) { + set visibleRanges(_range) { throw readonly('visibleRanges'); } - _acceptVisibleRanges(value: vscode.NotebookCellRange[]): void { + _acceptVisibleRanges(value: extHostTypes.NotebookCellRange[]): void { this._visibleRanges = value; } @@ -233,13 +233,13 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook return this._proxy.$trySetDecorations( this.id, - range, + extHostConverter.NotebookCellRange.from(range), decorationType.key ); } revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { - this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default); + this._proxy.$tryRevealRange(this.id, extHostConverter.NotebookCellRange.from(range), revealType ?? extHostTypes.NotebookEditorRevealType.Default); } async postMessage(message: any): Promise { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index e838b45bfb9..674ab28bdbf 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -31,7 +31,7 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -import { CellEditType, CellKind, ICellDto2, INotebookDecorationRenderOptions, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellDto2, ICellRange, INotebookDecorationRenderOptions, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; export interface PositionLike { @@ -1344,6 +1344,17 @@ export namespace LanguageSelector { } } +export namespace NotebookCellRange { + + export function from(range: vscode.NotebookCellRange): ICellRange { + return { start: range.start, end: range.end }; + } + + export function to(range: ICellRange): types.NotebookCellRange { + return new types.NotebookCellRange(range.start, range.end); + } +} + export namespace NotebookCellKind { export function from(data: vscode.NotebookCellKind): CellKind { switch (data) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 96a66824ee6..e7cd5a11828 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2849,6 +2849,31 @@ export enum ColorThemeKind { //#region Notebook +export class NotebookCellRange { + + private _start: number; + private _end: number; + + get start() { + return this._start; + } + + get end() { + return this._end; + } + + constructor(start: number, end: number) { + if (start < 0) { + throw illegalArgument('start must be positive'); + } + if (end < start) { + throw illegalArgument('end cannot be smaller than start'); + } + this._start = start; + this._end = end; + } +} + export class NotebookCellMetadata { constructor( From 3e2aebd790ce404d3dcc0e3aa3168e857ebd3400 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 16:03:04 +0100 Subject: [PATCH 077/176] Revert "use metadata classes inside NotebookCell and NotebookDocument" This reverts commit 8848ddd9c06aac2918d2bbd37ff3c8eefa7d76eb. --- .../workbench/api/common/extHostNotebook.ts | 2 +- .../api/common/extHostNotebookDocument.ts | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 8b5ff7fd411..89388531dd6 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -729,7 +729,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { that._onDidChangeNotebookDocumentMetadata.fire(event); } - }, viewType, modelData.contentOptions, new extHostTypes.NotebookDocumentMetadata().with(modelData.metadata ?? {}), uri, storageRoot); + }, viewType, modelData.contentOptions, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); document.acceptModelChanged({ versionId: modelData.versionId, diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index b8a9bc12073..0fe42bd9de4 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -12,8 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; class RawContentChangeEvent { @@ -48,7 +47,7 @@ export class ExtHostCell { readonly onDidDispose: Event = this._onDidDispose.event; private _outputs: IOutputDto[]; - private _metadata: extHostTypes.NotebookCellMetadata; + private _metadata: vscode.NotebookCellMetadata; readonly handle: number; readonly uri: URI; @@ -65,7 +64,7 @@ export class ExtHostCell { this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; this._outputs = _cellData.outputs; - this._metadata = new extHostTypes.NotebookCellMetadata().with(_cellData.metadata ?? {}); + this._metadata = _cellData.metadata ?? {}; } dispose() { @@ -100,8 +99,8 @@ export class ExtHostCell { this._outputs = newOutputs; } - setMetadata(newMetadata: NotebookCellMetadata): void { - this._metadata = this._metadata.with(newMetadata); + setMetadata(newMetadata: vscode.NotebookCellMetadata): void { + this._metadata = newMetadata; } } @@ -139,7 +138,7 @@ export class ExtHostNotebookDocument extends Disposable { private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, private readonly _contentOptions: vscode.NotebookDocumentContentOptions, - private _metadata: extHostTypes.NotebookDocumentMetadata, + private _metadata: Required, public readonly uri: URI, private readonly _storagePath: URI | undefined ) { @@ -191,9 +190,11 @@ export class ExtHostNotebookDocument extends Disposable { } acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) { - if (data.metadata) { - this._metadata = this._metadata.with(data.metadata); - } + const newMetadata = { + ...notebookDocumentMetadataDefaults, + ...data.metadata + }; + this._metadata = newMetadata; this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument }); } From 2ecfd1456620663576ac07ecfc8648c7317d5b43 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 15 Feb 2021 16:08:27 +0100 Subject: [PATCH 078/176] Setting for proc vs output port forwarding Fixes microsoft/vscode-remote-release#4274 --- .../contrib/remote/browser/remoteExplorer.ts | 11 +++++++---- .../contrib/remote/common/remote.contribution.ts | 10 ++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index a5325de6807..89a5e8545f4 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -219,10 +219,13 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, false)); } else if (environment?.os === OperatingSystem.Linux) { - this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, - openerService, externalOpenerService, tunnelService, hostService)); + const useProc = (this.configurationService.getValue('remote.autoForwardPortsSource') === 'process'); + if (useProc) { + this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, + openerService, externalOpenerService, tunnelService, hostService)); + } this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, - remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, true)); + remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, useProc)); } }); } @@ -435,7 +438,7 @@ class OutputAutomaticPortForwarding extends Disposable { } this.urlFinder = this._register(new UrlFinder(this.terminalService, this.debugService)); this._register(this.urlFinder.onDidMatchLocalUrl(async (localUrl) => { - if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, localUrl.host, localUrl.port)) { + if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, localUrl.host, localUrl.port)) { return; } if (this.portsAttributes.getAttributes(localUrl.port)?.onAutoForward === OnPortForward.Ignore) { diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 043cd4876ad..395e5c7cce5 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -134,6 +134,16 @@ Registry.as(ConfigurationExtensions.Configuration) markdownDescription: localize('remote.autoForwardPorts', "When enabled, new running processes are detected and ports that they listen on are automatically forwarded."), default: true }, + 'remote.autoForwardPortsSource': { + type: 'string', + markdownDescription: localize('remote.autoForwardPortsSource', "Sets the source from which ports are automatically forwarded when `remote.autoForwardPorts` is true. Requires a reload to take effect."), + enum: ['process', 'output'], + enumDescriptions: [ + localize('remote.autoForwardPortsSource.process', "Ports will be automatically forwarded when discovered by watching for processes that are started and include a port."), + localize('remote.autoForwardPortsSource.output', "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports forwarded based on output will not be \"un-forwarded\" until reload or until the port is closed by the user in the Ports view.") + ], + default: 'process' + }, // Consider making changes to extensions\configuration-editing\schemas\devContainer.schema.src.json // and extensions\configuration-editing\schemas\attachContainer.schema.json // to keep in sync with devcontainer.json schema. From 8fe9b7c5ec9ff9f996dfe286c497fbb10a012296 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 16:13:46 +0100 Subject: [PATCH 079/176] Fix that keybindings editor input actions can not be triggered via keyboard fixes #116688 --- src/vs/base/browser/ui/checkbox/checkbox.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index cd9e4a2718c..485d85d16d4 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -76,6 +76,26 @@ export class CheckboxActionViewItem extends BaseActionViewItem { } } + focus(): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = 0; + this.checkbox.focus(); + } + } + + blur(): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = -1; + this.checkbox.domNode.blur(); + } + } + + setFocusable(focusable: boolean): void { + if (this.checkbox) { + this.checkbox.domNode.tabIndex = focusable ? 0 : -1; + } + } + dispose(): void { this.disposables.dispose(); super.dispose(); @@ -191,7 +211,6 @@ export class Checkbox extends Widget { } enable(): void { - this.domNode.tabIndex = 0; this.domNode.setAttribute('aria-disabled', String(false)); } From 762dc9eeea6ceb43195838c36a5f53fa8394ffed Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 16:30:59 +0100 Subject: [PATCH 080/176] action bar: In case an action got disabled and it was last focused in the action bar We need to reset the tabIndex to be set on the first enabled item --- .../browser/ui/actionbar/actionViewItems.ts | 8 +++++++ src/vs/base/browser/ui/actionbar/actionbar.ts | 21 +++++++++++++++++-- src/vs/base/browser/ui/checkbox/checkbox.ts | 4 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 3d8ddb9c78c..582761b27e1 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -181,6 +181,10 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } } + get isFocusable(): boolean { + return this.element?.tabIndex === 0; + } + setFocusable(focusable: boolean): void { if (this.element) { this.element.tabIndex = focusable ? 0 : -1; @@ -288,6 +292,10 @@ export class ActionViewItem extends BaseActionViewItem { } } + get isFocusable(): boolean { + return this.label?.tabIndex === 0; + } + setFocusable(focusable: boolean): void { if (this.label) { this.label.tabIndex = focusable ? 0 : -1; diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 45afd63c40a..3f5402971bc 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./actionbar'; -import { Disposable, dispose } from 'vs/base/common/lifecycle'; -import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider } from 'vs/base/common/actions'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider, Action } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -82,6 +82,8 @@ export class ActionBar extends Disposable implements IActionRunner { private _onBeforeRun = this._register(new Emitter()); readonly onBeforeRun = this._onBeforeRun.event; + private actionChangeListeners: IDisposable[] = []; + constructor(container: HTMLElement, options: IActionBarOptions = {}) { super(); @@ -318,6 +320,17 @@ export class ActionBar extends Disposable implements IActionRunner { item.setActionContext(this.context); item.render(actionViewItemElement); + this.actionChangeListeners.push(action instanceof Action ? action.onDidChange(e => { + // In case an action got disabled and it was last focused in the action bar + // We need to reset the tabIndex to be set on the first enabled item + if (e.enabled === false && item instanceof BaseActionViewItem && item.isFocusable) { + const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled()); + if (firstEnabled instanceof BaseActionViewItem) { + firstEnabled.setFocusable(true); + } + } + }) : Disposable.None); + if (this.focusable && this.viewItems.every(i => !i.isEnabled()) && item instanceof BaseActionViewItem && item.isEnabled()) { // We need to allow for the first enabled item to be focused on using tab navigation #106441 item.setFocusable(true); @@ -366,12 +379,15 @@ export class ActionBar extends Disposable implements IActionRunner { if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); dispose(this.viewItems.splice(index, 1)); + dispose(this.actionChangeListeners.splice(index, 1)); this._actionIds.splice(index, 1); } } clear(): void { dispose(this.viewItems); + dispose(this.actionChangeListeners); + this.actionChangeListeners = []; this.viewItems = []; this._actionIds = []; DOM.clearNode(this.actionsList); @@ -513,6 +529,7 @@ export class ActionBar extends Disposable implements IActionRunner { dispose(): void { dispose(this.viewItems); + dispose(this.actionChangeListeners); this.viewItems = []; this._actionIds = []; diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 485d85d16d4..037ddbba862 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -90,6 +90,10 @@ export class CheckboxActionViewItem extends BaseActionViewItem { } } + get isFocusable(): boolean { + return this.checkbox?.domNode.tabIndex === 0; + } + setFocusable(focusable: boolean): void { if (this.checkbox) { this.checkbox.domNode.tabIndex = focusable ? 0 : -1; From f2a491fbc834c8db50325ca6c6f1bc1a7f373992 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 16:53:59 +0100 Subject: [PATCH 081/176] customSelectBox: do not buble key down and key up events to not conflict with action bar fixes #116693 --- src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 841f2ee44b0..9a2fab22e13 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -736,8 +736,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(e => this.onUpArrow(e), this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(e => this.onDownArrow(e), this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this)); this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this)); @@ -916,8 +916,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } // List navigation - have to handle a disabled option (jump over) - private onDownArrow(): void { + private onDownArrow(e: StandardKeyboardEvent): void { if (this.selected < this.options.length - 1) { + dom.EventHelper.stop(e, true); // Skip disabled options const nextOptionDisabled = this.options[this.selected + 1].isDisabled; @@ -937,8 +938,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } } - private onUpArrow(): void { + private onUpArrow(e: StandardKeyboardEvent): void { if (this.selected > 0) { + dom.EventHelper.stop(e, true); // Skip disabled options const previousOptionDisabled = this.options[this.selected - 1].isDisabled; if (previousOptionDisabled && this.selected > 1) { From 5f48de03e6c49a2638b3cace9721c4f98baa334e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 16:54:24 +0100 Subject: [PATCH 082/176] use metadata classes for cell and notebook document implementation, https://github.com/microsoft/vscode/issues/116333 --- .../workbench/api/common/extHostNotebook.ts | 2 +- .../api/common/extHostNotebookDocument.ts | 13 ++-- .../api/common/extHostTypeConverters.ts | 59 ++++++++++++------- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 89388531dd6..6b995a33914 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -729,7 +729,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { that._onDidChangeNotebookDocumentMetadata.fire(event); } - }, viewType, modelData.contentOptions, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); + }, viewType, modelData.contentOptions, typeConverters.NotebookDocumentMetadata.to(modelData.metadata ?? {}), uri, storageRoot); document.acceptModelChanged({ versionId: modelData.versionId, diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 0fe42bd9de4..3e9e9e41a85 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -12,6 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; @@ -47,7 +48,7 @@ export class ExtHostCell { readonly onDidDispose: Event = this._onDidDispose.event; private _outputs: IOutputDto[]; - private _metadata: vscode.NotebookCellMetadata; + private _metadata: extHostTypes.NotebookCellMetadata; readonly handle: number; readonly uri: URI; @@ -64,7 +65,7 @@ export class ExtHostCell { this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; this._outputs = _cellData.outputs; - this._metadata = _cellData.metadata ?? {}; + this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {}); } dispose() { @@ -99,8 +100,8 @@ export class ExtHostCell { this._outputs = newOutputs; } - setMetadata(newMetadata: vscode.NotebookCellMetadata): void { - this._metadata = newMetadata; + setMetadata(newMetadata: NotebookCellMetadata): void { + this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata); } } @@ -138,7 +139,7 @@ export class ExtHostNotebookDocument extends Disposable { private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, private readonly _contentOptions: vscode.NotebookDocumentContentOptions, - private _metadata: Required, + private _metadata: extHostTypes.NotebookDocumentMetadata, public readonly uri: URI, private readonly _storagePath: URI | undefined ) { @@ -194,7 +195,7 @@ export class ExtHostNotebookDocument extends Disposable { ...notebookDocumentMetadataDefaults, ...data.metadata }; - this._metadata = newMetadata; + this._metadata = this._metadata.with(newMetadata); this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument }); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 674ab28bdbf..910683b56be 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -31,7 +31,7 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -import { CellEditType, CellKind, ICellDto2, ICellRange, INotebookDecorationRenderOptions, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection'; export interface PositionLike { @@ -509,7 +509,7 @@ export namespace TextEdit { } export namespace WorkspaceEdit { - export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, notebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto { + export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors, extHostNotebooks?: ExtHostNotebookController): extHostProtocol.IWorkspaceEditDto { const result: extHostProtocol.IWorkspaceEditDto = { edits: [] }; @@ -544,7 +544,7 @@ export namespace WorkspaceEdit { resource: entry.uri, edit: entry.edit, notebookMetadata: entry.notebookMetadata, - notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version }); } else if (entry._type === types.FileEditType.CellOutput) { @@ -554,7 +554,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.Output, + editType: notebooks.CellEditType.Output, index: entry.index, append: entry.append, outputs: entry.newOutputs.map(NotebookCellOutput.from) @@ -568,7 +568,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.Metadata, + editType: notebooks.CellEditType.Metadata, index: entry.index, metadata: entry.newMetadata } @@ -579,9 +579,9 @@ export namespace WorkspaceEdit { _type: extHostProtocol.WorkspaceEditType.Cell, metadata: entry.metadata, resource: entry.uri, - notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version, + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version, edit: { - editType: CellEditType.Replace, + editType: notebooks.CellEditType.Replace, index: entry.index, count: entry.count, cells: entry.cells.map(NotebookCellData.from) @@ -593,7 +593,7 @@ export namespace WorkspaceEdit { metadata: entry.metadata, resource: entry.uri, edit: { - editType: CellEditType.OutputItems, + editType: notebooks.CellEditType.OutputItems, index: entry.index, outputId: entry.outputId, items: entry.newOutputItems?.map(item => ({ mime: item.mime, value: item.value, metadata: item.metadata })) || [], @@ -1346,31 +1346,50 @@ export namespace LanguageSelector { export namespace NotebookCellRange { - export function from(range: vscode.NotebookCellRange): ICellRange { + export function from(range: vscode.NotebookCellRange): notebooks.ICellRange { return { start: range.start, end: range.end }; } - export function to(range: ICellRange): types.NotebookCellRange { + export function to(range: notebooks.ICellRange): types.NotebookCellRange { return new types.NotebookCellRange(range.start, range.end); } } +export namespace NotebookCellMetadata { + + export function to(data: vscode.NotebookCellMetadata): types.NotebookCellMetadata { + return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runStartTime, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom); + } +} + +export namespace NotebookDocumentMetadata { + + export function from(data: types.NotebookDocumentMetadata): notebooks.NotebookDocumentMetadata { + return data; + } + + export function to(data: vscode.NotebookDocumentMetadata): types.NotebookDocumentMetadata { + return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted); + } + +} + export namespace NotebookCellKind { - export function from(data: vscode.NotebookCellKind): CellKind { + export function from(data: vscode.NotebookCellKind): notebooks.CellKind { switch (data) { case types.NotebookCellKind.Markdown: - return CellKind.Markdown; + return notebooks.CellKind.Markdown; case types.NotebookCellKind.Code: default: - return CellKind.Code; + return notebooks.CellKind.Code; } } - export function to(data: CellKind): vscode.NotebookCellKind { + export function to(data: notebooks.CellKind): vscode.NotebookCellKind { switch (data) { - case CellKind.Markdown: + case notebooks.CellKind.Markdown: return types.NotebookCellKind.Markdown; - case CellKind.Code: + case notebooks.CellKind.Code: default: return types.NotebookCellKind.Code; } @@ -1379,7 +1398,7 @@ export namespace NotebookCellKind { export namespace NotebookCellData { - export function from(data: vscode.NotebookCellData): ICellDto2 { + export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 { return { cellKind: NotebookCellKind.from(data.cellKind), language: data.language, @@ -1397,7 +1416,7 @@ export namespace NotebookCellData { } export namespace NotebookCellOutput { - export function from(output: types.NotebookCellOutput): IOutputDto { + export function from(output: types.NotebookCellOutput): notebooks.IOutputDto { const data = Object.create(null); const custom = Object.create(null); @@ -1418,7 +1437,7 @@ export namespace NotebookCellOutput { }; } - export function to(output: IOutputDto): vscode.NotebookCellOutput { + export function to(output: notebooks.IOutputDto): vscode.NotebookCellOutput { const items: types.NotebookCellOutputItem[] = output.outputs.map(op => new types.NotebookCellOutputItem(op.mime, op.value, op.metadata)); return new types.NotebookCellOutput(items, output.outputId); } @@ -1499,7 +1518,7 @@ export namespace NotebookExclusiveDocumentPattern { } export namespace NotebookDecorationRenderOptions { - export function from(options: vscode.NotebookDecorationRenderOptions): INotebookDecorationRenderOptions { + export function from(options: vscode.NotebookDecorationRenderOptions): notebooks.INotebookDecorationRenderOptions { return { backgroundColor: options.backgroundColor, borderColor: options.borderColor, From 5901b6a4ea192ac3fe4fb9242b1fc2f1a93f826d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 17:07:42 +0100 Subject: [PATCH 083/176] storage - move lifecycle into service --- src/vs/code/electron-main/app.ts | 4 +-- .../storage/browser/storageService.ts | 16 ++++----- .../storage/electron-main/storageMain.ts | 12 ++++++- .../electron-main/storageMainService.ts | 6 ++-- .../electron-main/storageMainService.test.ts | 33 ++++++++++++++++--- 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 918d2fb505d..c9aa9a1384e 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -580,9 +580,7 @@ export class CodeApplication extends Disposable { services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); // Storage - const storageMainService = new StorageMainService(this.logService, this.environmentService); - services.set(IStorageMainService, storageMainService); - this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.globalStorage.close())); + services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); // Backups const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index d7c9c986d84..0776161e830 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { runWhenIdle, RunOnceScheduler, Promises } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; -import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; +import { assertIsDefined } from 'vs/base/common/types'; export class BrowserStorageService extends AbstractStorageService { @@ -131,14 +131,12 @@ export class BrowserStorageService extends AbstractStorageService { } async logStorage(): Promise { - const [globalStorage, workspaceStorage, globalStorageFile, workspaceStorageFile] = assertAllDefined(this.globalStorage, this.workspaceStorage, this.globalStorageFile, this.workspaceStorageFile); - - const result = await Promise.all([ - globalStorage.items, - workspaceStorage.items - ]); - - return logStorage(result[0], result[1], globalStorageFile.toString(), workspaceStorageFile.toString()); + return logStorage( + assertIsDefined(this.globalStorage).items, + assertIsDefined(this.workspaceStorage).items, + assertIsDefined(this.globalStorageFile).toString(), + assertIsDefined(this.workspaceStorageFile).toString() + ); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 18494b1b00b..eb462d73cd1 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -16,6 +16,7 @@ import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; /** * Provides access to global and workspace storage from the @@ -197,9 +198,18 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { constructor( logService: ILogService, - private readonly environmentService: IEnvironmentService + private readonly environmentService: IEnvironmentService, + private readonly lifecycleMainService: ILifecycleMainService ) { super(logService); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Lifecycle + this.lifecycleMainService.onWillShutdown(e => e.join(this.close())); } protected async doInitialize(): Promise { diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 7ed969b0bd2..832943ebb04 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -6,6 +6,7 @@ import { once } from 'vs/base/common/functional'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -33,7 +34,8 @@ export class StorageMainService implements IStorageMainService { constructor( @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService ) { } @@ -46,7 +48,7 @@ export class StorageMainService implements IStorageMainService { return this.globalStorage; // only once } - const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); + const globalStorage = new GlobalStorageMain(this.logService, this.environmentService, this.lifecycleMainService); return globalStorage; } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 025bbf6226c..ae3f8165583 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -16,9 +16,12 @@ import { StorageMainService } from 'vs/platform/storage/electron-main/storageMai import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { generateUuid } from 'vs/base/common/uuid'; -import { isWindows } from 'vs/base/common/platform'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { joinPath } from 'vs/base/common/resources'; +import { ILifecycleMainService, LifecycleMainPhase, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { Event } from 'vs/base/common/event'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; flakySuite('StorageMainService (native)', function () { @@ -41,6 +44,28 @@ flakySuite('StorageMainService (native)', function () { } } + class StorageTestLifecycleMainService implements ILifecycleMainService { + + _serviceBrand: undefined; + + onBeforeShutdown = Event.None; + onWillShutdown = Event.None; + onBeforeWindowClose = Event.None; + onBeforeWindowUnload = Event.None; + + wasRestarted = false; + quitRequested = false; + + phase = LifecycleMainPhase.Ready; + + async reload(window: ICodeWindow, cli?: NativeParsedArgs): Promise { } + async unload(window: ICodeWindow, reason: UnloadReason): Promise { return true; } + relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; }): void { } + async quit(fromUpdate?: boolean): Promise { return true; } + async kill(code?: number): Promise { } + async when(phase: LifecycleMainPhase): Promise { } + } + let testDir: string; let environmentService: StorageTestEnvironmentService; @@ -126,17 +151,17 @@ flakySuite('StorageMainService (native)', function () { test('basics (global)', function () { return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService); + const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService()); return storageMainService.globalStorage; }, true); }); test('basics (workspace)', function () { - const workspace = { id: generateUuid(), uri: URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace') }; + const workspace = { id: generateUuid() }; return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService); + const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService()); return storageMainService.workspaceStorage(workspace); }, false); From 2e53ffced96d221dfcadbeefb5fd90be8d9f61dc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 17:16:16 +0100 Subject: [PATCH 084/176] add precondition to exec'ish cell commands --- .../contrib/notebook/browser/contrib/coreActions.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 9705204ab05..6e8611e59ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -21,7 +21,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -228,10 +228,11 @@ export function getWidgetFromUri(accessor: ServicesAccessor, uri: URI) { return undefined; } -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCell extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_COMMAND_ID, + precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), title: localize('notebookActions.execute', "Execute Cell"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -312,7 +313,7 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class StopExecuteCell extends NotebookCellAction { constructor() { super({ id: CANCEL_CELL_COMMAND_ID, @@ -442,10 +443,11 @@ export class DeleteCellAction extends MenuItemAction { } } -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_SELECT_BELOW, + precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -478,10 +480,11 @@ registerAction2(class extends NotebookCellAction { } }); -registerAction2(class extends NotebookCellAction { +registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_INSERT_BELOW, + precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, From d9c653c8b68449695e27d82967eeec4f71ea4d7e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 17:17:04 +0100 Subject: [PATCH 085/176] increase default timeout for event waiting, fixes https://github.com/microsoft/vscode/issues/116704 --- extensions/vscode-api-tests/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 4bddd45a5e3..8d94c8ea120 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -122,7 +122,7 @@ export function assertNoRpcFromEntry(entry: [obj: any, name: string]) { assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // happens... } -export async function asPromise(event: vscode.Event, timeout = 1000): Promise { +export async function asPromise(event: vscode.Event, timeout = 5000): Promise { return new Promise((resolve, reject) => { const handle = setTimeout(() => { From e4e0919c0ec20264fe69e40025f62d4140928df4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 17:30:58 +0100 Subject: [PATCH 086/176] refine precondition so that markdown cells always "execute" --- .../contrib/notebook/browser/contrib/coreActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 6e8611e59ec..26772735238 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -232,7 +232,7 @@ registerAction2(class ExecuteCell extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_COMMAND_ID, - precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.execute', "Execute Cell"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -447,7 +447,7 @@ registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_SELECT_BELOW, - precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, @@ -484,7 +484,7 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { constructor() { super({ id: EXECUTE_CELL_INSERT_BELOW, - precondition: ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), + precondition: ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), NOTEBOOK_CELL_TYPE.isEqualTo('markdown')), title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"), keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, From 6c12d9f2c4aa9f14ce1ca81033ebf31ea48c0bc0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 15 Feb 2021 17:54:05 +0100 Subject: [PATCH 087/176] storage - add first cut lifecycle controlled from main side --- src/vs/platform/storage/common/storageIpc.ts | 18 +++---- .../storage/electron-main/storageIpc.ts | 10 ---- .../storage/electron-main/storageMain.ts | 39 +++++++++------ .../electron-main/storageMainService.ts | 8 ++- .../electron-sandbox/storageService2.ts | 2 +- .../electron-main/storageMainService.test.ts | 50 +++++++++++++++++-- 6 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 5669ec1f22f..fe88c0815c7 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -57,9 +57,14 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD } async close(): Promise { - const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; - return this.channel.call('close', serializableRequest); + // The database connection is not owned by us, but rather on the + // main side, as such we do not forward the close() request but + // let main side handle this properly via lifecycle methods. + // + // However, we cleanup our listeners because we are no longer + // interested in change events from the global database + this.dispose(); } } @@ -86,15 +91,6 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I }); } } - - close(): Promise { - - // Remove our listeners on `close` because we are no longer - // interested in change events from the global database - this.dispose(); - - return super.close(); - } } class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 27084af2ee6..2fb542e34cf 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -104,16 +104,6 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel break; } - case 'close': { - if (workspace) { - // Only allow to close workspace storage databases but not - // the global database that is shared across multiple connections - return storage.close(); - } - - break; - } - default: throw new Error(`Call not found: ${command}`); } diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index eb462d73cd1..e0d15b548e3 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -16,7 +16,7 @@ import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; /** * Provides access to global and workspace storage from the @@ -114,8 +114,26 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { private initializePromise: Promise | undefined = undefined; - constructor(protected readonly logService: ILogService) { + constructor( + protected readonly logService: ILogService, + private readonly lifecycleMainService: ILifecycleMainService + ) { super(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Lifecycle: Warmup (in parallel to window open) + (async () => { + await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + + this.initialize(); + })(); + + // Lifecycle: Shutdown + this.lifecycleMainService.onWillShutdown(e => e.join(this.close())); } initialize(): Promise { @@ -199,17 +217,9 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { constructor( logService: ILogService, private readonly environmentService: IEnvironmentService, - private readonly lifecycleMainService: ILifecycleMainService + lifecycleMainService: ILifecycleMainService ) { - super(logService); - - this.registerListeners(); - } - - private registerListeners(): void { - - // Lifecycle - this.lifecycleMainService.onWillShutdown(e => e.join(this.close())); + super(logService, lifecycleMainService); } protected async doInitialize(): Promise { @@ -278,9 +288,10 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai constructor( private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier, logService: ILogService, - private readonly environmentService: IEnvironmentService + private readonly environmentService: IEnvironmentService, + lifecycleMainService: ILifecycleMainService ) { - super(logService); + super(logService, lifecycleMainService); } protected async doInitialize(): Promise { diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 832943ebb04..f9b408032b9 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -48,8 +48,14 @@ export class StorageMainService implements IStorageMainService { return this.globalStorage; // only once } + this.logService.trace(`StorageMainService: creating global storage`); + const globalStorage = new GlobalStorageMain(this.logService, this.environmentService, this.lifecycleMainService); + once(globalStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed global storage`); + }); + return globalStorage; } @@ -61,7 +67,7 @@ export class StorageMainService implements IStorageMainService { private readonly mapWorkspaceToStorage = new Map(); private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { - const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService); + const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService, this.lifecycleMainService); return workspaceStorage; } diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index b2dd29e21f2..e5415b5b907 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -146,7 +146,7 @@ export class NativeStorageService2 extends AbstractStorageService { this.globalStorage.items, this.workspaceStorage ? this.workspaceStorage.items : new Map(), this.environmentService.globalStorageHome.fsPath, - this.workspace ? joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath : '' + this.workspace ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : '' ); } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index ae3f8165583..cdde6a0aa04 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -5,7 +5,7 @@ import { promises } from 'fs'; import { tmpdir } from 'os'; -import { strictEqual } from 'assert'; +import { notStrictEqual, strictEqual } from 'assert'; import { URI } from 'vs/base/common/uri'; import { rimraf } from 'vs/base/node/pfs'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; @@ -18,10 +18,11 @@ import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron- import { generateUuid } from 'vs/base/common/uuid'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { joinPath } from 'vs/base/common/resources'; -import { ILifecycleMainService, LifecycleMainPhase, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { Event } from 'vs/base/common/event'; +import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { Emitter, Event } from 'vs/base/common/event'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { Promises } from 'vs/base/common/async'; flakySuite('StorageMainService (native)', function () { @@ -49,7 +50,24 @@ flakySuite('StorageMainService (native)', function () { _serviceBrand: undefined; onBeforeShutdown = Event.None; - onWillShutdown = Event.None; + + private readonly _onWillShutdown = new Emitter(); + readonly onWillShutdown = this._onWillShutdown.event; + + async fireOnWillShutdown(): Promise { + const joiners: Promise[] = []; + + this._onWillShutdown.fire({ + join(promise) { + if (promise) { + joiners.push(promise); + } + } + }); + + await Promises.settled(joiners); + } + onBeforeWindowClose = Event.None; onBeforeWindowUnload = Event.None; @@ -166,4 +184,28 @@ flakySuite('StorageMainService (native)', function () { return storageMainService.workspaceStorage(workspace); }, false); }); + + test('storage closed onWillShutdown', async function () { + const lifecycleMainService = new StorageTestLifecycleMainService(); + const workspace = { id: generateUuid() }; + const storageMainService = new StorageMainService(new NullLogService(), environmentService, lifecycleMainService); + + let storage = storageMainService.workspaceStorage(workspace); + let didCloseStorage = false; + storage.onDidCloseStorage(() => { + didCloseStorage = true; + }); + + strictEqual(storage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + + await storage.initialize(); + + await lifecycleMainService.fireOnWillShutdown(); + strictEqual(didCloseStorage, true); + + let storage2 = storageMainService.workspaceStorage(workspace); + notStrictEqual(storage, storage2); + + return storage2.close(); + }); }); From 8263f1c66a364d513b6a4a5779c4e1f76fb1fd5a Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 18:35:41 +0100 Subject: [PATCH 088/176] actionBar: do not eat up the arrow key if there is only one item in the action bar --- src/vs/base/browser/ui/actionbar/actionbar.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 3f5402971bc..808b9dcdcee 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -147,9 +147,9 @@ export class ActionBar extends Disposable implements IActionRunner { const focusedItem = typeof this.focusedItem === 'number' ? this.viewItems[this.focusedItem] : undefined; if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) { - eventHandled = this.focusPrevious(); + eventHandled = this.focusPrevious() && this.viewItems.length > 1; } else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) { - eventHandled = this.focusNext(); + eventHandled = this.focusNext() && this.viewItems.length > 1; } else if (event.equals(KeyCode.Escape) && this.cancelHasListener) { this._onDidCancel.fire(); } else if (event.equals(KeyCode.Tab) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) { From 5f5ceba51a31fc58eee54f0f59adff7efae5a8e7 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 20:29:33 +0100 Subject: [PATCH 089/176] actionBar: allow to focus disabled items --- .../browser/ui/actionbar/actionViewItems.ts | 11 +------ src/vs/base/browser/ui/actionbar/actionbar.ts | 31 +++++-------------- src/vs/base/browser/ui/checkbox/checkbox.ts | 4 --- .../ui/dropdown/dropdownActionViewItem.ts | 1 - 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 582761b27e1..9abeda1c547 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -14,7 +14,7 @@ import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { DataTransfers } from 'vs/base/browser/dnd'; import { isFirefox } from 'vs/base/browser/browser'; -import { $, addDisposableListener, append, EventHelper, EventLike, EventType, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, EventHelper, EventLike, EventType } from 'vs/base/browser/dom'; export interface IBaseActionViewItemOptions { draggable?: boolean; @@ -181,10 +181,6 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { } } - get isFocusable(): boolean { - return this.element?.tabIndex === 0; - } - setFocusable(focusable: boolean): void { if (this.element) { this.element.tabIndex = focusable ? 0 : -1; @@ -292,10 +288,6 @@ export class ActionViewItem extends BaseActionViewItem { } } - get isFocusable(): boolean { - return this.label?.tabIndex === 0; - } - setFocusable(focusable: boolean): void { if (this.label) { this.label.tabIndex = focusable ? 0 : -1; @@ -364,7 +356,6 @@ export class ActionViewItem extends BaseActionViewItem { if (this.label) { this.label.setAttribute('aria-disabled', 'true'); this.label.classList.add('disabled'); - removeTabIndexAndUpdateFocus(this.label); } if (this.element) { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 808b9dcdcee..8a912e1255c 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./actionbar'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider, Action } from 'vs/base/common/actions'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { IAction, IActionRunner, ActionRunner, IRunEvent, Separator, IActionViewItem, IActionViewItemProvider } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import * as types from 'vs/base/common/types'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -82,8 +82,6 @@ export class ActionBar extends Disposable implements IActionRunner { private _onBeforeRun = this._register(new Emitter()); readonly onBeforeRun = this._onBeforeRun.event; - private actionChangeListeners: IDisposable[] = []; - constructor(container: HTMLElement, options: IActionBarOptions = {}) { super(); @@ -225,13 +223,13 @@ export class ActionBar extends Disposable implements IActionRunner { // Some action bars should not be focusable at times // When an action bar is not focusable make sure to make all the elements inside it not focusable - // When an action bar is focusable again, make sure the first enabled item can be focused + // When an action bar is focusable again, make sure the first item can be focused setFocusable(focusable: boolean): void { this.focusable = focusable; if (this.focusable) { - const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled()); - if (firstEnabled instanceof BaseActionViewItem) { - firstEnabled.setFocusable(true); + const first = this.viewItems.find(vi => vi instanceof BaseActionViewItem); + if (first instanceof BaseActionViewItem) { + first.setFocusable(true); } } else { this.viewItems.forEach(vi => { @@ -320,18 +318,7 @@ export class ActionBar extends Disposable implements IActionRunner { item.setActionContext(this.context); item.render(actionViewItemElement); - this.actionChangeListeners.push(action instanceof Action ? action.onDidChange(e => { - // In case an action got disabled and it was last focused in the action bar - // We need to reset the tabIndex to be set on the first enabled item - if (e.enabled === false && item instanceof BaseActionViewItem && item.isFocusable) { - const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled()); - if (firstEnabled instanceof BaseActionViewItem) { - firstEnabled.setFocusable(true); - } - } - }) : Disposable.None); - - if (this.focusable && this.viewItems.every(i => !i.isEnabled()) && item instanceof BaseActionViewItem && item.isEnabled()) { + if (this.focusable && this.viewItems.length === 0 && item instanceof BaseActionViewItem) { // We need to allow for the first enabled item to be focused on using tab navigation #106441 item.setFocusable(true); } @@ -379,15 +366,12 @@ export class ActionBar extends Disposable implements IActionRunner { if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); dispose(this.viewItems.splice(index, 1)); - dispose(this.actionChangeListeners.splice(index, 1)); this._actionIds.splice(index, 1); } } clear(): void { dispose(this.viewItems); - dispose(this.actionChangeListeners); - this.actionChangeListeners = []; this.viewItems = []; this._actionIds = []; DOM.clearNode(this.actionsList); @@ -529,7 +513,6 @@ export class ActionBar extends Disposable implements IActionRunner { dispose(): void { dispose(this.viewItems); - dispose(this.actionChangeListeners); this.viewItems = []; this._actionIds = []; diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 037ddbba862..485d85d16d4 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -90,10 +90,6 @@ export class CheckboxActionViewItem extends BaseActionViewItem { } } - get isFocusable(): boolean { - return this.checkbox?.domNode.tabIndex === 0; - } - setFocusable(focusable: boolean): void { if (this.checkbox) { this.checkbox.domNode.tabIndex = focusable ? 0 : -1; diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 353b766ef1d..2aaac8a7ab3 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -179,5 +179,4 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem { this.dropdownMenuActionViewItem.render(this.element); } } - } From 3479bb35904eb046c0974090794cac9c76173796 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 15 Feb 2021 20:30:20 +0100 Subject: [PATCH 090/176] button checkbox and menu: do not automatically remove tabIndex from disabled items --- src/vs/base/browser/ui/button/button.ts | 3 +-- src/vs/base/browser/ui/checkbox/checkbox.ts | 2 -- src/vs/base/browser/ui/menu/menu.ts | 7 +++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index ebfe8d59f17..473eb2a2ec4 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -12,7 +12,7 @@ import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset } from 'vs/base/browser/dom'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { CSSIcon, Codicon } from 'vs/base/common/codicons'; @@ -214,7 +214,6 @@ export class Button extends Disposable implements IButton { } else { this._element.classList.add('disabled'); this._element.setAttribute('aria-disabled', String(true)); - removeTabIndexAndUpdateFocus(this._element); } } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 485d85d16d4..d9319424fc5 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./checkbox'; -import * as DOM from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; @@ -215,7 +214,6 @@ export class Checkbox extends Widget { } disable(): void { - DOM.removeTabIndexAndUpdateFocus(this.domNode); this.domNode.setAttribute('aria-disabled', String(true)); } } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index b94c9c99ac2..9a9132359ce 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -8,7 +8,7 @@ import * as strings from 'vs/base/common/strings'; import { IActionRunner, IAction, SubmenuAction, Separator, IActionViewItemProvider, EmptySubmenuAction } from 'vs/base/common/actions'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; -import { EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom'; +import { EventType, EventHelper, EventLike, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -618,20 +618,23 @@ class BaseMenuActionViewItem extends BaseActionViewItem { if (this.getAction().enabled) { if (this.element) { this.element.classList.remove('disabled'); + this.element.removeAttribute('aria-disabled'); } if (this.item) { this.item.classList.remove('disabled'); + this.item.removeAttribute('aria-disabled'); this.item.tabIndex = 0; } } else { if (this.element) { this.element.classList.add('disabled'); + this.element.setAttribute('aria-disabled', 'true'); } if (this.item) { this.item.classList.add('disabled'); - removeTabIndexAndUpdateFocus(this.item); + this.item.setAttribute('aria-disabled', 'true'); } } } From bf0e8299db7994d0f33617c0c8c51df8b10c649e Mon Sep 17 00:00:00 2001 From: Utku Gultopu Date: Mon, 15 Feb 2021 16:24:05 -0500 Subject: [PATCH 091/176] Fix misspelling of "likelihood" --- src/vs/platform/files/common/fileService.ts | 2 +- .../contrib/performance/browser/perfviewEditor.ts | 2 +- src/vs/workbench/services/timer/browser/timerService.ts | 8 ++++---- .../services/timer/electron-sandbox/timerService.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index ab2a5236895..3dfc15e444a 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -458,7 +458,7 @@ export class FileService extends Disposable implements IFileService { try { // if the etag is provided, we await the result of the validation - // due to the likelyhood of hitting a NOT_MODIFIED_SINCE result. + // due to the likelihood of hitting a NOT_MODIFIED_SINCE result. // otherwise, we let it run in parallel to the file reading for // optimal startup performance. if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED) { diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 51d3d495873..7e65342f581 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -156,7 +156,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { if (metrics.meminfo) { md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / ByteSize.KB).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / ByteSize.KB).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / ByteSize.KB).toFixed(2)}MB shared)`); } - md.li(`VM(likelyhood): ${metrics.isVMLikelyhood}%`); + md.li(`VM(likelihood): ${metrics.isVMLikelihood}%`); md.li(`Initial Startup: ${metrics.initialStartup}`); md.li(`Has ${metrics.windowCount - 1} other windows`); md.li(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`); diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index 324de644b65..fbf80f6aad0 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -68,7 +68,7 @@ export interface IMemoryInfo { "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isVMLikelihood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } @@ -324,7 +324,7 @@ export interface IStartupMetrics { }; readonly hasAccessibilitySupport: boolean; - readonly isVMLikelyhood?: number; + readonly isVMLikelihood?: number; readonly platform?: string; readonly release?: string; readonly arch?: string; @@ -546,7 +546,7 @@ export abstract class AbstractTimerService implements ITimerService { meminfo: undefined, cpus: undefined, loadavg: undefined, - isVMLikelyhood: undefined, + isVMLikelihood: undefined, initialStartup, hasAccessibilitySupport: this._accessibilityService.isScreenReaderOptimized(), emptyWorkbench: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY @@ -578,7 +578,7 @@ export class TimerService extends AbstractTimerService { return 1; } protected async _extendStartupInfo(info: Writeable): Promise { - info.isVMLikelyhood = 0; + info.isVMLikelihood = 0; info.platform = navigator.userAgent; info.release = navigator.appVersion; } diff --git a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts index cfd5a044410..feb1c404ae2 100644 --- a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts +++ b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts @@ -68,7 +68,7 @@ export class TimerService extends AbstractTimerService { sharedBytes: processMemoryInfo.shared }; - info.isVMLikelyhood = Math.round((virtualMachineHint * 100)); + info.isVMLikelihood = Math.round((virtualMachineHint * 100)); const rawCpus = osProperties.cpus; if (rawCpus && rawCpus.length > 0) { From 2b1ab52273b8d02b831b2b040fc203a85baa2810 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 07:40:36 +0100 Subject: [PATCH 092/176] storage - remove unused onWillSaveState event (main) --- .../storage/electron-main/storageMain.ts | 23 ------------------- .../electron-sandbox/storageService2.ts | 1 + 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index e0d15b548e3..9bb316f0ef3 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -29,17 +29,6 @@ export interface IStorageMain { */ readonly onDidChangeStorage: Event; - /** - * Emitted when the storage is about to persist. This is the right time - * to persist data to ensure it is stored before the application shuts - * down. - * - * Note: this event may be fired many times, not only on shutdown to prevent - * loss of state in situations where the shutdown is not sufficient to - * persist the data properly. - */ - readonly onWillSaveState: Event; - /** * Emitted when the storage is closed. */ @@ -104,9 +93,6 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { protected readonly _onDidChangeStorage = this._register(new Emitter()); readonly onDidChangeStorage = this._onDidChangeStorage.event; - protected readonly _onWillSaveState = this._register(new Emitter()); - readonly onWillSaveState = this._onWillSaveState.event; - private readonly _onDidCloseStorage = this._register(new Emitter()); readonly onDidCloseStorage = this._onDidCloseStorage.event; @@ -269,15 +255,6 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { storage.set(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); storage.set(currentSessionDateStorageKey, currentSessionDate); } - - close(): Promise { - - // Signal as event so that clients can still store data - this._onWillSaveState.fire(); - - // Do it - return super.close(); - } } export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index e5415b5b907..8d7c315bd6c 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -32,6 +32,7 @@ export class NativeStorageService2 extends AbstractStorageService { ) { super(); + // Connect to storage via channel client const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), workspace); this.globalStorage = new Storage(storageDataBaseClient.globalStorage); this.workspaceStorage = storageDataBaseClient.workspaceStorage ? new Storage(storageDataBaseClient.workspaceStorage) : undefined; From 3bb3da8281d5acfae47b6e7a3cb13ef67bb79ec1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 08:05:38 +0100 Subject: [PATCH 093/176] storage - move more things into abstract storage service --- .../storage/browser/storageService.ts | 49 +----- src/vs/platform/storage/common/storage.ts | 144 +++++++----------- .../electron-sandbox/storageService2.ts | 49 +----- .../platform/storage/node/storageService.ts | 55 +------ 4 files changed, 70 insertions(+), 227 deletions(-) diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 0776161e830..aa2eb5f6c7d 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,7 +5,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { StorageScope, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; @@ -14,7 +14,6 @@ import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { runWhenIdle, RunOnceScheduler, Promises } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; -import { assertIsDefined } from 'vs/base/common/types'; export class BrowserStorageService extends AbstractStorageService { @@ -100,56 +99,18 @@ export class BrowserStorageService extends AbstractStorageService { this.periodicFlushScheduler.schedule(); } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope): string | undefined; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return this.getStorage(scope).get(key, fallbackValue); + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope): boolean | undefined; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return this.getStorage(scope).getBoolean(key, fallbackValue); - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope): number | undefined; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getNumber(key, fallbackValue); - } - - protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { - this.getStorage(scope).set(key, value); - } - - protected doRemove(key: string, scope: StorageScope): void { - this.getStorage(scope).delete(key); - } - - private getStorage(scope: StorageScope): IStorage { - return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); - } - - async logStorage(): Promise { - return logStorage( - assertIsDefined(this.globalStorage).items, - assertIsDefined(this.workspaceStorage).items, - assertIsDefined(this.globalStorageFile).toString(), - assertIsDefined(this.workspaceStorageFile).toString() - ); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorageFile?.toString() : this.workspaceStorageFile?.toString(); } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { throw new Error('Migrating storage is currently unsupported in Web'); } - protected async doFlush(): Promise { - await Promises.settled([ - this.getStorage(StorageScope.GLOBAL).whenFlushed(), - this.getStorage(StorageScope.WORKSPACE).whenFlushed() - ]); - } - private doFlushWhenIdle(): void { // Dispose any previous idle runner diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 3d176f5b6d8..aad6f035908 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -8,6 +8,8 @@ import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; +import { InMemoryStorageDatabase, IStorage, Storage } from 'vs/base/parts/storage/common/storage'; +import { Promises } from 'vs/base/common/async'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; const TARGET_KEY = '__$__targetStorageMarker'; @@ -257,6 +259,24 @@ export abstract class AbstractStorageService extends Disposable implements IStor this._onWillSaveState.fire({ reason }); } + get(key: string, scope: StorageScope, fallbackValue: string): string; + get(key: string, scope: StorageScope): string | undefined; + get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { + return this.getStorage(scope)?.get(key, fallbackValue); + } + + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope): boolean | undefined; + getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { + return this.getStorage(scope)?.getBoolean(key, fallbackValue); + } + + getNumber(key: string, scope: StorageScope, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope): number | undefined; + getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + return this.getStorage(scope)?.getNumber(key, fallbackValue); + } + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void { // We remove the key for undefined/null values @@ -272,7 +292,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor this.updateKeyTarget(key, scope, target); // Store actual value - this.doStore(key, value, scope); + this.getStorage(scope)?.set(key, value); }); } @@ -285,7 +305,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor this.updateKeyTarget(key, scope, undefined); // Remove actual key - this.doRemove(key, scope); + this.getStorage(scope)?.delete(key); }); } @@ -326,7 +346,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor if (typeof target === 'number') { if (keyTargets[key] !== target) { keyTargets[key] = target; - this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + this.getStorage(scope)?.set(TARGET_KEY, JSON.stringify(keyTargets)); } } @@ -334,7 +354,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor else { if (typeof keyTargets[key] === 'number') { delete keyTargets[key]; - this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope); + this.getStorage(scope)?.set(TARGET_KEY, JSON.stringify(keyTargets)); } } } @@ -378,118 +398,62 @@ export abstract class AbstractStorageService extends Disposable implements IStor return this.getBoolean(IS_NEW_KEY, scope) === true; } - flush(): Promise { + async flush(): Promise { // Signal event to collect changes this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); // Await flush - return this.doFlush(); + await Promises.settled([ + this.getStorage(StorageScope.GLOBAL)?.whenFlushed() ?? Promise.resolve(), + this.getStorage(StorageScope.WORKSPACE)?.whenFlushed() ?? Promise.resolve() + ]); + } + + async logStorage(): Promise { + const globalItems = this.getStorage(StorageScope.GLOBAL)?.items ?? new Map(); + const workspaceItems = this.getStorage(StorageScope.WORKSPACE)?.items ?? new Map(); + + return logStorage( + globalItems, + workspaceItems, + this.getLogDetails(StorageScope.GLOBAL) ?? '', + this.getLogDetails(StorageScope.WORKSPACE) ?? '' + ); } // --- abstract - abstract get(key: string, scope: StorageScope, fallbackValue: string): string; - abstract get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; + protected abstract getStorage(scope: StorageScope): IStorage | undefined; - abstract getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - abstract getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; - - abstract getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - abstract getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; - - protected abstract doStore(key: string, value: string | boolean | number, scope: StorageScope): void; - - protected abstract doRemove(key: string, scope: StorageScope): void; - - protected abstract doFlush(): Promise; + protected abstract getLogDetails(scope: StorageScope): string | undefined; abstract migrate(toWorkspace: IWorkspaceInitializationPayload): Promise; - - abstract logStorage(): void; } export class InMemoryStorageService extends AbstractStorageService { - private readonly globalCache = new Map(); - private readonly workspaceCache = new Map(); + private globalStorage = new Storage(new InMemoryStorageDatabase()); + private workspaceStorage = new Storage(new InMemoryStorageDatabase()); - private getCache(scope: StorageScope): Map { - return scope === StorageScope.GLOBAL ? this.globalCache : this.workspaceCache; + constructor() { + super(); + + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value; + protected getStorage(scope: StorageScope): IStorage { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value === 'true'; - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - const value = this.getCache(scope).get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return parseInt(value, 10); - } - - protected doStore(key: string, value: string | boolean | number, scope: StorageScope): void { - - // Otherwise, convert to String and store - const valueStr = String(value); - - // Return early if value already set - const currentValue = this.getCache(scope).get(key); - if (currentValue === valueStr) { - return; - } - - // Update in cache - this.getCache(scope).set(key, valueStr); - - // Events - this.emitDidChangeValue(scope, key); - } - - protected doRemove(key: string, scope: StorageScope): void { - const wasDeleted = this.getCache(scope).delete(key); - if (!wasDeleted) { - return; // Return early if value already deleted - } - - // Events - this.emitDidChangeValue(scope, key); - } - - logStorage(): void { - logStorage(this.globalCache, this.workspaceCache, 'inMemory', 'inMemory'); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? 'inMemory (global)' : 'inMemory (workspace)'; } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { // not supported } - - async doFlush(): Promise { } - - async close(): Promise { } } export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index 8d7c315bd6c..c9bbecee81c 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { StorageScope, WillSaveStateReason, logStorage, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, WillSaveStateReason, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { Storage, IStorage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; -import { assertIsDefined } from 'vs/base/common/types'; import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; import { mark } from 'vs/base/common/performance'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; @@ -72,41 +71,12 @@ export class NativeStorageService2 extends AbstractStorageService { this.periodicFlushScheduler.schedule(); } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope): string | undefined; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return this.getStorage(scope).get(key, fallbackValue); + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope): boolean | undefined; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return this.getStorage(scope).getBoolean(key, fallbackValue); - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope): number | undefined; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getNumber(key, fallbackValue); - } - - protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { - this.getStorage(scope).set(key, value); - } - - protected doRemove(key: string, scope: StorageScope): void { - this.getStorage(scope).delete(key); - } - - private getStorage(scope: StorageScope): IStorage { - return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); - } - - protected async doFlush(): Promise { - await Promises.settled([ - this.globalStorage.whenFlushed(), - this.workspaceStorage?.whenFlushed() ?? Promise.resolve() - ]); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspace ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : undefined; } private doFlushWhenIdle(): void { @@ -142,15 +112,6 @@ export class NativeStorageService2 extends AbstractStorageService { ]); } - async logStorage(): Promise { - return logStorage( - this.globalStorage.items, - this.workspaceStorage ? this.workspaceStorage.items : new Map(), - this.environmentService.globalStorageHome.fsPath, - this.workspace ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : '' - ); - } - async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { // if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { // return; // no migration needed if running in memory diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 111bbf4874c..316263b1b52 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -6,7 +6,7 @@ import { promises } from 'fs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; +import { StorageScope, WillSaveStateReason, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage'; import { mark } from 'vs/base/common/performance'; @@ -170,47 +170,12 @@ export class NativeStorageService extends AbstractStorageService { } } - get(key: string, scope: StorageScope, fallbackValue: string): string; - get(key: string, scope: StorageScope): string | undefined; - get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return this.getStorage(scope).get(key, fallbackValue); + protected getStorage(scope: StorageScope): IStorage | undefined { + return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } - getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope): boolean | undefined; - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return this.getStorage(scope).getBoolean(key, fallbackValue); - } - - getNumber(key: string, scope: StorageScope, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope): number | undefined; - getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return this.getStorage(scope).getNumber(key, fallbackValue); - } - - protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void { - this.getStorage(scope).set(key, value); - } - - protected doRemove(key: string, scope: StorageScope): void { - this.getStorage(scope).delete(key); - } - - private getStorage(scope: StorageScope): IStorage { - return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage); - } - - protected async doFlush(): Promise { - const promises: Promise[] = []; - if (this.globalStorage) { - promises.push(this.globalStorage.whenFlushed()); - } - - if (this.workspaceStorage) { - promises.push(this.workspaceStorage.whenFlushed()); - } - - await Promises.settled(promises); + protected getLogDetails(scope: StorageScope): string | undefined { + return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStoragePath; } private doFlushWhenIdle(): void { @@ -246,21 +211,13 @@ export class NativeStorageService extends AbstractStorageService { ]); } - async logStorage(): Promise { - return logStorage( - this.globalStorage.items, - this.workspaceStorage ? this.workspaceStorage.items : new Map(), // Shared process storage does not has workspace storage - this.environmentService.globalStorageHome.fsPath, - this.workspaceStoragePath || ''); - } - async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { return; // no migration needed if running in memory } // Close workspace DB to be able to copy - await this.getStorage(StorageScope.WORKSPACE).close(); + await this.workspaceStorage?.close(); // Prepare new workspace storage folder const result = await this.prepareWorkspaceStorageFolder(toWorkspace); From 8dbc14946b3b85e450eab5adb43a9654b16ef70b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 08:27:47 +0100 Subject: [PATCH 094/176] debt - consistent event names in main --- src/vs/code/electron-main/protocol.ts | 4 +-- .../platform/menubar/electron-main/menubar.ts | 10 +++--- .../electron-main/nativeHostMainService.ts | 4 +-- .../url/electron-main/electronUrlListener.ts | 2 +- .../platform/windows/electron-main/window.ts | 32 +++++++++---------- .../platform/windows/electron-main/windows.ts | 16 +++++----- .../electron-main/windowsMainService.ts | 30 ++++++++--------- .../electron-main/windowsStateHandler.ts | 4 +-- .../windows/test/electron-main/window.test.ts | 8 ++--- .../platform/workspaces/common/workspaces.ts | 2 +- .../workspacesHistoryMainService.ts | 16 +++++----- .../electron-main/workspacesMainService.ts | 2 +- .../workspacesManagementMainService.ts | 16 +++++----- .../browser/parts/titlebar/menubarControl.ts | 16 +++++----- .../workspaces/browser/workspacesService.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- 16 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/vs/code/electron-main/protocol.ts b/src/vs/code/electron-main/protocol.ts index 20644f2c9b9..3e535155c76 100644 --- a/src/vs/code/electron-main/protocol.ts +++ b/src/vs/code/electron-main/protocol.ts @@ -48,10 +48,10 @@ export class FileProtocolHandler extends Disposable { } injectWindowsMainService(windowsMainService: IWindowsMainService): void { - this._register(windowsMainService.onWindowReady(window => { + this._register(windowsMainService.onDidSignalReadyWindow(window => { if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) { const disposables = new DisposableStore(); - disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose())); + disposables.add(Event.any(window.onDidClose, window.onDidDestroy)(() => disposables.dispose())); // Allow access to extension development path if (window.config.extensionDevelopmentPath) { diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 32f6b799eef..076f39011cb 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -168,9 +168,9 @@ export class Menubar { this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true); // // Listen to some events from window service to update menu - this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); - this.nativeHostMainService.onDidBlurWindow(() => this.onWindowFocusChange()); - this.nativeHostMainService.onDidFocusWindow(() => this.onWindowFocusChange()); + this.windowsMainService.onDidChangeWindowsCount(e => this.onDidChangeWindowsCount(e)); + this.nativeHostMainService.onDidBlurWindow(() => this.onDidChangeWindowFocus()); + this.nativeHostMainService.onDidFocusWindow(() => this.onDidChangeWindowFocus()); } private get currentEnableMenuBarMnemonics(): boolean { @@ -225,7 +225,7 @@ export class Menubar { } } - private onWindowsCountChanged(e: IWindowsCountChangedEvent): void { + private onDidChangeWindowsCount(e: IWindowsCountChangedEvent): void { if (!isMacintosh) { return; } @@ -237,7 +237,7 @@ export class Menubar { } } - private onWindowFocusChange(): void { + private onDidChangeWindowFocus(): void { if (!isMacintosh) { return; } diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 406fd62d878..918ca38c2cb 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -76,14 +76,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Events - readonly onDidOpenWindow = Event.map(this.windowsMainService.onWindowOpened, window => window.id); + readonly onDidOpenWindow = Event.map(this.windowsMainService.onDidOpenWindow, window => window.id); readonly onDidMaximizeWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidUnmaximizeWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidBlurWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onDidFocusWindow = Event.any( - Event.map(Event.filter(Event.map(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), + Event.map(Event.filter(Event.map(this.windowsMainService.onDidChangeWindowsCount, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) ); diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 50ca2ea249c..9ae6d13d2af 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -82,7 +82,7 @@ export class ElectronURLListener { if (isWindowReady) { this.flush(); } else { - Event.once(windowsMainService.onWindowReady)(this.flush, this, this.disposables); + Event.once(windowsMainService.onDidSignalReadyWindow)(this.flush, this, this.disposables); } } diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 7f42a6c6ae8..c437ab35c47 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -80,17 +80,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 - private readonly _onLoad = this._register(new Emitter()); - readonly onLoad = this._onLoad.event; + private readonly _onDidLoad = this._register(new Emitter()); + readonly onDidLoad = this._onDidLoad.event; - private readonly _onReady = this._register(new Emitter()); - readonly onReady = this._onReady.event; + private readonly _onDidSignalReady = this._register(new Emitter()); + readonly onDidSignalReady = this._onDidSignalReady.event; - private readonly _onClose = this._register(new Emitter()); - readonly onClose = this._onClose.event; + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; - private readonly _onDestroy = this._register(new Emitter()); - readonly onDestroy = this._onDestroy.event; + private readonly _onDidDestroy = this._register(new Emitter()); + readonly onDidDestroy = this._onDidDestroy.event; private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; @@ -367,7 +367,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Events - this._onReady.fire(); + this._onDidSignalReady.fire(); } ready(): Promise { @@ -395,8 +395,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { resolve(); } - const closeListener = this.onClose(() => handle()); - const loadListener = this.onLoad(() => handle()); + const closeListener = this.onDidClose(() => handle()); + const loadListener = this.onDidLoad(() => handle()); }); } @@ -409,7 +409,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Window close this._win.on('closed', () => { - this._onClose.fire(); + this._onDidClose.fire(); this.dispose(); }); @@ -538,7 +538,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._register(this.configurationService.onDidChangeConfiguration(() => this.onConfigurationUpdated())); // Handle Workspace events - this._register(this.workspacesManagementMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + this._register(this.workspacesManagementMainService.onDidDeleteUntitledWorkspace(e => this.onDidDeleteUntitledWorkspace(e))); // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; @@ -636,11 +636,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private destroyWindow(): void { - this._onDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event + this._onDidDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event this._win.destroy(); // make sure to destroy the window as it has crashed } - private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { + private onDidDeleteUntitledWorkspace(workspace: IWorkspaceIdentifier): void { // Make sure to update our workspace config if we detect that it // was deleted @@ -761,7 +761,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Event - this._onLoad.fire(); + this._onDidLoad.fire(); } async reload(cli?: NativeParsedArgs): Promise { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index c309ff263f4..89ade1c8667 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -62,10 +62,10 @@ export const enum WindowMode { export interface ICodeWindow extends IDisposable { - readonly onLoad: Event; - readonly onReady: Event; - readonly onClose: Event; - readonly onDestroy: Event; + readonly onDidLoad: Event; + readonly onDidSignalReady: Event; + readonly onDidClose: Event; + readonly onDidDestroy: Event; readonly whenClosedOrLoaded: Promise; @@ -132,11 +132,11 @@ export interface IWindowsMainService { readonly _serviceBrand: undefined; - readonly onWindowsCountChanged: Event; + readonly onDidChangeWindowsCount: Event; - readonly onWindowOpened: Event; - readonly onWindowReady: Event; - readonly onWindowDestroyed: Event; + readonly onDidOpenWindow: Event; + readonly onDidSignalReadyWindow: Event; + readonly onDidDestroyWindow: Event; open(openConfig: IOpenConfiguration): ICodeWindow[]; openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[]; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index c7625528cf4..c1244908aa5 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -118,17 +118,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private static readonly WINDOWS: ICodeWindow[] = []; - private readonly _onWindowOpened = this._register(new Emitter()); - readonly onWindowOpened = this._onWindowOpened.event; + private readonly _onDidOpenWindow = this._register(new Emitter()); + readonly onDidOpenWindow = this._onDidOpenWindow.event; - private readonly _onWindowReady = this._register(new Emitter()); - readonly onWindowReady = this._onWindowReady.event; + private readonly _onDidSignalReadyWindow = this._register(new Emitter()); + readonly onDidSignalReadyWindow = this._onDidSignalReadyWindow.event; - private readonly _onWindowDestroyed = this._register(new Emitter()); - readonly onWindowDestroyed = this._onWindowDestroyed.event; + private readonly _onDidDestroyWindow = this._register(new Emitter()); + readonly onDidDestroyWindow = this._onDidDestroyWindow.event; - private readonly _onWindowsCountChanged = this._register(new Emitter()); - readonly onWindowsCountChanged = this._onWindowsCountChanged.event; + private readonly _onDidChangeWindowsCount = this._register(new Emitter()); + readonly onDidChangeWindowsCount = this._onDidChangeWindowsCount.event; private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateService, this.lifecycleMainService, this.logService, this.configurationService)); @@ -155,7 +155,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private registerListeners(): void { // Signal a window is ready after having entered a workspace - this._register(this.workspacesManagementMainService.onWorkspaceEntered(event => this._onWindowReady.fire(event.window))); + this._register(this.workspacesManagementMainService.onDidEnterWorkspace(event => this._onDidSignalReadyWindow.fire(event.window))); } openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] { @@ -1191,15 +1191,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic WindowsMainService.WINDOWS.push(createdWindow); // Indicate new window via event - this._onWindowOpened.fire(createdWindow); + this._onDidOpenWindow.fire(createdWindow); // Indicate number change via event - this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() }); + this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() }); // Window Events - once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow)); - once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow)); - once(createdWindow.onDestroy)(() => this._onWindowDestroyed.fire(createdWindow)); + once(createdWindow.onDidSignalReady)(() => this._onDidSignalReadyWindow.fire(createdWindow)); + once(createdWindow.onDidClose)(() => this.onWindowClosed(createdWindow)); + once(createdWindow.onDidDestroy)(() => this._onDidDestroyWindow.fire(createdWindow)); createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); @@ -1264,7 +1264,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic WindowsMainService.WINDOWS.splice(index, 1); // Emit - this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() }); + this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() }); } getFocusedWindow(): ICodeWindow | undefined { diff --git a/src/vs/platform/windows/electron-main/windowsStateHandler.ts b/src/vs/platform/windows/electron-main/windowsStateHandler.ts index b0e50c85ff4..cc3e5457601 100644 --- a/src/vs/platform/windows/electron-main/windowsStateHandler.ts +++ b/src/vs/platform/windows/electron-main/windowsStateHandler.ts @@ -85,7 +85,7 @@ export class WindowsStateHandler extends Disposable { // Handle various lifecycle events around windows this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown()); - this.windowsMainService.onWindowsCountChanged(e => { + this.windowsMainService.onDidChangeWindowsCount(e => { if (e.newCount - e.oldCount > 0) { // clear last closed window state when a new window opens. this helps on macOS where // otherwise closing the last window, opening a new window and then quitting would @@ -95,7 +95,7 @@ export class WindowsStateHandler extends Disposable { }); // try to save state before destroy because close will not fire - this.windowsMainService.onWindowDestroyed(window => this.onBeforeWindowClose(window)); + this.windowsMainService.onDidDestroyWindow(window => this.onBeforeWindowClose(window)); } // Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS: diff --git a/src/vs/platform/windows/test/electron-main/window.test.ts b/src/vs/platform/windows/test/electron-main/window.test.ts index 94ae6eea848..b1aa09b7e13 100644 --- a/src/vs/platform/windows/test/electron-main/window.test.ts +++ b/src/vs/platform/windows/test/electron-main/window.test.ts @@ -32,10 +32,10 @@ suite('WindowsFinder', () => { function createTestCodeWindow(options: { lastFocusTime: number, openedFolderUri?: URI, openedWorkspace?: IWorkspaceIdentifier }): ICodeWindow { return new class implements ICodeWindow { - onLoad: Event = Event.None; - onReady: Event = Event.None; - onClose: Event = Event.None; - onDestroy: Event = Event.None; + onDidLoad: Event = Event.None; + onDidSignalReady: Event = Event.None; + onDidClose: Event = Event.None; + onDidDestroy: Event = Event.None; whenClosedOrLoaded: Promise = Promise.resolve(); id: number = -1; win: Electron.BrowserWindow = undefined!; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 897a204578e..8be3cea72d7 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -45,7 +45,7 @@ export interface IWorkspacesService { getWorkspaceIdentifier(workspacePath: URI): Promise; // Workspaces History - readonly onRecentlyOpenedChange: Event; + readonly onDidChangeRecentlyOpened: Event; addRecentlyOpened(recents: IRecent[]): Promise; removeRecentlyOpened(workspaces: URI[]): Promise; clearRecentlyOpened(): Promise; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index bd16a20bbb8..30b358c4ce8 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -30,7 +30,7 @@ export interface IWorkspacesHistoryMainService { readonly _serviceBrand: undefined; - readonly onRecentlyOpenedChange: CommonEvent; + readonly onDidChangeRecentlyOpened: CommonEvent; addRecentlyOpened(recents: IRecent[]): void; getRecentlyOpened(include?: ICodeWindow): IRecentlyOpened; @@ -57,8 +57,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa declare readonly _serviceBrand: undefined; - private readonly _onRecentlyOpenedChange = this._register(new Emitter()); - readonly onRecentlyOpenedChange: CommonEvent = this._onRecentlyOpenedChange.event; + private readonly _onDidChangeRecentlyOpened = this._register(new Emitter()); + readonly onDidChangeRecentlyOpened: CommonEvent = this._onDidChangeRecentlyOpened.event; private readonly macOSRecentDocumentsUpdater = this._register(new ThrottledDelayer(800)); @@ -80,7 +80,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList()); // Add to history when entering workspace - this._register(this.workspacesManagementMainService.onWorkspaceEntered(event => this.addRecentlyOpened([{ workspace: event.workspace }]))); + this._register(this.workspacesManagementMainService.onDidEnterWorkspace(event => this.addRecentlyOpened([{ workspace: event.workspace }]))); } private handleWindowsJumpList(): void { @@ -89,7 +89,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } this.updateWindowsJumpList(); - this._register(this.onRecentlyOpenedChange(() => this.updateWindowsJumpList())); + this._register(this.onDidChangeRecentlyOpened(() => this.updateWindowsJumpList())); } addRecentlyOpened(recentToAdd: IRecent[]): void { @@ -139,7 +139,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } this.saveRecentlyOpened({ workspaces, files }); - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); // Schedule update to recent documents on macOS dock if (isMacintosh) { @@ -165,7 +165,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa if (workspaces.length !== mru.workspaces.length || files.length !== mru.files.length) { this.saveRecentlyOpened({ files, workspaces }); - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); // Schedule update to recent documents on macOS dock if (isMacintosh) { @@ -238,7 +238,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa app.clearRecentDocuments(); // Event - this._onRecentlyOpenedChange.fire(); + this._onDidChangeRecentlyOpened.fire(); } getRecentlyOpened(include?: ICodeWindow): IRecentlyOpened { diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 5328a07dd5e..67a5e54db3f 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -50,7 +50,7 @@ export class WorkspacesMainService implements AddFirstParameterToFunctions { return this.workspacesHistoryMainService.getRecentlyOpened(this.windowsMainService.getWindowById(windowId)); diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index c9d22ba0af3..d738633f973 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -38,8 +38,8 @@ export interface IWorkspacesManagementMainService { readonly _serviceBrand: undefined; - readonly onUntitledWorkspaceDeleted: Event; - readonly onWorkspaceEntered: Event; + readonly onDidDeleteUntitledWorkspace: Event; + readonly onDidEnterWorkspace: Event; enterWorkspace(intoWindow: ICodeWindow, openedWindows: ICodeWindow[], path: URI): Promise; @@ -67,11 +67,11 @@ export class WorkspacesManagementMainService extends Disposable implements IWork private readonly untitledWorkspacesHome = this.environmentService.untitledWorkspacesHome; // local URI that contains all untitled workspaces - private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter()); - readonly onUntitledWorkspaceDeleted: Event = this._onUntitledWorkspaceDeleted.event; + private readonly _onDidDeleteUntitledWorkspace = this._register(new Emitter()); + readonly onDidDeleteUntitledWorkspace: Event = this._onDidDeleteUntitledWorkspace.event; - private readonly _onWorkspaceEntered = this._register(new Emitter()); - readonly onWorkspaceEntered: Event = this._onWorkspaceEntered.event; + private readonly _onDidEnterWorkspace = this._register(new Emitter()); + readonly onDidEnterWorkspace: Event = this._onDidEnterWorkspace.event; constructor( @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, @@ -198,7 +198,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork this.doDeleteUntitledWorkspaceSync(workspace); // Event - this._onUntitledWorkspaceDeleted.fire(workspace); + this._onDidDeleteUntitledWorkspace.fire(workspace); } async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { @@ -260,7 +260,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } // Emit as event - this._onWorkspaceEntered.fire({ window, workspace: result.workspace }); + this._onDidEnterWorkspace.fire({ window, workspace: result.workspace }); return result; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index f3587a51a51..96f49a70965 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -127,13 +127,13 @@ export abstract class MenubarControl extends Disposable { this.updateService.onStateChange(() => this.onUpdateStateChange()); // Listen for changes in recently opened menu - this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); + this._register(this.workspacesService.onDidChangeRecentlyOpened(() => { this.onDidChangeRecentlyOpened(); })); // Listen to keybindings change this._register(this.keybindingService.onDidUpdateKeybindings(() => this.updateMenubar())); // Update recent menu items on formatter registration - this._register(this.labelService.onDidChangeFormatters(() => { this.onRecentlyOpenedChange(); })); + this._register(this.labelService.onDidChangeFormatters(() => { this.onDidChangeRecentlyOpened(); })); } protected updateMenubar(): void { @@ -189,7 +189,7 @@ export abstract class MenubarControl extends Disposable { protected onDidChangeWindowFocus(hasFocus: boolean): void { // When we regain focus, update the recent menu items if (hasFocus) { - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); } } @@ -205,7 +205,7 @@ export abstract class MenubarControl extends Disposable { // Since we try not update when hidden, we should // try to update the recently opened list on visibility changes if (event.affectsConfiguration('window.menuBarVisibility')) { - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); } } @@ -213,7 +213,7 @@ export abstract class MenubarControl extends Disposable { return isMacintosh && isNative ? false : getMenuBarVisibility(this.configurationService) === 'hidden'; } - protected onRecentlyOpenedChange(): void { + protected onDidChangeRecentlyOpened(): void { // Do not update recently opened when the menubar is hidden #108712 if (!this.menubarHidden) { @@ -543,7 +543,7 @@ export class CustomMenubarControl extends MenubarControl { private onDidVisibilityChange(visible: boolean): void { this.visible = visible; - this.onRecentlyOpenedChange(); + this.onDidChangeRecentlyOpened(); this._onVisibilityChange.fire(visible); } @@ -765,12 +765,12 @@ export class CustomMenubarControl extends MenubarControl { super.onUpdateStateChange(); } - protected onRecentlyOpenedChange(): void { + protected onDidChangeRecentlyOpened(): void { if (!this.visible) { return; } - super.onRecentlyOpenedChange(); + super.onDidChangeRecentlyOpened(); } protected onUpdateKeybindings(): void { diff --git a/src/vs/workbench/services/workspaces/browser/workspacesService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts index 6d6b1d77002..28d39cec2f6 100644 --- a/src/vs/workbench/services/workspaces/browser/workspacesService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -26,7 +26,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS declare readonly _serviceBrand: undefined; private readonly _onRecentlyOpenedChange = this._register(new Emitter()); - readonly onRecentlyOpenedChange = this._onRecentlyOpenedChange.event; + readonly onDidChangeRecentlyOpened = this._onRecentlyOpenedChange.event; constructor( @IStorageService private readonly storageService: IStorageService, diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index e2d267e6704..aa394668a9b 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1413,7 +1413,7 @@ export class TestTextFileEditorModelManager extends TextFileEditorModelManager { export class TestWorkspacesService implements IWorkspacesService { _serviceBrand: undefined; - onRecentlyOpenedChange = Event.None; + onDidChangeRecentlyOpened = Event.None; async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { throw new Error('Method not implemented.'); } async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { } From f606206cc8f18650fd0378600c4ee30be2c951c9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 08:33:34 +0100 Subject: [PATCH 095/176] debt - consistently refer to main services --- src/vs/code/electron-main/app.ts | 52 +++++++++---------- src/vs/code/electron-main/main.ts | 40 +++++++------- .../backup/electron-main/backupMainService.ts | 6 +-- .../platform/driver/electron-main/driver.ts | 4 +- .../issue/electron-main/issueMainService.ts | 8 +-- .../platform/menubar/electron-main/menubar.ts | 6 +-- .../electron-main/nativeHostMainService.ts | 24 ++++----- .../electron-main/sharedProcess.ts | 10 ++-- .../electron-main/abstractUpdateService.ts | 6 +-- .../electron-main/updateService.darwin.ts | 4 +- .../electron-main/updateService.linux.ts | 4 +- .../electron-main/updateService.snap.ts | 8 +-- .../electron-main/updateService.win32.ts | 4 +- .../url/electron-main/electronUrlListener.ts | 4 +- .../electron-main/windowsMainService.ts | 12 ++--- .../workspacesHistoryMainService.ts | 4 +- .../workspacesManagementMainService.ts | 10 ++-- .../workspacesManagementMainService.test.ts | 6 +-- 18 files changed, 106 insertions(+), 106 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c9aa9a1384e..1a378fd011f 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -102,7 +102,7 @@ export class CodeApplication extends Disposable { private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly mainInstantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService, @@ -168,7 +168,7 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); app.on('remote-get-current-web-contents', event => { - if (this.environmentService.args.driver) { + if (this.environmentMainService.args.driver) { return; // the driver needs access to web contents } @@ -190,7 +190,7 @@ export class CodeApplication extends Disposable { } const srcUri = uri.fsPath.toLowerCase(); - const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase(); + const rootUri = URI.file(this.environmentMainService.appRoot).fsPath.toLowerCase(); return srcUri.startsWith(rootUri + sep); }; @@ -255,7 +255,7 @@ export class CodeApplication extends Disposable { runningTimeout = setTimeout(() => { this.windowsMainService?.open({ context: OpenContext.DOCK /* can also be opening from finder while app is running */, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: macOpenFileURIs, gotoLineMode: false, preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ @@ -328,7 +328,7 @@ export class CodeApplication extends Disposable { args = window.config; env = { ...process.env, ...window.config.userEnv }; } else { - args = this.environmentService.args; + args = this.environmentMainService.args; env = process.env; } @@ -376,7 +376,7 @@ export class CodeApplication extends Disposable { } } - if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) { + if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentMainService.cachedLanguagesPath, !isLinux)) { return undefined; } @@ -404,8 +404,8 @@ export class CodeApplication extends Disposable { async startup(): Promise { this.logService.debug('Starting VS Code'); - this.logService.debug(`from: ${this.environmentService.appRoot}`); - this.logService.debug('args:', this.environmentService.args); + this.logService.debug(`from: ${this.environmentMainService.appRoot}`); + this.logService.debug('args:', this.environmentMainService.args); // Make sure we associate the program with the app user model id // This will help Windows to associate the running program with @@ -448,10 +448,10 @@ export class CodeApplication extends Disposable { const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady); // Create driver - if (this.environmentService.driverHandle) { - const server = await serveDriver(mainProcessElectronServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService); + if (this.environmentMainService.driverHandle) { + const server = await serveDriver(mainProcessElectronServer, this.environmentMainService.driverHandle, this.environmentMainService, appInstantiationService); - this.logService.info('Driver started at:', this.environmentService.driverHandle); + this.logService.info('Driver started at:', this.environmentMainService.driverHandle); this._register(server); } @@ -468,7 +468,7 @@ export class CodeApplication extends Disposable { appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); // Tracing: Stop tracing after windows are ready if enabled - if (this.environmentService.args.trace) { + if (this.environmentMainService.args.trace) { appInstantiationService.invokeFunction(accessor => this.stopTracingEventually(accessor, windows)); } } @@ -509,7 +509,7 @@ export class CodeApplication extends Disposable { // Spawn shared process after the first window has opened and 3s have passed this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { - sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env)); + sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentMainService.args, process.env)); }, 3000)).schedule(); }); @@ -583,18 +583,18 @@ export class CodeApplication extends Disposable { services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); // Backups - const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); + const backupMainService = new BackupMainService(this.environmentMainService, this.configurationService, this.logService); services.set(IBackupMainService, backupMainService); // URL handling services.set(IURLService, new SyncDescriptor(NativeURLService)); // Telemetry - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { + if (!this.environmentMainService.isExtensionDevelopment && !this.environmentMainService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); - const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); - const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; + const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentMainService.installSourcePath); + const piiPaths = [this.environmentMainService.appRoot, this.environmentMainService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true }; services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); @@ -697,7 +697,7 @@ export class CodeApplication extends Disposable { const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; const pendingProtocolLinksToHandle = [ // Windows/Linux: protocol handler invokes CLI with --open-url - ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], + ...this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : [], // macOS: open-url events ...((global).getOpenUrls() || []) as string[] @@ -734,7 +734,7 @@ export class CodeApplication extends Disposable { // or open new windows. The URL handler will be invoked from // protocol invocations outside of VSCode. const app = this; - const environmentService = this.environmentService; + const environmentService = this.environmentMainService; urlService.registerHandler({ async handleURL(uri: URI, options?: IOpenURLOptions): Promise { @@ -789,10 +789,10 @@ export class CodeApplication extends Disposable { urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel)); // Watch Electron URLs and forward them to the UrlService - this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentService)); + this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentMainService)); // Open our first window - const args = this.environmentService.args; + const args = this.environmentMainService.args; const macOpenFiles: string[] = (global).macOpenFiles; const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; @@ -862,7 +862,7 @@ export class CodeApplication extends Disposable { mnemonicButtonLabel(localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")), ], cancelId: 1, - message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentService), product.nameShort), + message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentMainService), product.nameShort), detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"), noLink: true }); @@ -958,13 +958,13 @@ export class CodeApplication extends Disposable { } // Start to fetch shell environment (if needed) after window has opened - resolveShellEnv(this.logService, this.environmentService.args, process.env); + resolveShellEnv(this.logService, this.environmentMainService.args, process.env); // If enable-crash-reporter argv is undefined then this is a fresh start, // based on telemetry.enableCrashreporter settings, generate a UUID which // will be used as crash reporter id and also update the json file. try { - const argvContent = await this.fileService.readFile(this.environmentService.argvResource); + const argvContent = await this.fileService.readFile(this.environmentMainService.argvResource); const argvString = argvContent.value.toString(); const argvJSON = JSON.parse(stripComments(argvString)); if (argvJSON['enable-crash-reporter'] === undefined) { @@ -982,7 +982,7 @@ export class CodeApplication extends Disposable { ]; const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n')); - await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString)); + await this.fileService.writeFile(this.environmentMainService.argvResource, VSBuffer.fromString(newArgvString)); } } catch (error) { this.logService.error(error); @@ -1002,7 +1002,7 @@ export class CodeApplication extends Disposable { recordingStopped = true; // only once - const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); + const path = await contentTracing.stopRecording(joinPath(this.environmentMainService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); if (!timeout) { dialogMainService.showMessageBox({ diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 0ccc3092bd4..7cbdd078f09 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -181,9 +181,9 @@ class CodeMain { return [new InstantiationService(services, true), instanceEnvironment, environmentService, configurationService, stateService, bufferLogService]; } - private patchEnvironment(environmentService: IEnvironmentMainService): IProcessEnvironment { + private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { const instanceEnvironment: IProcessEnvironment = { - VSCODE_IPC_HOOK: environmentService.mainIPCHandle + VSCODE_IPC_HOOK: environmentMainService.mainIPCHandle }; ['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => { @@ -198,16 +198,16 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise { + private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise { // Environment service (paths) const environmentServiceInitialization = Promise.all([ - environmentService.extensionsPath, - environmentService.nodeCachedDataDir, - environmentService.logsPath, - environmentService.globalStorageHome.fsPath, - environmentService.workspaceStorageHome.fsPath, - environmentService.backupHome + environmentMainService.extensionsPath, + environmentMainService.nodeCachedDataDir, + environmentMainService.logsPath, + environmentMainService.globalStorageHome.fsPath, + environmentMainService.workspaceStorageHome.fsPath, + environmentMainService.backupHome ].map(path => path ? promises.mkdir(path, { recursive: true }) : undefined)); // Configuration service @@ -219,14 +219,14 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { + private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { // Try to setup a server for running. If that succeeds it means // we are the first instance to startup. Otherwise it is likely // that another instance is already running. let mainProcessNodeIpcServer: NodeIPCServer; try { - mainProcessNodeIpcServer = await nodeIPCServe(environmentService.mainIPCHandle); + mainProcessNodeIpcServer = await nodeIPCServe(environmentMainService.mainIPCHandle); once(lifecycleMainService.onWillShutdown)(() => mainProcessNodeIpcServer.dispose()); } catch (error) { @@ -235,7 +235,7 @@ class CodeMain { if (error.code !== 'EADDRINUSE') { // Show a dialog for errors that can be resolved by the user - this.handleStartupDataDirError(environmentService, error); + this.handleStartupDataDirError(environmentMainService, error); // Any other runtime error is just printed to the console throw error; @@ -244,7 +244,7 @@ class CodeMain { // there's a running instance, let's connect to it let client: NodeIPCClient; try { - client = await nodeIPCConnect(environmentService.mainIPCHandle, 'main'); + client = await nodeIPCConnect(environmentMainService.mainIPCHandle, 'main'); } catch (error) { // Handle unexpected connection errors by showing a dialog to the user @@ -263,18 +263,18 @@ class CodeMain { // let's delete it, since we can't connect to it and then // retry the whole thing try { - unlinkSync(environmentService.mainIPCHandle); + unlinkSync(environmentMainService.mainIPCHandle); } catch (error) { logService.warn('Could not delete obsolete instance handle', error); throw error; } - return this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, false); + return this.doStartup(args, logService, environmentMainService, lifecycleMainService, instantiationService, false); } // Tests from CLI require to be the only instance currently - if (environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break) { + if (environmentMainService.extensionTestsLocationURI && !environmentMainService.debugExtensionHost.break) { const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; logService.error(msg); client.dispose(); @@ -344,9 +344,9 @@ class CodeMain { return mainProcessNodeIpcServer; } - private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { + private handleStartupDataDirError(environmentMainService: IEnvironmentMainService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { - const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentService)); + const directories = coalesce([environmentMainService.userDataPath, environmentMainService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentMainService)); this.showStartupWarningDialog( localize('startupDataDirError', "Unable to write program user data."), @@ -369,9 +369,9 @@ class CodeMain { }); } - private async windowsAllowSetForegroundWindow(launchService: ILaunchMainService, logService: ILogService): Promise { + private async windowsAllowSetForegroundWindow(launchMainService: ILaunchMainService, logService: ILogService): Promise { if (isWindows) { - const processId = await launchService.getMainProcessId(); + const processId = await launchMainService.getMainProcessId(); logService.trace('Sending some foreground love to the running instance:', processId); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index ac5e6cd2eb1..ac5bb248927 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -38,12 +38,12 @@ export class BackupMainService implements IBackupMainService { private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) }; constructor( - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService ) { - this.backupHome = environmentService.backupHome; - this.workspacesJsonPath = environmentService.backupWorkspacesPath; + this.backupHome = environmentMainService.backupHome; + this.workspacesJsonPath = environmentMainService.backupWorkspacesPath; } async initialize(): Promise { diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index 62d7cb313e6..d4501169882 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -207,10 +207,10 @@ export class Driver implements IDriver, IWindowDriverRegistry { export async function serve( windowServer: IPCServer, handle: string, - environmentService: IEnvironmentMainService, + environmentMainService: IEnvironmentMainService, instantiationService: IInstantiationService ): Promise { - const verbose = environmentService.driverVerbose; + const verbose = environmentMainService.driverVerbose; const driver = instantiationService.createInstance(Driver, windowServer, { verbose }); const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index f6efb42c42d..f79bb21926c 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -38,7 +38,7 @@ export class IssueMainService implements ICommonIssueService { constructor( private machineId: string, private userEnv: IProcessEnvironment, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILaunchMainService private readonly launchMainService: ILaunchMainService, @ILogService private readonly logService: ILogService, @IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService, @@ -271,7 +271,7 @@ export class IssueMainService implements ICommonIssueService { this._processExplorerWindow.setMenuBarVisibility(false); const windowConfiguration = { - appRoot: this.environmentService.appRoot, + appRoot: this.environmentMainService.appRoot, windowId: this._processExplorerWindow.id, userEnv: this.userEnv, machineId: this.machineId, @@ -397,13 +397,13 @@ export class IssueMainService implements ICommonIssueService { } const windowConfiguration = { - appRoot: this.environmentService.appRoot, + appRoot: this.environmentMainService.appRoot, windowId: this._issueWindow.id, machineId: this.machineId, userEnv: this.userEnv, data, features, - disableExtensions: this.environmentService.disableExtensions, + disableExtensions: this.environmentMainService.disableExtensions, os: { type: os.type(), arch: os.arch(), diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 076f39011cb..87c5b80744b 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -67,7 +67,7 @@ export class Menubar { @IUpdateService private readonly updateService: IUpdateService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService, @IStateService private readonly stateService: IStateService, @@ -499,7 +499,7 @@ export class Menubar { const openInNewWindow = this.isOptionClick(event); const success = this.windowsMainService.open({ context: OpenContext.MENU, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: [openable], forceNewWindow: openInNewWindow, gotoLineMode: false @@ -716,7 +716,7 @@ export class Menubar { if (activeWindow) { this.logService.trace('menubar#runActionInRenderer', invocation); - if (isMacintosh && !this.environmentService.isBuilt && !activeWindow.isReady) { + if (isMacintosh && !this.environmentMainService.isBuilt && !activeWindow.isReady) { if ((invocation.type === 'commandId' && invocation.commandId === 'workbench.action.toggleDevTools') || (invocation.type !== 'commandId' && invocation.userSettingsLabel === 'alt+cmd+i')) { // prevent this action from running twice on macOS (https://github.com/microsoft/vscode/issues/62719) // we already register a keybinding in bootstrap-window.js for opening developer tools in case something diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 918ca38c2cb..929af851987 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -47,7 +47,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ILogService private readonly logService: ILogService ) { @@ -140,7 +140,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain context: OpenContext.API, contextWindowId: windowId, urisToOpen: toOpen, - cli: this.environmentService.args, + cli: this.environmentMainService.args, forceNewWindow: options.forceNewWindow, forceReuseWindow: options.forceReuseWindow, preferNewWindow: options.preferNewWindow, @@ -293,7 +293,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain this.windowsMainService.open({ context: OpenContext.DIALOG, contextWindowId: windowId, - cli: this.environmentService.args, + cli: this.environmentMainService.args, urisToOpen: openable, forceNewWindow: options.forceNewWindow }); @@ -386,7 +386,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const promptOptions = { name: product.nameLong.replace('-', ''), - icns: (isMacintosh && this.environmentService.isBuilt) ? join(dirname(this.environmentService.appRoot), `${product.nameShort}.icns`) : undefined + icns: (isMacintosh && this.environmentMainService.isBuilt) ? join(dirname(this.environmentMainService.appRoot), `${product.nameShort}.icns`) : undefined }; sudoPrompt.exec(sudoCommand.join(' '), promptOptions, (error: string, stdout: string, stderr: string) => { @@ -412,28 +412,28 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // Windows if (isWindows) { - if (this.environmentService.isBuilt) { + if (this.environmentMainService.isBuilt) { return join(dirname(process.execPath), 'bin', `${product.applicationName}.cmd`); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.bat'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.bat'); } // Linux if (isLinux) { - if (this.environmentService.isBuilt) { + if (this.environmentMainService.isBuilt) { return join(dirname(process.execPath), 'bin', `${product.applicationName}`); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.sh'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.sh'); } // macOS - if (this.environmentService.isBuilt) { - return join(this.environmentService.appRoot, 'bin', 'code'); + if (this.environmentMainService.isBuilt) { + return join(this.environmentMainService.appRoot, 'bin', 'code'); } - return join(this.environmentService.appRoot, 'scripts', 'code-cli.sh'); + return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.sh'); } async getOSStatistics(): Promise { @@ -505,7 +505,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region macOS Touchbar async newWindowTab(): Promise { - this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true }); + this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentMainService.args, forceNewTabbedWindow: true, forceEmpty: true }); } async showPreviousWindowTab(): Promise { diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index c0aad5ffa10..966a8830bf6 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -26,7 +26,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { constructor( private readonly machineId: string, private userEnv: NodeJS.ProcessEnv, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService @@ -164,11 +164,11 @@ export class SharedProcess extends Disposable implements ISharedProcess { const config: ISharedProcessConfiguration = { machineId: this.machineId, windowId: this.window.id, - appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, - backupWorkspacesPath: this.environmentService.backupWorkspacesPath, + appRoot: this.environmentMainService.appRoot, + nodeCachedDataDir: this.environmentMainService.nodeCachedDataDir, + backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, userEnv: this.userEnv, - args: this.environmentService.args, + args: this.environmentMainService.args, logLevel: this.logService.getLevel() }; diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index f15dec1b8a7..2d96ce768ad 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -46,7 +46,7 @@ export abstract class AbstractUpdateService implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService protected configurationService: IConfigurationService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IRequestService protected requestService: IRequestService, @ILogService protected logService: ILogService, ) { } @@ -57,11 +57,11 @@ export abstract class AbstractUpdateService implements IUpdateService { * https://github.com/microsoft/vscode/issues/89784 */ initialize(): void { - if (!this.environmentService.isBuilt) { + if (!this.environmentMainService.isBuilt) { return; // updates are never enabled when running out of sources } - if (this.environmentService.disableUpdates) { + if (this.environmentMainService.disableUpdates) { this.logService.info('update#ctor - updates are disabled by the environment'); return; } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index ddfa1ee4a5f..8d67a459903 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -32,11 +32,11 @@ export class DarwinUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } initialize(): void { diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 91565b13954..d00055b590f 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -23,12 +23,12 @@ export class LinuxUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } protected buildUpdateFeedUrl(quality: string): string { diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 1712182c365..e0d6010d06a 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -36,10 +36,10 @@ abstract class AbstractUpdateService2 implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @ILogService protected logService: ILogService, ) { - if (environmentService.disableUpdates) { + if (environmentMainService.disableUpdates) { this.logService.info('update#ctor - updates are disabled'); return; } @@ -140,11 +140,11 @@ export class SnapUpdateService extends AbstractUpdateService2 { private snap: string, private snapRevision: string, @ILifecycleMainService lifecycleMainService: ILifecycleMainService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @ILogService logService: ILogService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { - super(lifecycleMainService, environmentService, logService); + super(lifecycleMainService, environmentMainService, logService); const watcher = watch(path.dirname(this.snap)); const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index c92a0931dcc..3fcc37e91d4 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -63,13 +63,13 @@ export class Win32UpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentMainService environmentService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @IFileService private readonly fileService: IFileService, @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService ) { - super(lifecycleMainService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentMainService, requestService, logService); } initialize(): void { diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 9ae6d13d2af..e281cff7d5e 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -43,7 +43,7 @@ export class ElectronURLListener { initialUrisToHandle: { uri: URI, url: string }[], private readonly urlService: IURLService, windowsMainService: IWindowsMainService, - environmentService: IEnvironmentMainService + environmentMainService: IEnvironmentMainService ) { // the initial set of URIs we need to handle once the window is ready @@ -51,7 +51,7 @@ export class ElectronURLListener { // Windows: install as protocol handler if (isWindows) { - const windowsParameters = environmentService.isBuilt ? [] : [`"${environmentService.appRoot}"`]; + const windowsParameters = environmentMainService.isBuilt ? [] : [`"${environmentMainService.appRoot}"`]; windowsParameters.push('--open-url', '--'); app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, windowsParameters); } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index c1244908aa5..db2b237d45e 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -137,7 +137,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -159,7 +159,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] { - let cli = this.environmentService.args; + let cli = this.environmentMainService.args; const remote = options?.remoteAuthority; if (cli && (cli.remote !== remote)) { cli = { ...cli, remote }; @@ -670,7 +670,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic buttons: [localize('ok', "OK")], message: uri.scheme === Schemas.file ? localize('pathNotExistTitle', "Path does not exist") : localize('uriInvalidTitle', "URI can not be opened"), detail: uri.scheme === Schemas.file ? - localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentService)) : + localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentMainService)) : localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString()), noLink: true }; @@ -1135,9 +1135,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Build `INativeWindowConfiguration` from config and options const configuration = { ...options.cli } as INativeWindowConfiguration; - configuration.appRoot = this.environmentService.appRoot; + configuration.appRoot = this.environmentMainService.appRoot; configuration.machineId = this.machineId; - configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; + configuration.nodeCachedDataDir = this.environmentMainService.nodeCachedDataDir; configuration.mainPid = process.pid; configuration.execPath = process.execPath; configuration.userEnv = { ...this.initialUserEnv, ...options.userEnv }; @@ -1157,7 +1157,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupInfo) { - configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); + configuration.backupPath = join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder); } let window: ICodeWindow | undefined; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 30b358c4ce8..22cec5f95ad 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -66,7 +66,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @IStateService private readonly stateService: IStateService, @ILogService private readonly logService: ILogService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService ) { super(); @@ -402,7 +402,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } // Workspace: Untitled - if (extUriBiasedIgnorePathCase.isEqualOrParent(workspace.configPath, this.environmentService.userHome)) { + if (extUriBiasedIgnorePathCase.isEqualOrParent(workspace.configPath, this.environmentMainService.userHome)) { return { title: localize('untitledWorkspace', "Untitled (Workspace)"), description: '' }; } diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index d738633f973..cb397ad60e8 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -65,7 +65,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork declare readonly _serviceBrand: undefined; - private readonly untitledWorkspacesHome = this.environmentService.untitledWorkspacesHome; // local URI that contains all untitled workspaces + private readonly untitledWorkspacesHome = this.environmentMainService.untitledWorkspacesHome; // local URI that contains all untitled workspaces private readonly _onDidDeleteUntitledWorkspace = this._register(new Emitter()); readonly onDidDeleteUntitledWorkspace: Event = this._onDidDeleteUntitledWorkspace.event; @@ -74,7 +74,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork readonly onDidEnterWorkspace: Event = this._onDidEnterWorkspace.event; constructor( - @IEnvironmentMainService private readonly environmentService: IEnvironmentMainService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILogService private readonly logService: ILogService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService @@ -101,7 +101,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } private isWorkspacePath(uri: URI): boolean { - return isUntitledWorkspace(uri, this.environmentService) || hasWorkspaceFileExtension(uri); + return isUntitledWorkspace(uri, this.environmentMainService) || hasWorkspaceFileExtension(uri); } private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null { @@ -186,7 +186,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean { - return isUntitledWorkspace(workspace.configPath, this.environmentService); + return isUntitledWorkspace(workspace.configPath, this.environmentMainService); } deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { @@ -213,7 +213,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork rimrafSync(dirname(configPath)); // Mark Workspace Storage to be deleted - const workspaceStoragePath = join(this.environmentService.workspaceStorageHome.fsPath, workspace.id); + const workspaceStoragePath = join(this.environmentMainService.workspaceStorageHome.fsPath, workspace.id); if (existsSync(workspaceStoragePath)) { writeFileSync(join(workspaceStoragePath, 'obsolete'), ''); } diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts index fc4b803c38a..55ad732b8b3 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts @@ -81,14 +81,14 @@ suite('WorkspacesManagementMainService', () => { let testDir: string; let untitledWorkspacesHomePath: string; - let environmentService: EnvironmentMainService; + let environmentMainService: EnvironmentMainService; let service: WorkspacesManagementMainService; setup(async () => { testDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesmanagementmainservice'); untitledWorkspacesHomePath = path.join(testDir, 'Workspaces'); - environmentService = new class TestEnvironmentService extends EnvironmentMainService { + environmentMainService = new class TestEnvironmentService extends EnvironmentMainService { constructor() { super(parseArgs(process.argv, OPTIONS)); } @@ -97,7 +97,7 @@ suite('WorkspacesManagementMainService', () => { } }; - service = new WorkspacesManagementMainService(environmentService, new NullLogService(), new TestBackupMainService(), new TestDialogMainService()); + service = new WorkspacesManagementMainService(environmentMainService, new NullLogService(), new TestBackupMainService(), new TestDialogMainService()); return fs.promises.mkdir(untitledWorkspacesHomePath, { recursive: true }); }); From 72137a822335573fc47950d7cfd451374c309e52 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 16 Feb 2021 09:10:55 +0100 Subject: [PATCH 096/176] fixes #116663 --- src/vs/workbench/contrib/debug/browser/breakpointsView.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index a257c4d65bb..eca16433e3b 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -688,9 +688,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { // Need to react with a timeout on the blur event due to possible concurent splices #56443 setTimeout(() => { - if (!template.breakpoint.name) { - wrapUp(true); - } + wrapUp(!!inputBox.value); }); })); From 55393229982b2ef32d68b766fc69f06585be596b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 09:20:44 +0100 Subject: [PATCH 097/176] "e; is ", re #115391 --- src/vs/base/browser/markdownRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 2feb2ab7f0c..5378627e57a 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -385,7 +385,7 @@ export function renderMarkdownAsPlaintext(markdown: IMarkdownString) { } const unescapeInfo = new Map([ - ['"', ':'], + ['"', '"'], ['&', '&'], [''', '\''], ['<', '<'], From 5662aecf24197fe1343c471932bf07a460e7a0ec Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 16 Feb 2021 09:28:13 +0100 Subject: [PATCH 098/176] actionBar: adopt respectOrienationForPreviousAndNextKey option in lists and notebook --- src/vs/base/browser/ui/actionbar/actionbar.ts | 5 +++-- src/vs/base/browser/ui/toolbar/toolbar.ts | 2 ++ src/vs/workbench/browser/parts/views/treeView.ts | 3 ++- .../workbench/contrib/debug/browser/breakpointsView.ts | 6 +++--- src/vs/workbench/contrib/debug/browser/callStackView.ts | 7 ++++--- .../contrib/extensions/browser/extensionsList.ts | 3 ++- .../contrib/files/browser/views/openEditorsView.ts | 4 ++-- .../contrib/markers/browser/markersTreeViewer.ts | 3 ++- .../contrib/notebook/browser/diff/diffComponents.ts | 3 ++- .../notebook/browser/diff/notebookTextDiffList.ts | 3 ++- .../notebook/browser/view/renderers/cellRenderer.ts | 9 ++++++--- .../contrib/preferences/browser/settingsTree.ts | 1 + .../contrib/preferences/browser/settingsWidgets.ts | 2 +- src/vs/workbench/contrib/remote/browser/tunnelView.ts | 3 ++- .../contrib/scm/browser/scmRepositoryRenderer.ts | 2 +- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 5 +++-- .../contrib/search/browser/searchResultsView.ts | 6 +++--- .../contrib/testing/browser/testingExplorerView.ts | 3 ++- 18 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 8a912e1255c..83135785546 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -35,6 +35,7 @@ export interface IActionBarOptions { readonly triggerKeys?: ActionTrigger; readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; + // Pass true here when the up and down keys should not be eaten up by the ActionBar. For example, when an ActionBar is in the list. readonly respectOrientationForPreviousAndNextKey?: boolean; } @@ -145,9 +146,9 @@ export class ActionBar extends Disposable implements IActionRunner { const focusedItem = typeof this.focusedItem === 'number' ? this.viewItems[this.focusedItem] : undefined; if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) { - eventHandled = this.focusPrevious() && this.viewItems.length > 1; + eventHandled = this.focusPrevious(); } else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) { - eventHandled = this.focusNext() && this.viewItems.length > 1; + eventHandled = this.focusNext(); } else if (event.equals(KeyCode.Escape) && this.cancelHasListener) { this._onDidCancel.fire(); } else if (event.equals(KeyCode.Tab) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) { diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index c1348a93ddc..3b91ffa5573 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -28,6 +28,7 @@ export interface IToolBarOptions { anchorAlignmentProvider?: () => AnchorAlignment; renderDropdownAsChildElement?: boolean; moreIcon?: CSSIcon; + readonly respectOrientationForPreviousAndNextKey?: boolean; } /** @@ -63,6 +64,7 @@ export class ToolBar extends Disposable { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, + respectOrientationForPreviousAndNextKey: options.respectOrientationForPreviousAndNextKey, actionViewItemProvider: (action: IAction) => { if (action.id === ToggleMenuAction.ID) { this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9c49ffdcca2..f8961757153 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -873,7 +873,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer { return action.createActionViewItem(); } return new ExtensionActionViewItem(null, action, actionOptions); - } + }, + respectOrientationForPreviousAndNextKey: true }); actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index e54534fd378..9936ef5a754 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -537,7 +537,7 @@ class EditorGroupRenderer implements IListRenderer action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined + actionViewItemProvider: (action: IAction) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined, + respectOrientationForPreviousAndNextKey: true })); this.icon = dom.append(parent, dom.$('')); this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 235dd429996..24f880fbabf 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -150,7 +150,8 @@ class PropertyHeader extends Disposable { } return undefined; - } + }, + respectOrientationForPreviousAndNextKey: true }); this._register(this._toolbar); this._toolbar.context = { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index ce2bc9c6ec2..e25db2f37b0 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -188,7 +188,8 @@ export class CellDiffSideBySideRenderer implements IListRenderer extends Dispo rowElement.setAttribute('tabindex', item.selected ? '0' : '-1'); rowElement.classList.toggle('selected', item.selected); - const actionBar = new ActionBar(rowElement); + const actionBar = new ActionBar(rowElement, { respectOrientationForPreviousAndNextKey: true }); this.listDisposables.add(actionBar); actionBar.push(this.getActionsForItem(item, idx), { icon: true, label: true }); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index daa3fba23f9..02b75df99e9 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -225,7 +225,8 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action) - : undefined + : undefined, + respectOrientationForPreviousAndNextKey: true }); return { label, actionBar, icon }; From ac916cbb2dfeabf39aaec1feb196f0ed23b236f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 09:32:26 +0100 Subject: [PATCH 099/176] don't assert rpc strictness yet --- .../src/singlefolder-tests/notebook.document.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index 3a2c081f1d5..34a8218e3a1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -33,7 +33,7 @@ suite('Notebook Document', function () { const disposables: vscode.Disposable[] = []; suiteTeardown(async function () { - utils.assertNoRpc(); + // utils.assertNoRpc(); await utils.revertAllDirty(); await utils.closeAllEditors(); utils.disposeAll(disposables); From f829a7dfd7eae502eb020fbb7f636b7a3017a654 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 09:45:37 +0100 Subject: [PATCH 100/176] workaround for https://github.com/microsoft/vscode/issues/116751 --- src/vs/workbench/api/common/extHostTypes.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index e7cd5a11828..9bc85fa2ace 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2863,12 +2863,13 @@ export class NotebookCellRange { } constructor(start: number, end: number) { - if (start < 0) { - throw illegalArgument('start must be positive'); - } - if (end < start) { - throw illegalArgument('end cannot be smaller than start'); - } + // todo@rebornix + // if (start < 0) { + // throw illegalArgument('start must be positive'); + // } + // if (end < start) { + // throw illegalArgument('end cannot be smaller than start'); + // } this._start = start; this._end = end; } From a7470e5094bb8c8d11893643535eebd6676a4d9e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 09:55:35 +0100 Subject: [PATCH 101/176] storage - let service own lifecycle in main and implement storage warming when window loads --- src/vs/base/parts/storage/common/storage.ts | 11 +++- .../electron-main/lifecycleMainService.ts | 53 ++++++++++++++----- .../storage/electron-main/storageMain.ts | 31 +++-------- .../electron-main/storageMainService.ts | 52 ++++++++++++++++-- .../electron-main/storageMainService.test.ts | 13 +++-- .../platform/windows/electron-main/window.ts | 10 ++-- .../platform/windows/electron-main/windows.ts | 6 ++- .../electron-main/windowsMainService.ts | 4 +- .../electron-main/windowsStateHandler.ts | 42 +++++++-------- .../{window.test.ts => windowsFinder.test.ts} | 4 +- 10 files changed, 145 insertions(+), 81 deletions(-) rename src/vs/platform/windows/test/electron-main/{window.test.ts => windowsFinder.test.ts} (97%) diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index c2f5ff5a5cd..66023e7d651 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -89,6 +89,8 @@ export class Storage extends Disposable implements IStorage { private pendingDeletes = new Set(); private pendingInserts = new Map(); + private pendingClose: Promise | undefined = undefined; + private readonly whenFlushedCallbacks: Function[] = []; constructor( @@ -256,10 +258,15 @@ export class Storage extends Disposable implements IStorage { } async close(): Promise { - if (this.state === StorageState.Closed) { - return; // return if already closed + if (!this.pendingClose) { + this.pendingClose = this.doClose(); } + return this.pendingClose; + } + + private async doClose(): Promise { + // Update state this.state = StorageState.Closed; diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 29a7082b742..6d9a34dba09 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -11,9 +11,10 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Promises, Barrier, timeout } from 'vs/base/common/async'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const ILifecycleMainService = createDecorator('lifecycleMainService'); @@ -24,6 +25,11 @@ export const enum UnloadReason { LOAD = 4 } +export interface IWindowLoadEvent { + window: ICodeWindow; + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined; +} + export interface IWindowUnloadEvent { window: ICodeWindow; reason: UnloadReason; @@ -72,16 +78,27 @@ export interface ILifecycleMainService { readonly onWillShutdown: Event; /** - * An event that fires before a window closes. This event is fired after any veto has been dealt - * with so that listeners know for sure that the window will close without veto. + * An event that fires when a window is loading. This can either be a window opening for the + * first time or a window reloading or changing to another URL. */ - readonly onBeforeWindowClose: Event; + readonly onWillLoadWindow: Event; /** * An event that fires before a window is about to unload. Listeners can veto this event to prevent * the window from unloading. */ - readonly onBeforeWindowUnload: Event; + readonly onBeforeUnloadWindow: Event; + + /** + * An event that fires before a window closes. This event is fired after any veto has been dealt + * with so that listeners know for sure that the window will close without veto. + */ + readonly onBeforeCloseWindow: Event; + + /** + * Make a `ICodeWindow` known to the lifecycle main service. + */ + registerWindow(window: ICodeWindow): void; /** * Reload a window. All lifecycle event handlers are triggered. @@ -147,11 +164,14 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private readonly _onWillShutdown = this._register(new Emitter()); readonly onWillShutdown = this._onWillShutdown.event; - private readonly _onBeforeWindowClose = this._register(new Emitter()); - readonly onBeforeWindowClose = this._onBeforeWindowClose.event; + private readonly _onWillLoadWindow = this._register(new Emitter()); + readonly onWillLoadWindow = this._onWillLoadWindow.event; - private readonly _onBeforeWindowUnload = this._register(new Emitter()); - readonly onBeforeWindowUnload = this._onBeforeWindowUnload.event; + private readonly _onBeforeCloseWindow = this._register(new Emitter()); + readonly onBeforeCloseWindow = this._onBeforeCloseWindow.event; + + private readonly _onBeforeUnloadWindow = this._register(new Emitter()); + readonly onBeforeUnloadWindow = this._onBeforeUnloadWindow.event; private _quitRequested = false; get quitRequested(): boolean { return this._quitRequested; } @@ -314,10 +334,14 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe } registerWindow(window: ICodeWindow): void { + const windowListeners = new DisposableStore(); // track window count this.windowCounter++; + // Window Will Load + windowListeners.add(window.onWillLoad(e => this._onWillLoadWindow.fire({ window, workspace: e.workspace }))); + // Window Before Closing: Main -> Renderer window.win.on('close', e => { @@ -341,9 +365,9 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe this.windowToCloseRequest.add(windowId); - // Fire onBeforeWindowClose before actually closing - this.logService.trace(`Lifecycle#onBeforeWindowClose.fire() - window ID ${windowId}`); - this._onBeforeWindowClose.fire(window); + // Fire onBeforeCloseWindow before actually closing + this.logService.trace(`Lifecycle#onBeforeCloseWindow.fire() - window ID ${windowId}`); + this._onBeforeCloseWindow.fire(window); // No veto, close window now window.close(); @@ -357,6 +381,9 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // update window count this.windowCounter--; + // clear window listeners + windowListeners.dispose(); + // if there are no more code windows opened, fire the onWillShutdown event, unless // we are on macOS where it is perfectly fine to close the last window and // the application continues running (unless quit was actually requested) @@ -452,7 +479,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private onBeforeUnloadWindowInMain(window: ICodeWindow, reason: UnloadReason): Promise { const vetos: (boolean | Promise)[] = []; - this._onBeforeWindowUnload.fire({ + this._onBeforeUnloadWindow.fire({ reason, window, veto(value) { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 9bb316f0ef3..0b4cec2c83d 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -6,7 +6,7 @@ import { promises } from 'fs'; import { exists, writeFile } from 'vs/base/node/pfs'; import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; @@ -16,13 +16,12 @@ import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { generateUuid } from 'vs/base/common/uuid'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; /** * Provides access to global and workspace storage from the * electron-main side that is the owner of all storage connections. */ -export interface IStorageMain { +export interface IStorageMain extends IDisposable { /** * Emitted whenever data is updated or deleted. @@ -101,25 +100,9 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { private initializePromise: Promise | undefined = undefined; constructor( - protected readonly logService: ILogService, - private readonly lifecycleMainService: ILifecycleMainService + protected readonly logService: ILogService ) { super(); - - this.registerListeners(); - } - - private registerListeners(): void { - - // Lifecycle: Warmup (in parallel to window open) - (async () => { - await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - - this.initialize(); - })(); - - // Lifecycle: Shutdown - this.lifecycleMainService.onWillShutdown(e => e.join(this.close())); } initialize(): Promise { @@ -202,10 +185,9 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { constructor( logService: ILogService, - private readonly environmentService: IEnvironmentService, - lifecycleMainService: ILifecycleMainService + private readonly environmentService: IEnvironmentService ) { - super(logService, lifecycleMainService); + super(logService); } protected async doInitialize(): Promise { @@ -266,9 +248,8 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier, logService: ILogService, private readonly environmentService: IEnvironmentService, - lifecycleMainService: ILifecycleMainService ) { - super(logService, lifecycleMainService); + super(logService); } protected async doInitialize(): Promise { diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index f9b408032b9..e5a1e45f59d 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -4,11 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { once } from 'vs/base/common/functional'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IWindowSettings } from 'vs/platform/windows/common/windows'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; export const IStorageMainService = createDecorator('storageMainService'); @@ -28,15 +31,54 @@ export interface IStorageMainService { workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain; } -export class StorageMainService implements IStorageMainService { +export class StorageMainService extends Disposable implements IStorageMainService { declare readonly _serviceBrand: undefined; + private enableMainWorkspaceStorage = this.configurationService.getValue('window')?.enableExperimentalMainProcessWorkspaceStorage; + constructor( @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Global Storage: Warmup when any window opens + (async () => { + await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + + this.globalStorage.initialize(); + })(); + + // Workspace Storage: Warmup when related window with workspace loads + if (this.enableMainWorkspaceStorage) { + this._register(this.lifecycleMainService.onWillLoadWindow(async e => { + if (e.workspace) { + await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); + + this.workspaceStorage(e.workspace).initialize(); + } + })); + } + + // All Storage: Close when shutting down + this._register(this.lifecycleMainService.onWillShutdown(e => { + + // Global Storage + e.join(this.globalStorage.close()); + + // Workspace Storage(s) + for (const [, storage] of this.mapWorkspaceToStorage) { + e.join(storage.close()); + } + })); } //#region Global Storage @@ -50,7 +92,7 @@ export class StorageMainService implements IStorageMainService { this.logService.trace(`StorageMainService: creating global storage`); - const globalStorage = new GlobalStorageMain(this.logService, this.environmentService, this.lifecycleMainService); + const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); once(globalStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed global storage`); @@ -67,7 +109,7 @@ export class StorageMainService implements IStorageMainService { private readonly mapWorkspaceToStorage = new Map(); private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { - const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService, this.lifecycleMainService); + const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService); return workspaceStorage; } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index cdde6a0aa04..3bae7df6d67 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -23,6 +23,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { Promises } from 'vs/base/common/async'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; flakySuite('StorageMainService (native)', function () { @@ -68,14 +69,16 @@ flakySuite('StorageMainService (native)', function () { await Promises.settled(joiners); } - onBeforeWindowClose = Event.None; - onBeforeWindowUnload = Event.None; + onWillLoadWindow = Event.None; + onBeforeCloseWindow = Event.None; + onBeforeUnloadWindow = Event.None; wasRestarted = false; quitRequested = false; phase = LifecycleMainPhase.Ready; + registerWindow(window: ICodeWindow): void { } async reload(window: ICodeWindow, cli?: NativeParsedArgs): Promise { } async unload(window: ICodeWindow, reason: UnloadReason): Promise { return true; } relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; }): void { } @@ -169,7 +172,7 @@ flakySuite('StorageMainService (native)', function () { test('basics (global)', function () { return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService()); + const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService(), new TestConfigurationService()); return storageMainService.globalStorage; }, true); @@ -179,7 +182,7 @@ flakySuite('StorageMainService (native)', function () { const workspace = { id: generateUuid() }; return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService()); + const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService(), new TestConfigurationService()); return storageMainService.workspaceStorage(workspace); }, false); @@ -188,7 +191,7 @@ flakySuite('StorageMainService (native)', function () { test('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); const workspace = { id: generateUuid() }; - const storageMainService = new StorageMainService(new NullLogService(), environmentService, lifecycleMainService); + const storageMainService = new StorageMainService(new NullLogService(), environmentService, lifecycleMainService, new TestConfigurationService()); let storage = storageMainService.workspaceStorage(workspace); let didCloseStorage = false; diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index c437ab35c47..632a2db22cb 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -19,7 +19,7 @@ import product from 'vs/platform/product/common/product'; import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { browserCodeLoadingCacheStrategy, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { defaultWindowState, ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; +import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; @@ -80,8 +80,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 - private readonly _onDidLoad = this._register(new Emitter()); - readonly onDidLoad = this._onDidLoad.event; + private readonly _onWillLoad = this._register(new Emitter()); + readonly onWillLoad = this._onWillLoad.event; private readonly _onDidSignalReady = this._register(new Emitter()); readonly onDidSignalReady = this._onDidSignalReady.event; @@ -396,7 +396,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } const closeListener = this.onDidClose(() => handle()); - const loadListener = this.onDidLoad(() => handle()); + const loadListener = this.onWillLoad(() => handle()); }); } @@ -761,7 +761,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Event - this._onDidLoad.fire(); + this._onWillLoad.fire({ workspace: configuration.workspace }); } async reload(cli?: NativeParsedArgs): Promise { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 89ade1c8667..9a97d621b5c 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -60,9 +60,13 @@ export const enum WindowMode { Fullscreen } +export interface ILoadEvent { + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined; +} + export interface ICodeWindow extends IDisposable { - readonly onDidLoad: Event; + readonly onWillLoad: Event; readonly onDidSignalReady: Event; readonly onDidClose: Event; readonly onDidDestroy: Event; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index db2b237d45e..de3b4bf960b 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -14,7 +14,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow } from 'vs/platform/windows/electron-main/window'; import { BrowserWindow, MessageBoxOptions, WebContents } from 'electron'; -import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { ILifecycleMainService, UnloadReason, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest, IPathsToWaitFor, INativeWindowConfiguration, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; @@ -1204,7 +1204,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); // Lifecycle - (this.lifecycleMainService as LifecycleMainService).registerWindow(createdWindow); + this.lifecycleMainService.registerWindow(createdWindow); } // Existing window diff --git a/src/vs/platform/windows/electron-main/windowsStateHandler.ts b/src/vs/platform/windows/electron-main/windowsStateHandler.ts index cc3e5457601..75c96494b80 100644 --- a/src/vs/platform/windows/electron-main/windowsStateHandler.ts +++ b/src/vs/platform/windows/electron-main/windowsStateHandler.ts @@ -83,7 +83,7 @@ export class WindowsStateHandler extends Disposable { }); // Handle various lifecycle events around windows - this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); + this.lifecycleMainService.onBeforeCloseWindow(window => this.onBeforeCloseWindow(window)); this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown()); this.windowsMainService.onDidChangeWindowsCount(e => { if (e.newCount - e.oldCount > 0) { @@ -95,16 +95,16 @@ export class WindowsStateHandler extends Disposable { }); // try to save state before destroy because close will not fire - this.windowsMainService.onDidDestroyWindow(window => this.onBeforeWindowClose(window)); + this.windowsMainService.onDidDestroyWindow(window => this.onBeforeCloseWindow(window)); } - // Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS: + // Note that onBeforeShutdown() and onBeforeCloseWindow() are fired in different order depending on the OS: // - macOS: since the app will not quit when closing the last window, you will always first get - // the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window + // the onBeforeShutdown() event followed by N onBeforeCloseWindow() events for each window // - other: on other OS, closing the last window will quit the app so the order depends on the - // user interaction: closing the last window will first trigger onBeforeWindowClose() + // user interaction: closing the last window will first trigger onBeforeCloseWindow() // and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown() - // and then onBeforeWindowClose(). + // and then onBeforeCloseWindow(). // // Here is the behavior on different OS depending on action taken (Electron 1.7.x): // @@ -113,27 +113,27 @@ export class WindowsStateHandler extends Disposable { // - close(1): close one window via the window close button // - closeAll: close all windows via the taskbar command // - onBeforeShutdown(N): number of windows reported in this event handler - // - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler + // - onBeforeCloseWindow(N, M): number of windows reported and quitRequested boolean in this event handler // // macOS - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) // - quit(0): onBeforeShutdown(0) - // - close(1): onBeforeWindowClose(1, false) + // - close(1): onBeforeCloseWindow(1, false) // // Windows - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) - // - close(1): onBeforeWindowClose(2, false)[not last window] - // - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window] - // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) + // - close(1): onBeforeCloseWindow(2, false)[not last window] + // - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window] + // - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0) // // Linux - // - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true) - // - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true) - // - close(1): onBeforeWindowClose(2, false)[not last window] - // - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window] - // - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0) + // - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true) + // - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true) + // - close(1): onBeforeCloseWindow(2, false)[not last window] + // - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window] + // - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0) // private onBeforeShutdown(): void { this.shuttingDown = true; @@ -185,7 +185,7 @@ export class WindowsStateHandler extends Disposable { } // See note on #onBeforeShutdown() for details how these events are flowing - private onBeforeWindowClose(window: ICodeWindow): void { + private onBeforeCloseWindow(window: ICodeWindow): void { if (this.lifecycleMainService.quitRequested) { return; // during quit, many windows close in parallel so let it be handled in the before-quit handler } diff --git a/src/vs/platform/windows/test/electron-main/window.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts similarity index 97% rename from src/vs/platform/windows/test/electron-main/window.test.ts rename to src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index b1aa09b7e13..634fa2c810d 100644 --- a/src/vs/platform/windows/test/electron-main/window.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { join } from 'vs/base/common/path'; import { findWindowOnFile } from 'vs/platform/windows/electron-main/windowsFinder'; -import { ICodeWindow, IWindowState } from 'vs/platform/windows/electron-main/windows'; +import { ICodeWindow, ILoadEvent, IWindowState } from 'vs/platform/windows/electron-main/windows'; import { IWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -32,7 +32,7 @@ suite('WindowsFinder', () => { function createTestCodeWindow(options: { lastFocusTime: number, openedFolderUri?: URI, openedWorkspace?: IWorkspaceIdentifier }): ICodeWindow { return new class implements ICodeWindow { - onDidLoad: Event = Event.None; + onWillLoad: Event = Event.None; onDidSignalReady: Event = Event.None; onDidClose: Event = Event.None; onDidDestroy: Event = Event.None; From 1bb2e0a9babcb2a960a1a479e21d2849d83c82e0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 10:47:36 +0100 Subject: [PATCH 102/176] storage - use inMemory storage for tests --- .../storage/electron-main/storageMain.ts | 21 +++-- .../electron-main/storageMainService.ts | 20 +++-- .../electron-main/storageMainService.test.ts | 82 ++++--------------- 3 files changed, 47 insertions(+), 76 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 0b4cec2c83d..9d1f5c1b6ce 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -17,6 +17,15 @@ import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStora import { generateUuid } from 'vs/base/common/uuid'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +export interface IStorageMainOptions { + + /** + * If enabled, storage will not persist to disk + * but into memory. + */ + useInMemoryStorage?: boolean; +} + /** * Provides access to global and workspace storage from the * electron-main side that is the owner of all storage connections. @@ -184,6 +193,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { private static readonly STORAGE_NAME = 'state.vscdb'; constructor( + private readonly options: IStorageMainOptions, logService: ILogService, private readonly environmentService: IEnvironmentService ) { @@ -192,8 +202,8 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { protected async doInitialize(): Promise { let storagePath: string; - if (!!this.environmentService.extensionTestsLocationURI) { - storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; // no storage during extension tests! + if (this.options.useInMemoryStorage) { + storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; } else { storagePath = join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); } @@ -246,8 +256,9 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai constructor( private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier, + private readonly options: IStorageMainOptions, logService: ILogService, - private readonly environmentService: IEnvironmentService, + private readonly environmentService: IEnvironmentService ) { super(logService); } @@ -273,8 +284,8 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai private async prepareWorkspaceStorageFolder(): Promise<{ storageFilePath: string, wasCreated: boolean }> { - // Return early with in-memory when running extension tests - if (!!this.environmentService.extensionTestsLocationURI) { + // Return early if using inMemory storage + if (this.options.useInMemoryStorage) { return { storageFilePath: SQLiteStorageDatabase.IN_MEMORY_PATH, wasCreated: true }; } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index e5a1e45f59d..6b50b94256a 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -10,7 +10,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; -import { GlobalStorageMain, IStorageMain, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { GlobalStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IWindowSettings } from 'vs/platform/windows/common/windows'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -35,8 +35,6 @@ export class StorageMainService extends Disposable implements IStorageMainServic declare readonly _serviceBrand: undefined; - private enableMainWorkspaceStorage = this.configurationService.getValue('window')?.enableExperimentalMainProcessWorkspaceStorage; - constructor( @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -48,6 +46,16 @@ export class StorageMainService extends Disposable implements IStorageMainServic this.registerListeners(); } + protected getStorageOptions(): IStorageMainOptions { + return { + useInMemoryStorage: !!this.environmentService.extensionTestsLocationURI // no storage during extension tests! + }; + } + + protected enableMainWorkspaceStorage(): boolean { + return !!(this.configurationService.getValue('window')?.enableExperimentalMainProcessWorkspaceStorage); + } + private registerListeners(): void { // Global Storage: Warmup when any window opens @@ -58,7 +66,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic })(); // Workspace Storage: Warmup when related window with workspace loads - if (this.enableMainWorkspaceStorage) { + if (this.enableMainWorkspaceStorage()) { this._register(this.lifecycleMainService.onWillLoadWindow(async e => { if (e.workspace) { await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); @@ -92,7 +100,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic this.logService.trace(`StorageMainService: creating global storage`); - const globalStorage = new GlobalStorageMain(this.logService, this.environmentService); + const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.environmentService); once(globalStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed global storage`); @@ -109,7 +117,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic private readonly mapWorkspaceToStorage = new Map(); private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { - const workspaceStorage = new WorkspaceStorageMain(workspace, this.logService, this.environmentService); + const workspaceStorage = new WorkspaceStorageMain(workspace, this.getStorageOptions(), this.logService, this.environmentService); return workspaceStorage; } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 3bae7df6d67..0461769cede 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -3,46 +3,34 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { promises } from 'fs'; -import { tmpdir } from 'os'; import { notStrictEqual, strictEqual } from 'assert'; -import { URI } from 'vs/base/common/uri'; -import { rimraf } from 'vs/base/node/pfs'; -import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { NullLogService } from 'vs/platform/log/common/log'; import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, instanceStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platform/storage/electron-main/storageMain'; import { generateUuid } from 'vs/base/common/uuid'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; -import { joinPath } from 'vs/base/common/resources'; import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { Emitter, Event } from 'vs/base/common/event'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { Promises } from 'vs/base/common/async'; +import { Promises, timeout } from 'vs/base/common/async'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -flakySuite('StorageMainService (native)', function () { +suite('StorageMainService (native)', function () { - class StorageTestEnvironmentService extends NativeEnvironmentService { + class TestStorageMainService extends StorageMainService { - constructor(private globalStorageFolderPath: URI, private workspaceStorageFolderPath: URI, private _extensionsPath: string) { - super(parseArgs(process.argv, OPTIONS)); + protected getStorageOptions(): IStorageMainOptions { + return { + useInMemoryStorage: true + }; } - get globalStorageHome(): URI { - return this.globalStorageFolderPath; - } - - get workspaceStorageHome(): URI { - return this.workspaceStorageFolderPath; - } - - get extensionsPath(): string { - return this._extensionsPath; + protected enableMainWorkspaceStorage(): boolean { + return true; } } @@ -87,28 +75,7 @@ flakySuite('StorageMainService (native)', function () { async when(phase: LifecycleMainPhase): Promise { } } - let testDir: string; - let environmentService: StorageTestEnvironmentService; - - setup(async () => { - testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storageMainService'); - - await promises.mkdir(testDir, { recursive: true }); - - const globalStorageFolder = joinPath(URI.file(testDir), 'globalStorage'); - const workspaceStorageFolder = joinPath(URI.file(testDir), 'workspaceStorage'); - - await promises.mkdir(globalStorageFolder.fsPath, { recursive: true }); - - environmentService = new StorageTestEnvironmentService(globalStorageFolder, workspaceStorageFolder, testDir); - }); - - teardown(() => { - return rimraf(testDir); - }); - - async function testStorage(storageFn: () => IStorageMain, isGlobal: boolean): Promise { - let storage = storageFn(); + async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { // Telemetry: added after init if (isGlobal) { @@ -155,43 +122,27 @@ flakySuite('StorageMainService (native)', function () { // Close await storage.close(); - strictEqual(storageDidClose, true); - storageChangeListener.dispose(); storageCloseListener.dispose(); - - // Reopen - storage = storageFn(); - await storage.initialize(); - - strictEqual(storage.getNumber('barNumber'), 55); - strictEqual(storage.getBoolean('barBoolean'), true); - - await storage.close(); } test('basics (global)', function () { - return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService(), new TestConfigurationService()); + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); - return storageMainService.globalStorage; - }, true); + return testStorage(storageMainService.globalStorage, true); }); test('basics (workspace)', function () { const workspace = { id: generateUuid() }; + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); - return testStorage(() => { - const storageMainService = new StorageMainService(new NullLogService(), environmentService, new StorageTestLifecycleMainService(), new TestConfigurationService()); - - return storageMainService.workspaceStorage(workspace); - }, false); + return testStorage(storageMainService.workspaceStorage(workspace), false); }); test('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); const workspace = { id: generateUuid() }; - const storageMainService = new StorageMainService(new NullLogService(), environmentService, lifecycleMainService, new TestConfigurationService()); + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), lifecycleMainService, new TestConfigurationService()); let storage = storageMainService.workspaceStorage(workspace); let didCloseStorage = false; @@ -203,6 +154,7 @@ flakySuite('StorageMainService (native)', function () { await storage.initialize(); + await timeout(0); await lifecycleMainService.fireOnWillShutdown(); strictEqual(didCloseStorage, true); From 266e475931645a25cd1601a5dd3b0914911d95ea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 11:09:23 +0100 Subject: [PATCH 103/176] before removing cell documents capture its API objects, after inserting cell documents capture its API objects, fixes https://github.com/microsoft/vscode/issues/116711 --- .../api/common/extHostNotebookDocument.ts | 8 +-- .../test/browser/api/extHostNotebook.test.ts | 50 ++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 3e9e9e41a85..e9e715885c4 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -18,13 +18,13 @@ import * as vscode from 'vscode'; class RawContentChangeEvent { - constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { } + constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { } static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData { return Object.freeze({ start: event.start, deletedCount: event.deletedCount, - deletedItems: event.deletedItems.map(data => data.cell), + deletedItems: event.deletedItems, items: event.items.map(data => data.cell) }); } @@ -252,12 +252,14 @@ export class ExtHostNotebookDocument extends Disposable { this._cellDisposableMapping.delete(this._cells[j].handle); } + const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells); const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); for (let cell of deletedItems) { removedCellDocuments.push(cell.uri); + changeEvent.deletedItems.push(cell.cell); } - contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells)); + contentChangeEvents.push(changeEvent); }); this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 7619a7b337b..a85f77ba805 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -21,6 +21,7 @@ import { nullExtensionDescription } from 'vs/workbench/services/extensions/commo import { isEqual } from 'vs/base/common/resources'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { generateUuid } from 'vs/base/common/uuid'; +import { Event } from 'vs/base/common/event'; suite('NotebookCell#Document', function () { @@ -33,9 +34,11 @@ suite('NotebookCell#Document', function () { const notebookUri = URI.parse('test:///notebook.file'); const disposables = new DisposableStore(); - setup(async function () { + teardown(function () { disposables.clear(); + }); + setup(async function () { rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock() { $registerCommand() { } @@ -305,4 +308,49 @@ suite('NotebookCell#Document', function () { assert.strictEqual(notebook.notebookDocument.cells.length, 3); assert.strictEqual(second.index, 2); }); + + test('ERR MISSING extHostDocument for notebook cell: #116711', async function () { + + const p = Event.toPromise(extHostNotebooks.onDidChangeNotebookCells); + + // DON'T call this, make sure the cell-documents have not been created yet + // assert.strictEqual(notebook.notebookDocument.cells.length, 2); + + extHostNotebooks.$acceptModelChanged(notebook.uri, { + versionId: 100, + rawEvents: [{ + kind: NotebookCellsChangeType.ModelChange, + changes: [[0, 2, [{ + handle: 3, + uri: CellUri.generate(notebookUri, 3), + source: ['### Heading'], + eol: '\n', + language: 'markdown', + cellKind: CellKind.Markdown, + outputs: [], + }, { + handle: 4, + uri: CellUri.generate(notebookUri, 4), + source: ['console.log("aaa")', 'console.log("bbb")'], + eol: '\n', + language: 'javascript', + cellKind: CellKind.Code, + outputs: [], + }]]] + }] + }, false); + + assert.strictEqual(notebook.notebookDocument.cells.length, 2); + + const event = await p; + + assert.strictEqual(event.document === notebook.notebookDocument, true); + assert.strictEqual(event.changes.length, 1); + assert.strictEqual(event.changes[0].deletedCount, 2); + assert.strictEqual(event.changes[0].deletedItems[0].document.isClosed, true); + assert.strictEqual(event.changes[0].deletedItems[1].document.isClosed, true); + assert.strictEqual(event.changes[0].items.length, 2); + assert.strictEqual(event.changes[0].items[0].document.isClosed, false); + assert.strictEqual(event.changes[0].items[1].document.isClosed, false); + }); }); From 5e74ad5430203eadf89fd8e99695f4ebdb0e62ad Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 11:12:08 +0100 Subject: [PATCH 104/176] storage - fix unused storage variable in tests --- .../storage/test/electron-main/storageMainService.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 0461769cede..3aa295edca7 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -122,6 +122,8 @@ suite('StorageMainService (native)', function () { // Close await storage.close(); + strictEqual(storageDidClose, true); + storageChangeListener.dispose(); storageCloseListener.dispose(); } From 3dab064342ec56fbe80532ec32cae5709608b160 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Tue, 16 Feb 2021 11:25:51 +0100 Subject: [PATCH 105/176] Move component governance to compile stage --- build/azure-pipelines/darwin/product-build-darwin.yml | 4 ---- build/azure-pipelines/linux/product-build-alpine.yml | 4 ---- build/azure-pipelines/linux/product-build-linux.yml | 4 ---- build/azure-pipelines/product-compile.yml | 4 ++++ build/azure-pipelines/win32/product-build-win32.yml | 4 ---- 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 1f3cba946bd..3bbbbb461e1 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -388,7 +388,3 @@ steps: displayName: Upload configuration (for Bing settings search) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false')) continueOnError: true - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index 7e3aea8d231..4a1b8a2c64a 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -133,7 +133,3 @@ steps: artifact: vscode-server-linux-alpine-web displayName: Publish web server archive condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 1d26cee26f4..d0c8e0894e7 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -287,7 +287,3 @@ steps: artifactName: "snap-$(VSCODE_ARCH)" targetPath: .build/linux/snap-tarball condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 7855cde1663..4f2024a6209 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -137,3 +137,7 @@ steps: targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz artifactName: Compilation displayName: Publish compilation artifact + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + continueOnError: true diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 7076ebf13b0..5ca1f825865 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -322,7 +322,3 @@ steps: artifact: vscode-server-win32-$(VSCODE_ARCH)-web displayName: Publish web server archive condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - continueOnError: true From 58e22bc5226e156f7ec356650547871dd4477646 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 11:27:26 +0100 Subject: [PATCH 106/176] window - type win as null if disposed --- src/vs/code/electron-main/auth.ts | 4 ++-- .../electron-main/extensionHostDebugIpc.ts | 11 ++++++--- .../platform/driver/electron-main/driver.ts | 4 ++-- .../launch/electron-main/launchMainService.ts | 4 +++- .../electron-main/lifecycleMainService.ts | 6 +++-- .../electron-main/nativeHostMainService.ts | 24 +++++++++---------- .../electron-main/webviewMainService.ts | 2 +- .../platform/windows/electron-main/window.ts | 22 ++++++++--------- .../platform/windows/electron-main/windows.ts | 2 +- .../electron-main/windowsMainService.ts | 8 ++++--- .../test/electron-main/windowsFinder.test.ts | 2 +- 11 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index b9669f983c7..70df692cf81 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -201,7 +201,7 @@ export class ProxyAuthHandler extends Disposable { const proxyAuthResponseHandler = async (event: ElectronEvent, channel: string, reply: Credentials & { remember: boolean } | undefined /* canceled */) => { if (channel === payload.replyChannel) { this.logService.trace(`auth#doResolveProxyCredentials - exit - received credentials from window ${window.id}`); - window.win.webContents.off('ipc-message', proxyAuthResponseHandler); + window.win?.webContents.off('ipc-message', proxyAuthResponseHandler); // We got credentials from the window if (reply) { @@ -229,7 +229,7 @@ export class ProxyAuthHandler extends Disposable { } }; - window.win.webContents.on('ipc-message', proxyAuthResponseHandler); + window.win?.webContents.on('ipc-message', proxyAuthResponseHandler); }); // Remember credentials for the session in case diff --git a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts index f0ac255cb2a..9d37b27980d 100644 --- a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts +++ b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts @@ -43,7 +43,12 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens return {}; } - const debug = codeWindow.win.webContents.debugger; + const win = codeWindow.win; + if (!win) { + return {}; + } + + const debug = win.webContents.debugger; let listeners = debug.isAttached() ? Infinity : 0; const server = createServer(listener => { @@ -61,7 +66,7 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) => writeMessage(({ method, params, sessionId })); - codeWindow.win.on('close', () => { + win.on('close', () => { debug.removeListener('message', onMessage); listener.end(); closed = true; @@ -103,7 +108,7 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens }); await new Promise(r => server.listen(0, r)); - codeWindow.win.on('close', () => server.close()); + win.on('close', () => server.close()); return { rendererDebugPort: (server.address() as AddressInfo).port }; } diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index d4501169882..f2f76df80c3 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -62,7 +62,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { await this.whenUnfrozen(windowId); const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error('Invalid window'); } const webContents = window.win.webContents; @@ -101,7 +101,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { } const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error('Invalid window'); } const webContents = window.win.webContents; diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 0ec1d10331c..c613f990407 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -21,6 +21,7 @@ import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launch' import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; import { CancellationToken } from 'vs/base/common/cancellation'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { assertIsDefined } from 'vs/base/common/types'; export const ID = 'launchMainService'; export const ILaunchMainService = createDecorator(ID); @@ -293,8 +294,9 @@ export class LaunchMainService implements ILaunchMainService { private codeWindowToInfo(window: ICodeWindow): IWindowInfo { const folderURIs = this.getFolderURIs(window); + const win = assertIsDefined(window.win); - return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority); + return this.browserWindowToInfo(win, folderURIs, window.remoteAuthority); } private browserWindowToInfo(window: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo { diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 6d9a34dba09..82f80545895 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -15,6 +15,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Promises, Barrier, timeout } from 'vs/base/common/async'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { assertIsDefined } from 'vs/base/common/types'; export const ILifecycleMainService = createDecorator('lifecycleMainService'); @@ -343,7 +344,8 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe windowListeners.add(window.onWillLoad(e => this._onWillLoadWindow.fire({ window, workspace: e.workspace }))); // Window Before Closing: Main -> Renderer - window.win.on('close', e => { + const win = assertIsDefined(window.win); + win.on('close', e => { // The window already acknowledged to be closed const windowId = window.id; @@ -375,7 +377,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe }); // Window After Closing - window.win.on('closed', () => { + win.on('closed', () => { this.logService.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`); // update window count diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 929af851987..ba9d54e54fd 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -105,7 +105,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return windows.map(window => ({ id: window.id, workspace: window.openedWorkspace, - title: window.win.getTitle(), + title: window.win?.getTitle() ?? '', filename: window.getRepresentedFilename(), dirty: window.isDocumentEdited() })); @@ -176,7 +176,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async isMaximized(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { return window.win.isMaximized(); } @@ -185,21 +185,21 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async maximizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.maximize(); } } async unmaximizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.unmaximize(); } } async minimizeWindow(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.minimize(); } } @@ -217,7 +217,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { const [windowWidth, windowHeight] = window.win.getSize(); const [minWindowWidth, minWindowHeight] = window.win.getMinimumSize(); const [newMinWindowWidth, newMinWindowHeight] = [width ?? minWindowWidth, height ?? minWindowHeight]; @@ -250,7 +250,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain private toBrowserWindow(windowId: number | undefined): BrowserWindow | undefined { const window = this.windowById(windowId); - if (window) { + if (window?.win) { return window.win; } @@ -563,7 +563,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async closeWindowById(currentWindowId: number | undefined, targetWindowId?: number | undefined): Promise { const window = this.windowById(targetWindowId); - if (window) { + if (window?.win) { return window.win.close(); } } @@ -573,7 +573,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // If the user selected to exit from an extension development host window, do not quit, but just // close the window unless this is the last window that is opened. const window = this.windowsMainService.getLastActiveWindow(); - if (window?.isExtensionDevelopmentHost && this.windowsMainService.getWindowCount() > 1) { + if (window?.isExtensionDevelopmentHost && this.windowsMainService.getWindowCount() > 1 && window.win) { window.win.close(); } @@ -609,14 +609,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async openDevTools(windowId: number | undefined, options?: OpenDevToolsOptions): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { window.win.webContents.openDevTools(options); } } async toggleDevTools(windowId: number | undefined): Promise { const window = this.windowById(windowId); - if (window) { + if (window?.win) { const contents = window.win.webContents; contents.toggleDevTools(); } @@ -624,7 +624,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async sendInputEvent(windowId: number | undefined, event: MouseInputEvent): Promise { const window = this.windowById(windowId); - if (window && (event.type === 'mouseDown' || event.type === 'mouseUp')) { + if (window?.win && (event.type === 'mouseDown' || event.type === 'mouseUp')) { window.win.webContents.sendInputEvent(event); } } diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index f569607a424..dd3984d4ba5 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -87,7 +87,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer if (typeof (id as WebviewWindowId).windowId === 'number') { const { windowId } = (id as WebviewWindowId); const window = this.windowsMainService.getWindowById(windowId); - if (!window) { + if (!window?.win) { throw new Error(`Invalid windowId: ${windowId}`); } contents = window.win.webContents; diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 632a2db22cb..a78accbef8e 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -285,7 +285,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { get id(): number { return this._id; } private _win: BrowserWindow; - get win(): BrowserWindow { return this._win; } + get win(): BrowserWindow | null { return this._win; } get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } @@ -297,7 +297,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { setRepresentedFilename(filename: string): void { if (isMacintosh) { - this.win.setRepresentedFilename(filename); + this._win.setRepresentedFilename(filename); } else { this.representedFilename = filename; } @@ -305,7 +305,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { getRepresentedFilename(): string | undefined { if (isMacintosh) { - return this.win.getRepresentedFilename(); + return this._win.getRepresentedFilename(); } return this.representedFilename; @@ -680,7 +680,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } addTabbedWindow(window: ICodeWindow): void { - if (isMacintosh) { + if (isMacintosh && window.win) { this._win.addTabbedWindow(window.win); } } @@ -1262,26 +1262,26 @@ export class CodeWindow extends Disposable implements ICodeWindow { const action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); switch (action) { case 'Minimize': - this.win.minimize(); + this._win.minimize(); break; case 'None': break; case 'Maximize': default: - if (this.win.isMaximized()) { - this.win.unmaximize(); + if (this._win.isMaximized()) { + this._win.unmaximize(); } else { - this.win.maximize(); + this._win.maximize(); } } } // Linux/Windows: just toggle maximize/minimized state else { - if (this.win.isMaximized()) { - this.win.unmaximize(); + if (this._win.isMaximized()) { + this._win.unmaximize(); } else { - this.win.maximize(); + this._win.maximize(); } } } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 9a97d621b5c..5a1446b4c4f 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -74,7 +74,7 @@ export interface ICodeWindow extends IDisposable { readonly whenClosedOrLoaded: Promise; readonly id: number; - readonly win: BrowserWindow; + readonly win: BrowserWindow | null; /* `null` after being disposed */ readonly config: INativeWindowConfiguration | undefined; readonly openedWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index de3b4bf960b..f68cf41be11 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -35,7 +35,7 @@ import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier, IWorkspaces import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath'; import { CharCode } from 'vs/base/common/charCode'; import { getPathLabel } from 'vs/base/common/labels'; @@ -1200,8 +1200,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic once(createdWindow.onDidSignalReady)(() => this._onDidSignalReadyWindow.fire(createdWindow)); once(createdWindow.onDidClose)(() => this.onWindowClosed(createdWindow)); once(createdWindow.onDidDestroy)(() => this._onDidDestroyWindow.fire(createdWindow)); - createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); + + const webContents = assertIsDefined(createdWindow.win?.webContents); + webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow)); // Lifecycle this.lifecycleMainService.registerWindow(createdWindow); diff --git a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index 634fa2c810d..1fd596212bd 100644 --- a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -38,7 +38,7 @@ suite('WindowsFinder', () => { onDidDestroy: Event = Event.None; whenClosedOrLoaded: Promise = Promise.resolve(); id: number = -1; - win: Electron.BrowserWindow = undefined!; + win: Electron.BrowserWindow = null!; config: INativeWindowConfiguration | undefined; openedWorkspace = options.openedFolderUri ? { id: '', uri: options.openedFolderUri } : options.openedWorkspace; backupPath?: string | undefined; From c967932ba34ec52d61d72255425419839c1c4996 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Tue, 16 Feb 2021 11:50:05 +0100 Subject: [PATCH 107/176] Specify sourceScanPath --- build/azure-pipelines/product-compile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 4f2024a6209..05da3cd45e0 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -140,4 +140,5 @@ steps: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: "Component Detection" + sourceScanPath: $(Build.SourcesDirectory) continueOnError: true From 58a427566b6851734346b6ae10499e3df0b1088f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Tue, 16 Feb 2021 11:57:14 +0100 Subject: [PATCH 108/176] Revert change --- build/azure-pipelines/product-compile.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 05da3cd45e0..4f2024a6209 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -140,5 +140,4 @@ steps: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: "Component Detection" - sourceScanPath: $(Build.SourcesDirectory) continueOnError: true From 77493b59a55b557c47fdd653f8eeee65ee6cdb17 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 11:57:55 +0100 Subject: [PATCH 109/176] storage - test all storages closed when shutdown --- .../electron-main/storageMainService.ts | 2 -- .../electron-main/storageMainService.test.ts | 24 ++++++++++++------- .../electron-browser/desktop.main.ts | 2 +- .../electron-sandbox/desktop.main.ts | 2 +- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 6b50b94256a..a82e8efd0a8 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -69,8 +69,6 @@ export class StorageMainService extends Disposable implements IStorageMainServic if (this.enableMainWorkspaceStorage()) { this._register(this.lifecycleMainService.onWillLoadWindow(async e => { if (e.workspace) { - await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - this.workspaceStorage(e.workspace).initialize(); } })); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 3aa295edca7..4ed240e033b 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -146,22 +146,30 @@ suite('StorageMainService (native)', function () { const workspace = { id: generateUuid() }; const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), lifecycleMainService, new TestConfigurationService()); - let storage = storageMainService.workspaceStorage(workspace); - let didCloseStorage = false; - storage.onDidCloseStorage(() => { - didCloseStorage = true; + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; }); - strictEqual(storage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + let globalStorage = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); - await storage.initialize(); + strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + + await workspaceStorage.initialize(); await timeout(0); await lifecycleMainService.fireOnWillShutdown(); - strictEqual(didCloseStorage, true); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); let storage2 = storageMainService.workspaceStorage(workspace); - notStrictEqual(storage, storage2); + notStrictEqual(workspaceStorage, storage2); return storage2.close(); }); diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 057696c9420..103816e95c0 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -137,8 +137,8 @@ class DesktopMain extends Disposable { private registerListeners(workbench: Workbench, storageService: NativeStorageService | NativeStorageService2): void { // Workbench Lifecycle - this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); + this._register(workbench.onShutdown(() => this.dispose())); } private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService | NativeStorageService2 }> { diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 14d55ac5c31..709c3ab30c1 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -120,8 +120,8 @@ class DesktopMain extends Disposable { private registerListeners(workbench: Workbench, storageService: NativeStorageService2): void { // Workbench Lifecycle - this._register(workbench.onShutdown(() => this.dispose())); this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); + this._register(workbench.onShutdown(() => this.dispose())); } private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: NativeStorageService2 }> { From 38ae92f4b19a9d22c79bdaf148dddb59de646115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 16 Feb 2021 11:59:20 +0100 Subject: [PATCH 110/176] remove unused ui tests --- test/ui/splitview/package.json | 13 - test/ui/splitview/public/index.html | 260 - test/ui/splitview/server.js | 19 - test/ui/splitview/yarn.lock | 341 - test/ui/tree/package.json | 13 - test/ui/tree/public/compressed.json | 15620 -------------------------- test/ui/tree/public/index.html | 403 - test/ui/tree/server.js | 68 - test/ui/tree/tree.js | 24 - test/ui/tree/yarn.lock | 341 - 10 files changed, 17102 deletions(-) delete mode 100644 test/ui/splitview/package.json delete mode 100644 test/ui/splitview/public/index.html delete mode 100644 test/ui/splitview/server.js delete mode 100644 test/ui/splitview/yarn.lock delete mode 100644 test/ui/tree/package.json delete mode 100644 test/ui/tree/public/compressed.json delete mode 100644 test/ui/tree/public/index.html delete mode 100644 test/ui/tree/server.js delete mode 100644 test/ui/tree/tree.js delete mode 100644 test/ui/tree/yarn.lock diff --git a/test/ui/splitview/package.json b/test/ui/splitview/package.json deleted file mode 100644 index d6ce0c37374..00000000000 --- a/test/ui/splitview/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "splitview", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "devDependencies": { - "koa": "^2.5.1", - "koa-mount": "^3.0.0", - "koa-route": "^3.2.0", - "koa-static": "^5.0.0", - "mz": "^2.7.0" - } -} \ No newline at end of file diff --git a/test/ui/splitview/public/index.html b/test/ui/splitview/public/index.html deleted file mode 100644 index 0951af8ea52..00000000000 --- a/test/ui/splitview/public/index.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - Splitview - - - - -
-
- - - - - - diff --git a/test/ui/splitview/server.js b/test/ui/splitview/server.js deleted file mode 100644 index 67f25363671..00000000000 --- a/test/ui/splitview/server.js +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const fs = require('mz/fs'); -const path = require('path'); -const Koa = require('koa'); -const _ = require('koa-route'); -const serve = require('koa-static'); -const mount = require('koa-mount'); - -const app = new Koa(); - -app.use(serve('public')); -app.use(mount('/static', serve('../../out'))); - -app.listen(3000); -console.log('http://localhost:3000'); \ No newline at end of file diff --git a/test/ui/splitview/yarn.lock b/test/ui/splitview/yarn.lock deleted file mode 100644 index 237201a684e..00000000000 --- a/test/ui/splitview/yarn.lock +++ /dev/null @@ -1,341 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -accepts@^1.2.2: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -any-promise@^1.0.0, any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -content-disposition@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - -content-type@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookies@~0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" - integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs= - dependencies: - depd "~1.1.1" - keygrip "~1.0.2" - -debug@*, 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.6.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@^1.1.0, depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -error-inject@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" - integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= - -escape-html@~1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -fresh@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -http-assert@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" - integrity sha1-oxpc+IyHPsu1eWkH1NbxMujAHko= - dependencies: - deep-equal "~1.0.1" - http-errors "~1.6.1" - -http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-generator-function@^1.0.3: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" - integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -keygrip@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" - integrity sha1-rTKXxVcGneqLz+ek+kkbdcXd65E= - -koa-compose@^3.0.0, koa-compose@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" - integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= - dependencies: - any-promise "^1.1.0" - -koa-compose@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" - integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= - dependencies: - co "^4.6.0" - koa-compose "^3.0.0" - -koa-is-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" - integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= - -koa-mount@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197" - integrity sha1-CMqzuD0xRC7Yt+dcVLGr65IuwZc= - dependencies: - debug "^2.6.1" - koa-compose "^3.2.1" - -koa-route@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" - integrity sha1-dimLmaa8+p44yrb+XHmocz51i84= - dependencies: - debug "*" - methods "~1.1.0" - path-to-regexp "^1.2.0" - -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - -koa-static@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" - integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== - dependencies: - debug "^3.1.0" - koa-send "^5.0.0" - -koa@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c" - integrity sha512-cchwbMeG2dv3E2xTAmheDAuvR53tPgJZN/Hf1h7bTzJLSPcFZp8/t5+bNKJ6GaQZoydhZQ+1GNruhKdj3lIrug== - dependencies: - accepts "^1.2.2" - content-disposition "~0.5.0" - content-type "^1.0.0" - cookies "~0.7.0" - debug "*" - delegates "^1.0.0" - depd "^1.1.0" - destroy "^1.0.3" - error-inject "~1.0.0" - escape-html "~1.0.1" - fresh "^0.5.2" - http-assert "^1.1.0" - http-errors "^1.2.8" - is-generator-function "^1.0.3" - koa-compose "^4.0.0" - koa-convert "^1.2.0" - koa-is-json "^1.0.0" - mime-types "^2.0.7" - on-finished "^2.1.0" - only "0.0.2" - parseurl "^1.3.0" - statuses "^1.2.0" - type-is "^1.5.5" - vary "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -methods@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.0.7, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -on-finished@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -only@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - -parseurl@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= - -path-is-absolute@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-to-regexp@^1.2.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= - dependencies: - isarray "0.0.1" - -resolve-path@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" - integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= - dependencies: - http-errors "~1.6.2" - path-is-absolute "1.0.1" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -"statuses@>= 1.4.0 < 2", statuses@^1.2.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= - dependencies: - any-promise "^1.0.0" - -type-is@^1.5.5: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -vary@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= diff --git a/test/ui/tree/package.json b/test/ui/tree/package.json deleted file mode 100644 index 1a3b32d627d..00000000000 --- a/test/ui/tree/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "tree", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "devDependencies": { - "koa": "^2.5.1", - "koa-mount": "^3.0.0", - "koa-route": "^3.2.0", - "koa-static": "^5.0.0", - "mz": "^2.7.0" - } -} \ No newline at end of file diff --git a/test/ui/tree/public/compressed.json b/test/ui/tree/public/compressed.json deleted file mode 100644 index 939a0a29145..00000000000 --- a/test/ui/tree/public/compressed.json +++ /dev/null @@ -1,15620 +0,0 @@ -[ - { - "element": { - "name": "eclipse.platform.debug" - }, - "children": [ - { - "element": { - "name": ".git" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - }, - { - "element": { - "name": "branches" - }, - "children": [] - }, - { - "element": { - "name": "config" - }, - "incompressible": true - }, - { - "element": { - "name": "description" - }, - "incompressible": true - }, - { - "element": { - "name": "hooks" - }, - "children": [ - { - "element": { - "name": "applypatch-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "commit-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "fsmonitor-watchman.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "post-update.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-applypatch.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-commit.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-push.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-rebase.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "pre-receive.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "prepare-commit-msg.sample" - }, - "incompressible": true - }, - { - "element": { - "name": "update.sample" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "index" - }, - "incompressible": true - }, - { - "element": { - "name": "info" - }, - "children": [ - { - "element": { - "name": "exclude" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "logs" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - }, - { - "element": { - "name": "refs" - }, - "children": [ - { - "element": { - "name": "heads" - }, - "children": [ - { - "element": { - "name": "main" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "remotes" - }, - "children": [ - { - "element": { - "name": "origin" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "objects" - }, - "children": [ - { - "element": { - "name": "info" - }, - "children": [] - }, - { - "element": { - "name": "pack" - }, - "children": [ - { - "element": { - "name": "pack-2b1503bd0b85396d8596e9e99d956bfc3fbe497b.idx" - }, - "incompressible": true - }, - { - "element": { - "name": "pack-2b1503bd0b85396d8596e9e99d956bfc3fbe497b.pack" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "packed-refs" - }, - "incompressible": true - }, - { - "element": { - "name": "refs" - }, - "children": [ - { - "element": { - "name": "heads" - }, - "children": [ - { - "element": { - "name": "main" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "remotes" - }, - "children": [ - { - "element": { - "name": "origin" - }, - "children": [ - { - "element": { - "name": "HEAD" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "tags" - }, - "children": [ - { - "element": { - "name": "I20190722-1800" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "shallow" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": ".gitignore" - }, - "incompressible": true - }, - { - "element": { - "name": "CONTRIBUTING" - }, - "incompressible": true - }, - { - "element": { - "name": "LICENSE" - }, - "incompressible": true - }, - { - "element": { - "name": "NOTICE" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.externaltools" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "ExternalToolsCore.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "BackgroundResourceRefresher.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsCoreUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramLaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "BuilderCoreUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolBuilder.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "registry" - }, - "children": [ - { - "element": { - "name": "ExternalToolMigration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMigrationMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMigrationMessages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.core.variables" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "dynamicVariables.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "valueVariables.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ContributedValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DynamicVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EclipseHomeVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionEngine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesMessages.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "IDynamicVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDynamicVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStringVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStringVariableManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariableInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueVariableListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.core" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".options" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "DebugEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugException.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointManagerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEventFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEventSetListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationMigrationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationWorkingCopy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchMode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchesListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchesListener2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IProcessFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPrototypeAttributesLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Launch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "AbstractDebugCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugCommandRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDisconnectHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDropToFrameHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IEnabledStateRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRestartHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IResumeHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFiltersHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepIntoHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepOverHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepReturnHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITerminateHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "Breakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointImportParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDisconnect.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDropToFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IErrorReportingExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IFilteredStep.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IFlushableStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IIndexedValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDelegate2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILogicalStructureTypeDelegate2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockRetrieval.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockRetrievalExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableSourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRegister.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRegisterGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStep.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStepFilters.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStreamsProxy2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendResume.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITerminate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITriggerPoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueModification.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryByte.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RuntimeProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AbstractSourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableSourceLocator2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePathComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePathComputerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "containers" - }, - "children": [ - { - "element": { - "name": "AbstractSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSourceContainerTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArchiveSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompositeSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContainerSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LocalFileStorage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ZipEntryStorage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "BreakpointImportParticipantDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCoreMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCoreMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugOptions.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConfigurationElementConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExpressionsListener2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalDebugCoreConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMementoConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InputStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationWorkingCopy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchMode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchablePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "NullStreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OutputStreamMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Preferences.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PreferredDelegateModifyListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshScopeComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFilterManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamsProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPropertyResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemVariableResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpression.java" - }, - "incompressible": true - }, - { - "element": { - "name": "XMLMemento.java" - }, - "incompressible": true - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "CommandAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ForEachCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Request.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepFiltersCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommand.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "groups" - }, - "children": [ - { - "element": { - "name": "GroupLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupMemberChangeListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "observer" - }, - "children": [ - { - "element": { - "name": "ProcessObserver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamObserver.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "SourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLocatorMementoComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourcePathComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "containers" - }, - "children": [ - { - "element": { - "name": "ArchiveSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainerType.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ContainerResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DateTimeResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceResolver.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "breakpointImportParticipants.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationComparators.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchDelegates.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchModes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "logicalStructureProviders.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "logicalStructureTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "processFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceContainerTypes.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceLocators.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcePathComputers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "statusHandlers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "stepFilters.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "watchExpressionDelegates.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.core" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "pdavm" - }, - "children": [ - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "pdavm" - }, - "children": [ - { - "element": { - "name": "PDAVirtualMachine.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "tests" - }, - "children": [ - { - "element": { - "name": "vmtest10.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest2.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest3.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest6.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest8.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest9.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "vmtest_children.pda" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "readme.html" - }, - "incompressible": true - }, - { - "element": { - "name": "samples" - }, - "children": [ - { - "element": { - "name": "counter.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "drop.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "example.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "fibonacci.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "registers.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "stack.pda" - }, - "incompressible": true - }, - { - "element": { - "name": "structures.pda" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "build.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "midi" - }, - "children": [ - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "ClockControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LengthControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TempoControl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TimeControl.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pda" - }, - "children": [ - { - "element": { - "name": "DebugCorePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "PDALineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunToLineBreakpoint.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAWatchpoint.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "PDALaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "IPDAEventListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAArray.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAArrayEntry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordStructureDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "protocol" - }, - "children": [ - { - "element": { - "name": "PDABitFieldData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAChildrenCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAClearBreakpointCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDACommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDACommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADropFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvalCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvalResultEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEventStopCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAExitedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAFrameData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAGroupsCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAListResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDANoSuchLabelEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAPopDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAPushDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegisterData.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARegistersEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARestartCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAResumedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunControlEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetBreakpointCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetDataCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASetVarCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackDepthCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStackDepthCommandResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStartedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStepCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAStepReturnCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASuspendedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATerminateCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATerminatedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAUnimplementedInstructionEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMResumeCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMResumedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMStartedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMSuspendCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMSuspendedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVMTerminatedEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVarCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAWatchCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "PDASourceLookupDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourceLookupParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourcePathComputerDelegate.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "src_ant" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "ant" - }, - "children": [ - { - "element": { - "name": "tasks" - }, - "children": [ - { - "element": { - "name": "PreProcessor.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.memory" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "hex_tree.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "launch.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_segment.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_unit.gif" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "MemoryViewSamplePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "core" - }, - "children": [ - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleDebugTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleMemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleRegister.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleRegisterGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleStackFrame.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleThread.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "messages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "engine" - }, - "children": [ - { - "element": { - "name": "SampleEngine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleMemoryUnit.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launchconfig" - }, - "children": [ - { - "element": { - "name": "SampleLaunchConfigurationDelegateEx.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleLaunchTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SampleModelPresentation.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.mixedmode" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "mixedmode" - }, - "children": [ - { - "element": { - "name": "Activator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AntExtraTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClearPreferredDelegatesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DoNothingLaunchConfigurationDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DoNothingMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "messages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.examples.ui" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "OSGI-INF" - }, - "children": [ - { - "element": { - "name": "l10n" - }, - "children": [ - { - "element": { - "name": "bundle.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "pop.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "push.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "pop.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "push.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "clef.png" - }, - "incompressible": true - }, - { - "element": { - "name": "note.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "pda.gif" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "examples" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "midi" - }, - "children": [ - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "CheckboxModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlsMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiEventLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiEventModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiStepOverHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerColumnFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerControlsModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SequencerModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackColumnFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TrackModelProxy.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "detailpanes" - }, - "children": [ - { - "element": { - "name": "ClockSliderDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ControlDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TempoSliderDetailPane.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "ExampleLaunchStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiLaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MidiTabGroup.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pda" - }, - "children": [ - { - "element": { - "name": "DebugUIPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "AdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddPDAMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommandAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTargetContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDADebugTargetProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARestartDebugCommand.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAThreadEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAViewActionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAVirtualFindAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "PDABreakpointAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditorAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDARunToLineAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAToggleWatchpointsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAToggleWatchpointsTargetFactory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "editor" - }, - "children": [ - { - "element": { - "name": "AnnotationHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAContentAssistProcessor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAContentAssistant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAEditorMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAScanner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDASourceViewerConfiguration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopFrameActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordFinder.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launcher" - }, - "children": [ - { - "element": { - "name": "PDALaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDAMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PDATabGroup.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "presentation" - }, - "children": [ - { - "element": { - "name": "PDAModelPresentation.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "views" - }, - "children": [ - { - "element": { - "name": "AbstractDataStackViewHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CanPushTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CheckboxView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DataStackView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PushHandler.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.tests" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "Platform Debug Test Suite.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "image1.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "image2.gif" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "tests" - }, - "children": [ - { - "element": { - "name": "AbstractDebugTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AutomatedSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LocalSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerformanceSuite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestsPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint" - }, - "children": [ - { - "element": { - "name": "BreakpointOrderingTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleDocumentAdapterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleTestUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MockProcess.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamsProxyTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expressions" - }, - "children": [ - { - "element": { - "name": "ExpressionManagerTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launching" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AcceleratorSubstitutionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArgumentParsingTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArgumentsPrinter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CancellingLaunchDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugFileStore.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugFileSystem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchFavoriteTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchHistoryTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshTabTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestLaunchDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "SourceLookupFacilityTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestLaunch.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestSourceDirector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestSourceLocator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestStackFrame.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "statushandlers" - }, - "children": [ - { - "element": { - "name": "StatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StatusHandlerTests.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "stepfilters" - }, - "children": [ - { - "element": { - "name": "StepFiltersTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestStepFilter.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "view" - }, - "children": [ - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "DynamicRenderingBindings.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockDynamic.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockOne.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockThree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockTwo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingTypeDelegate.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "viewer" - }, - "children": [ - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "AbstractViewerModelTest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CheckTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ColumnPresentationTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTransformTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITestModelUpdatesListenerConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerCheckTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerDeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerFilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerLazyTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerPerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerPopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerSelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerStateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerTopIndexTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "JFaceViewerUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LazyTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PresentationContextTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TestModelUpdatesListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewerAutopopulateAgent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreePathWrapper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerContentTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerDeltaTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerFilterTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerLazyModeTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerPerformanceTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerPopupTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerSelectionTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerStateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualViewerUpdateTests.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VisibleVirtualItemValidator.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "test-import" - }, - "children": [ - { - "element": { - "name": "Import1.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import2.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import3.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import4.launch" - }, - "incompressible": true - }, - { - "element": { - "name": "Import5.launch" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "test.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "org.eclipse.debug.ui" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".options" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": ".api_filters" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "css" - }, - "children": [ - { - "element": { - "name": "e4-dark_debug_prefstyle.css" - }, - "incompressible": true - }, - { - "element": { - "name": "e4-light_debug_prefstyle.css" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "forceQualifierUpdate.txt" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "changevariablevalue_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "changevariablevalue_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.gif.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.gif@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_triggers.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dtool16" - }, - "children": [ - { - "element": { - "name": "debug_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dview16" - }, - "children": [ - { - "element": { - "name": "breakpoint_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "changevariablevalue_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "changevariablevalue_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "collapseall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copy_edit_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "copyviewtoclipboard_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_compact@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view_tree@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debuglast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "delete_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_auto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_hide@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_right@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under.png" - }, - "incompressible": true - }, - { - "element": { - "name": "det_pane_under@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "disconnect_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb.png" - }, - "incompressible": true - }, - { - "element": { - "name": "display_selected_mb@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group.png" - }, - "incompressible": true - }, - { - "element": { - "name": "dissolve_group@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame.png" - }, - "incompressible": true - }, - { - "element": { - "name": "drop_to_frame@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "enabled_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expandall@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "filter_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout.png" - }, - "incompressible": true - }, - { - "element": { - "name": "hierarchicalLayout@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "link_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryreset_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "metharg_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "monitorexpression_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "next_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prev_thread_nav@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "printview_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prop_ps@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_all_triggers.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk.png" - }, - "incompressible": true - }, - { - "element": { - "name": "removememory_tsk@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "reset_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "restart_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "resume_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runlast_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "runtoline_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_brkp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepbystep_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepinto_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepover_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stepreturn_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "suspend_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced.png" - }, - "incompressible": true - }, - { - "element": { - "name": "synced@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_all_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminate_rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "tnames_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "toggledetailpane_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto.png" - }, - "incompressible": true - }, - { - "element": { - "name": "unlink_proto@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeerr_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "writeout_co@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "etool16" - }, - "children": [ - { - "element": { - "name": "debug_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_exc@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart.png" - }, - "incompressible": true - }, - { - "element": { - "name": "term_restart@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watch_exp@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "eview16" - }, - "children": [ - { - "element": { - "name": "breakpoint_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoint_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_persp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "details_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "module_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "register_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "variable_view@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "watchlist_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "arraypartition_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "arraypartition_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_grp_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_type.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkp_type@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkpd_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "brkpd_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "check.png" - }, - "incompressible": true - }, - { - "element": { - "name": "check@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "common_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "common_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugts_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugts_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugtt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debugtt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "environment_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "envvar_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "envvar_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expression_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "expression_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "file_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "file_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "fldr_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "fldr_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericreggroup_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericreggroup_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericregister_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericregister_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericvariable_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "genericvariable_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr_top.png" - }, - "incompressible": true - }, - { - "element": { - "name": "inst_ptr_top@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "jar_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "jar_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "ldebug_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "ldebug_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lgroup_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lgroup_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lrun_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lrun_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memory_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memorychanged_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "memorychanged_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprc_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprc_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprct_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "osprct_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "persp_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "persp_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prj_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prj_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "proto_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "proto_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "read_obj_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "readwrite_obj_disabled@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "refresh_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "refresh_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rundebug.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rundebug@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_running_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stckframe_running_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminatedlaunch_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "terminatedlaunch_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "thread_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "thread_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threads_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threads_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threadt_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "threadt_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "uncheck.png" - }, - "incompressible": true - }, - { - "element": { - "name": "uncheck@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "workset.png" - }, - "incompressible": true - }, - { - "element": { - "name": "workset@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj_disabled.png" - }, - "incompressible": true - }, - { - "element": { - "name": "write_obj_disabled@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ovr16" - }, - "children": [ - { - "element": { - "name": "error.png" - }, - "incompressible": true - }, - { - "element": { - "name": "error@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prototype.png" - }, - "incompressible": true - }, - { - "element": { - "name": "prototype@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_breakpoint_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "skip_breakpoint_ov@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stcksync_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "stcksync_ov@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "transparent.png" - }, - "incompressible": true - }, - { - "element": { - "name": "transparent@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr_ov.png" - }, - "incompressible": true - }, - { - "element": { - "name": "var_cntnt_prvdr_ov@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "wizban" - }, - "children": [ - { - "element": { - "name": "adddir_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "addsrcloc_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "debug_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "editdir_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "edtsrclkup_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_brkpts_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "export_config_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_brkpts_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "import_config_wizban.png" - }, - "incompressible": true - }, - { - "element": { - "name": "profile_wiz.png" - }, - "incompressible": true - }, - { - "element": { - "name": "run_wiz.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "breakpointOrganizers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consoleColorProviders.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consoleLineTrackers.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "contextViewBindings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "debugModelContextBindings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "debugModelPresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "detailPaneFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTabGroups.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTabs.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchConfigurationTypeImages.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchGroups.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "launchShortcuts.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "memoryRenderings.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "sourceContainerPresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "stringVariablePresentations.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "toggleBreakpointsTargetFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "variableValueEditors.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "debug" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "AbstractDebugCheckboxSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugListSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointImageProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildBeforeLaunchStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ColorManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompositeDebugImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModelPropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPerspectiveFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPluginImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DelegatingModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DynamicInstructionPointerAnnotation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalDebugUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchHistoryChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchLabelChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageDescriptorRegistry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerAnnotation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerImageProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InstructionPointerManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LazyModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MultipleInputDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Pair.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceExtender.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SWTFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateToggleValue.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextGetSetEditingSupport.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableValueEditorManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingDirectoryStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractDebugActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractRemoveAllActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractSelectionActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "AddToFavoritesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CollapseAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConfigureColumnsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExecutionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchablePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenDebugConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenProfileConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenRunConfigurations.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllTerminatedAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetRunToLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunHistoryMenuAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StatusInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointsTargetManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleFilterAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewManagementAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpointGroups" - }, - "children": [ - { - "element": { - "name": "AbstractBreakpointsViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AdvancedGroupBreakpointsByAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointGroupMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointGroupMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointSelectionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClearDefaultBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyBreakpointsActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsByAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupBreakpointsByDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasteBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveFromWorkingSetAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectBreakpointWorkingsetDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SetDefaultBreakpointGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleDefaultGroupAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetsAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpointSortBy" - }, - "children": [ - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SortBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SortBreakpointsByAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "AccessWatchpointToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsCollapseAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsExpandAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeleteWorkingsetsMessageDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisableBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnableBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkBreakpointsWithDebugViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ModificationWatchpointToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModifyWatchpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenBreakpointMarkerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllTriggerPointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetMethodBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetToggleBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetToggleLineBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetWatchpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerEnableDisableBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowSupportedBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowTargetBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SkipAllBreakpointsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointObjectActionDelegate.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expressions" - }, - "children": [ - { - "element": { - "name": "AddWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConvertToWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyExpressionsToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisableWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditWatchExpressinInPlaceAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnableWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasteWatchExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ReevaluateWatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAllExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllExpressionsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionFactoryTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "ChangeVariableValueAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChangeVariableValueInputDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectAllVariablesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowTypesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleDetailPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "details" - }, - "children": [ - { - "element": { - "name": "DetailPaneAssignValueAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneMaxLengthAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneMaxLengthDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneWordWrapAction.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "IBreakpointContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OtherBreakpointCategory.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "commands" - }, - "children": [ - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ActionsUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugActionHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DisconnectCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DropToFrameCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExecuteActionRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ICommandParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IEnabledTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestartCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResumeCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepIntoCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepOverCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StepReturnCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAllAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAllActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRelaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRelaunchHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRemoveAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersCommandActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleStepFiltersCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateActionsRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpdateHandlerRequest.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "contextlaunching" - }, - "children": [ - { - "element": { - "name": "ContextMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextRunner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchingResourceManager.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "contexts" - }, - "children": [ - { - "element": { - "name": "DebugContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextSourceProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModelContextBindingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugWindowContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchSuspendTrigger.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SuspendTriggerAdapterFactory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elements" - }, - "children": [ - { - "element": { - "name": "adapters" - }, - "children": [ - { - "element": { - "name": "AsynchronousDebugLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultBreakpointsViewInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultVariableCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemorySegmentLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Messages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameSourceDisplayAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableColumnFactoryAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionCellModifier.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "groups" - }, - "children": [ - { - "element": { - "name": "CommonTabLite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupCycleHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupElementLaunchedHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GroupLaunchHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsupportedModeHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "hover" - }, - "children": [ - { - "element": { - "name": "DebugTextHover.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionInformationControlCreator.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "importexport" - }, - "children": [ - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "BreakpointImportExport.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsPathDecorator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EmbeddedBreakpointsViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IImportExportConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportExportMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardExportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardExportBreakpointsPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpoints.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpointsPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardImportBreakpointsSelectionPage.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launchconfigurations" - }, - "children": [ - { - "element": { - "name": "ExportLaunchConfigurationsWizard.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportLaunchConfigurationsWizardPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportLaunchConfigurationsWizard.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportLaunchConfigurationsWizardPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WizardMessages.properties" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ClosedProjectFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CollapseAllLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompileErrorProjectPromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CompileErrorPromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateLaunchConfigurationPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugModePromptStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeleteLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeletedProjectFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DuplicateLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DuplicateLaunchDelegatesStatusHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentVariable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FavoritesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterDropDownMenuCreator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterLaunchConfigurationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchCategoryFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationEditDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationFilteredTree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationPresentationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationPropertiesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabGroupWrapper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTabImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTreeContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTypeContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationTypeFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegateContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchDelegateNotAvailableHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchGroupFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchHistory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchTabContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OrganizeFavoritesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PerspectiveManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetWithPrototypeValuesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SaveScopeResourcesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectFavoritesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLaunchModesDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLaunchersDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowCommandLineDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnlinkPrototypeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetsFilter.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "IMemoryBlockConnection.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPersistableDebugElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingBindings.java" - }, - "incompressible": true - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "AbstractAsyncTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractAsyncTextRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewPresentationContext.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "elements" - }, - "children": [ - { - "element": { - "name": "BreakpointContainerLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContainerMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerInputMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegisterGroupMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ThreadContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WatchExpressionEditor.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "preferences" - }, - "children": [ - { - "element": { - "name": "BooleanFieldEditor2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencesMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPreferencesMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugPreferenceConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchConfigurationsPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchPerspectivePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchersPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchingPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessPropertyPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunDebugPropertiesPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariablePreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewManagementPreferencePage.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "quickaccess" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchQuickAccessElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProfileQuickAccessComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunQuickAccessComputer.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AddContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BasicContainerContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DownAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditSourceLookupPathAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LookupSourceAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Prompter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResolveDuplicatesHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RestoreDefaultAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceContainerWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupFacility.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupPanel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupUIUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UpAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainerType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "browsers" - }, - "children": [ - { - "element": { - "name": "ArchiveFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ArchiveSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DirectorySourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalArchiveSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectSourceContainerDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkspaceSourceContainerBrowser.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "stringsubstitution" - }, - "children": [ - { - "element": { - "name": "FilePrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FolderPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IArgumentSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PasswordPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PromptingResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResourceSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedResourceManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedResourceResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectedTextResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringPrompt.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringSubstitutionMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariablePresentationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPropertyArgumentSelector.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "viewers" - }, - "children": [ - { - "element": { - "name": "AbstractUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousSchedulingRuleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousTableModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousTableViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FindElementDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelNode.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PartPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableAddRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableEditorImpl.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableInsertRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRemoveRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableReplaceRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breadcrumb" - }, - "children": [ - { - "element": { - "name": "AbstractBreadcrumb.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItemDetails.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbItemDropDown.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreadcrumbDropDownSite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeViewerDropDown.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "ChildrenCountUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementCompareRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementMementoRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FilterTransform.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HasChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InternalTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InternalVirtualTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LabelUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MementoUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SubTreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TimeTriggeredProgressMonitorDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerAdapterService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerStateTracker.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerUpdateMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualCopyToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualFindAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "ICheckUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ICheckboxModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenCountUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentation2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IColumnPresentationFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementCompareRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementMementoProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IElementMementoRequest.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHasChildrenUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelChangedListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelDelta.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelDeltaVisitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxy2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelProxyFactory2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelSelectionPolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IModelSelectionPolicyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStateUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IStatusMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ITreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewActionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputRequestor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerInputUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerUpdate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IViewerUpdateListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualItemListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualItemValidator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModelDelta.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeModelViewerFilter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewerInputService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualItem.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualTree.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VirtualTreeModelViewer.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "provisional" - }, - "children": [ - { - "element": { - "name": "AbstractColumnPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsynchronousLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAsynchronousContentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAsynchronousLabelAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IChildrenRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IContainerRequestMonitor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILabelRequestMonitor.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "update" - }, - "children": [ - { - "element": { - "name": "BreakpointContainerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointManagerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugTargetProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultExpressionModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultModelProxyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultModelSelectionPolicyFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultSelectionPolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultVariableViewModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultWatchExpressionModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EventHandlerModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionManagerModelProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchManagerProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlockProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRetrievalProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StackFrameEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ThreadEventHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewEventHandler.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "views" - }, - "children": [ - { - "element": { - "name": "DebugModelPresentationContext.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIViewsMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUIViewsMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugExceptionHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "breakpoints" - }, - "children": [ - { - "element": { - "name": "BreakpointContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointContainerWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointOrganizerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointOrganizerManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointPersistableElementAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointSetOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypeOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetCache.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetElementAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointWorkingSetPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsDragAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsDropAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointsViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ElementComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FileBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProjectBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetBreakpointOrganizer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetCategory.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleLineNotifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleRemoveAllTerminatedAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleRemoveLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleShowPreferencesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTerminateAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessConsolePageParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProcessTypePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowStandardErrorAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowStandardOutAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowWhenContentChangesAction.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "expression" - }, - "children": [ - { - "element": { - "name": "ExpressionDropAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExpressionView.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "launch" - }, - "children": [ - { - "element": { - "name": "BreadcrumbDropDownAutoExpandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreadcrumbWorkbenchPart.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementAdapterFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementHelper.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugToolBarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugViewModeAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "Decoration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DecorationManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageImageDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewBreadcrumb.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewCopyToClipboardActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchViewMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceNotFoundEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceNotFoundEditorInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StandardDecoration.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TerminateAndRemoveHandler.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "AbstractMemoryViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingContextAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CodePagesPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryViewTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LinkRenderingPanesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryBlocksTreeViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewIdRegistry.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewPrefAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewSynchronizationService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTreeModelContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewTreeViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryViewUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MonitorMemoryBlockDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "NewMemoryViewAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PinMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PropertyChangeNotifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveMemoryRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RemoveRenderingContextAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingViewPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetMemoryBlockPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RetargetAddMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SetPaddedStringPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SwitchMemoryBlockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SynchronizeInfo.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleMemoryMonitorsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleSplitPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleViewPaneAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneOrientationAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneRenderingMgr.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewPaneSelectionProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ViewTabEnablementManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "renderings" - }, - "children": [ - { - "element": { - "name": "ASCIIRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ASCIIRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractBaseTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTableRenderingLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractVirtualContentTableModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncCopyTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncPrintTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingUpdatePolicy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncTableRenderingViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AsyncVirtualContentTableViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BasicDebugViewContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BigEndianAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CopyTableRenderingToClipboardAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CreateRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultEndianessAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ErrorRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FormatTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FormatTableRenderingDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressComposite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "GoToAddressDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexIntegerRenderingDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HexRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IContentChangeComputer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPresentationErrorListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVirtualContentListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LittleEndianAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemorySegment.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PendingPropertyChanges.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrintTableRenderingAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ReformatAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RenderingsUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ResetToBaseAddressAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SignedIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SignedIntegerRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingCellModifier.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentDescriptor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingContentProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLabelProviderEx.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingLine.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingModel.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPrefAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TableRenderingPropertiesPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsignedIntegerRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "UnsignedIntegerRenderingTypeDelegate.java" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "modules" - }, - "children": [ - { - "element": { - "name": "IHelpContextIdProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ModulesViewMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "registers" - }, - "children": [ - { - "element": { - "name": "RegistersView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegistersViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RegistersViewMessages.properties" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "AvailableLogicalStructuresAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditVariableLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IndexedValuePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IndexedVariablePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LogicalStructureCache.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SelectionDragAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleLogicalStructureAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleShowColumnsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableViewToggleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "VariablesViewResourceBundleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "details" - }, - "children": [ - { - "element": { - "name": "AbstractDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AvailableDetailPanesAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DefaultDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DetailPaneProxy.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneContainer2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageDetailPane.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "AbstractBreakpointOrganizerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractDebugView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchConfigurationTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypeCategory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugPopup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugUITools.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DeferredDebugElementWorkbenchAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EnvironmentTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointOrganizerDelegateExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IBreakpointTypeCategory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugEditorPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugModelPresentationExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugUIConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPane3.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDetailPaneFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInstructionPointerPresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTab2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchConfigurationTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchShortcut.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchShortcut2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourcePresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IValueDetailListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "InspectPopupDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrototypeDecorator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PrototypeTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RefreshTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StringVariableSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingDirectoryBlock.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "AbstractLaunchHistoryAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractLaunchToolbarAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AddMemoryRenderingActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BreakpointTypesContribution.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ContextualLaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugCommandHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExportBreakpointsOperation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAddMemoryBlocksTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IAddMemoryRenderingsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ILaunchable.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRunToLineTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTarget.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetExtension2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IToggleBreakpointsTargetManagerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IVariableValueEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapter2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IWatchExpressionFactoryAdapterExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImportBreakpointsOperation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchAsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "LaunchShortcutsAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenLaunchDialogAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RelaunchLastAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerBreakpointTypesActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerEnableDisableBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerRunToLineActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RulerToggleBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "RunToLineHandler.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleBreakpointAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleMethodBreakpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ToggleWatchpointActionDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleColorProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FileLink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleColorProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleHyperlink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleLineTracker.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleLineTrackerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "contexts" - }, - "children": [ - { - "element": { - "name": "AbstractDebugContextProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "DebugContextEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextProvider2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IDebugContextService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendTrigger.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISuspendTriggerListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "memory" - }, - "children": [ - { - "element": { - "name": "AbstractMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractMemoryRenderingBindingsProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTableRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "AbstractTextRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryBlockTablePresentation.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingBindingsListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingBindingsProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSite.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSite2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingSynchronizationService.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingType.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IMemoryRenderingTypeDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IRepositionableMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IResettableMemoryRendering.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MemoryRenderingElement.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - }, - { - "element": { - "name": "sourcelookup" - }, - "children": [ - { - "element": { - "name": "AbstractSourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonSourceNotFoundEditor.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CommonSourceNotFoundEditorInput.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceContainerBrowser.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceDisplay.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ISourceLookupResult.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SourceLookupTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetSourceContainer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "stringsubstitution" - }, - "children": [ - { - "element": { - "name": "IArgumentSelector.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.ui.console" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "clcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "cview16" - }, - "children": [ - { - "element": { - "name": "console_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dlcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "dview16" - }, - "children": [ - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "elcl16" - }, - "children": [ - { - "element": { - "name": "clear_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "clear_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "lock_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con.png" - }, - "incompressible": true - }, - { - "element": { - "name": "new_con@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin.png" - }, - "incompressible": true - }, - { - "element": { - "name": "pin@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co.png" - }, - "incompressible": true - }, - { - "element": { - "name": "rem_co@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap.png" - }, - "incompressible": true - }, - { - "element": { - "name": "wordwrap@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "eview16" - }, - "children": [ - { - "element": { - "name": "console_view.gif" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view.png" - }, - "incompressible": true - }, - { - "element": { - "name": "console_view@2x.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "consoleFactories.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consolePageParticipants.exsd" - }, - "incompressible": true - }, - { - "element": { - "name": "consolePatternMatchListeners.exsd" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "scripts" - }, - "children": [ - { - "element": { - "name": "exportplugin.xml" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "src" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "AbstractConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleDocumentPartitioner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsolePageParticipant.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHyperlink.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IHyperlink2.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleInputStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleOutputStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPatternMatchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPatternMatchListenerDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IScrollLockStateProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "MessageConsoleStream.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchEvent.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsole.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsolePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextConsoleViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "actions" - }, - "children": [ - { - "element": { - "name": "ClearOutputAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "CloseConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextViewerAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TextViewerGotoLineAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "package.html" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "console" - }, - "children": [ - { - "element": { - "name": "ConsoleDocument.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleDocumentAdapter.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleDropDownAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleFactoryExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleHyperlinkPosition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleManager.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePageParticipantExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePatternMatcher.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsolePluginImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleResourceBundleMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleResourceBundleMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleTypePropertyTester.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleUIPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleView.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleViewConsoleFactory.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ConsoleWorkbenchPart.java" - }, - "incompressible": true - }, - { - "element": { - "name": "FollowHyperlinkAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "HyperlinkUpdater.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IConsoleHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IInternalConsoleConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePartition.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsolePartitioner.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IOConsoleViewer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchListener.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PatternMatchListenerExtension.java" - }, - "incompressible": true - }, - { - "element": { - "name": "PinConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ScrollLockAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ShowConsoleAction.java" - }, - "incompressible": true - }, - { - "element": { - "name": "StreamDecoder.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WordWrapAction.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "org.eclipse.ui.externaltools" - }, - "children": [ - { - "element": { - "name": ".classpath" - }, - "incompressible": true - }, - { - "element": { - "name": ".gitignore" - }, - "incompressible": true - }, - { - "element": { - "name": ".project" - }, - "incompressible": true - }, - { - "element": { - "name": ".settings" - }, - "children": [ - { - "element": { - "name": ".api_filters" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.resources.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.core.runtime.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.core.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.jdt.ui.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.api.tools.prefs" - }, - "incompressible": true - }, - { - "element": { - "name": "org.eclipse.pde.prefs" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "External Tools Base" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "ExternalToolsBuildTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsBuilderTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsLaunchConfigurationMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsLaunchConfigurationMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUtil.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IgnoreWhiteSpaceComparator.java" - }, - "incompressible": true - }, - { - "element": { - "name": "WorkingSetComparator.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "menu" - }, - "children": [ - { - "element": { - "name": "ExternalToolMenuDelegate.java" - }, - "incompressible": true - }, - { - "element": { - "name": "OpenExternalToolsConfigurations.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "model" - }, - "children": [ - { - "element": { - "name": "BuilderUtils.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsImages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsModelMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPlugin.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPreferenceInitializer.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IExternalToolsHelpContextIds.java" - }, - "incompressible": true - }, - { - "element": { - "name": "IPreferenceConstants.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ImageDescriptorRegistry.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "BuilderLabelProvider.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuilderPropertyPage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "EditCommandDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsPreferencePage.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUIMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsUIMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "FileSelectionDialog.java" - }, - "incompressible": true - }, - { - "element": { - "name": "TreeAndListGroup.java" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "variables" - }, - "children": [ - { - "element": { - "name": "BuildFilesResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildProjectResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "BuildTypeResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "SystemPathResolver.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "VariableMessages.properties" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "META-INF" - }, - "children": [ - { - "element": { - "name": "MANIFEST.MF" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "Program Tools Support" - }, - "children": [ - { - "element": { - "name": "org" - }, - "children": [ - { - "element": { - "name": "eclipse" - }, - "children": [ - { - "element": { - "name": "ui" - }, - "children": [ - { - "element": { - "name": "externaltools" - }, - "children": [ - { - "element": { - "name": "internal" - }, - "children": [ - { - "element": { - "name": "program" - }, - "children": [ - { - "element": { - "name": "launchConfigurations" - }, - "children": [ - { - "element": { - "name": "ExternalToolsProgramMessages.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ExternalToolsProgramMessages.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramBuilderTabGroup.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramMainTab.java" - }, - "incompressible": true - }, - { - "element": { - "name": "ProgramTabGroup.java" - }, - "incompressible": true - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "about.html" - }, - "incompressible": true - }, - { - "element": { - "name": "build.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "icons" - }, - "children": [ - { - "element": { - "name": "full" - }, - "children": [ - { - "element": { - "name": "dtool16" - }, - "children": [ - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "etool16" - }, - "children": [ - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "obj16" - }, - "children": [ - { - "element": { - "name": "build_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "build_tab@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "builder.png" - }, - "incompressible": true - }, - { - "element": { - "name": "builder@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "classpath.png" - }, - "incompressible": true - }, - { - "element": { - "name": "classpath@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools.png" - }, - "incompressible": true - }, - { - "element": { - "name": "external_tools@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "invalid_build_tool.png" - }, - "incompressible": true - }, - { - "element": { - "name": "invalid_build_tool@2x.png" - }, - "incompressible": true - }, - { - "element": { - "name": "main_tab.png" - }, - "incompressible": true - }, - { - "element": { - "name": "main_tab@2x.png" - }, - "incompressible": true - } - ] - }, - { - "element": { - "name": "wizban" - }, - "children": [ - { - "element": { - "name": "ext_tools_wiz.png" - }, - "incompressible": true - } - ] - } - ] - } - ] - }, - { - "element": { - "name": "plugin.properties" - }, - "incompressible": true - }, - { - "element": { - "name": "plugin.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - }, - { - "element": { - "name": "schema" - }, - "children": [ - { - "element": { - "name": "configurationDuplicationMaps.exsd" - }, - "incompressible": true - } - ] - } - ] - }, - { - "element": { - "name": "pom.xml" - }, - "incompressible": true - } - ] - } -] diff --git a/test/ui/tree/public/index.html b/test/ui/tree/public/index.html deleted file mode 100644 index 3f66d505016..00000000000 --- a/test/ui/tree/public/index.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - Tree - - - - - - - - - -
- - - - - - diff --git a/test/ui/tree/server.js b/test/ui/tree/server.js deleted file mode 100644 index 5b5dd3f1a6c..00000000000 --- a/test/ui/tree/server.js +++ /dev/null @@ -1,68 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const fs = require('mz/fs'); -const path = require('path'); -const Koa = require('koa'); -const _ = require('koa-route'); -const serve = require('koa-static'); -const mount = require('koa-mount'); - -const app = new Koa(); -const root = path.dirname(path.dirname(__dirname)); - -async function getTree(fsPath, level) { - const element = path.basename(fsPath); - const stat = await fs.stat(fsPath); - - if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 4) { - return { element }; - } - - const childNames = await fs.readdir(fsPath); - const children = await Promise.all(childNames.map(async childName => await getTree(path.join(fsPath, childName), level + 1))); - return { element, collapsible: true, collapsed: false, children }; -} - -async function readdir(relativePath) { - const absolutePath = relativePath ? path.join(root, relativePath) : root; - const childNames = await fs.readdir(absolutePath); - const childStats = await Promise.all(childNames.map(async name => await fs.stat(path.join(absolutePath, name)))); - const result = []; - - for (let i = 0; i < childNames.length; i++) { - const name = childNames[i]; - const path = relativePath ? `${relativePath}/${name}` : name; - const stat = childStats[i]; - - if (stat.isFile()) { - result.push({ type: 'file', name, path }); - } else if (!stat.isDirectory() || name === '.git' || name === '.build') { - continue; - } else { - result.push({ type: 'dir', name, path }); - } - } - - return result; -} - -app.use(serve('public')); -app.use(mount('/static', serve('../../out'))); -app.use(_.get('/api/ls', async ctx => { - const relativePath = ctx.query.path; - const absolutePath = path.join(root, relativePath); - - ctx.body = await getTree(absolutePath, 0); -})); - -app.use(_.get('/api/readdir', async ctx => { - const relativePath = ctx.query.path; - - ctx.body = await readdir(relativePath); -})); - -app.listen(3000); -console.log('http://localhost:3000'); \ No newline at end of file diff --git a/test/ui/tree/tree.js b/test/ui/tree/tree.js deleted file mode 100644 index 4d5a9b5f271..00000000000 --- a/test/ui/tree/tree.js +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const path = require('path'); -const fs = require('fs'); - -function collect(location) { - const element = { name: path.basename(location) }; - const stat = fs.statSync(location); - - if (!stat.isDirectory()) { - return { element, incompressible: true }; - } - - const children = fs.readdirSync(location) - .map(child => path.join(location, child)) - .map(collect); - - return { element, children }; -} - -console.log(JSON.stringify(collect(process.cwd()))); \ No newline at end of file diff --git a/test/ui/tree/yarn.lock b/test/ui/tree/yarn.lock deleted file mode 100644 index 237201a684e..00000000000 --- a/test/ui/tree/yarn.lock +++ /dev/null @@ -1,341 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -accepts@^1.2.2: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -any-promise@^1.0.0, any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -content-disposition@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - -content-type@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookies@~0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" - integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs= - dependencies: - depd "~1.1.1" - keygrip "~1.0.2" - -debug@*, 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.6.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@^1.1.0, depd@~1.1.1, depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -error-inject@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" - integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= - -escape-html@~1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -fresh@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -http-assert@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" - integrity sha1-oxpc+IyHPsu1eWkH1NbxMujAHko= - dependencies: - deep-equal "~1.0.1" - http-errors "~1.6.1" - -http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-generator-function@^1.0.3: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" - integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -keygrip@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" - integrity sha1-rTKXxVcGneqLz+ek+kkbdcXd65E= - -koa-compose@^3.0.0, koa-compose@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" - integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= - dependencies: - any-promise "^1.1.0" - -koa-compose@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" - integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= - dependencies: - co "^4.6.0" - koa-compose "^3.0.0" - -koa-is-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" - integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= - -koa-mount@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197" - integrity sha1-CMqzuD0xRC7Yt+dcVLGr65IuwZc= - dependencies: - debug "^2.6.1" - koa-compose "^3.2.1" - -koa-route@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" - integrity sha1-dimLmaa8+p44yrb+XHmocz51i84= - dependencies: - debug "*" - methods "~1.1.0" - path-to-regexp "^1.2.0" - -koa-send@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" - integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== - dependencies: - debug "^3.1.0" - http-errors "^1.6.3" - mz "^2.7.0" - resolve-path "^1.4.0" - -koa-static@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" - integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== - dependencies: - debug "^3.1.0" - koa-send "^5.0.0" - -koa@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c" - integrity sha512-cchwbMeG2dv3E2xTAmheDAuvR53tPgJZN/Hf1h7bTzJLSPcFZp8/t5+bNKJ6GaQZoydhZQ+1GNruhKdj3lIrug== - dependencies: - accepts "^1.2.2" - content-disposition "~0.5.0" - content-type "^1.0.0" - cookies "~0.7.0" - debug "*" - delegates "^1.0.0" - depd "^1.1.0" - destroy "^1.0.3" - error-inject "~1.0.0" - escape-html "~1.0.1" - fresh "^0.5.2" - http-assert "^1.1.0" - http-errors "^1.2.8" - is-generator-function "^1.0.3" - koa-compose "^4.0.0" - koa-convert "^1.2.0" - koa-is-json "^1.0.0" - mime-types "^2.0.7" - on-finished "^2.1.0" - only "0.0.2" - parseurl "^1.3.0" - statuses "^1.2.0" - type-is "^1.5.5" - vary "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -methods@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.0.7, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -on-finished@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -only@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - -parseurl@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= - -path-is-absolute@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-to-regexp@^1.2.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= - dependencies: - isarray "0.0.1" - -resolve-path@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" - integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= - dependencies: - http-errors "~1.6.2" - path-is-absolute "1.0.1" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -"statuses@>= 1.4.0 < 2", statuses@^1.2.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= - dependencies: - any-promise "^1.0.0" - -type-is@^1.5.5: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -vary@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= From 28de0a46867c0df2bd31a31cbca1b6c52ddc4645 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Tue, 16 Feb 2021 11:59:32 +0100 Subject: [PATCH 111/176] Fix YAML typo --- build/azure-pipelines/product-compile.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 4f2024a6209..c51ed5644c6 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -140,4 +140,6 @@ steps: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: "Component Detection" + inputs: + sourceScanPath: $(Build.SourcesDirectory) continueOnError: true From a1b9523db1077eb1fdf2dee97acd888abf132f2c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 13:39:49 +0100 Subject: [PATCH 112/176] update my-work notebook --- .vscode/notebooks/my-work.github-issues | 48 +++++++++++++++---------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 1502c29db16..71847660727 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -3,114 +3,124 @@ "kind": 1, "language": "markdown", "value": "##### `Config`: This should be changed every month/milestone", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\"", - "editable": true + "outputs": [] }, { "kind": 1, "language": "github-issues", "value": "## Milestone Work", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos $milestone assignee:@me is:open", - "editable": true + "outputs": [] }, { "kind": 1, "language": "github-issues", "value": "## Bugs, Debt, Features...", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### My Bugs", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:bug", - "editable": true + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Debt & Engineering", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", - "editable": true + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Performance 🐌 🔜 🏎", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", - "editable": true + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Feature Requests", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", - "editable": true + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", - "editable": true + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### Personal Inbox\n", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "\n#### Missing Type label", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream", - "editable": true + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "#### Not Actionable", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:\"needs more info\"", - "editable": true + "outputs": [] } ] \ No newline at end of file From d2cfc0792e04567b67c0d87575933f62a239cacd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 13:50:58 +0100 Subject: [PATCH 113/176] update editable state --- .vscode/notebooks/my-work.github-issues | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 71847660727..c72d5cde062 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -10,6 +10,7 @@ "kind": 2, "language": "github-issues", "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\"", + "editable": true, "outputs": [] }, { @@ -43,6 +44,7 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:bug", + "editable": true, "outputs": [] }, { @@ -56,6 +58,7 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering", + "editable": true, "outputs": [] }, { @@ -69,6 +72,7 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak", + "editable": true, "outputs": [] }, { @@ -82,12 +86,14 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc", + "editable": true, "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"", + "editable": true, "outputs": [] }, { @@ -108,6 +114,7 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream", + "editable": true, "outputs": [] }, { @@ -121,6 +128,7 @@ "kind": 2, "language": "github-issues", "value": "$repos assignee:@me is:open label:\"needs more info\"", + "editable": true, "outputs": [] } ] \ No newline at end of file From 2ad3fe122412f392c1178eb7a850cd0c6ea51fa4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 14:30:18 +0100 Subject: [PATCH 114/176] storage - skip failing test --- .../storage/test/electron-main/storageMainService.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 4ed240e033b..cb3ae06d0fa 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -16,7 +16,7 @@ import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, UnloadReason import { Emitter, Event } from 'vs/base/common/event'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { Promises, timeout } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('StorageMainService (native)', function () { @@ -141,7 +141,7 @@ suite('StorageMainService (native)', function () { return testStorage(storageMainService.workspaceStorage(workspace), false); }); - test('storage closed onWillShutdown', async function () { + test.skip('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); const workspace = { id: generateUuid() }; const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), lifecycleMainService, new TestConfigurationService()); @@ -162,7 +162,6 @@ suite('StorageMainService (native)', function () { await workspaceStorage.initialize(); - await timeout(0); await lifecycleMainService.fireOnWillShutdown(); strictEqual(didCloseGlobalStorage, true); From 04b7b5ee4c71d4895496dca6bb09185ba9734c03 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 14:48:45 +0100 Subject: [PATCH 115/176] storage - let renderer close workspace DB --- src/vs/platform/storage/common/storageIpc.ts | 29 +++++++++++-------- .../storage/electron-main/storageIpc.ts | 12 ++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index fe88c0815c7..8ff562fc44f 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -29,9 +29,9 @@ export interface ISerializableItemsChangeEvent { abstract class BaseStorageDatabaseClient extends Disposable implements IStorageDatabase { - abstract onDidChangeItemsExternal: Event; + abstract readonly onDidChangeItemsExternal: Event; - constructor(protected channel: IChannel, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { + constructor(protected channel: IChannel, protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { super(); } @@ -56,16 +56,7 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD return this.channel.call('updateItems', serializableRequest); } - async close(): Promise { - - // The database connection is not owned by us, but rather on the - // main side, as such we do not forward the close() request but - // let main side handle this properly via lifecycle methods. - // - // However, we cleanup our listeners because we are no longer - // interested in change events from the global database - this.dispose(); - } + abstract close(): Promise; } class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { @@ -91,6 +82,14 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I }); } } + + async close(): Promise { + + // The global storage database is shared across all instances so + // we do not await it. However we dispose the listener for external + // changes because we no longer interested int it. + this.dispose(); + } } class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { @@ -100,6 +99,12 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { super(channel, workspace); } + + async close(): Promise { + const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + + return this.channel.call('close', serializableRequest); + } } export class StorageDatabaseChannelClient extends Disposable { diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 2fb542e34cf..551953afbd0 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -104,6 +104,18 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel break; } + case 'close': { + + // We only allow to close workspace scoped storage because + // global storage is shared across all windows and closes + // only on shutdown. + if (workspace) { + return storage.close(); + } + + break; + } + default: throw new Error(`Call not found: ${command}`); } From 974b2143344d56d7728047b843a06b02e9942cc0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 14:31:15 +0100 Subject: [PATCH 116/176] rename: resolveNotebook is openNotebook --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 6 +++--- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostNotebook.ts | 2 +- .../contrib/notebook/browser/notebookServiceImpl.ts | 2 +- src/vs/workbench/contrib/notebook/common/notebookService.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 5866a81d96a..b9ba768326d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -480,7 +480,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }, viewOptions: options.viewOptions, reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => { - const data = await this._proxy.$resolveNotebookData(viewType, mainthreadTextModel.uri); + const data = await this._proxy.$openNotebook(viewType, mainthreadTextModel.uri); mainthreadTextModel.metadata = data.metadata; mainthreadTextModel.transientOptions = contentOptions; @@ -494,8 +494,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }); }); }, - resolveNotebookDocument: async (viewType: string, uri: URI, backupId?: string) => { - const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId); + openNotebook: async (viewType: string, uri: URI, backupId?: string) => { + const data = await this._proxy.$openNotebook(viewType, uri, backupId); return { data, transientOptions: contentOptions diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d8dea657c6f..c2f4eed1aee 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1785,7 +1785,7 @@ export interface INotebookKernelInfoDto2 { } export interface ExtHostNotebookShape { - $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; + $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise; $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise; $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 6b995a33914..b58b4880b57 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -442,7 +442,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise { + async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise { const provider = this._notebookContentProviders.get(viewType); if (!provider) { throw new Error(`NO provider for '${viewType}'`); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 3aabfc323aa..5049ff03ab4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -768,7 +768,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu return notebookModel; } else { - const dataDto = await provider.controller.resolveNotebookDocument(viewType, uri, backupId); + const dataDto = await provider.controller.openNotebook(viewType, uri, backupId); let cells = dataDto.data.cells.length ? dataDto.data.cells : (uri.scheme === Schemas.untitled ? [{ cellKind: CellKind.Code, language: 'plaintext', //TODO@jrieken unsure what this is diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index b0faba9fcd8..c8cdc11f85a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -23,7 +23,7 @@ export interface IMainNotebookController { supportBackup: boolean; viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; }; options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; - resolveNotebookDocument(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>; + openNotebook(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; From 5fc7d91fb31d27ad58edc44d3456188638b81954 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 15:00:17 +0100 Subject: [PATCH 117/176] workaround #116691 --- src/vs/workbench/services/log/electron-sandbox/logService.ts | 1 - src/vs/workbench/services/log/electron-sandbox/loggerService.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/log/electron-sandbox/logService.ts b/src/vs/workbench/services/log/electron-sandbox/logService.ts index d0fba0e728c..e9f19330624 100644 --- a/src/vs/workbench/services/log/electron-sandbox/logService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/logService.ts @@ -37,5 +37,4 @@ export class NativeLogService extends LogService { this._register(disposables); } - } diff --git a/src/vs/workbench/services/log/electron-sandbox/loggerService.ts b/src/vs/workbench/services/log/electron-sandbox/loggerService.ts index 50795cdf780..fbe8bc70033 100644 --- a/src/vs/workbench/services/log/electron-sandbox/loggerService.ts +++ b/src/vs/workbench/services/log/electron-sandbox/loggerService.ts @@ -40,7 +40,7 @@ class Logger extends AbstractMessageLogger { loggerOptions?: ILoggerOptions, ) { super(loggerOptions?.always); - (this.file ? this.channel.call('createLogger', [file, loggerOptions]) : this.channel.call('createConsoleMainLogger', [file, loggerOptions])) + (this.file ? this.channel.call('createLogger', [file, loggerOptions]) : this.channel.call('createConsoleLogger', [file, loggerOptions])) .then(() => { this._log(this.buffer); this.isLoggerCreated = true; From 6b35ff74c14e665a0aa89b58775534390d71c6bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 15:44:29 +0100 Subject: [PATCH 118/176] storage - bring back tests for close on shutdown --- src/vs/base/parts/storage/common/storage.ts | 9 +++++- .../electron-main/storageMainService.test.ts | 28 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index 66023e7d651..a2c28e17070 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -84,7 +84,7 @@ export class Storage extends Disposable implements IStorage { private cache = new Map(); - private readonly flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); + private readonly flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY); private pendingDeletes = new Set(); private pendingInserts = new Map(); @@ -319,6 +319,13 @@ export class Storage extends Disposable implements IStorage { return new Promise(resolve => this.whenFlushedCallbacks.push(resolve)); } + + dispose(): void { + this.flushDelayer.cancel(); // workaround https://github.com/microsoft/vscode/issues/116777 + this.flushDelayer.dispose(); + + super.dispose(); + } } export class InMemoryStorageDatabase implements IStorageDatabase { diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index cb3ae06d0fa..a0aa7f151d3 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -19,7 +19,7 @@ import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { Promises } from 'vs/base/common/async'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -suite('StorageMainService (native)', function () { +suite('StorageMainService', function () { class TestStorageMainService extends StorageMainService { @@ -141,7 +141,7 @@ suite('StorageMainService (native)', function () { return testStorage(storageMainService.workspaceStorage(workspace), false); }); - test.skip('storage closed onWillShutdown', async function () { + test('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); const workspace = { id: generateUuid() }; const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), lifecycleMainService, new TestConfigurationService()); @@ -160,6 +160,7 @@ suite('StorageMainService (native)', function () { strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + await globalStorage.initialize(); await workspaceStorage.initialize(); await lifecycleMainService.fireOnWillShutdown(); @@ -172,4 +173,27 @@ suite('StorageMainService (native)', function () { return storage2.close(); }); + + test('storage closed before init works', async function () { + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + const workspace = { id: generateUuid() }; + + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; + }); + + let globalStorage1 = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage1.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); + + await globalStorage1.close(); + await workspaceStorage.close(); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); + }); }); From 8d9002679558432eca926b6bb4f767eaae6b1068 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 15:50:18 +0100 Subject: [PATCH 119/176] set output and editable properties --- .vscode/notebooks/inbox.github-issues | 27 ++++++++++++++++------- .vscode/notebooks/papercuts.github-issues | 23 ++++++++++++------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index be0a2609cb2..69c1b7b0aa9 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -3,45 +3,56 @@ "kind": 1, "language": "markdown", "value": "## tl;dr: Triage Inbox\n\nAll inbox issues but not those that need more information. These issues need to be triaged, e.g assigned to a user or ask for more information", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox -label:\"needs more info\"" + "value": "$inbox -label:\"needs more info\"", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "##### `Config`: defines the inbox query", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item " + "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ", + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## Inbox tracking and Issue triage", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/main/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## All Inbox Items\n\nAll issues that have no assignee and that have neither **feature requests** nor **test plan items** nor **plan items**.", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", - "value": "$inbox" + "value": "$inbox", + "editable": true, + "outputs": [] } ] \ No newline at end of file diff --git a/.vscode/notebooks/papercuts.github-issues b/.vscode/notebooks/papercuts.github-issues index 4da4734ce5d..01bb21518fc 100644 --- a/.vscode/notebooks/papercuts.github-issues +++ b/.vscode/notebooks/papercuts.github-issues @@ -3,42 +3,49 @@ "kind": 1, "language": "markdown", "value": "## Papercuts\n\nThis notebook serves as an ongoing collection of papercut issues that we encounter while dogfooding. With that in mind only promote issues that really turn you off, e.g. issues that make you want to stop using VS Code or its extensions. To mark an issue (bug, feature-request, etc.) as papercut add the labels: `papercut :drop_of_blood:`", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## All Papercuts\n\nThese are all papercut issues that we encounter while dogfooding vscode or extensions that we author.", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open -label:notebook label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "## Native Notebook", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open label:notebook label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### My Papercuts", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "repo:microsoft/vscode is:open assignee:@me label:\"papercut :drop_of_blood:\"", - "editable": true + "editable": true, + "outputs": [] } -] +] \ No newline at end of file From c9607e61e0fe8850de2b2b276234d719c8ce6ce7 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 16 Feb 2021 16:15:26 +0100 Subject: [PATCH 120/176] actionBar: always respect orientation, so only left / right navgiate in horizontal and up/down in vertical --- src/vs/base/browser/ui/actionbar/actionbar.ts | 18 ++++++++---------- src/vs/base/browser/ui/menu/menu.ts | 1 - src/vs/base/browser/ui/toolbar/toolbar.ts | 2 -- .../workbench/browser/parts/views/treeView.ts | 3 +-- .../contrib/debug/browser/breakpointsView.ts | 6 +++--- .../contrib/debug/browser/callStackView.ts | 7 +++---- .../extensions/browser/extensionsList.ts | 3 +-- .../files/browser/views/openEditorsView.ts | 4 ++-- .../markers/browser/markersTreeViewer.ts | 3 +-- .../notebook/browser/diff/diffComponents.ts | 3 +-- .../browser/diff/notebookTextDiffList.ts | 3 +-- .../browser/view/renderers/cellRenderer.ts | 9 +++------ .../preferences/browser/settingsTree.ts | 1 - .../preferences/browser/settingsWidgets.ts | 2 +- .../contrib/remote/browser/tunnelView.ts | 3 +-- .../scm/browser/scmRepositoryRenderer.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 5 ++--- .../search/browser/searchResultsView.ts | 6 +++--- .../testing/browser/testingExplorerView.ts | 3 +-- 19 files changed, 33 insertions(+), 51 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 83135785546..0dd80a02478 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -35,8 +35,6 @@ export interface IActionBarOptions { readonly triggerKeys?: ActionTrigger; readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; - // Pass true here when the up and down keys should not be eaten up by the ActionBar. For example, when an ActionBar is in the list. - readonly respectOrientationForPreviousAndNextKey?: boolean; } export interface IActionOptions extends IActionViewItemOptions { @@ -120,22 +118,22 @@ export class ActionBar extends Disposable implements IActionRunner { switch (this._orientation) { case ActionsOrientation.HORIZONTAL: - previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; - nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; + previousKeys = [KeyCode.LeftArrow]; + nextKeys = [KeyCode.RightArrow]; break; case ActionsOrientation.HORIZONTAL_REVERSE: - previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; - nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; + previousKeys = [KeyCode.RightArrow]; + nextKeys = [KeyCode.LeftArrow]; this.domNode.className += ' reverse'; break; case ActionsOrientation.VERTICAL: - previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; - nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; + previousKeys = [KeyCode.UpArrow]; + nextKeys = [KeyCode.DownArrow]; this.domNode.className += ' vertical'; break; case ActionsOrientation.VERTICAL_REVERSE: - previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow]; - nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow]; + previousKeys = [KeyCode.DownArrow]; + nextKeys = [KeyCode.UpArrow]; this.domNode.className += ' vertical reverse'; break; } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 9a9132359ce..025428f4046 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -86,7 +86,6 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, - respectOrientationForPreviousAndNextKey: true, triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true } }); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 3b91ffa5573..c1348a93ddc 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -28,7 +28,6 @@ export interface IToolBarOptions { anchorAlignmentProvider?: () => AnchorAlignment; renderDropdownAsChildElement?: boolean; moreIcon?: CSSIcon; - readonly respectOrientationForPreviousAndNextKey?: boolean; } /** @@ -64,7 +63,6 @@ export class ToolBar extends Disposable { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, - respectOrientationForPreviousAndNextKey: options.respectOrientationForPreviousAndNextKey, actionViewItemProvider: (action: IAction) => { if (action.id === ToggleMenuAction.ID) { this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index f8961757153..9c49ffdcca2 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -873,8 +873,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer { return action.createActionViewItem(); } return new ExtensionActionViewItem(null, action, actionOptions); - }, - respectOrientationForPreviousAndNextKey: true + } }); actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 9936ef5a754..e54534fd378 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -537,7 +537,7 @@ class EditorGroupRenderer implements IListRenderer action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined, - respectOrientationForPreviousAndNextKey: true + actionViewItemProvider: (action: IAction) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined })); this.icon = dom.append(parent, dom.$('')); this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 24f880fbabf..235dd429996 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -150,8 +150,7 @@ class PropertyHeader extends Disposable { } return undefined; - }, - respectOrientationForPreviousAndNextKey: true + } }); this._register(this._toolbar); this._toolbar.context = { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index e25db2f37b0..ce2bc9c6ec2 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -188,8 +188,7 @@ export class CellDiffSideBySideRenderer implements IListRenderer extends Dispo rowElement.setAttribute('tabindex', item.selected ? '0' : '-1'); rowElement.classList.toggle('selected', item.selected); - const actionBar = new ActionBar(rowElement, { respectOrientationForPreviousAndNextKey: true }); + const actionBar = new ActionBar(rowElement); this.listDisposables.add(actionBar); actionBar.push(this.getActionsForItem(item, idx), { icon: true, label: true }); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 02b75df99e9..daa3fba23f9 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -225,8 +225,7 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action) - : undefined, - respectOrientationForPreviousAndNextKey: true + : undefined }); return { label, actionBar, icon }; From e537fd9e3182ec03cf26d195fc17b184715628ec Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 16 Feb 2021 16:31:33 +0100 Subject: [PATCH 121/176] first cut of proposed "inline values provider" API --- src/vs/editor/common/modes.ts | 81 +++++++++- src/vs/vscode.proposed.d.ts | 143 +++++++++++++++++- .../api/browser/mainThreadLanguageFeatures.ts | 25 +++ .../workbench/api/common/extHost.api.impl.ts | 7 + .../workbench/api/common/extHost.protocol.ts | 7 + .../api/common/extHostApiCommands.ts | 8 + .../api/common/extHostLanguageFeatures.ts | 42 ++++- .../api/common/extHostTypeConverters.ts | 60 ++++++++ src/vs/workbench/api/common/extHostTypes.ts | 45 ++++++ .../debug/browser/debugEditorContribution.ts | 90 +++++++++-- 10 files changed, 487 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f4b946213dd..3731b7dee38 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -266,7 +266,7 @@ export interface HoverProvider { } /** - * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. * @internal */ @@ -275,15 +275,16 @@ 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. + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. * @internal */ export interface EvaluatableExpressionProvider { @@ -295,6 +296,73 @@ export interface EvaluatableExpressionProvider { provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * An open ended information bag passed to the inline value provider. + * A minimal context containes just the document location where the debugger has stopped. + * @internal + */ +export interface InlineValueContext { + stoppedLocation: Range; +} + +/** + * Provide inline value as text. + * @internal + */ +export interface InlineValueText { + type: 'text'; + text: string; + range: IRange; +} + +/** + * Provide inline value through a variable lookup. + * @internal + */ +export interface InlineValueVariableLookup { + type: 'variable'; + variableName: string; + caseSensitiveLookup: boolean; + range: IRange; +} + +/** + * Provide inline value through an expression evaluation. + * @internal + */ +export interface InlineValueExpression { + type: 'expression'; + expression: string; + range: IRange; +} + +/** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + * @internal + */ +export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueExpression; + +/** + * The inline values provider interface defines the contract between extensions and + * the debugger's inline values feature. + * @internal + */ +export interface InlineValuesProvider { + /** + */ + onDidChangeInlineValues?: Event | undefined; + /** + * Provide the "inline values" for the given range 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. + */ + provideInlineValues(model: model.ITextModel, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; +} + export const enum CompletionItemKind { Method, Function, @@ -1745,6 +1813,11 @@ export const HoverProviderRegistry = new LanguageFeatureRegistry( */ export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const InlineValuesProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6bd29a1c0f9..341b5b78df6 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -734,8 +734,149 @@ declare module 'vscode' { //#endregion + //#region inline value provider: https://github.com/microsoft/vscode/issues/105690 + + /** + * The inline values provider interface defines the contract between extensions and the VS Code debugger inline values feature. + * In this contract the provider returns inline value information for a given document range + * and VS Code shows this information in the editor at the end of lines. + */ + export interface InlineValuesProvider { + + /** + * An optional event to signal that inline values have changed. + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeInlineValues?: Event | undefined; + + /** + * Provide "inline value" information for a given document and range. + * VS Code calls this method whenever debugging stops in the given document. + * The returned inline values information is rendered in the editor at the end of lines. + * + * @param document The document for which the inline values information is needed. + * @param viewPort The visible document range for which inline values should be computed. + * @param context A bag containing contextual information like the current location. + * @param token A cancellation token. + * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; + } + + /** + * An open ended information bag passed to the inline value provider. + * A minimal context containes just the document location where the debugger has stopped. + * Additional optional information might be scope information or variables and their values. + */ + export interface InlineValueContext { + /** + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + stoppedLocation: Range; + + // ... more to come, e.g. Scope information or variable/value candidate information + } + + /** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + */ + export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; + + /** + * Provide inline value as text. + */ + export class InlineValueText { + /** + * The text of the inline value. + */ + readonly text: string; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueText object. + * + * @param text The value to be shown for the line. + * @param range The document line where to show the inline value. + */ + constructor(text: string, range: Range); + } + + /** + * Provide inline value through a variable lookup. + */ + export class InlineValueVariableLookup { + /** + * The name of the variable to look up. + */ + readonly variableName: string; + /** + * How to perform the lookup. + */ + readonly caseSensitiveLookup: boolean; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueVariableLookup object. + * + * @param variableName The name of the variable to look up. + * @param range The document line where to show the inline value. + * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. + */ + constructor(variableName: string, range: Range, caseSensitiveLookup?: boolean); + } + + /** + * Provide inline value through an expression evaluation. + */ + export class InlineValueEvaluatableExpression { + /** + * The expression to evaluate. + */ + readonly expression: string; + /** + * The range of the inline value. + */ + readonly range: Range; + /** + * Creates a new InlineValueEvaluatableExpression object. + * + * @param expression The expression to evaluate. + * @param range The document line where to show the inline value. + */ + constructor(expression: string, range: Range); + } + + export namespace languages { + + /** + * Register a provider that returns inline values for text documents. + * If debugging has stopped VS Code shows inline values in the editor at the end of lines. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline values provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; + } + + //#endregion + // eslint-disable-next-line vscode-dts-region-comments - //#region debug + //#region @weinand: variables view action contributions /** * A DebugProtocolVariableContainer is an opaque stand-in type for the intersection of the Scope and Variable types defined in the Debug Adapter Protocol. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 04d6dfed752..fd83c6e81a0 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -250,6 +250,31 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha })); } + // --- inline values + + $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { + const provider = { + provideInlineValues: (model: ITextModel, viewPort: EditorRange, context: modes.InlineValueContext, token: CancellationToken): Promise => { + return this._proxy.$provideInlineValues(handle, model.uri, viewPort, context, token); + } + }; + + if (typeof eventHandle === 'number') { + const emitter = new Emitter(); + this._registrations.set(eventHandle, emitter); + provider.onDidChangeInlineValues = emitter.event; + } + + this._registrations.set(handle, modes.InlineValuesProviderRegistry.register(selector, provider)); + } + + $emitInlineValuesEvent(eventHandle: number, event?: any): void { + const obj = this._registrations.get(eventHandle); + if (obj instanceof Emitter) { + obj.fire(event); + } + } + // --- 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 b9bacd2d7f6..45af435ed3d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -409,6 +409,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerEvaluatableExpressionProvider(selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider): vscode.Disposable { return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier); }, + registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier); + }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, @@ -1165,6 +1169,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I EndOfLine: extHostTypes.EndOfLine, EnvironmentVariableMutatorType: extHostTypes.EnvironmentVariableMutatorType, EvaluatableExpression: extHostTypes.EvaluatableExpression, + InlineValueText: extHostTypes.InlineValueText, + InlineValueVariableLookup: extHostTypes.InlineValueVariableLookup, + InlineValueEvaluatableExpression: extHostTypes.InlineValueEvaluatableExpression, EventEmitter: Emitter, ExtensionKind: extHostTypes.ExtensionKind, ExtensionMode: extHostTypes.ExtensionMode, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c2f4eed1aee..ceef2474341 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -378,6 +378,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; + $emitInlineValuesEvent(eventHandle: number, event?: any): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; @@ -1466,6 +1468,10 @@ export interface ILinkedEditingRangesDto { wordPattern?: IRegExpDto; } +export interface IInlineValueContextDto { + stoppedLocation: IRange; +} + export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; @@ -1477,6 +1483,7 @@ export interface ExtHostLanguageFeaturesShape { $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; + $provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: modes.InlineValueContext, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 93f77a9576d..6bdfa6fb2b4 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -359,6 +359,14 @@ const newCommands: ApiCommand[] = [ }; })) ), + // --- debug support + new ApiCommand( + 'vscode.executeInlineValueProvider', '_executeInlineValueProvider', 'Execute inline value provider', + [ApiCommandArgument.Uri, ApiCommandArgument.Range], + new ApiCommandResult('A promise that resolves to an array of InlineValue objects', result => { + return result.map(typeConverters.InlineValue.to); + }) + ), // --- open'ish commands new ApiCommand( 'vscode.open', '_workbench.open', 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.', diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 34ca106504d..5d96d713260 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -291,6 +291,24 @@ class EvaluatableExpressionAdapter { } } +class InlineValuesAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.InlineValuesProvider, + ) { } + + public provideInlineValues(resource: URI, viewPort: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise { + const doc = this._documents.getDocument(resource); + return asPromise(() => this._provider.provideInlineValues(doc, typeConvert.Range.to(viewPort), typeConvert.InlineValueContext.to(context), token)).then(value => { + if (Array.isArray(value)) { + return value.map(iv => typeConvert.InlineValue.from(iv)); + } + return undefined; + }); + } +} + class DocumentHighlightAdapter { constructor( @@ -1334,7 +1352,8 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter - | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter + | SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter + | EvaluatableExpressionAdapter | InlineValuesAdapter | LinkedEditingRangeAdapter | InlineHintsAdapter; class AdapterData { @@ -1568,6 +1587,27 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined); } + // --- debug inline values + + registerInlineValuesProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider, extensionId?: ExtensionIdentifier): vscode.Disposable { + + const eventHandle = typeof provider.onDidChangeInlineValues === 'function' ? this._nextHandle() : undefined; + const handle = this._addNewAdapter(new InlineValuesAdapter(this._documents, provider), extension); + + this._proxy.$registerInlineValuesProvider(handle, this._transformDocumentSelector(selector), eventHandle); + let result = this._createDisposable(handle); + + if (eventHandle !== undefined) { + const subscription = provider.onDidChangeInlineValues!(_ => this._proxy.$emitInlineValuesEvent(eventHandle)); + result = Disposable.from(result, subscription); + } + return result; + } + + $provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise { + return this._withAdapter(handle, InlineValuesAdapter, adapter => adapter.provideInlineValues(URI.revive(resource), range, context, 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 910683b56be..d3afe4eb214 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -850,6 +850,66 @@ export namespace EvaluatableExpression { } } +export namespace InlineValue { + export function from(inlineValue: vscode.InlineValue): modes.InlineValue { + if (inlineValue instanceof types.InlineValueText) { + return { + type: 'text', + text: inlineValue.text, + range: Range.from(inlineValue.range) + }; + } else if (inlineValue instanceof types.InlineValueVariableLookup) { + return { + type: 'variable', + variableName: inlineValue.variableName, + caseSensitiveLookup: inlineValue.caseSensitiveLookup, + range: Range.from(inlineValue.range) + }; + } else if (inlineValue instanceof types.InlineValueEvaluatableExpression) { + return { + type: 'expression', + expression: inlineValue.expression, + range: Range.from(inlineValue.range) + }; + } else { + throw new Error(`Unknown 'InlineValue' type`); + } + } + + export function to(inlineValue: modes.InlineValue): vscode.InlineValue { + switch (inlineValue.type) { + case 'text': + return { + text: inlineValue.text, + range: Range.to(inlineValue.range) + }; + case 'variable': + return { + variableName: inlineValue.variableName, + caseSensitiveLookup: inlineValue.caseSensitiveLookup, + range: Range.to(inlineValue.range) + }; + case 'expression': + return { + expression: inlineValue.expression, + range: Range.to(inlineValue.range) + }; + } + } +} + +export namespace InlineValueContext { + export function from(inlineValueContext: vscode.InlineValueContext): extHostProtocol.IInlineValueContextDto { + return { + stoppedLocation: Range.from(inlineValueContext.stoppedLocation) + }; + } + + export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext { + return new types.InlineValueContext(Range.to(inlineValueContext.stoppedLocation)); + } +} + 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 9bc85fa2ace..c815d8322af 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2439,6 +2439,51 @@ export class EvaluatableExpression implements vscode.EvaluatableExpression { } } +@es5ClassCompat +export class InlineValueText implements vscode.InlineValueText { + readonly range: Range; + readonly text: string; + + constructor(text: string, range: Range) { + this.text = text; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueVariableLookup implements vscode.InlineValueVariableLookup { + readonly variableName: string; + readonly caseSensitiveLookup: boolean; + readonly range: Range; + + constructor(variableName: string, range: Range, caseSensitiveLookup: boolean) { + this.variableName = variableName; + this.caseSensitiveLookup = caseSensitiveLookup; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueEvaluatableExpression implements vscode.InlineValueEvaluatableExpression { + readonly expression: string; + readonly range: Range; + + constructor(expression: string, range: Range) { + this.expression = expression; + this.range = range; + } +} + +@es5ClassCompat +export class InlineValueContext implements vscode.InlineValueContext { + + readonly stoppedLocation: vscode.Range; + + constructor(range: vscode.Range) { + this.stoppedLocation = range; + } +} + //#region file api export enum FileChangeType { diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index f3fa36a2c2e..346d7c395aa 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -11,7 +11,10 @@ import { setProperty } from 'vs/base/common/jsonEdit'; import { Constants } from 'vs/base/common/uint'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { StandardTokenType } from 'vs/editor/common/modes'; +import { InlineValuesProviderRegistry, StandardTokenType } from 'vs/editor/common/modes'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { flatten } from 'vs/base/common/arrays'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; @@ -47,7 +50,7 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped -function createInlineValueDecoration(lineNumber: number, contentText: string): IDecorationOptions { +function createInlineValueDecoration(lineNumber: number, contentText: string, column = Constants.MAX_SAFE_SMALL_INTEGER): IDecorationOptions { // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; @@ -57,8 +60,8 @@ function createInlineValueDecoration(lineNumber: number, contentText: string): I range: { startLineNumber: lineNumber, endLineNumber: lineNumber, - startColumn: Constants.MAX_SAFE_SMALL_INTEGER, - endColumn: Constants.MAX_SAFE_SMALL_INTEGER + startColumn: column, + endColumn: column }, renderOptions: { after: { @@ -572,20 +575,77 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.removeInlineValuesScheduler.cancel(); - const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); - // Get all top level children in the scope chain - const decorationsPerScope = await Promise.all(scopes.map(async scope => { - const children = await scope.getChildren(); - let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); - if (scope.range) { - range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); - } + let allDecorations: IDecorationOptions[]; - return createInlineValueDecorationsInsideRange(children, range, model, this.wordToLineNumbersMap); - })); + if (InlineValuesProviderRegistry.has(model)) { + const findVariable = async (key: string): Promise => { + const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); + for (let scope of scopes) { + const variables = await scope.getChildren(); + for (let v of variables) { + if (v.name === key) { + return v.value; + } + } + } + return undefined; + }; + + const ranges = this.editor.getVisibleRangesPlusViewportAboveBelow(); + + const ctx = { stoppedLocation: new Range(1, 1, stackFrame.range.startLineNumber, stackFrame.range.startColumn) }; + const token = new CancellationTokenSource().token; + + const providers = InlineValuesProviderRegistry.ordered(model).reverse(); + + allDecorations = []; + + const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineValues(model, range, ctx, token)).then(async (result) => { + if (result) { + for (let iv of result) { + let text: string; + switch (iv.type) { + case 'text': + text = iv.text; + break; + case 'variable': + const value = await findVariable(iv.variableName); + text = `${iv.variableName} = ${value}`; + break; + case 'expression': + text = `eval(${iv.expression})`; + break; + } + if (text) { + allDecorations.push(createInlineValueDecoration(iv.range.startLineNumber, text, iv.range.startColumn)); + } + } + } + }, err => { + onUnexpectedExternalError(err); + })))); + + await Promise.all(promises); + + } else { // one-size-fits-all strategy + + const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); + // Get all top level variables in the scope chain + const decorationsPerScope = await Promise.all(scopes.map(async scope => { + const variables = await scope.getChildren(); + + let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); + if (scope.range) { + range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); + } + + return createInlineValueDecorationsInsideRange(variables, range, model, this.wordToLineNumbersMap); + })); + + allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); + } - const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []); this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations); } From 8aa800e459adc6f56610df90b1bcff30e03acb01 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 16:15:47 +0100 Subject: [PATCH 122/176] allow to exclude a context key from registry, allow to provide type eplicitly, https://github.com/microsoft/vscode/issues/114867 --- src/vs/platform/contextkey/common/contextkey.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 22405c003f8..6ecb8a5dc76 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -1273,12 +1273,16 @@ export class RawContextKey extends ContextKeyDefinedExpr { private readonly _defaultValue: T | undefined; - constructor(readonly key: string, defaultValue: T | undefined, description?: string) { + constructor(readonly key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string, description: string }) { super(key); this._defaultValue = defaultValue; // collect all context keys into a central place - RawContextKey._info.push({ key, description, type: typeof defaultValue }); + if (typeof metaOrHide === 'object') { + RawContextKey._info.push({ ...metaOrHide, key }); + } else if (metaOrHide !== true) { + RawContextKey._info.push({ key, description: metaOrHide, type: typeof defaultValue }); + } } public bindTo(target: IContextKeyService): IContextKey { From 6200437f3f111bb677f972f49aae29c2a1cd488b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 16:33:41 +0100 Subject: [PATCH 123/176] add description to some RawContextKey-instances --- src/vs/editor/browser/core/keybindingCancellation.ts | 2 +- src/vs/editor/contrib/message/messageController.ts | 2 +- .../callHierarchy/browser/callHierarchy.contribution.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/core/keybindingCancellation.ts b/src/vs/editor/browser/core/keybindingCancellation.ts index 1f260f9b6cd..03d3347862b 100644 --- a/src/vs/editor/browser/core/keybindingCancellation.ts +++ b/src/vs/editor/browser/core/keybindingCancellation.ts @@ -22,7 +22,7 @@ interface IEditorCancellationTokens { cancel(editor: ICodeEditor): void; } -const ctxCancellableOperation = new RawContextKey('cancellableOperation', false); +const ctxCancellableOperation = new RawContextKey('cancellableOperation', false, 'Whether the editor runs a cancellable operation, e.g. like \'Peek References\''); registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 319f0d521d7..8f3a8dbbc2e 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -24,7 +24,7 @@ export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; - static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false); + static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false, 'Whether the editor is currently showing an inline message'); static get(editor: ICodeEditor): MessageController { return editor.getContribution(MessageController.ID); diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index 85c2711cb46..a3b90078e4e 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -26,9 +26,9 @@ import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false); -const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false); -const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined); +const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false, 'Whether a call hierarchy provider is available'); +const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false, 'Whether call hierarchy peek is currently showing'); +const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined, { type: 'string', description: 'Whether call hierarchy shows incoming or outgoing calls' }); function sanitizedDirection(candidate: string): CallHierarchyDirection { return candidate === CallHierarchyDirection.CallsFrom || candidate === CallHierarchyDirection.CallsTo From a17ad41546ce886c99477e71a82cbdc65b31d69b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Feb 2021 16:41:40 +0100 Subject: [PATCH 124/176] use nls#localize for context key descriptions --- src/vs/editor/browser/core/keybindingCancellation.ts | 3 ++- src/vs/editor/contrib/message/messageController.ts | 2 +- .../callHierarchy/browser/callHierarchy.contribution.ts | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/core/keybindingCancellation.ts b/src/vs/editor/browser/core/keybindingCancellation.ts index 03d3347862b..206fbeac885 100644 --- a/src/vs/editor/browser/core/keybindingCancellation.ts +++ b/src/vs/editor/browser/core/keybindingCancellation.ts @@ -12,6 +12,7 @@ import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cance import { LinkedList } from 'vs/base/common/linkedList'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { localize } from 'vs/nls'; const IEditorCancellationTokens = createDecorator('IEditorCancelService'); @@ -22,7 +23,7 @@ interface IEditorCancellationTokens { cancel(editor: ICodeEditor): void; } -const ctxCancellableOperation = new RawContextKey('cancellableOperation', false, 'Whether the editor runs a cancellable operation, e.g. like \'Peek References\''); +const ctxCancellableOperation = new RawContextKey('cancellableOperation', false, localize('cancellableOperation', 'Whether the editor runs a cancellable operation, e.g. like \'Peek References\'')); registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 8f3a8dbbc2e..d6f3357d3cd 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -24,7 +24,7 @@ export class MessageController implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; - static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false, 'Whether the editor is currently showing an inline message'); + static readonly MESSAGE_VISIBLE = new RawContextKey('messageVisible', false, nls.localize('messageVisible', 'Whether the editor is currently showing an inline message')); static get(editor: ICodeEditor): MessageController { return editor.getContribution(MessageController.ID); diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index a3b90078e4e..bcdb3736d19 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -26,9 +26,9 @@ import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false, 'Whether a call hierarchy provider is available'); -const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false, 'Whether call hierarchy peek is currently showing'); -const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined, { type: 'string', description: 'Whether call hierarchy shows incoming or outgoing calls' }); +const _ctxHasCallHierarchyProvider = new RawContextKey('editorHasCallHierarchyProvider', false, localize('editorHasCallHierarchyProvider', 'Whether a call hierarchy provider is available')); +const _ctxCallHierarchyVisible = new RawContextKey('callHierarchyVisible', false, localize('callHierarchyVisible', 'Whether call hierarchy peek is currently showing')); +const _ctxCallHierarchyDirection = new RawContextKey('callHierarchyDirection', undefined, { type: 'string', description: localize('callHierarchyDirection', 'Whether call hierarchy shows incoming or outgoing calls') }); function sanitizedDirection(candidate: string): CallHierarchyDirection { return candidate === CallHierarchyDirection.CallsFrom || candidate === CallHierarchyDirection.CallsTo From 7a9867c841e0222a08d29cbe6d0cc1512ba4c6e0 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 16 Feb 2021 07:48:31 -0800 Subject: [PATCH 125/176] first draft editor (#116599) --- src/vs/base/common/network.ts | 2 + .../workspace/common/workspaceTrust.ts | 5 + .../preferences/browser/settingsEditor2.ts | 2 +- .../preferences/browser/settingsTree.ts | 2 +- .../browser/workspace.contribution.ts | 29 +- .../browser/workspaceTrustEditor.css | 73 +++ .../workspace/browser/workspaceTrustEditor.ts | 177 +++++++ .../workspace/browser/workspaceTrustTree.ts | 471 ++++++++++++++++++ .../browser/workspaceTrustEditorInput.ts | 42 ++ .../workspaces/common/workspaceTrust.ts | 50 ++ 10 files changed, 848 insertions(+), 5 deletions(-) create mode 100644 src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css create mode 100644 src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts create mode 100644 src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts create mode 100644 src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index a409e7722ab..402d5149722 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -64,6 +64,8 @@ export namespace Schemas { export const vscodeSettings = 'vscode-settings'; + export const vscodeWorkspaceTrust = 'vscode-workspace-trust'; + export const webviewPanel = 'webview-panel'; /** diff --git a/src/vs/platform/workspace/common/workspaceTrust.ts b/src/vs/platform/workspace/common/workspaceTrust.ts index 56f4fae82dc..629eddaf9a0 100644 --- a/src/vs/platform/workspace/common/workspaceTrust.ts +++ b/src/vs/platform/workspace/common/workspaceTrust.ts @@ -37,6 +37,11 @@ export interface IWorkspaceTrustModel { setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void; getFolderTrustState(folder: URI): WorkspaceTrustState; + + setTrustedFolders(folders: URI[]): void; + setUntrustedFolders(folders: URI[]): void; + + getTrustStateInfo(): IWorkspaceTrustStateInfo; } export interface IWorkspaceTrustRequest { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 5f4734250dd..8d7405e04fe 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -61,7 +61,7 @@ export const enum SettingsFocusContext { SettingControl } -function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { +export function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { return Iterable.map(group.children, g => { return { element: g, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 6cee6eb3ffe..298ac757dd9 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1828,7 +1828,7 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate extends ObjectTreeModel { +export class NonCollapsibleObjectTreeModel extends ObjectTreeModel { isCollapsible(element: T): boolean { return false; } diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 8d6cb051ee9..d88325ffb9c 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./workspaceTrustEditor'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Severity } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceTrustService, WorkspaceTrustState, WorkspaceTrustStateChangeEvent, workspaceTrustStateToString } from 'vs/platform/workspace/common/workspaceTrust'; @@ -24,7 +26,10 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; -import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; +import { WorkspaceTrustEditor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustEditor'; +import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; +import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust'; const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, localize('workspaceTrustIcon', "Icon for workspace trust badge.")); @@ -181,6 +186,20 @@ Registry.as(WorkbenchExtensions.Workbench).regi LifecyclePhase.Ready ); +/** + * Trusted Workspace GUI Editor + */ +Registry.as(EditorExtensions.Editors).registerEditor( + EditorDescriptor.create( + WorkspaceTrustEditor, + WorkspaceTrustEditor.ID, + localize('workspaceTrustEditor', "Workspace Trust Editor") + ), + [ + new SyncDescriptor(WorkspaceTrustEditorInput) + ] +); + /* * Actions */ @@ -285,7 +304,11 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: WORKSPACE_TRUST_URI, mode: 'jsonc', options: { pinned: true } }); + const instantiationService = accessor.get(IInstantiationService); + + const input = instantiationService.createInstance(WorkspaceTrustEditorInput); + + editorService.openEditor(input); return; } }); diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css new file mode 100644 index 00000000000..371217bd035 --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-icon-label.file-icon.workspacetrusteditor-name-file-icon.ext-file-icon.tab-label::before { + font-family: 'codicon'; + content: '\eb53'; +} + +.workspace-trust-editor.settings-editor { + max-width: 1000px; + padding-top: 11px; + padding-left: 15px; + padding-right: 15px; + margin: auto; +} + +.workspace-trust-editor.settings-editor > .workspace-trust-header { + border-bottom: solid 1px; +} + +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-title { + font-size: 24px; + font-weight: 600; + padding: 10px 0px; +} + +/** Buttons Container */ +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { + display: flex; + align-items: center; + justify-content: flex-end; + padding-right: 1px; + overflow: hidden; /* buttons row should never overflow */ +} + +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { + display: flex; + white-space: nowrap; + padding: 20px 10px 10px; +} + +/** Buttons */ +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons { + display: flex; + overflow: hidden; +} + +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons .monaco-button { + width: fit-content; + width: -moz-fit-content; + padding: 5px 10px; + overflow: hidden; + text-overflow: ellipsis; + margin: 4px 5px; /* allows button focus outline to be visible */ + outline-offset: 2px !important; +} + +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header { + border-color: #cccccc; +} + +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header{ + border-color: #3c3c3c; +} + + +/** Settings */ +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents { + padding-left: 0px; +} + diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts new file mode 100644 index 00000000000..33bc5e1d73a --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, append, clearNode, Dimension, EventHelper } from 'vs/base/browser/dom'; +import { ButtonBar } from 'vs/base/browser/ui/button/button'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Iterable } from 'vs/base/common/iterator'; +import { isArray } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor'; +import { IWorkspaceTrustSettingChangeEvent, WorkspaceTrustSettingArrayRenderer, WorkspaceTrustTree, WorkspaceTrustTreeModel } from 'vs/workbench/contrib/workspace/browser/workspaceTrustTree'; +import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; +import { WorkspaceTrustEditorModel } from 'vs/workbench/services/workspaces/common/workspaceTrust'; + +export class WorkspaceTrustEditor extends EditorPane { + static readonly ID: string = 'workbench.editor.workspaceTrust'; + private rootElement!: HTMLElement; + private headerContainer!: HTMLElement; + private headerTitle!: HTMLElement; + private headerDescription!: HTMLElement; + private headerButtons!: HTMLElement; + private configurationContainer!: HTMLElement; + private trustSettingsTree!: WorkspaceTrustTree; + private workspaceTrustSettingsTreeModel!: WorkspaceTrustTreeModel; + private workspaceTrustEditorModel!: WorkspaceTrustEditorModel; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @ICommandService private readonly commandService: ICommandService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { super(WorkspaceTrustEditor.ID, telemetryService, themeService, storageService); } + + protected createEditor(parent: HTMLElement): void { + this.rootElement = append(parent, $('.workspace-trust-editor.settings-editor', { tabindex: '-1' })); + + this.createHeaderElement(this.rootElement); + this.createConfigurationElement(this.rootElement); + } + + async setInput(input: WorkspaceTrustEditorInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + + await super.setInput(input, options, context, token); + if (token.isCancellationRequested) { return; } + + const model = await input.resolve(); + if (token.isCancellationRequested || !(model instanceof WorkspaceTrustEditorModel)) { + return; + } + + this._register(model.dataModel.onDidChangeTrustState(() => { + this.render(model); + })); + + this.render(model); + + this.workspaceTrustEditorModel = model; + } + + private getHeaderTitleText(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return localize('trustedHeader', "This Workspace is Trusted"); + case WorkspaceTrustState.Untrusted: + return localize('untrustedHeader', "This Workspace is Not Trusted"); + case WorkspaceTrustState.Unknown: + return localize('unknownHeader', "This Workspace has Not Been Trusted"); + } + } + + private getHeaderDescriptionText(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return localize('trustedHeaderDescription', "All features requiring trust in this workspace are enabled. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inheret the current workspace trust status."); + case WorkspaceTrustState.Untrusted: + return localize('untrustedHeaderDescription', "This workspace has limited functionality as some features will not work until trust is given to the current workspace. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inherit the current workspace trust status."); + case WorkspaceTrustState.Unknown: + return localize('unknownHeaderDescription', "This workspace has limited functionality as some features will not work until trust is given to the current workspace. Below is the current list of features that will be disabled you grant trust to the workspace. Note that after trust is given, new features requiring trust will automatically inherit the current workspace trust status."); + } + } + + private render(model: WorkspaceTrustEditorModel): void { + this.headerTitle.innerText = this.getHeaderTitleText(model.currentWorkspaceTrustState); + this.headerDescription.innerText = this.getHeaderDescriptionText(model.currentWorkspaceTrustState); + + clearNode(this.headerButtons); + const buttonBar = this._register(new ButtonBar(this.headerButtons)); + + const createButton = (label: string, command: string) => { + const button = buttonBar.addButton({ title: true }); + button.label = label; + this._register(button.onDidClick(e => { + if (e) { + EventHelper.stop(e); + } + + this.commandService.executeCommand(command); + })); + }; + + if (model.currentWorkspaceTrustState !== WorkspaceTrustState.Trusted) { + createButton(localize('trustButton', "Trust"), 'workbench.trust.grant'); + } + + if (model.currentWorkspaceTrustState !== WorkspaceTrustState.Untrusted) { + createButton(localize('doNotTrustButton', "Don't Trust"), 'workbench.trust.deny'); + } + + createButton(localize('learnMore', "Learn More"), 'workbench.trust.learnMore'); + + this.workspaceTrustSettingsTreeModel.update(model.dataModel.getTrustStateInfo()); + + this.trustSettingsTree.setChildren(null, Iterable.map(this.workspaceTrustSettingsTreeModel.settings, s => { return { element: s }; })); + } + + private createHeaderElement(parent: HTMLElement): void { + this.headerContainer = append(parent, $('.workspace-trust-header')); + this.headerTitle = append(this.headerContainer, $('.workspace-trust-title')); + this.headerDescription = append(this.headerContainer, $('.workspace-trust-description')); + + const buttonsRow = append(this.headerContainer, $('.workspace-trust-buttons-row')); + this.headerButtons = append(buttonsRow, $('.workspace-trust-buttons')); + } + + private createConfigurationElement(parent: HTMLElement): void { + this.configurationContainer = append(parent, $('.workspace-trust-settings.settings-body')); + + const workspaceTrustTreeContainer = append(this.configurationContainer, $('.workspace-trust-settings-tree-container.settings-tree-container')); + const renderer = this.instantiationService.createInstance(WorkspaceTrustSettingArrayRenderer,); + + this.trustSettingsTree = this._register(this.instantiationService.createInstance(WorkspaceTrustTree, + workspaceTrustTreeContainer, + [renderer])); + + this.workspaceTrustSettingsTreeModel = this.instantiationService.createInstance(WorkspaceTrustTreeModel); + + this._register(renderer.onDidChangeSetting(e => this.onDidChangeSetting(e))); + } + + private onDidChangeSetting(change: IWorkspaceTrustSettingChangeEvent) { + if (this.workspaceTrustEditorModel) { + if (isArray(change.value)) { + console.log(change.key); + console.log(change.value); + if (change.key === 'trustedFolders') { + this.workspaceTrustEditorModel.dataModel.setTrustedFolders(change.value.map(item => URI.file(item))); + } + + if (change.key === 'untrustedFolders') { + this.workspaceTrustEditorModel.dataModel.setUntrustedFolders(change.value.map(item => URI.file(item))); + } + } + } + } + + layout(dimension: Dimension): void { + if (!this.isVisible()) { + return; + } + + const listHeight = dimension.height - this.configurationContainer.offsetTop; + this.configurationContainer.style.height = `${listHeight}`; + + this.trustSettingsTree.layout(listHeight, dimension.width); + } +} diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts new file mode 100644 index 00000000000..1abfc3cb2d7 --- /dev/null +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustTree.ts @@ -0,0 +1,471 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { addDisposableListener, append, EventType, $, createStyleSheet, trackFocus } from 'vs/base/browser/dom'; +import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IList } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; +import { ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { Color, RGBA } from 'vs/base/common/color'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { isArray } from 'vs/base/common/types'; +import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { NonCollapsibleObjectTreeModel } from 'vs/workbench/contrib/preferences/browser/settingsTree'; +import { focusedRowBackground, focusedRowBorder, IListDataItem, ISettingListChangeEvent, ListSettingWidget, rowHoverBackground, settingsHeaderForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { attachStyler } from 'vs/platform/theme/common/styler'; +import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IWorkspaceTrustStateInfo, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust'; + + +export class WorkspaceTrustSettingsTreeEntry { + id: string; + displayLabel: string; + setting: { + key: string; + description: string; + }; + value: string[]; + + constructor(key: string, displayLabel: string, description: string, value: string[]) { + this.setting = { key, description }; + this.displayLabel = displayLabel; + this.value = value; + this.id = key; + } +} + +export interface IWorkspaceTrustSettingItemTemplate { + onChange?: (value: T) => void; + + toDispose: DisposableStore; + context?: WorkspaceTrustSettingsTreeEntry; + containerElement: HTMLElement; + labelElement: HTMLElement; + descriptionElement: HTMLElement; + controlElement: HTMLElement; + elementDisposables: DisposableStore; +} + +interface IWorkspaceTrustSettingListItemTemplate extends IWorkspaceTrustSettingItemTemplate { + listWidget: ListSettingWidget; + validationErrorMessageElement: HTMLElement; +} + +export interface IWorkspaceTrustSettingChangeEvent { + key: string; + value: any; // undefined => reset/unconfigure +} + + +export class WorkspaceTrustSettingArrayRenderer extends Disposable implements ITreeRenderer { + templateId = 'template.setting.array'; + + static readonly CONTROL_CLASS = 'setting-control-focus-target'; + static readonly CONTROL_SELECTOR = '.' + WorkspaceTrustSettingArrayRenderer.CONTROL_CLASS; + static readonly CONTENTS_CLASS = 'setting-item-contents'; + static readonly CONTENTS_SELECTOR = '.' + WorkspaceTrustSettingArrayRenderer.CONTENTS_CLASS; + static readonly ALL_ROWS_SELECTOR = '.monaco-list-row'; + + static readonly SETTING_KEY_ATTR = 'data-key'; + static readonly SETTING_ID_ATTR = 'data-id'; + static readonly ELEMENT_FOCUSABLE_ATTR = 'data-focusable'; + + protected readonly _onDidChangeSetting = this._register(new Emitter()); + readonly onDidChangeSetting: Event = this._onDidChangeSetting.event; + + private readonly _onDidFocusSetting = this._register(new Emitter()); + readonly onDidFocusSetting: Event = this._onDidFocusSetting.event; + + private readonly _onDidChangeIgnoredSettings = this._register(new Emitter()); + readonly onDidChangeIgnoredSettings: Event = this._onDidChangeIgnoredSettings.event; + + constructor( + @IThemeService protected readonly _themeService: IThemeService, + @IContextViewService protected readonly _contextViewService: IContextViewService, + @IOpenerService protected readonly _openerService: IOpenerService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @ICommandService protected readonly _commandService: ICommandService, + @IContextMenuService protected readonly _contextMenuService: IContextMenuService, + @IKeybindingService protected readonly _keybindingService: IKeybindingService, + @IConfigurationService protected readonly _configService: IConfigurationService, + ) { + super(); + } + + renderCommonTemplate(tree: any, _container: HTMLElement, typeClass: string): IWorkspaceTrustSettingItemTemplate { + _container.classList.add('setting-item'); + _container.classList.add('setting-item-' + typeClass); + + const container = append(_container, $(WorkspaceTrustSettingArrayRenderer.CONTENTS_SELECTOR)); + container.classList.add('settings-row-inner-container'); + const titleElement = append(container, $('.setting-item-title')); + const labelCategoryContainer = append(titleElement, $('.setting-item-cat-label-container')); + const labelElement = append(labelCategoryContainer, $('span.setting-item-label')); + const descriptionElement = append(container, $('.setting-item-description')); + const modifiedIndicatorElement = append(container, $('.setting-item-modified-indicator')); + modifiedIndicatorElement.title = localize('modified', "Modified"); + + const valueElement = append(container, $('.setting-item-value')); + const controlElement = append(valueElement, $('div.setting-item-control')); + const toDispose = new DisposableStore(); + + const template: IWorkspaceTrustSettingItemTemplate = { + toDispose, + elementDisposables: new DisposableStore(), + containerElement: container, + labelElement, + descriptionElement, + controlElement + }; + + // Prevent clicks from being handled by list + toDispose.add(addDisposableListener(controlElement, EventType.MOUSE_DOWN, e => e.stopPropagation())); + + toDispose.add(addDisposableListener(titleElement, EventType.MOUSE_ENTER, e => container.classList.add('mouseover'))); + toDispose.add(addDisposableListener(titleElement, EventType.MOUSE_LEAVE, e => container.classList.remove('mouseover'))); + + return template; + } + + addSettingElementFocusHandler(template: IWorkspaceTrustSettingItemTemplate): void { + const focusTracker = trackFocus(template.containerElement); + template.toDispose.add(focusTracker); + focusTracker.onDidBlur(() => { + if (template.containerElement.classList.contains('focused')) { + template.containerElement.classList.remove('focused'); + } + }); + + focusTracker.onDidFocus(() => { + template.containerElement.classList.add('focused'); + + if (template.context) { + this._onDidFocusSetting.fire(template.context); + } + }); + } + + renderTemplate(container: HTMLElement): IWorkspaceTrustSettingListItemTemplate { + const common = this.renderCommonTemplate(null, container, 'list'); + const descriptionElement = common.containerElement.querySelector('.setting-item-description')!; + const validationErrorMessageElement = $('.setting-item-validation-message'); + descriptionElement.after(validationErrorMessageElement); + + const listWidget = this._instantiationService.createInstance(ListSettingWidget, common.controlElement); + listWidget.domNode.classList.add(WorkspaceTrustSettingArrayRenderer.CONTROL_CLASS); + common.toDispose.add(listWidget); + + const template: IWorkspaceTrustSettingListItemTemplate = { + ...common, + listWidget, + validationErrorMessageElement + }; + + this.addSettingElementFocusHandler(template); + + common.toDispose.add( + listWidget.onDidChangeList(e => { + const newList = this.computeNewList(template, e); + if (newList !== null && template.onChange) { + template.onChange(newList); + } + }) + ); + + return template; + } + + private computeNewList(template: IWorkspaceTrustSettingListItemTemplate, e: ISettingListChangeEvent): string[] | undefined | null { + if (template.context) { + let newValue: string[] = []; + if (isArray(template.context.value)) { + newValue = [...template.context.value]; + } + + if (e.targetIndex !== undefined) { + // Delete value + if (!e.item?.value && e.originalItem.value && e.targetIndex > -1) { + newValue.splice(e.targetIndex, 1); + } + // Update value + else if (e.item?.value && e.originalItem.value) { + if (e.targetIndex > -1) { + newValue[e.targetIndex] = e.item.value; + } + // For some reason, we are updating and cannot find original value + // Just append the value in this case + else { + newValue.push(e.item.value); + } + } + // Add value + else if (e.item?.value && !e.originalItem.value && e.targetIndex >= newValue.length) { + newValue.push(e.item.value); + } + } + + return newValue; + } + + return undefined; + } + + renderElement(node: ITreeNode, index: number, template: IWorkspaceTrustSettingListItemTemplate): void { + const element = node.element; + template.context = element; + + template.containerElement.setAttribute(WorkspaceTrustSettingArrayRenderer.SETTING_KEY_ATTR, element.setting.key); + template.containerElement.setAttribute(WorkspaceTrustSettingArrayRenderer.SETTING_ID_ATTR, element.id); + + template.labelElement.textContent = element.displayLabel; + + template.descriptionElement.innerText = element.setting.description; + + const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value }); + this.renderValue(element, template, onChange); + } + + protected renderValue(dataElement: WorkspaceTrustSettingsTreeEntry, template: IWorkspaceTrustSettingListItemTemplate, onChange: (value: string[] | undefined) => void): void { + const value = getListDisplayValue(dataElement); + template.listWidget.setValue(value); + template.context = dataElement; + + template.onChange = (v) => { + onChange(v); + renderArrayValidations(dataElement, template, v, false); + }; + + renderArrayValidations(dataElement, template, value.map(v => v.value), true); + } + + disposeTemplate(template: IWorkspaceTrustSettingItemTemplate): void { + dispose(template.toDispose); + } + + disposeElement(_element: ITreeNode, _index: number, template: IWorkspaceTrustSettingItemTemplate, _height: number | undefined): void { + if (template.elementDisposables) { + template.elementDisposables.clear(); + } + } +} + +export class WorkspaceTrustTree extends WorkbenchObjectTree { + constructor( + container: HTMLElement, + renderers: ITreeRenderer[], + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super('WorkspaceTrustTree', container, + new WorkspaceTrustTreeDelegate(), + renderers, + { + horizontalScrolling: false, + supportDynamicHeights: true, + identityProvider: { + getId(e) { + return e.id; + } + }, + accessibilityProvider: new WorkspaceTrustTreeAccessibilityProvider(), + styleController: id => new DefaultStyleController(createStyleSheet(container), id), + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), + multipleSelectionSupport: false, + }, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService, + ); + + this.disposables.add(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + // Links appear inside other elements in markdown. CSS opacity acts like a mask. So we have to dynamically compute the description color to avoid + // applying an opacity to the link color. + const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); + + // Hack for subpixel antialiasing + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored { color: ${fgWithOpacity}; }`); + } + + const errorColor = theme.getColor(errorForeground); + if (errorColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-deprecation-message { color: ${errorColor}; }`); + } + + const invalidInputBackground = theme.getColor(inputValidationErrorBackground); + if (invalidInputBackground) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { background-color: ${invalidInputBackground}; }`); + } + + const invalidInputForeground = theme.getColor(inputValidationErrorForeground); + if (invalidInputForeground) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { color: ${invalidInputForeground}; }`); + } + + const invalidInputBorder = theme.getColor(inputValidationErrorBorder); + if (invalidInputBorder) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item.invalid-input .setting-item-control .monaco-inputbox.idle { outline-width: 0; border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + } + + const focusedRowBackgroundColor = theme.getColor(focusedRowBackground); + if (focusedRowBackgroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list-row.focused .settings-row-inner-container { background-color: ${focusedRowBackgroundColor}; }`); + } + + const rowHoverBackgroundColor = theme.getColor(rowHoverBackground); + if (rowHoverBackgroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list-row:not(.focused) .settings-row-inner-container:hover { background-color: ${rowHoverBackgroundColor}; }`); + } + + const focusedRowBorderColor = theme.getColor(focusedRowBorder); + if (focusedRowBorderColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::before, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .setting-item-contents::after { border-top: 1px solid ${focusedRowBorderColor} }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::before, + .workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .monaco-list:focus-within .monaco-list-row.focused .settings-group-title-label::after { border-top: 1px solid ${focusedRowBorderColor} }`); + } + + const headerForegroundColor = theme.getColor(settingsHeaderForeground); + if (headerForegroundColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .settings-group-title-label { color: ${headerForegroundColor}; }`); + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-label { color: ${headerForegroundColor}; }`); + } + + const focusBorderColor = theme.getColor(focusBorder); + if (focusBorderColor) { + collector.addRule(`.workspace-trust-editor > .workspace-trust-settings > .workspace-trust-settings-tree-container .setting-item-contents .setting-item-markdown a:focus { outline-color: ${focusBorderColor} }`); + } + })); + + this.getHTMLElement().classList.add('settings-editor-tree'); + + this.disposables.add(attachStyler(themeService, { + listBackground: editorBackground, + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: foreground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: foreground, + listFocusBackground: editorBackground, + listFocusForeground: foreground, + listHoverForeground: foreground, + listHoverBackground: editorBackground, + listHoverOutline: editorBackground, + listFocusOutline: editorBackground, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground + }, colors => { + this.style(colors); + })); + + this.disposables.add(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.list.smoothScrolling')) { + this.updateOptions({ + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling') + }); + } + })); + } + + protected createModel(user: string, view: IList>, options: IObjectTreeOptions): ITreeModel { + return new NonCollapsibleObjectTreeModel(user, view, options); + } +} + +export class WorkspaceTrustTreeModel { + + settings: WorkspaceTrustSettingsTreeEntry[] = []; + + update(trustInfo: IWorkspaceTrustStateInfo): void { + this.settings = []; + if (trustInfo.localFolders) { + const trustedFolders = trustInfo.localFolders.filter(folder => folder.trustState === WorkspaceTrustState.Trusted).map(folder => folder.uri); + const untrustedFolders = trustInfo.localFolders.filter(folder => folder.trustState === WorkspaceTrustState.Untrusted).map(folder => folder.uri); + + this.settings.push(new WorkspaceTrustSettingsTreeEntry( + 'trustedFolders', + localize('trustedFolders', "Trusted Folders"), + localize('trustedFoldersDescription', "All workspaces under the following folders will be trusted. In the event of a conflict with untrusted folders, the nearest parent will determine trust."), + trustedFolders)); + + this.settings.push(new WorkspaceTrustSettingsTreeEntry( + 'untrustedFolders', + localize('untrustedFolders', "Untrusted Folders"), + localize('untrustedFoldersDescription', "All workspaces under the following folders will not be trusted. In the event of a conflict with trusted folders, the nearest parent will determine trust."), + untrustedFolders)); + } + } +} + +class WorkspaceTrustTreeAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(element: WorkspaceTrustSettingsTreeEntry) { + if (element instanceof WorkspaceTrustSettingsTreeEntry) { + return `element.displayLabel`; + } + + return null; + } + + getWidgetAriaLabel() { + return localize('settings', "Workspace Trust Setting"); + } +} + +class WorkspaceTrustTreeDelegate extends CachedListVirtualDelegate { + + getTemplateId(element: WorkspaceTrustSettingsTreeEntry): string { + return 'template.setting.array'; + } + + hasDynamicHeight(element: WorkspaceTrustSettingsTreeEntry): boolean { + return true; + } + + protected estimateHeight(element: WorkspaceTrustSettingsTreeEntry): number { + return 104; + } +} + +function getListDisplayValue(element: WorkspaceTrustSettingsTreeEntry): IListDataItem[] { + if (!element.value || !isArray(element.value)) { + return []; + } + + return element.value.map((key: string) => { + return { + value: key + }; + }); +} + +function renderArrayValidations(dataElement: WorkspaceTrustSettingsTreeEntry, template: IWorkspaceTrustSettingListItemTemplate, v: string[] | undefined, arg3: boolean) { +} + diff --git a/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts new file mode 100644 index 00000000000..78181c577ee --- /dev/null +++ b/src/vs/workbench/services/workspaces/browser/workspaceTrustEditorInput.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust'; +import { EditorInput } from 'vs/workbench/common/editor'; +import { WorkspaceTrustEditorModel, WorkspaceTrustService } from 'vs/workbench/services/workspaces/common/workspaceTrust'; + +export class WorkspaceTrustEditorInput extends EditorInput { + static readonly ID: string = 'workbench.input.workspaceTrust'; + + readonly resource: URI = URI.from({ + scheme: Schemas.vscodeWorkspaceTrust, + path: `workspaceTrustEditor` + }); + + constructor( + @IWorkspaceTrustService private readonly workspaceTrustService: WorkspaceTrustService + ) { + super(); + } + + getTypeId(): string { + return WorkspaceTrustEditorInput.ID; + } + + matches(otherInput: unknown): boolean { + return otherInput instanceof WorkspaceTrustEditorInput; + } + + getName(): string { + return localize('workspaceTrustEditorInputName', "Workspace Trust"); + } + + async resolve(): Promise { + return this.workspaceTrustService.workspaceTrustEditorModel; + } +} diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index f8ea335eae8..192c25c7aed 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -13,6 +13,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceTrustModel, IWorkspaceTrustRequest, IWorkspaceTrustRequestModel, IWorkspaceTrustService, IWorkspaceTrustStateInfo, WorkspaceTrustState, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust'; import { isEqual, isEqualOrParent } from 'vs/base/common/extpath'; +import { EditorModel } from 'vs/workbench/common/editor'; export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled'; export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key'; @@ -23,6 +24,18 @@ export const WorkspaceTrustContext = { TrustState: new RawContextKey('workspaceTrustState', WorkspaceTrustState.Unknown) }; +export class WorkspaceTrustEditorModel extends EditorModel { + constructor( + readonly dataModel: IWorkspaceTrustModel, + private readonly workspaceTrustService: WorkspaceTrustService + ) { + super(); + } + + get currentWorkspaceTrustState(): WorkspaceTrustState { + return this.workspaceTrustService.getWorkspaceTrustState(); + } +} export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustModel { private storageKey = WORKSPACE_TRUST_STORAGE_KEY; @@ -82,6 +95,30 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo this._onDidChangeTrustState.fire(); } + setTrustedFolders(folders: URI[]): void { + this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(folder => folder.trustState !== WorkspaceTrustState.Trusted); + for (const folder of folders) { + this.trustStateInfo.localFolders.push({ + trustState: WorkspaceTrustState.Trusted, + uri: folder.fsPath + }); + } + + this.saveTrustInfo(); + } + + setUntrustedFolders(folders: URI[]): void { + this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(folder => folder.trustState !== WorkspaceTrustState.Untrusted); + for (const folder of folders) { + this.trustStateInfo.localFolders.push({ + trustState: WorkspaceTrustState.Untrusted, + uri: folder.fsPath + }); + } + + this.saveTrustInfo(); + } + setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void { let changed = false; @@ -135,6 +172,10 @@ export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustMo return result; } + + getTrustStateInfo(): IWorkspaceTrustStateInfo { + return this.trustStateInfo; + } } export class WorkspaceTrustRequestModel extends Disposable implements IWorkspaceTrustRequestModel { @@ -166,6 +207,7 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust _serviceBrand: undefined; private readonly dataModel: IWorkspaceTrustModel; readonly requestModel: IWorkspaceTrustRequestModel; + private editorModel?: WorkspaceTrustEditorModel; private readonly _onDidChangeTrustState = this._register(new Emitter()); readonly onDidChangeTrustState = this._onDidChangeTrustState.event; @@ -212,6 +254,14 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust this._onDidChangeTrustState.fire({ previousTrustState: previousState, currentTrustState: this._currentTrustState }); } + get workspaceTrustEditorModel(): WorkspaceTrustEditorModel { + if (this.editorModel === undefined) { + this.editorModel = this._register(new WorkspaceTrustEditorModel(this.dataModel, this)); + } + + return this.editorModel; + } + private calculateWorkspaceTrustState(): WorkspaceTrustState { if (!this.isWorkspaceTrustEnabled()) { return WorkspaceTrustState.Trusted; From c441c567a31da4f6ddf4e9c7f0d14c1ad9395cf2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 17:10:12 +0100 Subject: [PATCH 126/176] storage - implement first cut migration support --- .../electron-sandbox/storageService2.ts | 83 +++++++++++++------ 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index c9bbecee81c..5f8efb05026 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, MutableDisposable } from 'vs/base/common/lifecycle'; import { StorageScope, WillSaveStateReason, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { Storage, IStorage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -16,8 +16,14 @@ import { joinPath } from 'vs/base/common/resources'; export class NativeStorageService2 extends AbstractStorageService { + // Global Storage is readonly and shared across windows private readonly globalStorage: IStorage; - private readonly workspaceStorage: IStorage | undefined; + + // Workspace Storage is scoped to a window but can change + // in the current window, when entering a workspace! + private workspaceStorage: IStorage | undefined = undefined; + private workspaceStorageId: string | undefined = undefined; + private workspaceStorageDisposables = this._register(new MutableDisposable()); private initializePromise: Promise | undefined; @@ -25,23 +31,44 @@ export class NativeStorageService2 extends AbstractStorageService { private runWhenIdleDisposable: IDisposable | undefined = undefined; constructor( - private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, - mainProcessService: IMainProcessService, + workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, + private readonly mainProcessService: IMainProcessService, private readonly environmentService: IEnvironmentService ) { super(); - // Connect to storage via channel client - const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), workspace); - this.globalStorage = new Storage(storageDataBaseClient.globalStorage); - this.workspaceStorage = storageDataBaseClient.workspaceStorage ? new Storage(storageDataBaseClient.workspaceStorage) : undefined; - - this.registerListeners(); + this.globalStorage = this.createGlobalStorage(); + this.workspaceStorage = this.createWorkspaceStorage(workspace); } - private registerListeners(): void { - this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); - this._register(this.workspaceStorage?.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)) ?? Disposable.None); + private createGlobalStorage(): IStorage { + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), undefined); + + const globalStorage = new Storage(storageDataBaseClient.globalStorage); + + this._register(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + + return globalStorage; + } + + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; + private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { + + // Keep id around for logging + this.workspaceStorageId = workspace?.id; + + // Create new + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), workspace); + if (storageDataBaseClient.workspaceStorage) { + const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); + + this.workspaceStorageDisposables.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + + return workspaceStorage; + } + + return undefined; } initialize(): Promise { @@ -76,7 +103,7 @@ export class NativeStorageService2 extends AbstractStorageService { } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspace ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspace.id, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : undefined; + return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : undefined; } private doFlushWhenIdle(): void { @@ -113,22 +140,24 @@ export class NativeStorageService2 extends AbstractStorageService { } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { - // if (this.workspaceStoragePath === SQLiteStorageDatabase.IN_MEMORY_PATH) { - // return; // no migration needed if running in memory - // } - // // Close workspace DB to be able to copy - // await this.getStorage(StorageScope.WORKSPACE).close(); + // Keep current workspace storage items around to restore + const oldWorkspaceStorage = this.workspaceStorage; + const oldItems = oldWorkspaceStorage?.items ?? new Map(); - // // Prepare new workspace storage folder - // const result = await this.prepareWorkspaceStorageFolder(toWorkspace); + // Close current which will change to new workspace storage + if (oldWorkspaceStorage) { + await oldWorkspaceStorage.close(); + oldWorkspaceStorage.dispose(); + } - // const newWorkspaceStoragePath = join(result.path, NativeStorageService.WORKSPACE_STORAGE_NAME); + // Create new workspace storage & init + this.workspaceStorage = this.createWorkspaceStorage(toWorkspace); + await this.workspaceStorage.init(); - // // Copy current storage over to new workspace storage - // await copy(assertIsDefined(this.workspaceStoragePath), newWorkspaceStoragePath, { preserveSymlinks: false }); - - // // Recreate and init workspace storage - // return this.createWorkspaceStorage(newWorkspaceStoragePath).init(); + // Copy over previous keys + for (const [key, value] of oldItems) { + this.workspaceStorage.set(key, value); + } } } From 7ce6ee7559094a83be0a5e081d54e70acac8fb9c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 08:03:07 -0800 Subject: [PATCH 127/176] testing: fix event listener leak Fixes #116775 --- .../testing/browser/testingDecorations.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 567258a3597..415b2ae90ad 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -33,6 +33,7 @@ import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/te export class TestingDecorations extends Disposable implements IEditorContribution { private collection = this._register(new MutableDisposable>()); + private currentUri?: URI; private lastDecorations: ITestDecoration[] = []; constructor( @@ -52,9 +53,22 @@ export class TestingDecorations extends Disposable implements IEditorContributio } } })); + + this._register(this.results.onTestChanged(({ item: result }) => { + if (this.currentUri && result.item.location?.uri.toString() === this.currentUri.toString()) { + this.setDecorations(this.currentUri); + } + })); + this._register(this.results.onResultsChanged(() => { + if (this.currentUri) { + this.setDecorations(this.currentUri); + } + })); } private attachModel(uri?: URI) { + this.currentUri = uri; + if (!uri) { this.collection.value = undefined; this.clearDecorations(); @@ -62,15 +76,6 @@ export class TestingDecorations extends Disposable implements IEditorContributio } this.collection.value = this.testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, uri, () => this.setDecorations(uri)); - this._register(this.results.onTestChanged(({ item: result }) => { - if (result.item.location?.uri.toString() === uri.toString()) { - this.setDecorations(uri); - } - })); - this._register(this.results.onResultsChanged(() => { - this.setDecorations(uri); - })); - this.setDecorations(uri); } From 68b67a345596557390e7618dd931af736a198ec9 Mon Sep 17 00:00:00 2001 From: Utku Gultopu Date: Tue, 16 Feb 2021 11:48:03 -0500 Subject: [PATCH 128/176] Keep misspelled property name in timerService Per https://github.com/microsoft/vscode/pull/116728#pullrequestreview-590976270. --- .../contrib/performance/browser/perfviewEditor.ts | 2 +- src/vs/workbench/services/timer/browser/timerService.ts | 8 ++++---- .../services/timer/electron-sandbox/timerService.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 7e65342f581..4b810831bab 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -156,7 +156,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { if (metrics.meminfo) { md.li(`Memory(Process): ${(metrics.meminfo.workingSetSize / ByteSize.KB).toFixed(2)} MB working set(${(metrics.meminfo.privateBytes / ByteSize.KB).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / ByteSize.KB).toFixed(2)}MB shared)`); } - md.li(`VM(likelihood): ${metrics.isVMLikelihood}%`); + md.li(`VM(likelihood): ${metrics.isVMLikelyhood}%`); md.li(`Initial Startup: ${metrics.initialStartup}`); md.li(`Has ${metrics.windowCount - 1} other windows`); md.li(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`); diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index fbf80f6aad0..324de644b65 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -68,7 +68,7 @@ export interface IMemoryInfo { "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "isVMLikelihood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } @@ -324,7 +324,7 @@ export interface IStartupMetrics { }; readonly hasAccessibilitySupport: boolean; - readonly isVMLikelihood?: number; + readonly isVMLikelyhood?: number; readonly platform?: string; readonly release?: string; readonly arch?: string; @@ -546,7 +546,7 @@ export abstract class AbstractTimerService implements ITimerService { meminfo: undefined, cpus: undefined, loadavg: undefined, - isVMLikelihood: undefined, + isVMLikelyhood: undefined, initialStartup, hasAccessibilitySupport: this._accessibilityService.isScreenReaderOptimized(), emptyWorkbench: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY @@ -578,7 +578,7 @@ export class TimerService extends AbstractTimerService { return 1; } protected async _extendStartupInfo(info: Writeable): Promise { - info.isVMLikelihood = 0; + info.isVMLikelyhood = 0; info.platform = navigator.userAgent; info.release = navigator.appVersion; } diff --git a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts index feb1c404ae2..cfd5a044410 100644 --- a/src/vs/workbench/services/timer/electron-sandbox/timerService.ts +++ b/src/vs/workbench/services/timer/electron-sandbox/timerService.ts @@ -68,7 +68,7 @@ export class TimerService extends AbstractTimerService { sharedBytes: processMemoryInfo.shared }; - info.isVMLikelihood = Math.round((virtualMachineHint * 100)); + info.isVMLikelyhood = Math.round((virtualMachineHint * 100)); const rawCpus = osProperties.cpus; if (rawCpus && rawCpus.length > 0) { From d22941af5d75800591770e69fd6ef08593cf1d75 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 16 Feb 2021 18:14:55 +0100 Subject: [PATCH 129/176] startDebugActionViewItem: fix focus navigation within ActionBar --- .../debug/browser/debugActionViewItems.ts | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index b600fc9943f..c6021398d25 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction, IActionRunner, IActionViewItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -19,20 +19,19 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ADD_CONFIGURATION_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons'; const $ = dom.$; -export class StartDebugActionViewItem implements IActionViewItem { +export class StartDebugActionViewItem extends BaseActionViewItem { private static readonly SEPARATOR = '─────────'; - actionRunner!: IActionRunner; private container!: HTMLElement; private start!: HTMLElement; private selectBox: SelectBox; - private options: { label: string, handler: (() => Promise) }[] = []; + private debugOptions: { label: string, handler: (() => Promise) }[] = []; private toDispose: IDisposable[]; private selected = 0; private providers: { label: string, type: string, pick: () => Promise<{ launch: ILaunch, config: IConfig } | undefined> }[] = []; @@ -47,6 +46,7 @@ export class StartDebugActionViewItem implements IActionViewItem { @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IContextViewService contextViewService: IContextViewService, ) { + super(context, action); this.toDispose = []; this.selectBox = new SelectBox([], -1, contextViewService, undefined, { ariaLabel: nls.localize('debugLaunchConfigurations', 'Debug Launch Configurations') }); this.toDispose.push(this.selectBox); @@ -72,7 +72,6 @@ export class StartDebugActionViewItem implements IActionViewItem { this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart))); this.start.title = this.action.label; this.start.setAttribute('role', 'button'); - this.start.tabIndex = 0; this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.CLICK, () => { this.start.blur(); @@ -104,7 +103,7 @@ export class StartDebugActionViewItem implements IActionViewItem { } })); this.toDispose.push(this.selectBox.onDidSelect(async e => { - const target = this.options[e.index]; + const target = this.debugOptions[e.index]; const shouldBeSelected = target.handler ? await target.handler() : false; if (shouldBeSelected) { this.selected = e.index; @@ -151,11 +150,13 @@ export class StartDebugActionViewItem implements IActionViewItem { if (fromRight) { this.selectBox.focus(); } else { + this.start.tabIndex = 0; this.start.focus(); } } blur(): void { + this.start.tabIndex = -1; this.container.blur(); } @@ -165,7 +166,7 @@ export class StartDebugActionViewItem implements IActionViewItem { private updateOptions(): void { this.selected = 0; - this.options = []; + this.debugOptions = []; const manager = this.debugService.getConfigurationManager(); const inWorkspace = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; let lastGroup: string | undefined; @@ -173,17 +174,17 @@ export class StartDebugActionViewItem implements IActionViewItem { manager.getAllConfigurations().forEach(({ launch, name, presentation }) => { if (lastGroup !== presentation?.group) { lastGroup = presentation?.group; - if (this.options.length) { - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); + if (this.debugOptions.length) { + this.debugOptions.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.debugOptions.length - 1); } } if (name === manager.selectedConfiguration.name && launch === manager.selectedConfiguration.launch) { - this.selected = this.options.length; + this.selected = this.debugOptions.length; } const label = inWorkspace ? `${name} (${launch.name})` : name; - this.options.push({ + this.debugOptions.push({ label, handler: async () => { await manager.selectConfiguration(launch, name); return true; @@ -194,9 +195,9 @@ export class StartDebugActionViewItem implements IActionViewItem { // Only take 3 elements from the recent dynamic configurations to not clutter the dropdown manager.getRecentDynamicConfigurations().slice(0, 3).forEach(({ name, type }) => { if (type === manager.selectedConfiguration.type && manager.selectedConfiguration.name === name) { - this.selected = this.options.length; + this.selected = this.debugOptions.length; } - this.options.push({ + this.debugOptions.push({ label: name, handler: async () => { await manager.selectConfiguration(undefined, name, undefined, { type }); @@ -205,16 +206,16 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - if (this.options.length === 0) { - this.options.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); + if (this.debugOptions.length === 0) { + this.debugOptions.push({ label: nls.localize('noConfigurations', "No Configurations"), handler: async () => false }); } - this.options.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); - disabledIdxs.push(this.options.length - 1); + this.debugOptions.push({ label: StartDebugActionViewItem.SEPARATOR, handler: () => Promise.resolve(false) }); + disabledIdxs.push(this.debugOptions.length - 1); this.providers.forEach(p => { - this.options.push({ + this.debugOptions.push({ label: `${p.label}...`, handler: async () => { const picked = await p.pick(); @@ -229,7 +230,7 @@ export class StartDebugActionViewItem implements IActionViewItem { manager.getLaunches().filter(l => !l.hidden).forEach(l => { const label = inWorkspace ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); - this.options.push({ + this.debugOptions.push({ label, handler: async () => { await this.commandService.executeCommand(ADD_CONFIGURATION_ID, l.uri.toString()); return false; @@ -237,7 +238,7 @@ export class StartDebugActionViewItem implements IActionViewItem { }); }); - this.selectBox.setOptions(this.options.map((data, index) => { text: data.label, isDisabled: disabledIdxs.indexOf(index) !== -1 }), this.selected); + this.selectBox.setOptions(this.debugOptions.map((data, index) => { text: data.label, isDisabled: disabledIdxs.indexOf(index) !== -1 }), this.selected); } } From 5c005324bb4194e9b7ab0734834b5c16ef16a748 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Feb 2021 18:44:11 +0100 Subject: [PATCH 130/176] tests - enable workspace tests again for now --- scripts/test-integration.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index b1d5e9588e3..537347a6dba 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -68,10 +68,8 @@ after_suite "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR after_suite -# TODO(deepak1556): Disable workspace test temporarily -# https://github.com/microsoft/vscode/issues/111288 -#"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -#after_suite +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +after_suite "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR after_suite From 53d2a737761f12dfc86ab871d6d2ad42ed0fa40f Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 16 Feb 2021 10:48:58 -0800 Subject: [PATCH 131/176] Clean up focused cell styling (fixes #116797) --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 1 + .../contrib/notebook/browser/notebookEditorWidget.ts | 1 + .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ++-- .../contrib/notebook/browser/viewModel/codeCellViewModel.ts | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 3d015269f38..543decf9627 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -287,6 +287,7 @@ position: absolute; width: 1px; height: 100%; + z-index: 1; } /* top border */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 6f8e12d51b9..862443a95d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2532,6 +2532,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`); + collector.addRule(`.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before, .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before { top: -${CELL_TOP_MARGIN}px; height: calc(100% + ${CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN}px)}`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 05567647704..c976351d704 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -266,8 +266,8 @@ abstract class AbstractCellRenderer { if (actions.primary.length || actions.secondary.length) { templateData.container.classList.add('cell-has-toolbar-actions'); if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT}px`; - templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT}px`; + templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; + templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; } } else { templateData.container.classList.remove('cell-has-toolbar-actions'); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index c59b6d52458..bda35e8bd33 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -131,7 +131,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } const statusbarHeight = this.getEditorStatusbarHeight(); - const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN; + const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; @@ -152,7 +152,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } else { outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight; - const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight + CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN; + const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight; const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight; const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; From 197f453aa9560872370e4b8e4b3b2f9a93c4ad68 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 16 Feb 2021 11:30:42 -0800 Subject: [PATCH 132/176] Show tailored notification when paste isn't supported --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 7674a991804..3e3dafe4c3c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -35,6 +35,7 @@ import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/c import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { equals } from 'vs/base/common/arrays'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -244,7 +245,11 @@ export class TerminalViewPane extends ViewPane { await terminal.copySelection(); terminal.clearSelection(); } else { - terminal.paste(); + if (!BrowserFeatures.clipboard.readText) { + terminal.paste(); + } else { + this._notificationService.info('This browser doesn\'t support the clipboard.readText API needed to trigger a paste'); + } } // Clear selection after all click event bubbling is finished on Mac to prevent // right-click selecting a word which is seemed cannot be disabled. There is a From e7d2a864e4a7e4c82ef7901559beeacd9bf8a0da Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 12:48:16 -0800 Subject: [PATCH 133/176] testing: add contextual commands for running tests Fixes #116589 --- src/vs/base/common/map.ts | 82 +++++++++ src/vs/base/test/common/map.test.ts | 30 +++- .../hierarchalByLocation.ts | 25 +-- .../testing/browser/testExplorerActions.ts | 159 +++++++++++++++++- .../testing/browser/testing.contribution.ts | 4 + .../testing/common/testResultService.ts | 22 ++- 6 files changed, 305 insertions(+), 17 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 0e08876e537..685cbaa95e4 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -1048,3 +1048,85 @@ export class LRUCache extends LinkedMap { } } } + +type IndexRecord = [fn: (value: V) => R, map: Map]; + +/** + * Map that supports multiple indicies whose keys are derived from the value. + */ +export class IndexedSet implements Set { + public get size() { + return this.source.size; + } + + private source = new Set(); + private readonly indexes: IndexRecord[] = []; + + /** + * Creates a map that maintains a copy of the items in the set, indexed + * by the given 'indexer' function. + */ + index(indexer: (value: V) => R) { + const map = new Map(); + for (const value of this.source) { + map.set(indexer(value), value); + } + + this.indexes.push([indexer, map]); + return map as ReadonlyMap; + } + + add(value: V): this { + this.source.add(value); + for (const [index, map] of this.indexes) { + map.set(index(value), value); + } + + return this; + } + + clear(): void { + this.source.clear(); + for (const [, map] of this.indexes) { + map.clear(); + } + } + + delete(value: V): boolean { + if (!this.source.delete(value)) { + return false; + } + + for (const [index, map] of this.indexes) { + map.delete(index(value)); + } + + return true; + } + + forEach(callbackfn: (value: V, value2: V, set: Set) => void, thisArg?: any): void { + this.source.forEach(callbackfn, thisArg); + } + + has(value: V): boolean { + return this.source.has(value); + } + + [Symbol.toStringTag]: string; + + values(): IterableIterator { + return this.source.values(); + } + + entries(): IterableIterator<[V, V]> { + return this.source.entries(); + } + + keys(): IterableIterator { + return this.source.keys(); + } + + [Symbol.iterator](): IterableIterator { + return this.source.values(); + } +} diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index f157455531a..0c9fc51e097 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator, ConfigKeysIterator } from 'vs/base/common/map'; -import { URI } from 'vs/base/common/uri'; +import { ConfigKeysIterator, IndexedSet, LinkedMap, LRUCache, PathIterator, ResourceMap, StringIterator, TernarySearchTree, Touch, UriIterator } from 'vs/base/common/map'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; suite('Map', () => { @@ -1020,4 +1020,30 @@ suite('Map', () => { assert.strictEqual(map.get(windowsFile), 'true'); assert.strictEqual(map.get(uncFile), 'true'); }); + + test('IndexedSet - add', () => { + const i = new IndexedSet(); + i.add(2); + const map = i.index(v => v ** 2); + assert.deepStrictEqual(map, new Map([[4, 2]])); + + i.add(3); + assert.deepStrictEqual(map, new Map([[9, 3], [4, 2]])); + }); + + test('IndexedSet - delete', () => { + const i = new IndexedSet(); + const map = i.index(v => v ** 2); + i.add(2); + i.delete(2); + assert.deepStrictEqual(map, new Map([])); + }); + + test('IndexedSet - clear', () => { + const i = new IndexedSet(); + const map = i.index(v => v ** 2); + i.add(2); + i.clear(); + assert.deepStrictEqual(map, new Map([])); + }); }); diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index 47cbf47ad77..6cab52d3a1b 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -7,6 +7,7 @@ import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { Emitter } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IndexedSet } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; @@ -39,11 +40,13 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes private readonly updateEmitter = new Emitter(); private readonly changes = new NodeChangeList(); private readonly locations = new TestLocationStore(); + private readonly itemSource = new IndexedSet(); + private readonly itemsByExtId = this.itemSource.index(v => v.test.item.extId); /** * Map of item IDs to test item objects. */ - protected readonly items = new Map(); + protected readonly items = this.itemSource.index(v => v.test.id); /** * Root folders @@ -82,15 +85,13 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes // when test states change, reflect in the tree // todo: optimize this to avoid needing to iterate this._register(results.onTestChanged(({ item: result }) => { - for (const i of this.items.values()) { - if (i.test.item.extId === result.item.extId) { - i.ownState = result.state.state; - i.retired = result.retired; - refreshComputedState(computedStateAccessor, i, this.addUpdated, result.computedState); - this.addUpdated(i); - this.updateEmitter.fire(); - return; - } + const item = this.itemsByExtId.get(result.item.extId); + if (item) { + item.ownState = result.state.state; + item.retired = result.retired; + refreshComputedState(computedStateAccessor, item, this.addUpdated, result.computedState); + this.addUpdated(item); + this.updateEmitter.fire(); } })); @@ -219,14 +220,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes protected unstoreItem(item: HierarchicalElement) { item.parentItem.children.delete(item); - this.items.delete(item.test.id); + this.itemSource.delete(item); this.locations.remove(item); return item.children; } protected storeItem(item: HierarchicalElement) { item.parentItem.children.add(item); - this.items.set(item.test.id, item); + this.itemSource.add(item); this.locations.add(item); const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1]; diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 2dac0056fb6..52f558c3f00 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -25,6 +25,7 @@ import { TestExplorerViewMode, TestExplorerViewSorting, Testing } from 'vs/workb import { InternalTestItem, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection'; import { ITestingAutoRun } from 'vs/workbench/contrib/testing/common/testingAutoRun'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { ITestResult, ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService, waitForAllRoots, waitForAllTests } from 'vs/workbench/contrib/testing/common/testService'; import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService'; @@ -593,7 +594,6 @@ export class DebugAtCursor extends RunOrDebugAtCursor { } } - abstract class RunOrDebugCurrentFile extends Action2 { /** * @override @@ -667,3 +667,160 @@ export class DebugCurrentFile extends RunOrDebugCurrentFile { return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); } } + +abstract class RunOrDebugTestResults extends Action2 { + /** + * @override + */ + public async run(accessor: ServicesAccessor) { + const testService = accessor.get(ITestService); + const extIds = this.getTestExtIdsToRun(accessor); + if (extIds.size === 0) { + return; + } + + const workspaceTests = accessor.get(IWorkspaceTestCollectionService).subscribeToWorkspaceTests(); + + try { + await Promise.all(workspaceTests.workspaceFolderCollections.map(([, c]) => waitForAllTests(c))); + + const toRun: InternalTestItem[] = []; + for (const [, collection] of workspaceTests.workspaceFolderCollections) { + for (const node of collection.all) { + if (extIds.has(node.item.extId) && this.filter(node)) { + toRun.push(node); + extIds.delete(node.item.extId); + } + } + } + + await this.runTest(testService, toRun); + } finally { + workspaceTests.dispose(); + } + } + + protected abstract getTestExtIdsToRun(accessor: ServicesAccessor): Set; + + protected abstract filter(node: InternalTestItem): boolean; + + protected abstract runTest(service: ITestService, node: InternalTestItem[]): Promise; +} + +abstract class RunOrDebugFailedTests extends RunOrDebugTestResults { + /** + * @inheritdoc + */ + protected getTestExtIdsToRun(accessor: ServicesAccessor): Set { + const { results } = accessor.get(ITestResultService); + const extIds = new Set(); + for (let i = results.length - 1; i >= 0; i--) { + for (const test of results[i].tests) { + if (isFailedState(test.state.state)) { + extIds.add(test.item.extId); + } else { + extIds.delete(test.item.extId); + } + } + } + + return extIds; + } +} + +abstract class RunOrDebugLastRun extends RunOrDebugTestResults { + /** + * @inheritdoc + */ + protected getTestExtIdsToRun(accessor: ServicesAccessor): Set { + const lastResult = accessor.get(ITestResultService).results[0]; + const extIds = new Set(); + if (!lastResult) { + return extIds; + } + + for (const test of lastResult.tests) { + if (test.direct) { + extIds.add(test.item.extId); + } + } + + return extIds; + } +} + +export class ReRunFailedTests extends RunOrDebugFailedTests { + constructor() { + super({ + id: 'testing.reRunFailTests', + title: localize('testing.reRunFailTests', "Re-run Failed Tests"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugFailedTests extends RunOrDebugFailedTests { + constructor() { + super({ + id: 'testing.debugFailTests', + title: localize('testing.debugFailTests', "Debug Failed Tests"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class ReRunLastRun extends RunOrDebugLastRun { + constructor() { + super({ + id: 'testing.reRunLastRun', + title: localize('testing.reRunLastRun', "Re-run Last Run"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.runnable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} + +export class DebugLastRun extends RunOrDebugLastRun { + constructor() { + super({ + id: 'testing.debugLastRun', + title: localize('testing.debugLastRun', "Debug Last Run"), + f1: true, + category, + }); + } + + protected filter(node: InternalTestItem): boolean { + return node.item.debuggable; + } + + protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise { + return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 6ef650fcedf..411a66f1fa6 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -101,6 +101,10 @@ registerAction2(Action.DebugAtCursor); registerAction2(Action.RunAtCursor); registerAction2(Action.DebugCurrentFile); registerAction2(Action.RunCurrentFile); +registerAction2(Action.ReRunFailedTests); +registerAction2(Action.DebugFailedTests); +registerAction2(Action.ReRunLastRun); +registerAction2(Action.DebugLastRun); registerAction2(CloseTestPeek); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index c380e53708b..6cc3d553a54 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -56,6 +56,11 @@ export interface ITestResult { */ readonly isAutoRun?: boolean; + /** + * Gets all tests involved in the run. + */ + tests: IterableIterator; + /** * Gets the state of the test by its extension-assigned ID. */ @@ -147,6 +152,7 @@ const makeNodeAndChildren = ( test: IncrementalTestCollectionItem, byExtId: Map, byInternalId: Map, + isExecutedDirectly = true, ): TestResultItem => { const existing = byInternalId.get(test.id); if (existing) { @@ -154,10 +160,14 @@ const makeNodeAndChildren = ( } const mapped = itemToNode(test, byExtId, byInternalId); + if (isExecutedDirectly) { + mapped.direct = true; + } + for (const childId of test.children) { const child = collection.getNodeById(childId); if (child) { - makeNodeAndChildren(collection, child, byExtId, byInternalId); + makeNodeAndChildren(collection, child, byExtId, byInternalId, false); } } @@ -173,6 +183,7 @@ export interface TestResultItem extends IncrementalTestCollectionItem { state: ITestState; computedState: TestRunState; retired: boolean; + direct?: true; } /** @@ -230,7 +241,7 @@ export class LiveTestResult implements ITestResult { public readonly counts: { [K in TestRunState]: number } = makeEmptyCounts(); /** - * Gets all tests involved in the run by ID. + * @inheritdoc */ public get tests() { return this.testByInternalId.values(); @@ -420,6 +431,13 @@ class HydratedTestResult implements ITestResult { */ public readonly isComplete = true; + /** + * @inheritdoc + */ + public get tests() { + return this.byExtId.values(); + } + private readonly byExtId = new Map(); constructor(private readonly serialized: ISerializedResults) { From 52d1b626f3139b786f1fe592037ab5c02e37cdae Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 16 Feb 2021 14:05:49 -0800 Subject: [PATCH 134/176] Pick up latest TS version for building VS Code --- package.json | 2 +- .../base/browser/ui/dropdown/dropdownActionViewItem.ts | 5 ++++- src/vs/editor/common/modes/languageFeatureRegistry.ts | 4 ++-- src/vs/editor/common/modes/languageSelector.ts | 2 +- src/vs/workbench/api/common/extHost.api.impl.ts | 5 +++-- src/vs/workbench/api/common/extHostTypeConverters.ts | 9 +++++---- .../contrib/files/browser/views/explorerViewer.ts | 2 +- .../extensions/common/abstractExtensionService.ts | 10 +++++----- .../workbench/services/extensions/common/extensions.ts | 4 ++-- yarn.lock | 10 +++++----- 10 files changed, 29 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 3b0ce899f1c..1669245e977 100644 --- a/package.json +++ b/package.json @@ -190,7 +190,7 @@ "style-loader": "^1.0.0", "ts-loader": "^6.2.1", "tsec": "0.1.3", - "typescript": "4.2.0-dev.20201207", + "typescript": "^4.3.0-dev.20210216", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 2aaac8a7ab3..5b46a52a549 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -172,7 +172,10 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem { const menuActionsProvider = { getActions: () => { const actionsProvider = (this.options).menuActionsOrProvider; - return [this._action, ...(Array.isArray(actionsProvider) ? actionsProvider : actionsProvider.getActions())]; + return [this._action, ...(Array.isArray(actionsProvider) + ? actionsProvider + : (actionsProvider as IActionProvider).getActions()) // TODO: microsoft/TypeScript#42768 + ]; } }; this.dropdownMenuActionViewItem = new DropdownMenuActionViewItem(this._register(new Action('dropdownAction', undefined)), menuActionsProvider, this.contextMenuProvider, { classNames: ['dropdown', ...Codicon.dropDownButton.classNamesArray, ...(this.options).menuActionClassNames || []] }); diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index b5cd1ca4a75..7dec322f53f 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -9,7 +9,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { MovingAverage } from 'vs/base/common/numbers'; import { ITextModel } from 'vs/editor/common/model'; -import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; +import { LanguageFilter, LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; interface Entry { @@ -25,7 +25,7 @@ function isExclusive(selector: LanguageSelector): boolean { } else if (Array.isArray(selector)) { return selector.every(isExclusive); } else { - return !!selector.exclusive; + return !!(selector as LanguageFilter).exclusive; // TODO: microsoft/TypeScript#42768 } } diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index eff5d35ab30..02826877fdc 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -55,7 +55,7 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI, } else if (selector) { // filter -> select accordingly, use defaults for scheme - const { language, pattern, scheme, hasAccessToAllModels } = selector; + const { language, pattern, scheme, hasAccessToAllModels } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768 if (!candidateIsSynchronized && !hasAccessToAllModels) { return 0; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 45af435ed3d..9b95c71438e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -198,10 +198,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } else if (typeof selector === 'string') { informOnce(selector); } else { - if (typeof selector.scheme === 'undefined') { + const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768 + if (typeof filter.scheme === 'undefined') { informOnce(selector); } - if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') { + if (!extension.enableProposedApi && typeof filter.exclusive === 'boolean') { throwProposedApiError(extension); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d3afe4eb214..8b57657a364 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1394,11 +1394,12 @@ export namespace LanguageSelector { } else if (typeof selector === 'string') { return selector; } else { + const filter = selector as vscode.DocumentFilter; // TODO: microsoft/TypeScript#42768 return { - language: selector.language, - scheme: selector.scheme, - pattern: typeof selector.pattern === 'undefined' ? undefined : GlobPattern.from(selector.pattern), - exclusive: selector.exclusive + language: filter.language, + scheme: filter.scheme, + pattern: typeof filter.pattern === 'undefined' ? undefined : GlobPattern.from(filter.pattern), + exclusive: filter.exclusive }; } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 6bf781d2e31..67fdc77b546 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1229,7 +1229,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { res = await reader.read(); } - writeableStream.end(res.value instanceof Uint8Array ? VSBuffer.wrap(res.value) : undefined); + writeableStream.end(undefined); } catch (error) { writeableStream.end(error); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c8742070a12..94f39dc0dbf 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -21,7 +21,7 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -535,14 +535,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx }); } - public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { + public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { return this._installedExtensionsReady.wait().then(() => { const availableExtensions = this._registry.getAllExtensionDescriptions(); const result: ExtensionPointContribution[] = []; for (const desc of availableExtensions) { if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { - result.push(new ExtensionPointContribution(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes])); + result.push(new ExtensionPointContribution(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T)); } } @@ -691,13 +691,13 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } } - private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { + private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { const users: IExtensionPointUser[] = []; for (const desc of availableExtensions) { if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { users.push({ description: desc, - value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes], + value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T, collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) }); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 33883bedb25..ed224cb6eca 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; @@ -224,7 +224,7 @@ export interface IExtensionService { /** * Read all contributions to an extension point. */ - readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]>; + readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]>; /** * Get information about extensions status. diff --git a/yarn.lock b/yarn.lock index 8695942fcec..4fbbc3752b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9687,11 +9687,6 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@4.2.0-dev.20201207: - version "4.2.0-dev.20201207" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201207.tgz#19a34bc7d2d42a7467c512c63f135587ac848807" - integrity sha512-fPHBDi/fgdX4WiRC7cFVv/aL069PgUaDWuLYUSHatWZujz/Lkc9bkf/zL3rKdNSCxlNKAMs3fhJv/yompOphZA== - typescript@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" @@ -9702,6 +9697,11 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.3.0-dev.20210216: + version "4.3.0-dev.20210216" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210216.tgz#233327e6094008c02265ba140f8d9ece9133421e" + integrity sha512-pJLcC/kqnE+0rftTRc2/gYBkz9nl+kJfaU8sSOLYnzUvD8p+LOZMzXfaLoKPdGFJ6U9+Ox/sYV9HBTJVEjSTYg== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" From 2cc13674e3308f08664d0b4a93d8e28df51a7bbc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 16 Feb 2021 14:09:38 -0800 Subject: [PATCH 135/176] Also bump build version and run formatter on all files --- build/package.json | 2 +- build/yarn.lock | 10 +++++----- src/vs/workbench/common/editor.ts | 6 +++--- .../workbench/contrib/debug/browser/breakpointsView.ts | 10 +++++----- .../test/electron-browser/api/extHostSearch.test.ts | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/build/package.json b/build/package.json index 7de0c8f734c..a6ece3f020f 100644 --- a/build/package.json +++ b/build/package.json @@ -51,7 +51,7 @@ "p-limit": "^3.1.0", "plist": "^3.0.1", "source-map": "0.6.1", - "typescript": "4.2.0-dev.20201207", + "typescript": "^4.3.0-dev.20210216", "vsce": "1.48.0", "vscode-universal": "deepak1556/universal#61454d96223b774c53cda10f72c2098c0ce02d58" }, diff --git a/build/yarn.lock b/build/yarn.lock index a111c831be1..28014afaad9 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1695,16 +1695,16 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@4.2.0-dev.20201207: - version "4.2.0-dev.20201207" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.0-dev.20201207.tgz#19a34bc7d2d42a7467c512c63f135587ac848807" - integrity sha512-fPHBDi/fgdX4WiRC7cFVv/aL069PgUaDWuLYUSHatWZujz/Lkc9bkf/zL3rKdNSCxlNKAMs3fhJv/yompOphZA== - typescript@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@^4.3.0-dev.20210216: + version "4.3.0-dev.20210216" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210216.tgz#233327e6094008c02265ba140f8d9ece9133421e" + integrity sha512-pJLcC/kqnE+0rftTRc2/gYBkz9nl+kJfaU8sSOLYnzUvD8p+LOZMzXfaLoKPdGFJ6U9+Ox/sYV9HBTJVEjSTYg== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 5471bd95d6f..9feceb6188b 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1549,9 +1549,9 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService pinned: true, override: path.overrideId } : { - pinned: true, - override: path.overrideId - }; + pinned: true, + override: path.overrideId + }; let input: IResourceEditorInput | IUntitledTextResourceEditorInput; if (!exists) { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index eca16433e3b..07ed0eec5e9 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -856,11 +856,11 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea startColumn: breakpoint.column || 1, endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER } : { - startLineNumber: breakpoint.lineNumber, - startColumn: breakpoint.column || 1, - endLineNumber: breakpoint.lineNumber, - endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER - }; + startLineNumber: breakpoint.lineNumber, + startColumn: breakpoint.column || 1, + endLineNumber: breakpoint.lineNumber, + endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER + }; return editorService.openEditor({ resource: breakpoint.uri, diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index 1935dbb5adc..e5c69a38e4e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -713,10 +713,10 @@ suite('ExtHostSearch', () => { match: null // Don't care about this right now } } : { - uri: r.uri.toString(), - text: r.text, - lineNumber: r.lineNumber - }); + uri: r.uri.toString(), + text: r.text, + lineNumber: r.lineNumber + }); return assert.deepEqual( makeComparable(actualTextSearchResults), From 7459443550449afa07ec43da67b52b8ea03568d0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 16 Feb 2021 14:28:34 -0800 Subject: [PATCH 136/176] Update monaco.d.ts --- src/vs/monaco.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5b0421be3f5..4aa4fc84b84 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4080,12 +4080,12 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; - autoClosingBrackets: IEditorOption; - autoClosingOvertype: IEditorOption; - autoClosingQuotes: IEditorOption; + autoClosingBrackets: IEditorOption; + autoClosingOvertype: IEditorOption; + autoClosingQuotes: IEditorOption; autoIndent: IEditorOption; automaticLayout: IEditorOption; - autoSurround: IEditorOption; + autoSurround: IEditorOption; stickyTabStops: IEditorOption; codeLens: IEditorOption; codeLensFontFamily: IEditorOption; From 12677674727e9fda5f57fd06fb363cb9275a5495 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Feb 2021 14:57:40 -0800 Subject: [PATCH 137/176] fix #116598. broadcast output items change. --- .../src/singlefolder-tests/notebook.test.ts | 28 +++++++++++++++++++ .../api/common/extHostNotebookDocument.ts | 21 +++++++++++++- .../common/model/notebookTextModel.ts | 16 +++++++++++ .../contrib/notebook/common/notebookCommon.ts | 19 +++++++++---- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index b437120c23c..80b241e0322 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -1460,6 +1460,34 @@ suite('Notebook API tests', function () { await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); + + test('#116598, output items change event.', async function () { + assertInitalState(); + + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const edit = new vscode.WorkspaceEdit(); + edit.appendNotebookCellOutput(resource, 0, [new vscode.NotebookCellOutput([ + new vscode.NotebookCellOutputItem('application/foo', 'bar'), + new vscode.NotebookCellOutputItem('application/json', { data: true }, { metadata: true }), + ])]); + await vscode.workspace.applyEdit(edit); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs.length, 1); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs.length, 2); + + const appendEdit = new vscode.WorkspaceEdit(); + const newItem = new vscode.NotebookCellOutputItem('text/plain', '1'); + appendEdit.appendNotebookCellOutputItems( + resource, + 0, + vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].id, + [newItem] + ); + await vscode.workspace.applyEdit(appendEdit); + assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs.length, 3); + assert.deepStrictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs[2], newItem); + }); // }); // suite('webview', () => { diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index e9e715885c4..b20e4c9ed1e 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -13,7 +13,7 @@ import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/ap import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { IMainCellDto, IOutputDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; class RawContentChangeEvent { @@ -100,6 +100,17 @@ export class ExtHostCell { this._outputs = newOutputs; } + setOutputItems(outputId: string, append: boolean, newOutputItems: IOutputItemDto[]) { + const output = this._outputs.find(op => op.outputId === outputId); + if (output) { + if (append) { + output.outputs = [...output.outputs, ...newOutputItems]; + } else { + output.outputs = newOutputItems; + } + } + } + setMetadata(newMetadata: NotebookCellMetadata): void { this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata); } @@ -211,6 +222,8 @@ export class ExtHostNotebookDocument extends Disposable { this._moveCell(e.index, e.newIdx); } else if (e.kind === NotebookCellsChangeType.Output) { this._setCellOutputs(e.index, e.outputs); + } else if (e.kind === NotebookCellsChangeType.OutputItem) { + this._setCellOutputItems(e.index, e.outputId, e.append, e.outputItems); } else if (e.kind === NotebookCellsChangeType.ChangeLanguage) { this._changeCellLanguage(e.index, e.language); } else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) { @@ -301,6 +314,12 @@ export class ExtHostNotebookDocument extends Disposable { this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); } + private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void { + const cell = this._cells[index]; + cell.setOutputItems(outputId, append, outputItems); + this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); + } + private _changeCellLanguage(index: number, language: string): void { const cell = this._cells[index]; const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3885b5e3d02..5d5364651e5 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -661,6 +661,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const output = cell.outputs[outputIndex]; output.appendData(items); + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.OutputItem, + index: this._cells.indexOf(cell), + outputId: output.outputId, + outputItems: items, + append: true, + transient: this.transientOptions.transientOutputs + }, true); } private _replaceNotebookCellOutputItems(cellHandle: number, outputId: string, items: IOutputItemDto[]) { @@ -677,6 +685,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const output = cell.outputs[outputIndex]; output.replaceData(items); + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.OutputItem, + index: this._cells.indexOf(cell), + outputId: output.outputId, + outputItems: items, + append: false, + transient: this.transientOptions.transientOutputs + }, true); } private _moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index dee355a50bb..be7d26f468d 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -237,9 +237,10 @@ export enum NotebookCellsChangeType { Initialize = 6, ChangeCellMetadata = 7, Output = 8, - ChangeCellContent = 9, - ChangeDocumentMetadata = 10, - Unknown = 11 + OutputItem = 9, + ChangeCellContent = 10, + ChangeDocumentMetadata = 11, + Unknown = 12 } export interface NotebookCellsInitializeEvent { @@ -270,6 +271,14 @@ export interface NotebookOutputChangedEvent { readonly outputs: IOutputDto[]; } +export interface NotebookOutputItemChangedEvent { + readonly kind: NotebookCellsChangeType.OutputItem; + readonly index: number; + readonly outputId: string; + readonly outputItems: IOutputItemDto[]; + readonly append: boolean; +} + export interface NotebookCellsChangeLanguageEvent { readonly kind: NotebookCellsChangeType.ChangeLanguage; readonly index: number; @@ -291,14 +300,14 @@ export interface NotebookDocumentUnknownChangeEvent { readonly kind: NotebookCellsChangeType.Unknown; } -export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent; +export type NotebookRawContentEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent; export type NotebookCellsChangedEventDto = { readonly rawEvents: NotebookRawContentEventDto[]; readonly versionId: number; }; -export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; +export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent) & { transient: boolean; }; export type NotebookTextModelChangedEvent = { readonly rawEvents: NotebookRawContentEvent[]; readonly versionId: number; From da1439d5dbae09d6dd88fd2fb914f4f7c31aa111 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 14:19:02 -0800 Subject: [PATCH 138/176] testing: tweak autorun icon checked state --- .../contrib/testing/browser/media/testing.css | 16 ++++++++++++++-- .../workbench/contrib/testing/browser/theme.ts | 9 ++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 19896bdbd8d..5e3adce155b 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -60,8 +60,20 @@ margin-right: 8px; } -.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::before { - padding: 2px; +.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::after { + content: ''; + display: none; + position: absolute; + width: 0.4em; + height: 0.4em; + top: 50%; + left: 50%; + margin: 0.1em 0 0 0.05em; + border-radius: 100%; +} + +.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun.checked::after { + display: block; } .codicon-testing-loading-icon::before { diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index 3c80d02eae0..635baca1d34 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -8,6 +8,7 @@ import { localize } from 'vs/nls'; import { editorErrorForeground, editorForeground, editorHintForeground, editorInfoForeground, editorWarningForeground, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes'; +import { ACTIVITY_BAR_BADGE_BACKGROUND } from 'vs/workbench/common/theme'; export const testingColorIconFailed = registerColor('testing.iconFailed', { dark: '#f14c4c', @@ -135,15 +136,17 @@ registerThemingParticipant((theme, collector) => { //#region active buttons const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder); if (inputActiveOptionBorderColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { border-color: ${inputActiveOptionBorderColor}; }`); + collector.addRule(`.testing-filter-button.checked { border-color: ${inputActiveOptionBorderColor}; }`); } const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground); if (inputActiveOptionForegroundColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { color: ${inputActiveOptionForegroundColor}; }`); + collector.addRule(`.testing-filter-button.checked { color: ${inputActiveOptionForegroundColor}; }`); } const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground); if (inputActiveOptionBackgroundColor) { - collector.addRule(`.testing-filter-button.checked, .codicon-testing-autorun.checked::before { background-color: ${inputActiveOptionBackgroundColor}; }`); + collector.addRule(`.testing-filter-button.checked { background-color: ${inputActiveOptionBackgroundColor}; }`); } + const badgeColor = theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND); + collector.addRule(`.monaco-workbench .part > .title > .title-actions .action-label.codicon-testing-autorun::after { background-color: ${badgeColor}; }`); //#endregion }); From b050d09527bc80fc8b9f5e35f860b69c92d06a49 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 14:39:27 -0800 Subject: [PATCH 139/176] testing: filter focus on view visible Fixes #114991 --- .../contrib/testing/browser/testingExplorerView.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index aa396f4e514..0b43ef1509d 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -280,6 +280,12 @@ export class TestingExplorerViewModel extends Disposable { } })); + this._register(onDidChangeVisibility(visible => { + if (visible) { + filterState.focusInput(); + } + })); + this.updatePreferredProjection(); this.onDidChangeSelection = this.tree.onDidChangeSelection; From 3d19580d17ab9251f75c296645733208089fd0c8 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 15:08:40 -0800 Subject: [PATCH 140/176] fix: hide debug/run actions if no tests are applicable Fixes #115017 --- .../testing/browser/testExplorerActions.ts | 10 ++++--- .../contrib/testing/common/testServiceImpl.ts | 27 ++++++++++++++++--- .../testing/common/testingContextKeys.ts | 2 ++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 52f558c3f00..62310f4dea3 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -197,7 +197,10 @@ abstract class RunOrDebugAllAllAction extends Action2 { group: 'navigation', when: ContextKeyAndExpr.create([ ContextKeyEqualsExpr.create('view', Testing.ExplorerViewId), - ContextKeyEqualsExpr.create(TestingContextKeys.isRunning.serialize(), false), + TestingContextKeys.isRunning.isEqualTo(false), + debug + ? TestingContextKeys.hasDebuggableTests.isEqualTo(true) + : TestingContextKeys.hasRunnableTests.isEqualTo(true), ]) } }); @@ -518,7 +521,6 @@ abstract class RunOrDebugAtCursor extends Action2 { return; } - const testService = accessor.get(ITestService); const collection = testService.subscribeToDiffs(ExtHostTestingResource.TextDocument, model.uri); @@ -694,7 +696,9 @@ abstract class RunOrDebugTestResults extends Action2 { } } - await this.runTest(testService, toRun); + if (toRun.length) { + await this.runTest(testService, toRun); + } } finally { workspaceTests.dispose(); } diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 95d4a9e7bc8..8b65c2c83ff 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -39,12 +39,16 @@ export class TestService extends Disposable implements ITestService { private readonly busyStateChangeEmitter = new Emitter(); private readonly changeProvidersEmitter = new Emitter<{ delta: number }>(); private readonly providerCount: IContextKey; + private readonly hasRunnable: IContextKey; + private readonly hasDebuggable: IContextKey; private readonly runningTests = new Map(); private rootProviderCount = 0; constructor(@IContextKeyService contextKeyService: IContextKeyService, @INotificationService private readonly notificationService: INotificationService, @ITestResultService private readonly testResults: ITestResultService) { super(); this.providerCount = TestingContextKeys.providerCount.bindTo(contextKeyService); + this.hasDebuggable = TestingContextKeys.hasDebuggableTests.bindTo(contextKeyService); + this.hasRunnable = TestingContextKeys.hasRunnableTests.bindTo(contextKeyService); } /** @@ -216,11 +220,14 @@ export class TestService extends Disposable implements ITestService { */ public publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff) { const sub = this.testSubscriptions.get(getTestSubscriptionKey(resource, URI.revive(uri))); - if (sub) { - sub.collection.apply(diff); - // console.log('accept', sub.collection, diff); - sub.onDiff.fire(diff); + if (!sub) { + return; } + + sub.collection.apply(diff); + sub.onDiff.fire(diff); + this.hasDebuggable.set(!!this.findTest(t => t.item.debuggable)); + this.hasRunnable.set(!!this.findTest(t => t.item.runnable)); } /** @@ -240,6 +247,18 @@ export class TestService extends Disposable implements ITestService { this.providerCount.set(this.testControllers.size); this.changeProvidersEmitter.fire({ delta: -1 }); } + + private findTest(predicate: (t: InternalTestItem) => boolean): InternalTestItem | undefined { + for (const { collection } of this.testSubscriptions.values()) { + for (const test of collection.all) { + if (predicate(test)) { + return test; + } + } + } + + return undefined; + } } export class MainThreadTestCollection extends AbstractIncrementalTestCollection implements IMainThreadTestCollection { diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index 3a1f40ad723..6f951ef63ac 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -9,6 +9,8 @@ import { TestExplorerViewMode, TestExplorerViewSorting } from 'vs/workbench/cont export namespace TestingContextKeys { export const providerCount = new RawContextKey('testing.providerCount', 0); + export const hasDebuggableTests = new RawContextKey('testing.hasDebuggableTests', false); + export const hasRunnableTests = new RawContextKey('testing.hasRunnableTests', false); export const viewMode = new RawContextKey('testing.explorerViewMode', TestExplorerViewMode.List); export const viewSorting = new RawContextKey('testing.explorerViewSorting', TestExplorerViewSorting.ByLocation); export const isRunning = new RawContextKey('testing.isRunning', false); From 69393e9a2faecae9579d13f7ce4e0f094973b8a5 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 15:45:26 -0800 Subject: [PATCH 141/176] testing: improve labeling in peek for accessibility --- src/vs/monaco.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 4aa4fc84b84..5b0421be3f5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4080,12 +4080,12 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; - autoClosingBrackets: IEditorOption; - autoClosingOvertype: IEditorOption; - autoClosingQuotes: IEditorOption; + autoClosingBrackets: IEditorOption; + autoClosingOvertype: IEditorOption; + autoClosingQuotes: IEditorOption; autoIndent: IEditorOption; automaticLayout: IEditorOption; - autoSurround: IEditorOption; + autoSurround: IEditorOption; stickyTabStops: IEditorOption; codeLens: IEditorOption; codeLensFontFamily: IEditorOption; From 269cf7a98c1ac8c606be63664c756fa45e752ecc Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 15:45:26 -0800 Subject: [PATCH 142/176] testing: improve labeling in peek for accessibility --- src/vs/editor/browser/widget/diffEditorWidget.ts | 7 +++++++ src/vs/editor/common/config/editorOptions.ts | 8 ++++++++ src/vs/monaco.d.ts | 8 ++++++++ .../contrib/testing/browser/testingOutputPeek.ts | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index c67cb67e60c..6256e77b6ec 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -1151,6 +1151,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } else { result.wordWrapOverride1 = this._diffWordWrap; } + if (options.originalAriaLabel) { + result.ariaLabel = options.originalAriaLabel; + } result.readOnly = !this._originalIsEditable; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return { @@ -1164,6 +1167,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE private _adjustOptionsForRightHandSide(options: Readonly): editorBrowser.IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(options); + if (options.modifiedAriaLabel) { + result.ariaLabel = options.modifiedAriaLabel; + } + result.wordWrapOverride1 = this._diffWordWrap; result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index bce85536162..fd183e4fd8c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -690,6 +690,14 @@ export interface IDiffEditorOptions extends IEditorOptions { * Control the wrapping of the diff editor. */ diffWordWrap?: 'off' | 'on' | 'inherit'; + /** + * Aria label for original editor. + */ + originalAriaLabel?: string; + /** + * Aria label for modifed editor. + */ + modifiedAriaLabel?: string; } //#endregion diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5b0421be3f5..c8b2e0aec53 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3247,6 +3247,14 @@ declare namespace monaco.editor { * Control the wrapping of the diff editor. */ diffWordWrap?: 'off' | 'on' | 'inherit'; + /** + * Aria label for original editor. + */ + originalAriaLabel?: string; + /** + * Aria label for modifed editor. + */ + modifiedAriaLabel?: string; } /** diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 3ee275e166a..3e3bb38e45a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { alert } from 'vs/base/browser/ui/aria/aria'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -203,6 +204,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.value!.create(); } + alert(message.message.toString()); this.peek.value!.setModel(dto); } @@ -321,6 +323,8 @@ const diffEditorOptions: IDiffEditorOptions = { renderOverviewRuler: false, ignoreTrimWhitespace: false, renderSideBySide: true, + originalAriaLabel: localize('testingOutputExpected', 'Expected result'), + modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), }; class TestingDiffOutputPeek extends TestingOutputPeek { From dfee0857c2312cc299227730817307b7e7d74267 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 16:16:31 -0800 Subject: [PATCH 143/176] testing: run test on enter press Ref https://github.com/microsoft/vscode/issues/114653 --- .../testing/browser/testingExplorerView.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 0b43ef1509d..c65800ac901 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -19,7 +20,9 @@ import { Event } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { splitGlobAware } from 'vs/base/common/glob'; import { Iterable } from 'vs/base/common/iterator'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/testing'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -223,6 +226,7 @@ export class TestingExplorerViewModel extends Disposable { listContainer: HTMLElement, onDidChangeVisibility: Event, private listener: TestSubscriptionListener | undefined, + @ITestService private readonly testService: ITestService, @ITestExplorerFilterState filterState: TestExplorerFilterState, @IInstantiationService private readonly instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @@ -274,7 +278,9 @@ export class TestingExplorerViewModel extends Disposable { this._register(this.tree); this._register(dom.addStandardDisposableListener(this.tree.getHTMLElement(), 'keydown', evt => { - if (DefaultKeyboardNavigationDelegate.mightProducePrintableCharacter(evt)) { + if (evt.equals(KeyCode.Enter)) { + this.handleExecuteKeypress(evt); + } else if (DefaultKeyboardNavigationDelegate.mightProducePrintableCharacter(evt)) { filterState.text.value = evt.browserEvent.key; filterState.focusInput(); } @@ -401,6 +407,27 @@ export class TestingExplorerViewModel extends Disposable { : false; } + private handleExecuteKeypress(evt: IKeyboardEvent) { + const focused = this.tree.getFocus(); + const selected = this.tree.getSelection(); + let targeted: (ITestTreeElement | null)[]; + if (focused.length === 1 && selected.includes(focused[0])) { + evt.browserEvent?.preventDefault(); + targeted = selected; + } else { + targeted = focused; + } + + const toRun = targeted + .map(e => e?.test) + .filter(isDefined) + .filter(e => e.item.runnable); + + if (toRun.length) { + this.testService.runTests({ debug: false, tests: toRun.map(t => ({ providerId: t.providerId, testId: t.id })) }); + } + } + private updatePreferredProjection() { this.projection?.dispose(); if (!this.listener) { From 90c5ceafc38c90b94f615efc67fa3f3a4a884d2e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Feb 2021 16:38:30 -0800 Subject: [PATCH 144/176] monaco: fix conflicted file --- src/vs/monaco.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c8b2e0aec53..7b729f29901 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4088,12 +4088,12 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; - autoClosingBrackets: IEditorOption; - autoClosingOvertype: IEditorOption; - autoClosingQuotes: IEditorOption; + autoClosingBrackets: IEditorOption; + autoClosingOvertype: IEditorOption; + autoClosingQuotes: IEditorOption; autoIndent: IEditorOption; automaticLayout: IEditorOption; - autoSurround: IEditorOption; + autoSurround: IEditorOption; stickyTabStops: IEditorOption; codeLens: IEditorOption; codeLensFontFamily: IEditorOption; From b0bd28137a6c5f9b3a557213e6b32d5f6cc43b1c Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Feb 2021 16:02:39 -0800 Subject: [PATCH 145/176] add test for #115855. --- .../src/singlefolder-tests/notebook.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 80b241e0322..acc936a56b7 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -1488,6 +1488,29 @@ suite('Notebook API tests', function () { assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs.length, 3); assert.deepStrictEqual(vscode.window.activeNotebookEditor!.document.cells[0].outputs[0].outputs[2], newItem); }); + + test('#115855 onDidSaveNotebookDocument', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + const cellsChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookCells); + await vscode.window.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); + }); + + const cellChangeEventRet = await cellsChangeEvent; + assert.strictEqual(cellChangeEventRet.document === vscode.window.activeNotebookEditor?.document, true); + assert.strictEqual(cellChangeEventRet.document.isDirty, true); + + await withEvent(vscode.notebook.onDidSaveNotebookDocument, async event => { + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await event; + assert.strictEqual(cellChangeEventRet.document.isDirty, false); + }); + await saveAllFilesAndCloseAll(resource); + }); + // }); // suite('webview', () => { From b3f8737839d406381b66a0397fe488bb7866cc53 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Feb 2021 16:40:26 -0800 Subject: [PATCH 146/176] fix #116808. --- .../src/singlefolder-tests/notebook.test.ts | 17 ++++++++++++++++- .../workbench/api/browser/mainThreadNotebook.ts | 1 + src/vs/workbench/api/common/extHostNotebook.ts | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index acc936a56b7..004b53a635a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -1403,7 +1403,7 @@ suite('Notebook API tests', function () { assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); // no kernel -> no default language - assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined); + // assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined); assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript'); await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); @@ -1511,6 +1511,21 @@ suite('Notebook API tests', function () { await saveAllFilesAndCloseAll(resource); }); + + test('#116808, active kernel should not be undefined', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await withEvent(vscode.notebook.onDidChangeActiveNotebookKernel, async event => { + await event; + assert.notStrictEqual(vscode.window.activeNotebookEditor?.kernel, undefined); + assert.strictEqual(vscode.window.activeNotebookEditor?.kernel?.id, 'mainKernel'); + }); + + await saveAllFilesAndCloseAll(resource); + }); + // }); // suite('webview', () => { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index b9ba768326d..f761a9fec14 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -556,6 +556,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const result: INotebookKernel[] = []; const kernelsDto = await that._proxy.$provideNotebookKernels(handle, uri, token); for (const dto of kernelsDto) { + console.log('kerneldto', dto.providerHandle); result.push({ id: dto.id, diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index b58b4880b57..04a5cd56a2c 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -123,6 +123,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable { label: kernel.label, extension: this._extension.identifier, extensionLocation: this._extension.extensionLocation, + providerHandle: this._handle, description: kernel.description, detail: kernel.detail, isPreferred: kernel.isPreferred, From 79b8259abb8fd077fe2431e4e773b62cf7e55c7e Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Feb 2021 19:41:30 -0800 Subject: [PATCH 147/176] change cell language with cell change events. --- .../src/singlefolder-tests/notebook.test.ts | 28 ++++- .../notebook/browser/contrib/coreActions.ts | 102 ++++++++++++++---- 2 files changed, 110 insertions(+), 20 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 004b53a635a..81a03ed3250 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -135,7 +135,7 @@ suite('Notebook API tests', function () { id: 'mainKernel', label: 'Notebook Test Kernel', isPreferred: true, - supportedLanguages: ['typescript'], + supportedLanguages: ['typescript', 'javascript'], executeAllCells: async (_document: vscode.NotebookDocument) => { const edit = new vscode.WorkspaceEdit(); @@ -174,7 +174,7 @@ suite('Notebook API tests', function () { id: 'secondaryKernel', label: 'Notebook Secondary Test Kernel', isPreferred: false, - supportedLanguages: ['typescript'], + supportedLanguages: ['typescript', 'javascript'], executeAllCells: async (_document: vscode.NotebookDocument) => { const edit = new vscode.WorkspaceEdit(); edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ @@ -471,6 +471,30 @@ suite('Notebook API tests', function () { await saveFileAndCloseAll(resource); }); + test('change cell language', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'typescript'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Code); + await withEvent(vscode.notebook.onDidChangeCellLanguage, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'javascript'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'javascript'); + }); + + // switch to markdown will change the cell kind + await withEvent(vscode.notebook.onDidChangeNotebookCells, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'markdown'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'markdown'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Markdown); + }); + + await saveAllFilesAndCloseAll(resource); + }); + test('edit API (replaceCells)', async function () { assertInitalState(); const resource = await createRandomFile('', undefined, '.vsctestnb'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 26772735238..56f85535ae3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1607,19 +1607,81 @@ interface ILanguagePickInput extends IQuickPickItem { description: string; } -export class ChangeCellLanguageAction extends NotebookCellAction { + +interface IChangeCellContext extends INotebookCellActionContext { + // TODO@rebornix : `cells` + // range: ICellRange; + language?: string; +} + +export class ChangeCellLanguageAction extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_LANGUAGE, title: localize('changeLanguage', 'Change Cell Language'), + description: { + description: localize('changeLanguage', 'Change Cell Language'), + args: [ + { + name: 'range', + description: 'The cell range', + schema: { + 'type': 'object', + 'required': ['start', 'end'], + 'properties': { + 'start': { + 'type': 'number' + }, + 'end': { + 'type': 'number' + } + } + } + }, + { + name: 'language', + description: 'The target cell language', + schema: { + 'type': 'string' + } + } + ] + } }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - this.showLanguagePicker(accessor, context); + protected getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange, ...additionalArgs: any[]): IChangeCellContext | undefined { + if (!context || typeof context.start !== 'number' || typeof context.end !== 'number' || context.start >= context.end) { + return; + } + + const language = additionalArgs.length && typeof additionalArgs[0] === 'string' ? additionalArgs[0] : undefined; + const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor); + + if (!activeEditorContext || !activeEditorContext.notebookEditor.viewModel || context.start >= activeEditorContext.notebookEditor.viewModel.viewCells.length) { + return; + } + + const cells = activeEditorContext.notebookEditor.viewModel.viewCells; + + // TODO@rebornix, support multiple cells + return { + notebookEditor: activeEditorContext.notebookEditor, + cell: cells[context.start], + language + }; } - private async showLanguagePicker(accessor: ServicesAccessor, context: INotebookCellActionContext) { + + async runWithContext(accessor: ServicesAccessor, context: IChangeCellContext): Promise { + if (context.language) { + await this.setLanguage(context, context.language); + } else { + await this.showLanguagePicker(accessor, context); + } + } + + private async showLanguagePicker(accessor: ServicesAccessor, context: IChangeCellContext) { const topItems: ILanguagePickInput[] = []; const mainItems: ILanguagePickInput[] = []; @@ -1671,21 +1733,25 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const selection = await quickInputService.pick(picks, { placeHolder: localize('pickLanguageToConfigure', "Select Language Mode") }) as ILanguagePickInput | undefined; if (selection && selection.languageId) { - if (selection.languageId === 'markdown' && context.cell?.language !== 'markdown') { - const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); - if (newCell) { - context.notebookEditor.focusNotebookCell(newCell, 'editor'); - } - } else if (selection.languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { - await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); - } else { - const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); - context.notebookEditor.viewModel.notebookDocument.applyEdits( - context.notebookEditor.viewModel.notebookDocument.versionId, - [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], - true, undefined, () => undefined, undefined - ); + await this.setLanguage(context, selection.languageId); + } + } + + private async setLanguage(context: IChangeCellContext, languageId: string) { + if (languageId === 'markdown' && context.cell?.language !== 'markdown') { + const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); } + } else if (languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { + await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, languageId); + } else { + const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel.notebookDocument.applyEdits( + context.notebookEditor.viewModel.notebookDocument.versionId, + [{ editType: CellEditType.CellLanguage, index, language: languageId }], + true, undefined, () => undefined, undefined + ); } } From c03c75876517713fc02717dd85892b7b2199e0a4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 16 Feb 2021 20:40:18 -0800 Subject: [PATCH 148/176] Select the first search row when initially focusing it from the search inputs, now that the coloring is easier to differentiate --- src/vs/workbench/contrib/search/browser/searchView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 8350e27ee2d..027b9fcd189 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1102,7 +1102,7 @@ export class SearchView extends ViewPane { this.tree.domFocus(); const selection = this.tree.getSelection(); if (selection.length === 0) { - this.tree.focusNext(); + this.tree.focusNext(undefined, undefined, getSelectionKeyboardEvent()); } } } From 90f5ef65a09c975315cfff7022cb37f5e75ab539 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 16 Feb 2021 21:07:55 -0800 Subject: [PATCH 149/176] Delete notebookTestMain --- extensions/vscode-notebook-tests/package.json | 2 +- .../src/{notebookSmokeTestMain.ts => extension.ts} | 2 +- .../vscode-notebook-tests/src/notebookTestMain.ts | 14 -------------- 3 files changed, 2 insertions(+), 16 deletions(-) rename extensions/vscode-notebook-tests/src/{notebookSmokeTestMain.ts => extension.ts} (98%) delete mode 100644 extensions/vscode-notebook-tests/src/notebookTestMain.ts diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 153ce130e24..78164843df9 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -8,7 +8,7 @@ "activationEvents": [ "*" ], - "main": "./out/notebookTestMain", + "main": "./out/extension", "enableProposedApi": true, "engines": { "vscode": "^1.25.0" diff --git a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts b/extensions/vscode-notebook-tests/src/extension.ts similarity index 98% rename from extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts rename to extensions/vscode-notebook-tests/src/extension.ts index 0e3e7426f23..bf66b529bc2 100644 --- a/extensions/vscode-notebook-tests/src/notebookSmokeTestMain.ts +++ b/extensions/vscode-notebook-tests/src/extension.ts @@ -11,7 +11,7 @@ function wait(ms: number): Promise { return new Promise(r => setTimeout(r, ms)); } -export function smokeTestActivate(context: vscode.ExtensionContext): any { +export function activate(context: vscode.ExtensionContext): any { context.subscriptions.push(vscode.commands.registerCommand('vscode-notebook-tests.createNewNotebook', async () => { const workspacePath = vscode.workspace.workspaceFolders![0].uri.fsPath; const notebookPath = path.join(workspacePath, 'test.smoke-nb'); diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts deleted file mode 100644 index 8cf19b430be..00000000000 --- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts +++ /dev/null @@ -1,14 +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 vscode from 'vscode'; -import { smokeTestActivate } from './notebookSmokeTestMain'; - -export function activate(context: vscode.ExtensionContext): any { - smokeTestActivate(context); - - - -} From a36e9b3e092a6f989c3d88507cb3a5980c9724ce Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 07:30:18 +0100 Subject: [PATCH 150/176] add prefix to main errors when logged into renderer --- src/vs/code/electron-main/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 1a378fd011f..4d17030027a 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -388,7 +388,7 @@ export class CodeApplication extends Disposable { // take only the message and stack property const friendlyError = { - message: err.message, + message: `[uncaught exception in main]: ${err.message}`, stack: err.stack }; From 45704f8f340bbd57010d5046926f5e69cbf442bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 07:48:58 +0100 Subject: [PATCH 151/176] storage - lift more things up to abstract impl --- .../parts/storage/test/node/storage.test.ts | 1 + .../storage/browser/storageService.ts | 75 +++++-------------- src/vs/platform/storage/common/storage.ts | 64 +++++++++++++++- .../electron-sandbox/storageService2.ts | 44 +---------- .../platform/storage/node/storageService.ts | 45 ++--------- .../test/browser/storageService.test.ts | 4 +- .../storage/test/node/storageService.test.ts | 8 +- src/vs/workbench/browser/web.main.ts | 4 +- .../electron-browser/desktop.main.ts | 4 +- 9 files changed, 101 insertions(+), 148 deletions(-) diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 46193801f71..990e531905e 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -104,6 +104,7 @@ flakySuite('Storage Library', function () { strictEqual(deletePromiseResolved, true); await storage.close(); + await storage.close(); // it is ok to call this multiple times }); test('external changes', async () => { diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index aa2eb5f6c7d..8c60c691d1f 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { StorageScope, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -12,11 +12,13 @@ import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { IStorage, Storage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; -import { runWhenIdle, RunOnceScheduler, Promises } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; export class BrowserStorageService extends AbstractStorageService { + private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000; // every 5s because async operations are not permitted on shutdown + private globalStorage: IStorage | undefined; private workspaceStorage: IStorage | undefined; @@ -26,38 +28,26 @@ export class BrowserStorageService extends AbstractStorageService { private globalStorageFile: URI | undefined; private workspaceStorageFile: URI | undefined; - private initializePromise: Promise | undefined; - - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 5000 /* every 5s */)); - private runWhenIdleDisposable: IDisposable | undefined = undefined; - get hasPendingUpdate(): boolean { return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate); } constructor( + private readonly payload: IWorkspaceInitializationPayload, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IFileService private readonly fileService: IFileService ) { - super(); + super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); } - initialize(payload: IWorkspaceInitializationPayload): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(payload); - } - - return this.initializePromise; - } - - private async doInitialize(payload: IWorkspaceInitializationPayload): Promise { + protected async doInitialize(): Promise { // Ensure state folder exists const stateRoot = joinPath(this.environmentService.userRoamingDataHome, 'state'); await this.fileService.createFolder(stateRoot); // Workspace Storage - this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`); + this.workspaceStorageFile = joinPath(stateRoot, `${this.payload.id}.json`); this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); @@ -90,13 +80,6 @@ export class BrowserStorageService extends AbstractStorageService { } else if (firstWorkspaceOpen) { this.workspaceStorage.set(IS_NEW_KEY, false); } - - // In the browser we do not have support for long running unload sequences. As such, - // we cannot ask for saving state in that moment, because that would result in a - // long running operation. - // Instead, periodically ask customers to save save. The library will be clever enough - // to only save state that has actually changed. - this.periodicFlushScheduler.schedule(); } protected getStorage(scope: StorageScope): IStorage | undefined { @@ -111,30 +94,17 @@ export class BrowserStorageService extends AbstractStorageService { throw new Error('Migrating storage is currently unsupported in Web'); } - private doFlushWhenIdle(): void { - - // Dispose any previous idle runner - dispose(this.runWhenIdleDisposable); - - // Run when idle - this.runWhenIdleDisposable = runWhenIdle(() => { - - // this event will potentially cause new state to be stored - // since new state will only be created while the document - // has focus, one optimization is to not run this when the - // document has no focus, assuming that state has not changed - // - // another optimization is to not collect more state if we - // have a pending update already running which indicates - // that the connection is either slow or disconnected and - // thus unhealthy. - if (document.hasFocus() && !this.hasPendingUpdate) { - this.flush(); - } - - // repeat - this.periodicFlushScheduler.schedule(); - }); + protected shouldScheduleFlush(): boolean { + // this flush() will potentially cause new state to be stored + // since new state will only be created while the document + // has focus, one optimization is to not run this when the + // document has no focus, assuming that state has not changed + // + // another optimization is to not collect more state if we + // have a pending update already running which indicates + // that the connection is either slow or disconnected and + // thus unhealthy. + return document.hasFocus() && !this.hasPendingUpdate; } close(): void { @@ -148,13 +118,6 @@ export class BrowserStorageService extends AbstractStorageService { // get triggered in this phase. this.dispose(); } - - dispose(): void { - dispose(this.runWhenIdleDisposable); - this.runWhenIdleDisposable = undefined; - - super.dispose(); - } } export class FileStorageDatabase extends Disposable implements IStorageDatabase { diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index aad6f035908..ce08361d389 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -5,11 +5,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, MutableDisposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { InMemoryStorageDatabase, IStorage, Storage } from 'vs/base/parts/storage/common/storage'; -import { Promises } from 'vs/base/common/async'; +import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; const TARGET_KEY = '__$__targetStorageMarker'; @@ -220,10 +220,16 @@ interface IKeyTargets { [key: string]: StorageTarget } +export interface IStorageServiceOptions { + flushInterval: number; +} + export abstract class AbstractStorageService extends Disposable implements IStorageService { declare readonly _serviceBrand: undefined; + private static DEFAULT_FLUSH_INTERVAL = 60 * 1000; // every minute + private readonly _onDidChangeValue = this._register(new PauseableEmitter()); readonly onDidChangeValue = this._onDidChangeValue.event; @@ -233,6 +239,56 @@ export abstract class AbstractStorageService extends Disposable implements IStor private readonly _onWillSaveState = this._register(new Emitter()); readonly onWillSaveState = this._onWillSaveState.event; + private initializationPromise: Promise | undefined; + + private readonly flushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), this.options.flushInterval)); + private readonly runFlushWhenIdle = this._register(new MutableDisposable()); + + constructor(private options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) { + super(); + } + + private doFlushWhenIdle(): void { + this.runFlushWhenIdle.value = runWhenIdle(() => { + if (this.shouldScheduleFlush()) { + this.flush(); + } + + // repeat + this.flushScheduler.schedule(); + }); + } + + protected shouldScheduleFlush(): boolean { + return true; + } + + protected stopFlushScheduler(): void { + dispose([this.runFlushWhenIdle, this.flushScheduler]); + } + + initialize(): Promise { + if (!this.initializationPromise) { + this.initializationPromise = (async () => { + + // Ask subclasses to initialize storage + await this.doInitialize(); + + // On some OS we do not get enough time to persist state on shutdown (e.g. when + // Windows restarts after applying updates). In other cases, VSCode might crash, + // so we periodically save state to reduce the chance of loosing any state. + // In the browser we do not have support for long running unload sequences. As such, + // we cannot ask for saving state in that moment, because that would result in a + // long running operation. + // Instead, periodically ask customers to save save. The library will be clever enough + // to only save state that has actually changed. + this.flushScheduler.schedule(); + })(); + } + + return this.initializationPromise; + } + protected emitDidChangeValue(scope: StorageScope, key: string): void { // Specially handle `TARGET_KEY` @@ -424,6 +480,8 @@ export abstract class AbstractStorageService extends Disposable implements IStor // --- abstract + protected abstract doInitialize(): Promise; + protected abstract getStorage(scope: StorageScope): IStorage | undefined; protected abstract getLogDetails(scope: StorageScope): string | undefined; @@ -451,6 +509,8 @@ export class InMemoryStorageService extends AbstractStorageService { return scope === StorageScope.GLOBAL ? 'inMemory (global)' : 'inMemory (workspace)'; } + protected async doInitialize(): Promise { } + async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { // not supported } diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index 5f8efb05026..f025fcb9591 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose, MutableDisposable } from 'vs/base/common/lifecycle'; +import { MutableDisposable } from 'vs/base/common/lifecycle'; import { StorageScope, WillSaveStateReason, AbstractStorageService } from 'vs/platform/storage/common/storage'; import { Storage, IStorage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; -import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; import { mark } from 'vs/base/common/performance'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; @@ -25,11 +25,6 @@ export class NativeStorageService2 extends AbstractStorageService { private workspaceStorageId: string | undefined = undefined; private workspaceStorageDisposables = this._register(new MutableDisposable()); - private initializePromise: Promise | undefined; - - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60 * 1000 /* every minute */)); - private runWhenIdleDisposable: IDisposable | undefined = undefined; - constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, private readonly mainProcessService: IMainProcessService, @@ -71,15 +66,7 @@ export class NativeStorageService2 extends AbstractStorageService { return undefined; } - initialize(): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(); - } - - return this.initializePromise; - } - - private async doInitialize(): Promise { + protected async doInitialize(): Promise { // Init all storage locations mark('code/willInitStorage'); @@ -91,11 +78,6 @@ export class NativeStorageService2 extends AbstractStorageService { } finally { mark('code/didInitStorage'); } - - // On some OS we do not get enough time to persist state on shutdown (e.g. when - // Windows restarts after applying updates). In other cases, VSCode might crash, - // so we periodically save state to reduce the chance of loosing any state. - this.periodicFlushScheduler.schedule(); } protected getStorage(scope: StorageScope): IStorage | undefined { @@ -106,28 +88,10 @@ export class NativeStorageService2 extends AbstractStorageService { return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath} [!!! Experimental Main Storage !!!]` : undefined; } - private doFlushWhenIdle(): void { - - // Dispose any previous idle runner - dispose(this.runWhenIdleDisposable); - - // Run when idle - this.runWhenIdleDisposable = runWhenIdle(() => { - - // send event to collect state - this.flush(); - - // repeat - this.periodicFlushScheduler.schedule(); - }); - } - async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally - this.periodicFlushScheduler.dispose(); - dispose(this.runWhenIdleDisposable); - this.runWhenIdleDisposable = undefined; + this.stopFlushScheduler(); // Signal as event so that clients can still store data this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 316263b1b52..3d99d334345 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -15,7 +15,7 @@ import { copy, exists, writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { assertIsDefined } from 'vs/base/common/types'; -import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; +import { Promises } from 'vs/base/common/async'; export class NativeStorageService extends AbstractStorageService { @@ -28,13 +28,9 @@ export class NativeStorageService extends AbstractStorageService { private workspaceStorage: IStorage | undefined; private workspaceStorageListener: IDisposable | undefined; - private initializePromise: Promise | undefined; - - private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 60000 /* every minute */)); - private runWhenIdleDisposable: IDisposable | undefined = undefined; - constructor( private globalStorageDatabase: IStorageDatabase, + private payload: IWorkspaceInitializationPayload | undefined, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { @@ -49,26 +45,13 @@ export class NativeStorageService extends AbstractStorageService { this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); } - initialize(payload?: IWorkspaceInitializationPayload): Promise { - if (!this.initializePromise) { - this.initializePromise = this.doInitialize(payload); - } - - return this.initializePromise; - } - - private async doInitialize(payload?: IWorkspaceInitializationPayload): Promise { + protected async doInitialize(): Promise { // Init all storage locations await Promises.settled([ this.initializeGlobalStorage(), - payload ? this.initializeWorkspaceStorage(payload) : Promise.resolve() + this.payload ? this.initializeWorkspaceStorage(this.payload) : Promise.resolve() ]); - - // On some OS we do not get enough time to persist state on shutdown (e.g. when - // Windows restarts after applying updates). In other cases, VSCode might crash, - // so we periodically save state to reduce the chance of loosing any state. - this.periodicFlushScheduler.schedule(); } private initializeGlobalStorage(): Promise { @@ -178,28 +161,10 @@ export class NativeStorageService extends AbstractStorageService { return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStoragePath; } - private doFlushWhenIdle(): void { - - // Dispose any previous idle runner - dispose(this.runWhenIdleDisposable); - - // Run when idle - this.runWhenIdleDisposable = runWhenIdle(() => { - - // send event to collect state - this.flush(); - - // repeat - this.periodicFlushScheduler.schedule(); - }); - } - async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally - this.periodicFlushScheduler.dispose(); - dispose(this.runWhenIdleDisposable); - this.runWhenIdleDisposable = undefined; + this.stopFlushScheduler(); // Signal as event so that clients can still store data this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index fdeb01f61c6..f3e92ff0880 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -28,9 +28,9 @@ suite('StorageService (browser)', function () { const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.userData, userDataProvider)); - const storageService = disposables.add(new BrowserStorageService({ userRoamingDataHome: URI.file('/User').with({ scheme: Schemas.userData }) } as unknown as IEnvironmentService, fileService)); + const storageService = disposables.add(new BrowserStorageService({ id: String(Date.now()) }, { userRoamingDataHome: URI.file('/User').with({ scheme: Schemas.userData }) } as unknown as IEnvironmentService, fileService)); - await storageService.initialize({ id: String(Date.now()) }); + await storageService.initialize(); return storageService; }, diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index ce8eb321aac..68ba513e051 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -42,8 +42,8 @@ flakySuite('StorageService (native)', function () { await promises.mkdir(testDir, { recursive: true }); - const storageService = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); - await storageService.initialize({ id: String(Date.now()) }); + const storageService = new NativeStorageService(new InMemoryStorageDatabase(), { id: String(Date.now()) }, new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + await storageService.initialize(); return storageService; }, @@ -55,8 +55,8 @@ flakySuite('StorageService (native)', function () { }); test('Migrate Data', async function () { - const storage = new NativeStorageService(new InMemoryStorageDatabase(), new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); - await storage.initialize({ id: String(Date.now()) }); + const storage = new NativeStorageService(new InMemoryStorageDatabase(), { id: String(Date.now()) }, new NullLogService(), new StorageTestEnvironmentService(URI.file(testDir), testDir)); + await storage.initialize(); storage.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); storage.store('barNumber', 55, StorageScope.WORKSPACE, StorageTarget.MACHINE); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index bdecdbc0c90..9aea863b585 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -302,10 +302,10 @@ class BrowserMain extends Disposable { } private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService): Promise { - const storageService = new BrowserStorageService(environmentService, fileService); + const storageService = new BrowserStorageService(payload, environmentService, fileService); try { - await storageService.initialize(payload); + await storageService.initialize(); return storageService; } catch (error) { diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 103816e95c0..bcd53444318 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -329,11 +329,11 @@ class DesktopMain extends Disposable { storageService = new NativeStorageService2(payload, mainProcessService, this.environmentService); } else { const storageDataBaseClient = new StorageDatabaseChannelClient(mainProcessService.getChannel('storage'), payload); - storageService = new NativeStorageService(storageDataBaseClient.globalStorage, logService, this.environmentService); + storageService = new NativeStorageService(storageDataBaseClient.globalStorage, payload, logService, this.environmentService); } try { - await storageService.initialize(payload); + await storageService.initialize(); return storageService; } catch (error) { From 3f0d8a14759cddea93f0dbddc12a4ef1e5586e86 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 08:21:44 +0100 Subject: [PATCH 152/176] storage - some :lipstick: --- .../storage/browser/storageService.ts | 2 +- src/vs/platform/storage/common/storage.ts | 14 ++++++------- src/vs/platform/storage/common/storageIpc.ts | 19 ++++++++++++++++-- .../electron-sandbox/storageService2.ts | 20 +++++++++---------- .../platform/storage/node/storageService.ts | 2 +- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 8c60c691d1f..5cfb899602e 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -94,7 +94,7 @@ export class BrowserStorageService extends AbstractStorageService { throw new Error('Migrating storage is currently unsupported in Web'); } - protected shouldScheduleFlush(): boolean { + protected shouldFlushWhenIdle(): boolean { // this flush() will potentially cause new state to be stored // since new state will only be created while the document // has focus, one optimization is to not run this when the diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index ce08361d389..faecb2618ea 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -241,7 +241,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor private initializationPromise: Promise | undefined; - private readonly flushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), this.options.flushInterval)); + private readonly flushWhenIdleScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), this.options.flushInterval)); private readonly runFlushWhenIdle = this._register(new MutableDisposable()); constructor(private options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) { @@ -250,21 +250,21 @@ export abstract class AbstractStorageService extends Disposable implements IStor private doFlushWhenIdle(): void { this.runFlushWhenIdle.value = runWhenIdle(() => { - if (this.shouldScheduleFlush()) { + if (this.shouldFlushWhenIdle()) { this.flush(); } // repeat - this.flushScheduler.schedule(); + this.flushWhenIdleScheduler.schedule(); }); } - protected shouldScheduleFlush(): boolean { + protected shouldFlushWhenIdle(): boolean { return true; } - protected stopFlushScheduler(): void { - dispose([this.runFlushWhenIdle, this.flushScheduler]); + protected stopFlushWhenIdle(): void { + dispose([this.runFlushWhenIdle, this.flushWhenIdleScheduler]); } initialize(): Promise { @@ -282,7 +282,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // long running operation. // Instead, periodically ask customers to save save. The library will be clever enough // to only save state that has actually changed. - this.flushScheduler.schedule(); + this.flushWhenIdleScheduler.schedule(); })(); } diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 8ff562fc44f..a95938a3ae5 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -109,8 +109,23 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement export class StorageDatabaseChannelClient extends Disposable { - readonly globalStorage = new GlobalStorageDatabaseClient(this.channel); - readonly workspaceStorage = this.workspace ? new WorkspaceStorageDatabaseClient(this.channel, this.workspace) : undefined; + private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; + get globalStorage() { + if (!this._globalStorage) { + this._globalStorage = new GlobalStorageDatabaseClient(this.channel); + } + + return this._globalStorage; + } + + private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; + get workspaceStorage() { + if (!this._workspaceStorage && this.workspace) { + this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); + } + + return this._workspaceStorage; + } constructor( private channel: IChannel, diff --git a/src/vs/platform/storage/electron-sandbox/storageService2.ts b/src/vs/platform/storage/electron-sandbox/storageService2.ts index f025fcb9591..f8d6e9ae7c3 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService2.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService2.ts @@ -23,7 +23,7 @@ export class NativeStorageService2 extends AbstractStorageService { // in the current window, when entering a workspace! private workspaceStorage: IStorage | undefined = undefined; private workspaceStorageId: string | undefined = undefined; - private workspaceStorageDisposables = this._register(new MutableDisposable()); + private workspaceStorageDisposable = this._register(new MutableDisposable()); constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, @@ -49,21 +49,21 @@ export class NativeStorageService2 extends AbstractStorageService { private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { - - // Keep id around for logging - this.workspaceStorageId = workspace?.id; - - // Create new const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), workspace); + if (storageDataBaseClient.workspaceStorage) { const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); - this.workspaceStorageDisposables.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + this.workspaceStorageDisposable.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); + this.workspaceStorageId = workspace?.id; return workspaceStorage; - } + } else { + this.workspaceStorageDisposable.clear(); + this.workspaceStorageId = undefined; - return undefined; + return undefined; + } } protected async doInitialize(): Promise { @@ -91,7 +91,7 @@ export class NativeStorageService2 extends AbstractStorageService { async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally - this.stopFlushScheduler(); + this.stopFlushWhenIdle(); // Signal as event so that clients can still store data this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 3d99d334345..3b33b0ba27b 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -164,7 +164,7 @@ export class NativeStorageService extends AbstractStorageService { async close(): Promise { // Stop periodic scheduler and idle runner as we now collect state normally - this.stopFlushScheduler(); + this.stopFlushWhenIdle(); // Signal as event so that clients can still store data this.emitWillSaveState(WillSaveStateReason.SHUTDOWN); From 89b85a05d4320f297600a36c3b3010a6f8c9978d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 10:23:52 +0100 Subject: [PATCH 153/176] storage - more consolidation --- .../storage/electron-main/storageIpc.ts | 7 +- .../storage/electron-main/storageMain.ts | 124 +++++++----------- .../electron-main/storageMainService.ts | 4 +- .../electron-main/storageMainService.test.ts | 54 ++++++-- .../platform/windows/electron-main/window.ts | 2 +- 5 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 551953afbd0..f8a6224c5c7 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -91,14 +91,15 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel case 'updateItems': { const items: ISerializableUpdateRequest = arg; + if (items.insert) { for (const [key, value] of items.insert) { - storage.store(key, value); + storage.set(key, value); } } if (items.delete) { - items.delete.forEach(key => storage.remove(key)); + items.delete.forEach(key => storage.delete(key)); } break; @@ -125,7 +126,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; try { - await storage.initialize(); + await storage.init(); } catch (error) { this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); } diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 9d1f5c1b6ce..c9a430dccef 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -50,7 +50,7 @@ export interface IStorageMain extends IDisposable { /** * Required call to ensure the service can be used. */ - initialize(): Promise; + init(): Promise; /** * Retrieve an element stored with the given key from storage. Use @@ -59,32 +59,16 @@ export interface IStorageMain extends IDisposable { get(key: string, fallbackValue: string): string; get(key: string, fallbackValue?: string): string | undefined; - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. - */ - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. - */ - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - /** * Store a string value under the given key to storage. The value will * be converted to a string. */ - store(key: string, value: string | boolean | number | undefined | null): void; + set(key: string, value: string | boolean | number | undefined | null): void; /** * Delete an element stored under the provided key from storage. */ - remove(key: string): void; + delete(key: string): void; /** * Close the storage connection. @@ -114,17 +98,26 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { super(); } - initialize(): Promise { + init(): Promise { if (!this.initializePromise) { this.initializePromise = (async () => { try { - const storage = await this.doInitialize(); - // Replace our in-memory storage with the initialized - // one once that is finished and use it from then on + // Create storage via subclasses + const storage = await this.doCreate(); + + // Replace our in-memory storage with the real + // once as soon as possible without awaiting + // the init call. this.storage.dispose(); this.storage = storage; + // Re-emit storage changes via event + this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + + // Await storage init + await this.doInit(storage); + // Ensure we track wether storage is new or not const isNewStorage = storage.getBoolean(IS_NEW_KEY); if (isNewStorage === undefined) { @@ -148,7 +141,11 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { }; } - protected abstract doInitialize(): Promise; + protected doInit(storage: IStorage): Promise { + return storage.init(); + } + + protected abstract doCreate(): Promise; get items(): Map { return this.storage.items; } @@ -158,28 +155,24 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { return this.storage.get(key, fallbackValue); } - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - return this.storage.getBoolean(key, fallbackValue); - } - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - getNumber(key: string, fallbackValue?: number): number | undefined { - return this.storage.getNumber(key, fallbackValue); - } - - store(key: string, value: string | boolean | number | undefined | null): Promise { + set(key: string, value: string | boolean | number | undefined | null): Promise { return this.storage.set(key, value); } - remove(key: string): Promise { + delete(key: string): Promise { return this.storage.delete(key); } async close(): Promise { + // Ensure we are not accidentally leaving + // a pending initialized storage behind in + // case close() was called before init() + // finishes + if (this.initializePromise) { + await this.initializePromise; + } + // Propagate to storage lib await this.storage.close(); @@ -200,7 +193,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { super(logService); } - protected async doInitialize(): Promise { + protected async doCreate(): Promise { let storagePath: string; if (this.options.useInMemoryStorage) { storagePath = SQLiteStorageDatabase.IN_MEMORY_PATH; @@ -208,24 +201,19 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { storagePath = join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); } - // Create Storage - const storage = new Storage(new SQLiteStorageDatabase(storagePath, { + return new Storage(new SQLiteStorageDatabase(storagePath, { logging: this.createLogginOptions() })); + } - // Re-emit storage changes via event - this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); - - // Forward init to SQLite DB - await storage.init(); + protected async doInit(storage: IStorage): Promise { + await super.doInit(storage); // Apply global telemetry values as part of the initialization this.updateTelemetryState(storage); - - return storage; } - private updateTelemetryState(storage: Storage): void { + private updateTelemetryState(storage: IStorage): void { // Instance UUID (once) const instanceId = storage.get(instanceStorageKey, undefined); @@ -263,23 +251,12 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai super(logService); } - protected async doInitialize(): Promise { - - // Prepare workspace storage folder for DB + protected async doCreate(): Promise { const { storageFilePath, wasCreated } = await this.prepareWorkspaceStorageFolder(); - // Create Storage - const storage = new Storage(new SQLiteStorageDatabase(storageFilePath, { + return new Storage(new SQLiteStorageDatabase(storageFilePath, { logging: this.createLogginOptions() }), { hint: wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined }); - - // Re-emit storage changes via event - this._register(storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); - - // Forward init to SQLite DB - await storage.init(); - - return storage; } private async prepareWorkspaceStorageFolder(): Promise<{ storageFilePath: string, wasCreated: boolean }> { @@ -298,15 +275,16 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai return { storageFilePath: workspaceStorageDatabasePath, wasCreated: false }; } + // Ensure storage folder exists await promises.mkdir(workspaceStorageFolderPath, { recursive: true }); - // Write metadata into folder + // Write metadata into folder (but do not await) this.ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath); return { storageFilePath: workspaceStorageDatabasePath, wasCreated: true }; } - private ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath: string): void { + private async ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath: string): Promise { let meta: object | undefined = undefined; if (isSingleFolderWorkspaceIdentifier(this.workspace)) { meta = { folder: this.workspace.uri.toString() }; @@ -315,17 +293,15 @@ export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMai } if (meta) { - (async () => { - try { - const workspaceStorageMetaPath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_META_NAME); - const storageExists = await exists(workspaceStorageMetaPath); - if (!storageExists) { - await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); - } - } catch (error) { - this.logService.error(`StorageMain#ensureWorkspaceStorageFolderMeta(): Unable to create workspace storage metadata due to ${error}`); + try { + const workspaceStorageMetaPath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_META_NAME); + const storageExists = await exists(workspaceStorageMetaPath); + if (!storageExists) { + await writeFile(workspaceStorageMetaPath, JSON.stringify(meta, undefined, 2)); } - })(); + } catch (error) { + this.logService.error(`StorageMain#ensureWorkspaceStorageFolderMeta(): Unable to create workspace storage metadata due to ${error}`); + } } } } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index a82e8efd0a8..842bee97c24 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -62,14 +62,14 @@ export class StorageMainService extends Disposable implements IStorageMainServic (async () => { await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - this.globalStorage.initialize(); + this.globalStorage.init(); })(); // Workspace Storage: Warmup when related window with workspace loads if (this.enableMainWorkspaceStorage()) { this._register(this.lifecycleMainService.onWillLoadWindow(async e => { if (e.workspace) { - this.workspaceStorage(e.workspace).initialize(); + this.workspaceStorage(e.workspace).init(); } })); } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index a0aa7f151d3..2a91d25b5d9 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -81,12 +81,12 @@ suite('StorageMainService', function () { if (isGlobal) { strictEqual(storage.items.size, 0); strictEqual(storage.get(instanceStorageKey), undefined); - await storage.initialize(); + await storage.init(); strictEqual(typeof storage.get(instanceStorageKey), 'string'); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); strictEqual(typeof storage.get(currentSessionDateStorageKey), 'string'); } else { - await storage.initialize(); + await storage.init(); } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; @@ -100,24 +100,24 @@ suite('StorageMainService', function () { // Basic store/get/remove const size = storage.items.size; - storage.store('bar', 'foo'); + storage.set('bar', 'foo'); strictEqual(storageChangeEvent!.key, 'bar'); - storage.store('barNumber', 55); - storage.store('barBoolean', true); + storage.set('barNumber', 55); + storage.set('barBoolean', true); strictEqual(storage.get('bar'), 'foo'); - strictEqual(storage.getNumber('barNumber'), 55); - strictEqual(storage.getBoolean('barBoolean'), true); + strictEqual(storage.get('barNumber'), '55'); + strictEqual(storage.get('barBoolean'), 'true'); strictEqual(storage.items.size, size + 3); - storage.remove('bar'); + storage.delete('bar'); strictEqual(storage.get('bar'), undefined); strictEqual(storage.items.size, size + 2); // IS_NEW - strictEqual(storage.getBoolean(IS_NEW_KEY), true); + strictEqual(storage.get(IS_NEW_KEY), 'true'); // Close await storage.close(); @@ -160,8 +160,8 @@ suite('StorageMainService', function () { strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed - await globalStorage.initialize(); - await workspaceStorage.initialize(); + await globalStorage.init(); + await workspaceStorage.init(); await lifecycleMainService.fireOnWillShutdown(); @@ -184,13 +184,39 @@ suite('StorageMainService', function () { didCloseWorkspaceStorage = true; }); - let globalStorage1 = storageMainService.globalStorage; + let globalStorage = storageMainService.globalStorage; let didCloseGlobalStorage = false; - globalStorage1.onDidCloseStorage(() => { + globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); - await globalStorage1.close(); + await globalStorage.close(); + await workspaceStorage.close(); + + strictEqual(didCloseGlobalStorage, true); + strictEqual(didCloseWorkspaceStorage, true); + }); + + test('storage closed before init awaits works', async function () { + const storageMainService = new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS)), new StorageTestLifecycleMainService(), new TestConfigurationService()); + const workspace = { id: generateUuid() }; + + let workspaceStorage = storageMainService.workspaceStorage(workspace); + let didCloseWorkspaceStorage = false; + workspaceStorage.onDidCloseStorage(() => { + didCloseWorkspaceStorage = true; + }); + + let globalStorage = storageMainService.globalStorage; + let didCloseGlobalStorage = false; + globalStorage.onDidCloseStorage(() => { + didCloseGlobalStorage = true; + }); + + globalStorage.init(); + workspaceStorage.init(); + + await globalStorage.close(); await workspaceStorage.close(); strictEqual(didCloseGlobalStorage, true); diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index a78accbef8e..c386a584857 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -269,7 +269,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Request handling this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentMainService, this.fileService, { get: key => storageMainService.globalStorage.get(key), - store: (key, value) => storageMainService.globalStorage.store(key, value) + store: (key, value) => storageMainService.globalStorage.set(key, value) }); // Eventing From 7a0caf4d86e400d82e401c8c37f36ae510145134 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 17 Feb 2021 10:33:46 +0100 Subject: [PATCH 154/176] Let Remote extensions set port source (#116838) --- src/vs/vscode.proposed.d.ts | 8 ++++ .../api/browser/mainThreadTunnelService.ts | 37 ++++++++++++++++--- .../workbench/api/common/extHost.api.impl.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 7 ++++ .../api/node/extHostTunnelService.ts | 3 ++ .../contrib/remote/browser/remoteExplorer.ts | 6 +-- .../contrib/remote/browser/tunnelView.ts | 3 +- .../remote/common/remoteExplorerService.ts | 4 ++ 8 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 341b5b78df6..bf24ab9b655 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -204,6 +204,12 @@ declare module 'vscode' { elevationRequired?: boolean; } + export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 + } + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; export class RemoteAuthorityResolverError extends Error { @@ -238,6 +244,8 @@ declare module 'vscode' { elevation: boolean; public: boolean; }; + + candidatePortSource?: CandidatePortSource; } export namespace workspace { diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index bc7c653b981..052f5a24516 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { CandidatePort, IRemoteExplorerService, makeAddress } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { PORT_AUTO_FORWARD_SETTING } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @extHostNamedCustomer(MainContext.MainThreadTunnelService) export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape { @@ -27,7 +27,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun @ITunnelService private readonly tunnelService: ITunnelService, @INotificationService private readonly notificationService: INotificationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTunnelService); @@ -132,6 +133,32 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun }); } + async $setCandidatePortSource(source: CandidatePortSource): Promise { + // Must wait for the remote environment before trying to set settings there. + this.remoteAgentService.getEnvironment().then(() => { + switch (source) { + case CandidatePortSource.None: { + const autoDetectionEnablement = this.configurationService.inspect(PORT_AUTO_FORWARD_SETTING); + if (autoDetectionEnablement.userRemote === undefined) { + // Only update the remote setting if the user hasn't already set it. + this.configurationService.updateValue(PORT_AUTO_FORWARD_SETTING, false, ConfigurationTarget.USER_REMOTE); + } + break; + } + case CandidatePortSource.Output: { + const candidatePortSourceSetting = this.configurationService.inspect(PORT_AUTO_SOURCE_SETTING); + if (candidatePortSourceSetting.userRemote === undefined) { + // Only update the remote setting if the user hasn't already set it. + this.configurationService.updateValue(PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, ConfigurationTarget.USER_REMOTE); + } + break; + } + default: // Do nothing, the defaults for these settings should be used. + } + }).catch(() => { + // The remote failed to get setup. Errors from that area will already be surfaced to the user. + }); + } dispose(): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9b95c71438e..32d13f792e0 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { score } from 'vs/editor/common/modes/languageSelector'; import * as files from 'vs/platform/files/common/files'; -import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -1134,6 +1134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, CancellationError: errors.CancellationError, CancellationTokenSource: CancellationTokenSource, + CandidatePortSource: CandidatePortSource, CodeAction: extHostTypes.CodeAction, CodeActionKind: extHostTypes.CodeActionKind, CodeActionTrigger: extHostTypes.CodeActionTrigger, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ceef2474341..fdb63d9c2d2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -992,6 +992,12 @@ export interface MainThreadWindowShape extends IDisposable { $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } +export enum CandidatePortSource { + None = 0, + Process = 1, + Output = 2 +} + export interface MainThreadTunnelServiceShape extends IDisposable { $openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise; $closeTunnel(remote: { host: string, port: number }): Promise; @@ -1000,6 +1006,7 @@ export interface MainThreadTunnelServiceShape extends IDisposable { $setRemoteTunnelService(processId: number): Promise; $setCandidateFilter(): Promise; $onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise; + $setCandidatePortSource(source: CandidatePortSource): Promise; } export interface MainThreadTimelineShape extends IDisposable { diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index c0cc5d43940..9b9dd79dc9c 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -196,6 +196,9 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise { if (provider) { + if (provider.candidatePortSource !== undefined) { + await this._proxy.$setCandidatePortSource(provider.candidatePortSource); + } if (provider.showCandidatePort) { this._showCandidatePort = provider.showCandidatePort; await this._proxy.$setCandidateFilter(); diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 89a5e8545f4..e0c1a0c44cb 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -6,8 +6,8 @@ import * as nls from 'vs/nls'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Extensions, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PortsAttributes, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; -import { PORT_AUTO_FORWARD_SETTING, forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; +import { IRemoteExplorerService, makeAddress, mapHasAddressLocalhostOrAllInterfaces, OnPortForward, PortsAttributes, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_PROCESS, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { forwardedPortsViewEnabled, ForwardPortAction, OpenPortInBrowserAction, TunnelPanel, TunnelPanelDescriptor, TunnelViewModel, OpenPortInPreviewAction } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -219,7 +219,7 @@ export class AutomaticPortForwarding extends Disposable implements IWorkbenchCon this._register(new OutputAutomaticPortForwarding(terminalService, notificationService, openerService, externalOpenerService, remoteExplorerService, configurationService, debugService, tunnelService, remoteAgentService, hostService, false)); } else if (environment?.os === OperatingSystem.Linux) { - const useProc = (this.configurationService.getValue('remote.autoForwardPortsSource') === 'process'); + const useProc = (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS); if (useProc) { this._register(new ProcAutomaticPortForwarding(configurationService, remoteExplorerService, notificationService, openerService, externalOpenerService, tunnelService, hostService)); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index daa3fba23f9..d0fb151868d 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IMenuService, MenuId, IMenu, MenuRegistry, ILocalizedString, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { createAndFillInContextMenuActions, createAndFillInActionBarActions, createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress, CandidatePort, TunnelPrivacy } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, TunnelModel, makeAddress, TunnelType, ITunnelItem, Tunnel, mapHasAddressLocalhostOrAllInterfaces, TUNNEL_VIEW_ID, parseAddress, CandidatePort, TunnelPrivacy, PORT_AUTO_FORWARD_SETTING } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -48,7 +48,6 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isWeb } from 'vs/base/common/platform'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); -export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; class TunnelTreeVirtualDelegate implements IListVirtualDelegate { getHeight(element: ITunnelItem): number { diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 1ced7e337bf..f3420ff3d97 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -23,6 +23,10 @@ export const IRemoteExplorerService = createDecorator('r export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType'; const TUNNELS_TO_RESTORE = 'remote.tunnels.toRestore'; export const TUNNEL_VIEW_ID = '~remote.forwardedPorts'; +export const PORT_AUTO_FORWARD_SETTING = 'remote.autoForwardPorts'; +export const PORT_AUTO_SOURCE_SETTING = 'remote.autoForwardPortsSource'; +export const PORT_AUTO_SOURCE_SETTING_PROCESS = 'process'; +export const PORT_AUTO_SOURCE_SETTING_OUTPUT = 'output'; export enum TunnelType { Candidate = 'Candidate', From 1bf78c50fe9ec9cd526ed97f723cf4e0bbc2ae7b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 10:35:50 +0100 Subject: [PATCH 155/176] add NotebookCellOutput#metadata, also use API type internally and translate dto sooner, fixes https://github.com/microsoft/vscode/issues/116792 --- src/vs/vscode.proposed.d.ts | 10 +++-- .../api/common/extHostNotebookDocument.ts | 18 ++++----- .../api/common/extHostTypeConverters.ts | 37 ++++++++++--------- src/vs/workbench/api/common/extHostTypes.ts | 26 ++++++++++--- .../model/notebookCellOutputTextModel.ts | 12 +++--- .../contrib/notebook/common/notebookCommon.ts | 7 +--- 6 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index bf24ab9b655..a06d8165fab 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1507,9 +1507,9 @@ declare module 'vscode' { readonly mime: string; readonly value: unknown; - readonly metadata?: Record; + readonly metadata?: Record; - constructor(mime: string, value: unknown, metadata?: Record); + constructor(mime: string, value: unknown, metadata?: Record); } // @jrieken @@ -1517,7 +1517,11 @@ declare module 'vscode' { export class NotebookCellOutput { readonly id: string; readonly outputs: NotebookCellOutputItem[]; - constructor(outputs: NotebookCellOutputItem[], id?: string); + readonly metadata?: Record; + + constructor(outputs: NotebookCellOutputItem[], metadata?: Record); + + constructor(outputs: NotebookCellOutputItem[], id: string, metadata?: Record); } //#endregion diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index b20e4c9ed1e..4bd68b65686 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -47,7 +47,7 @@ export class ExtHostCell { private _onDidDispose = new Emitter(); readonly onDidDispose: Event = this._onDidDispose.event; - private _outputs: IOutputDto[]; + private _outputs: extHostTypes.NotebookCellOutput[]; private _metadata: extHostTypes.NotebookCellMetadata; readonly handle: number; @@ -64,7 +64,7 @@ export class ExtHostCell { this.handle = _cellData.handle; this.uri = URI.revive(_cellData.uri); this.cellKind = _cellData.cellKind; - this._outputs = _cellData.outputs; + this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to); this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {}); } @@ -87,7 +87,7 @@ export class ExtHostCell { cellKind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind), document: data.document, get language() { return data!.document.languageId; }, - get outputs() { return that._outputs.map(extHostTypeConverters.NotebookCellOutput.to); }, + get outputs() { return that._outputs.slice(0); }, set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); }, get metadata() { return that._metadata; }, set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); }, @@ -97,17 +97,17 @@ export class ExtHostCell { } setOutputs(newOutputs: IOutputDto[]): void { - this._outputs = newOutputs; + this._outputs = newOutputs.map(extHostTypeConverters.NotebookCellOutput.to); } setOutputItems(outputId: string, append: boolean, newOutputItems: IOutputItemDto[]) { - const output = this._outputs.find(op => op.outputId === outputId); + const newItems = newOutputItems.map(extHostTypeConverters.NotebookCellOutputItem.to); + const output = this._outputs.find(op => op.id === outputId); if (output) { - if (append) { - output.outputs = [...output.outputs, ...newOutputItems]; - } else { - output.outputs = newOutputItems; + if (!append) { + output.outputs.length = 0; } + output.outputs.push(...newItems); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 8b57657a364..039418367df 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -596,7 +596,7 @@ export namespace WorkspaceEdit { editType: notebooks.CellEditType.OutputItems, index: entry.index, outputId: entry.outputId, - items: entry.newOutputItems?.map(item => ({ mime: item.mime, value: item.value, metadata: item.metadata })) || [], + items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [], append: entry.append } }); @@ -1476,31 +1476,32 @@ export namespace NotebookCellData { } } +export namespace NotebookCellOutputItem { + export function from(item: types.NotebookCellOutputItem): notebooks.IOutputItemDto { + return { + mime: item.mime, + value: item.value, + metadata: item.metadata + }; + } + + export function to(item: notebooks.IOutputItemDto): types.NotebookCellOutputItem { + return new types.NotebookCellOutputItem(item.mime, item.value, item.metadata); + } +} + export namespace NotebookCellOutput { export function from(output: types.NotebookCellOutput): notebooks.IOutputDto { - - const data = Object.create(null); - const custom = Object.create(null); - - for (let item of output.outputs) { - data[item.mime] = item.value; - custom[item.mime] = item.metadata; - } - return { outputId: output.id, - outputs: (output.outputs || []).map(op => ({ - mime: op.mime, - value: op.value, - metadata: op.metadata - })) || [], - // metadata: isEmptyObject(custom) ? undefined : { custom } + outputs: output.outputs.map(NotebookCellOutputItem.from), + metadata: output.metadata }; } export function to(output: notebooks.IOutputDto): vscode.NotebookCellOutput { - const items: types.NotebookCellOutputItem[] = output.outputs.map(op => new types.NotebookCellOutputItem(op.mime, op.value, op.metadata)); - return new types.NotebookCellOutput(items, output.outputId); + const items = output.outputs.map(NotebookCellOutputItem.to); + return new types.NotebookCellOutput(items, output.outputId, output.metadata); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index c815d8322af..7a2b4b9a1b9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -703,8 +703,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void { - let newOutputs: NotebookCellOutput[] = outputs; - this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs, newMetadata: undefined }); + this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined }); } replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { @@ -2993,15 +2992,30 @@ export class NotebookCellOutputItem { constructor( readonly mime: string, readonly value: unknown, // JSON'able - readonly metadata?: any + readonly metadata?: Record ) { } } export class NotebookCellOutput { + + readonly outputs: NotebookCellOutputItem[]; + readonly id: string; + readonly metadata?: Record; + constructor( - readonly outputs: NotebookCellOutputItem[], - readonly id: string = generateUuid() - ) { } + outputs: NotebookCellOutputItem[], + idOrMetadata?: string | Record, + metadata?: Record + ) { + this.outputs = outputs; + if (typeof idOrMetadata === 'string') { + this.id = idOrMetadata; + this.metadata = metadata; + } else { + this.id = generateUuid(); + this.metadata = idOrMetadata ?? metadata; + } + } } export enum NotebookCellKind { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts index 0172d6a1f2d..aa05e729203 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts @@ -16,9 +16,11 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp get outputs() { return this._rawOutput.outputs || []; } - // get metadata(): NotebookCellOutputMetadata | undefined { - // return this._rawOutput.metadata; - // } + + get metadata(): Record | undefined { + return this._rawOutput.metadata; + } + get outputId(): string { return this._rawOutput.outputId; } @@ -47,10 +49,10 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp this._onDidChangeData.fire(); } - toJSON() { + toJSON(): IOutputDto { return { // data: this._data, - // metadata: this._rawOutput.metadata, + metadata: this._rawOutput.metadata, outputs: this._rawOutput.outputs, outputId: this._rawOutput.outputId }; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index be7d26f468d..8196d8ac5f2 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -159,13 +159,8 @@ export interface IOutputItemDto { export interface IOutputDto { outputs: IOutputItemDto[]; - /** - * { mime_type: value } - */ - // data: { [key: string]: unknown; } - - // metadata?: NotebookCellOutputMetadata; outputId: string; + metadata?: Record; } export interface ICellOutput { From 44dbd182557ee8c164f3917a2f9dc09a37ff623c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 17 Feb 2021 11:47:52 +0100 Subject: [PATCH 156/176] Wait for tree to be registered before disposing This fixes the tree view rpc test Fixes #116776 --- src/vs/workbench/api/browser/mainThreadTreeViews.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostTreeViews.ts | 7 ++++--- src/vs/workbench/test/browser/api/extHostTreeViews.test.ts | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index cfe9d842e8d..40b6794bfae 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -31,7 +31,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index fdb63d9c2d2..2e330fd4ee5 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -285,7 +285,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): void; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem; }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index ca1914d2e38..df51908e74a 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -84,7 +84,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, @@ -110,7 +110,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { reveal: (element: T, options?: IRevealOptions): Promise => { return treeView.reveal(element, options); }, - dispose: () => { + dispose: async () => { + // Wait for the registration promise to finish before doing the dispose. + await registerPromise; this.treeViews.delete(viewId); treeView.dispose(); } @@ -240,7 +242,6 @@ class ExtHostTreeView extends Disposable { } } this.dataProvider = options.treeDataProvider; - this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany }); if (this.dataProvider.onDidChangeTreeData) { this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element }))); } diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 22f2eeef016..134e347dd65 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -26,7 +26,7 @@ suite('ExtHostTreeView', function () { onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>(); - $registerTreeViewDataProvider(treeViewId: string): void { + async $registerTreeViewDataProvider(treeViewId: string): Promise { } $refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise { From 13070015bddbca61ad566d1abb8aa3ea95edf9cb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 11:52:11 +0100 Subject: [PATCH 157/176] expose context key info command, add first version of completion item provider for package.json and keybindings.json files, https://github.com/microsoft/vscode/issues/9303 --- .../src/configurationEditingMain.ts | 40 +++++++++++++++++++ .../contextkey/browser/contextKeyService.ts | 12 ++++++ .../platform/contextkey/common/contextkey.ts | 4 +- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index a3ef34f3d83..f4c2ab34174 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -22,6 +22,9 @@ export function activate(context: vscode.ExtensionContext): void { // task.json variable suggestions context.subscriptions.push(registerVariableCompletions('**/tasks.json')); + + // keybindings.json/package.json context key suggestions + context.subscriptions.push(registerContextKeyCompletions()); } function registerSettingsCompletions(): vscode.Disposable { @@ -136,3 +139,40 @@ vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', lan return result; } }, { label: 'Launch Targets' }); + +function registerContextKeyCompletions(): vscode.Disposable { + type ContextKeyInfo = { key: string, type?: string, description?: string }; + return vscode.languages.registerCompletionItemProvider( + [{ language: 'json', pattern: '**/package.json' }, { language: 'jsonc', pattern: '**/keybindings.json' }], + { + async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) { + + const replacing = document.getWordRangeAtPosition(position, /[\w.-\d]/); + const inserting = replacing?.with(undefined, position); + if (!replacing || !inserting) { + return; + } + + const location = getLocation(document.getText(), document.offsetAt(position)); + if (!location.matches(['*', 'when']) && !location.matches(['contributes', 'menus', '*', '*', 'when'])) { + return; + } + + const data = await vscode.commands.executeCommand('getContextKeyInfo'); + if (token.isCancellationRequested || !data) { + return; + } + + const result = new vscode.CompletionList(); + for (const item of data) { + const completion = new vscode.CompletionItem(item.key, vscode.CompletionItemKind.Constant); + completion.detail = item.type; + completion.range = { replacing, inserting }; + completion.documentation = item.description; + result.items.push(completion); + } + return result; + } + } + ); +} diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index 00e2bde4875..e40016315b3 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -8,6 +8,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { distinct } from 'vs/base/common/objects'; +import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, SET_CONTEXT_COMMAND_ID, ContextKeyExpression, RawContextKey, ContextKeyInfo } from 'vs/platform/contextkey/common/contextkey'; @@ -578,6 +579,17 @@ CommandsRegistry.registerCommand(SET_CONTEXT_COMMAND_ID, function (accessor, con accessor.get(IContextKeyService).createKey(String(contextKey), contextValue); }); +CommandsRegistry.registerCommand({ + id: 'getContextKeyInfo', + handler() { + return [...RawContextKey.all()].sort((a, b) => a.key.localeCompare(b.key)); + }, + description: { + description: localize('getContextKeyInfo', "A command that returns information about context keys"), + args: [] + } +}); + CommandsRegistry.registerCommand('_generateContextKeyInfo', function () { const result: ContextKeyInfo[] = []; const seen = new Set(); diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 6ecb8a5dc76..af2326a7565 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -1259,7 +1259,7 @@ export class ContextKeyOrExpr implements IContextKeyExpression { export interface ContextKeyInfo { readonly key: string; - readonly type: string; + readonly type?: string; readonly description?: string; } @@ -1281,7 +1281,7 @@ export class RawContextKey extends ContextKeyDefinedExpr { if (typeof metaOrHide === 'object') { RawContextKey._info.push({ ...metaOrHide, key }); } else if (metaOrHide !== true) { - RawContextKey._info.push({ key, description: metaOrHide, type: typeof defaultValue }); + RawContextKey._info.push({ key, description: metaOrHide, type: defaultValue !== null && defaultValue !== undefined ? typeof defaultValue : undefined }); } } From 678acbe536491d9dccf1b983291fe61b29e26fcc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 13:16:28 +0100 Subject: [PATCH 158/176] outputs in notebooks --- .vscode/notebooks/api.github-issues | 1305 ++++++++++++++++++++++++++- 1 file changed, 1299 insertions(+), 6 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 71fd646776f..c0154336692 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -3,36 +3,1329 @@ "kind": 1, "language": "markdown", "value": "#### Config", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"February 2021\"", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 1, "language": "markdown", "value": "### Finalization", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo $milestone label:api-finalization", - "editable": true + "editable": true, + "outputs": [ + { + "mime": "text/markdown", + "value": "- [#88309](https://github.com/microsoft/vscode/issues/88309 \"Authentication Provider API\") Authentication Provider API [api-finalization, authentication, feature-request, settings-sync]- [@RMacfarlane](https://github.com/RMacfarlane \"Issue 88309 is assigned to RMacfarlane\")\n\n" + }, + { + "mime": "x-application/github-issues", + "value": [ + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/88309", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/88309/events", + "html_url": "https://github.com/microsoft/vscode/issues/88309", + "id": 547141160, + "node_id": "MDU6SXNzdWU1NDcxNDExNjA=", + "number": 88309, + "title": "Authentication Provider API", + "user": { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1702048079, + "node_id": "MDU6TGFiZWwxNzAyMDQ4MDc5", + "url": "https://api.github.com/repos/microsoft/vscode/labels/authentication", + "name": "authentication", + "color": "c5def5", + "default": false, + "description": "Authentication issues" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 1684021718, + "node_id": "MDU6TGFiZWwxNjg0MDIxNzE4", + "url": "https://api.github.com/repos/microsoft/vscode/labels/settings-sync", + "name": "settings-sync", + "color": "1d76db", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 23, + "created_at": "2020-01-08T22:31:35Z", + "updated_at": "2021-02-10T20:41:11Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "### Problem\r\n\r\nThere are currently some extensions that attempt to provide authentication abilities that can be reused by other extensions. (An example being the Azure Account extension). Now that we've begun working on login for settings sync, it's worth revisiting if authentication should be a first-class concept in VS Code. By exposing an API to contribute an authentication flow\r\n\r\n- the core of VSCode can potentially leverage authentication\r\n- other extensions can leverage authentication\r\n- UI for account management could be centralized\r\n\r\n### Proposal\r\n\r\nI propose introducing a concept of an \"AuthenticationProvider\". Such a provider implements methods for logging in and logging out of a specified account, and exposes a list of accounts that are currently available with an event listener for changes to these. This abstracts away refreshing tokens from consumers - the AuthenticationProvider extension can manage refreshing in the background and fire an event when the accessToken has been changed.\r\n\r\n```ts\r\nexport interface Account {\r\n\treadonly id: string;\r\n\treadonly accessToken: string;\r\n\treadonly displayName: string;\r\n}\r\n\r\nexport interface AuthenticationProvider {\r\n\treadonly id: string; // perhaps \"type\"? Would be something like \"GitHub\", \"MSA\", etc.\r\n\treadonly displayName: string;\r\n\r\n\taccounts: ReadonlyArray;\r\n\tonDidChangeAccounts: Event>;\r\n\r\n\tlogin(): Promise;\r\n\tlogout(accountId: string): Promise;\r\n}\r\n\r\nexport namespace authentication {\r\n\texport function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;\r\n\texport const authenticationProviders: ReadonlyArray;\r\n}\r\n```\r\n\r\nConsumers would need to know the id of the provider they're looking for. For example, the settings sync code would look for an \"MSA\" provider since this is what the setting sync backend currently needs.\r\n\r\nSince the authentication provider extension would be activated in each VS Code window, the extension would be responsible for synchronizing state across instances. By default, such extensions would have [\"ui\", \"workspace\"] extensionKind, so that they can store and read credentials on the local machine in both the desktop and web case.", + "performed_via_github_app": null, + "score": 1 + } + ] + } + ] }, { "kind": 1, "language": "markdown", "value": "### Proposals", - "editable": true + "editable": true, + "outputs": [] }, { "kind": 2, "language": "github-issues", "value": "$repo $milestone is:open label:api-proposal ", - "editable": true + "editable": true, + "outputs": [ + { + "mime": "text/markdown", + "value": "- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-proposal, custom-editors, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n- [#115626](https://github.com/microsoft/vscode/issues/115626 \"Microsoft Auth Provider should support overriding client id and tenant id\") Microsoft Auth Provider should support overriding client id and tenant id [api-proposal, authentication]- [@TylerLeonhardt](https://github.com/TylerLeonhardt \"Issue 115626 is assigned to TylerLeonhardt\")\n\n- [#115616](https://github.com/microsoft/vscode/issues/115616 \"Provide extension API to exclude ports from forwarding\") Provide extension API to exclude ports from forwarding [api, api-proposal, feature-request, remote-explorer]\n- [#114123](https://github.com/microsoft/vscode/issues/114123 \"Resolve the conflict run button in editor context menu\") Resolve the conflict run button in editor context menu [api-proposal, feature-request, menus]- [@jrieken](https://github.com/jrieken \"Issue 114123 is assigned to jrieken\")\n\n- [#109277](https://github.com/microsoft/vscode/issues/109277 \"Let extensions hook into url opening\") Let extensions hook into url opening [api, api-proposal, under-discussion]- [@mjbvz](https://github.com/mjbvz \"Issue 109277 is assigned to mjbvz\")\n\n- [#107467](https://github.com/microsoft/vscode/issues/107467 \"Testing in VS Code\") Testing in VS Code [api-proposal, plan-item, under-discussion]- [@connor4312](https://github.com/connor4312 \"Issue 107467 is assigned to connor4312\")\n\n- [#105690](https://github.com/microsoft/vscode/issues/105690 \"Extension API for Inline Values\") Extension API for Inline Values [api, api-proposal, debug, feature-request]- [@weinand](https://github.com/weinand \"Issue 105690 is assigned to weinand\")\n\n" + }, + { + "mime": "x-application/github-issues", + "value": [ + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115631", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/events", + "html_url": "https://github.com/microsoft/vscode/issues/115631", + "id": 799606785, + "node_id": "MDU6SXNzdWU3OTk2MDY3ODU=", + "number": 115631, + "title": "Provide a way for custom editors to process untitled files without relying on textDocument", + "user": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1713330180, + "node_id": "MDU6TGFiZWwxNzEzMzMwMTgw", + "url": "https://api.github.com/repos/microsoft/vscode/labels/custom-editors", + "name": "custom-editors", + "color": "c5def5", + "default": false, + "description": "Custom editor API (webview based editors)" + }, + { + "id": 1839857516, + "node_id": "MDU6TGFiZWwxODM5ODU3NTE2", + "url": "https://api.github.com/repos/microsoft/vscode/labels/notebook", + "name": "notebook", + "color": "c5def5", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 1, + "created_at": "2021-02-02T19:29:05Z", + "updated_at": "2021-02-02T21:58:36Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Currently the \"Reopen with\" experience for untitled files and custom binary editors needs better support. See #114711. After discussion in the API call the best proposal seems to be placing the untitled file data in the OpenEditor / OpenNotebook context. There interface would be modified as shown:\r\n```ts\r\n\t/**\r\n\t * Additional information about the opening custom document.\r\n\t */\r\n\tinterface CustomDocumentOpenContext {\r\n\t\t/**\r\n\t\t * The id of the backup to restore the document from or `undefined` if there is no backup.\r\n\t\t *\r\n\t\t * If this is provided, your extension should restore the editor from the backup instead of reading the file\r\n\t\t * from the user's workspace.\r\n\t\t */\r\n\t\treadonly backupId?: string;\r\n\t\t/**\r\n\t\t * If the URI is an untitled file, this will be populated with the byte data of that file\r\n\t\t *\r\n\t\t * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in\r\n\t\t */\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n\r\n\tinterface NotebookDocumentOpenContext {\r\n\t\treadonly backupId?: string;\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n```\r\nThe extension other would then not be required to resolve the URI to a text document (which would have been disposed of). ", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115626", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115626/events", + "html_url": "https://github.com/microsoft/vscode/issues/115626", + "id": 799566516, + "node_id": "MDU6SXNzdWU3OTk1NjY1MTY=", + "number": 115626, + "title": "Microsoft Auth Provider should support overriding client id and tenant id", + "user": { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1702048079, + "node_id": "MDU6TGFiZWwxNzAyMDQ4MDc5", + "url": "https://api.github.com/repos/microsoft/vscode/labels/authentication", + "name": "authentication", + "color": "c5def5", + "default": false, + "description": "Authentication issues" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "RMacfarlane", + "id": 3672607, + "node_id": "MDQ6VXNlcjM2NzI2MDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/3672607?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/RMacfarlane", + "html_url": "https://github.com/RMacfarlane", + "followers_url": "https://api.github.com/users/RMacfarlane/followers", + "following_url": "https://api.github.com/users/RMacfarlane/following{/other_user}", + "gists_url": "https://api.github.com/users/RMacfarlane/gists{/gist_id}", + "starred_url": "https://api.github.com/users/RMacfarlane/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/RMacfarlane/subscriptions", + "organizations_url": "https://api.github.com/users/RMacfarlane/orgs", + "repos_url": "https://api.github.com/users/RMacfarlane/repos", + "events_url": "https://api.github.com/users/RMacfarlane/events{/privacy}", + "received_events_url": "https://api.github.com/users/RMacfarlane/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 0, + "created_at": "2021-02-02T18:42:12Z", + "updated_at": "2021-02-02T18:58:50Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "\r\n\r\n\r\n\r\n\r\n\r\nThe Microsoft Auth Provider uses a specific AAD application with client id hardcoded here:\r\nhttps://github.com/microsoft/vscode/blob/582ea371c2bf785d88458dab95828387ad94a63d/extensions/microsoft-authentication/src/AADHelper.ts#L25-L26\r\n\r\nHowever, this application only has access to a handful of scopes, and to add _allowed_ scopes to this client id is a manual process (which for an external extension author means opening an issue here and then having one of us add that scope to the _allowed_ scopes for the application)\r\n\r\nAs an extension author, I should easily be able to create my own AAD application (in the Azure Portal for example) and use that client id instead of the one vscode uses so that I can have control over the scopes I care about and, if this exists, I can get telemetry when my client id is used.\r\n\r\nSince we have abstracted auth providers, I think it's fitting to be able to pass additional auth provider specific options down to an auth provider. For example, the Microsoft auth provider would take a client id and tenant that would replace the hard coded string above.\r\n\r\nProposal:\r\n\r\n```ts\r\n /**\r\n\t * Options to be used when getting an [AuthenticationSession](#AuthenticationSession) from an [AuthenticationProvider](#AuthenticationProvider).\r\n\t */\r\n\texport interface AuthenticationGetSessionOptions {\r\n\t\t/**\r\n\t\t * Whether login should be performed if there is no matching session.\r\n\t\t *\r\n\t\t * If true, a modal dialog will be shown asking the user to sign in. If false, a numbered badge will be shown\r\n\t\t * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This\r\n\t\t * allows quietly prompting the user to sign in.\r\n\t\t *\r\n\t\t * Defaults to false.\r\n\t\t */\r\n\t\tcreateIfNone?: boolean;\r\n\r\n\t\t/**\r\n\t\t * Whether the existing user session preference should be cleared.\r\n\t\t *\r\n\t\t * For authentication providers that support being signed into multiple accounts at once, the user will be\r\n\t\t * prompted to select an account to use when [getSession](#authentication.getSession) is called. This preference\r\n\t\t * is remembered until [getSession](#authentication.getSession) is called with this flag.\r\n\t\t *\r\n\t\t * Defaults to false.\r\n\t\t */\r\n\t\tclearSessionPreference?: boolean;\r\n\r\n\t\t/*************/\r\n\t\t/*** NEW ***/\r\n\t\t/*************/\r\n /**\r\n * Provider specific options for getting this session (i.e. client id, tenant)\r\n */\r\n\t\tproviderOptions?: { [key: string]: any; }\r\n\t}\r\n```\r\n\r\nThe Auth Provider would then need to be responsible for deciding if it already has created a session with these options or if it needs to create a new session based on these options.", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115616", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115616/events", + "html_url": "https://github.com/microsoft/vscode/issues/115616", + "id": 799392757, + "node_id": "MDU6SXNzdWU3OTkzOTI3NTc=", + "number": 115616, + "title": "Provide extension API to exclude ports from forwarding", + "user": { + "login": "alexr00", + "id": 38270282, + "node_id": "MDQ6VXNlcjM4MjcwMjgy", + "avatar_url": "https://avatars.githubusercontent.com/u/38270282?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alexr00", + "html_url": "https://github.com/alexr00", + "followers_url": "https://api.github.com/users/alexr00/followers", + "following_url": "https://api.github.com/users/alexr00/following{/other_user}", + "gists_url": "https://api.github.com/users/alexr00/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alexr00/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alexr00/subscriptions", + "organizations_url": "https://api.github.com/users/alexr00/orgs", + "repos_url": "https://api.github.com/users/alexr00/repos", + "events_url": "https://api.github.com/users/alexr00/events{/privacy}", + "received_events_url": "https://api.github.com/users/alexr00/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 1772775110, + "node_id": "MDU6TGFiZWwxNzcyNzc1MTEw", + "url": "https://api.github.com/repos/microsoft/vscode/labels/remote-explorer", + "name": "remote-explorer", + "color": "1d76db", + "default": false, + "description": "Remote explorer view" + } + ], + "state": "open", + "locked": false, + "assignee": null, + "assignees": [ + { + "login": "alexr00", + "id": 38270282, + "node_id": "MDQ6VXNlcjM4MjcwMjgy", + "avatar_url": "https://avatars.githubusercontent.com/u/38270282?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/alexr00", + "html_url": "https://github.com/alexr00", + "followers_url": "https://api.github.com/users/alexr00/followers", + "following_url": "https://api.github.com/users/alexr00/following{/other_user}", + "gists_url": "https://api.github.com/users/alexr00/gists{/gist_id}", + "starred_url": "https://api.github.com/users/alexr00/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/alexr00/subscriptions", + "organizations_url": "https://api.github.com/users/alexr00/orgs", + "repos_url": "https://api.github.com/users/alexr00/repos", + "events_url": "https://api.github.com/users/alexr00/events{/privacy}", + "received_events_url": "https://api.github.com/users/alexr00/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 15, + "created_at": "2021-02-02T15:37:45Z", + "updated_at": "2021-02-12T16:08:10Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "From @weinand:\r\nToday the tunneling service blindly forwards all communication ports.\r\nThis includes ports that are used for debugging (even if our remote debugging feature does not need these ports to be forwarded).\r\nThis is confusing for users because they see ports that they are not really interested in.\r\n\r\nI propose to add extension API so that individual ports or port ranges can be excluded from forwarding.\r\nDebug extensions could use this API.\r\n\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/114123", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/114123/events", + "html_url": "https://github.com/microsoft/vscode/issues/114123", + "id": 783094648, + "node_id": "MDU6SXNzdWU3ODMwOTQ2NDg=", + "number": 114123, + "title": "Resolve the conflict run button in editor context menu", + "user": { + "login": "jdneo", + "id": 6193897, + "node_id": "MDQ6VXNlcjYxOTM4OTc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6193897?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jdneo", + "html_url": "https://github.com/jdneo", + "followers_url": "https://api.github.com/users/jdneo/followers", + "following_url": "https://api.github.com/users/jdneo/following{/other_user}", + "gists_url": "https://api.github.com/users/jdneo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jdneo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jdneo/subscriptions", + "organizations_url": "https://api.github.com/users/jdneo/orgs", + "repos_url": "https://api.github.com/users/jdneo/repos", + "events_url": "https://api.github.com/users/jdneo/events{/privacy}", + "received_events_url": "https://api.github.com/users/jdneo/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 795791582, + "node_id": "MDU6TGFiZWw3OTU3OTE1ODI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/menus", + "name": "menus", + "color": "1d76db", + "default": false, + "description": "Menu items and widget issues" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "jrieken", + "id": 1794099, + "node_id": "MDQ6VXNlcjE3OTQwOTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1794099?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jrieken", + "html_url": "https://github.com/jrieken", + "followers_url": "https://api.github.com/users/jrieken/followers", + "following_url": "https://api.github.com/users/jrieken/following{/other_user}", + "gists_url": "https://api.github.com/users/jrieken/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jrieken/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jrieken/subscriptions", + "organizations_url": "https://api.github.com/users/jrieken/orgs", + "repos_url": "https://api.github.com/users/jrieken/repos", + "events_url": "https://api.github.com/users/jrieken/events{/privacy}", + "received_events_url": "https://api.github.com/users/jrieken/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "jrieken", + "id": 1794099, + "node_id": "MDQ6VXNlcjE3OTQwOTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1794099?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jrieken", + "html_url": "https://github.com/jrieken", + "followers_url": "https://api.github.com/users/jrieken/followers", + "following_url": "https://api.github.com/users/jrieken/following{/other_user}", + "gists_url": "https://api.github.com/users/jrieken/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jrieken/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jrieken/subscriptions", + "organizations_url": "https://api.github.com/users/jrieken/orgs", + "repos_url": "https://api.github.com/users/jrieken/repos", + "events_url": "https://api.github.com/users/jrieken/events{/privacy}", + "received_events_url": "https://api.github.com/users/jrieken/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 15, + "created_at": "2021-01-11T06:09:50Z", + "updated_at": "2021-02-09T13:40:28Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "\r\n\r\n\r\n\r\n\r\n\r\n### Problem\r\nSince the contribution point: `editor/title` is open for all the extensions, sometimes different extensions may have conflicts at this area. For example, in Java, such conflicts affect the run experience when the user installs both the Java extensions and the Code Runner extension:\r\n\r\n![image](https://user-images.githubusercontent.com/6193897/104149682-30373100-5412-11eb-84be-8f05bfa9c042.png)\r\n\r\nThis is a very open and big topic, as a author of VS Code extensions, it will be great if VS Code as a platform, can provide solutions for this issue.\r\n\r\n### Potential Solutions\r\nBelow are just rough ideas on this, it's open for discussion!\r\n\r\n#### Editor Metadata\r\nExtensions can register context value per document/editor, and register command on the editor title area according to the context value. For example. the Java Debugger can use this to mark if the current Java file is executable or not. And register the run/debug command into the editor context area if it's executable.\r\n\r\n> This can somehow achieved by using the `in` expression of the when clause, something like `resource in hasMainMethodFiles`. But we also need to [get the context value dynamically from the code](https://github.com/microsoft/vscode/issues/10471#issuecomment-718548790) to handle with the corporation between multiple extensions. \r\n\r\nMeanwhile the Java feature team can contribute changes to the Code Runner extension to align the UX (for example, hide the run button from Code Runner if the current Java file contains an executable Main class).\r\n\r\n#### Official Support for the run experience in the editor title area.\r\nThis also may have some opportunity since I believe it's somehow related with #85759, if VS Code team will consider provide official `run/debug` functionality area in the editor title.\r\n\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/109277", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/109277/events", + "html_url": "https://github.com/microsoft/vscode/issues/109277", + "id": 728636389, + "node_id": "MDU6SXNzdWU3Mjg2MzYzODk=", + "number": 109277, + "title": "Let extensions hook into url opening", + "user": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 578047123, + "node_id": "MDU6TGFiZWw1NzgwNDcxMjM=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/under-discussion", + "name": "under-discussion", + "color": "dcdcdc", + "default": false, + "description": "Issue is under discussion for relevance, priority, approach" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 10, + "created_at": "2020-10-24T02:27:26Z", + "updated_at": "2021-02-11T23:51:08Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "## Overview\r\nLet extensions hook into url opening. Motivating use case: I click on a link in the integrated terminal and it opens in my [browser preview extension](https://marketplace.visualstudio.com/items?itemName=auchenberg.vscode-browser-preview)\r\n\r\nPotential places to handle links:\r\n\r\n- Links in the terminal\r\n- Links in documents\r\n- Links from the remote port forwarding views\r\n- Debugger launch?\r\n- Open external?\r\n\r\n## Additional requirements\r\n\r\n- A url opener should be able to decline opening a link\r\n\r\n Some openers may only support specific types of links, such as `localhost`\r\n\r\n- Clicking a link should activate relevant extensions\r\n\r\n We'd need a new activation event so that extensions can make sure they handle link opening\r\n\r\n- Let users fallback to VS Code's default behavior\r\n\r\n This typically is to open using the default browser\r\n\r\n- Handle multiple url openers being registered at the same time\r\n\r\n Users should be able to select which opener to use in this case. They should potentially be able to specify a default opener.\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/107467", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/107467/events", + "html_url": "https://github.com/microsoft/vscode/issues/107467", + "id": 709128519, + "node_id": "MDU6SXNzdWU3MDkxMjg1MTk=", + "number": 107467, + "title": "Testing in VS Code", + "user": { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 293426086, + "node_id": "MDU6TGFiZWwyOTM0MjYwODY=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/plan-item", + "name": "plan-item", + "color": "dcdcdc", + "default": false, + "description": "VS Code - planned item for upcoming" + }, + { + "id": 578047123, + "node_id": "MDU6TGFiZWw1NzgwNDcxMjM=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/under-discussion", + "name": "under-discussion", + "color": "dcdcdc", + "default": false, + "description": "Issue is under discussion for relevance, priority, approach" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "connor4312", + "id": 2230985, + "node_id": "MDQ6VXNlcjIyMzA5ODU=", + "avatar_url": "https://avatars.githubusercontent.com/u/2230985?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/connor4312", + "html_url": "https://github.com/connor4312", + "followers_url": "https://api.github.com/users/connor4312/followers", + "following_url": "https://api.github.com/users/connor4312/following{/other_user}", + "gists_url": "https://api.github.com/users/connor4312/gists{/gist_id}", + "starred_url": "https://api.github.com/users/connor4312/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/connor4312/subscriptions", + "organizations_url": "https://api.github.com/users/connor4312/orgs", + "repos_url": "https://api.github.com/users/connor4312/repos", + "events_url": "https://api.github.com/users/connor4312/events{/privacy}", + "received_events_url": "https://api.github.com/users/connor4312/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "sandy081", + "id": 10746682, + "node_id": "MDQ6VXNlcjEwNzQ2Njgy", + "avatar_url": "https://avatars.githubusercontent.com/u/10746682?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sandy081", + "html_url": "https://github.com/sandy081", + "followers_url": "https://api.github.com/users/sandy081/followers", + "following_url": "https://api.github.com/users/sandy081/following{/other_user}", + "gists_url": "https://api.github.com/users/sandy081/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sandy081/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sandy081/subscriptions", + "organizations_url": "https://api.github.com/users/sandy081/orgs", + "repos_url": "https://api.github.com/users/sandy081/repos", + "events_url": "https://api.github.com/users/sandy081/events{/privacy}", + "received_events_url": "https://api.github.com/users/sandy081/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 53, + "created_at": "2020-09-25T17:19:53Z", + "updated_at": "2021-02-16T21:11:36Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "## State of the World\r\n\r\nTesting support in VS Code has been a feature request for [a long time](https://github.com/microsoft/vscode/issues/9505). The VS Code community has build excellent extensions around testing, for example:\r\n\r\n- The [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer) from @hbenl\r\n- [Wallaby.js](https://wallabyjs.com/) from the Wallaby team\r\n- [Jest](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest) from @orta\r\n- ...and many more\r\n\r\nEach implementation of testing presents a different set of features, UI, and idiomaticity. Because there is no sanctioned approach to tests in VS Code, extension developers tend to make bespoke implementations, as we've seen in the Python and Java language extensions. Ideally, like in debugging, a VS Code user would have just about the same experience as they work between projects and languages.\r\n\r\n## VS Code's Approach\r\n\r\n> Investigate how VS Code can improve the testing support. Several extensions are already providing testing support, explore what APIs/UIs could be added to improve these testing extensions and the test running experience. -- [2020 Roadmap](https://github.com/microsoft/vscode/wiki/Roadmap#testing)\r\n\r\nThe Test Explorer UI presents the best point of inspiration for us, as there are many existing extensions built on its API: it's capable and proven. Regardless of the direction we take in VS Code, we should have a way for its Test Adapters to be upgraded to the new world.\r\n\r\nWallaby is an excellent extension, but it's tailored and purpose-built to JavaScript, and includes functionality which is not readily portable to other languages. While it is a good source for inspiration, we're not aiming to encompass Wallaby's feature set in the extension points we provide, at least not yet.\r\n\r\nWe're prototyping an API in the extension host, but there are a number of approaches we can take:\r\n\r\n\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\r\n\t\t\r\n\t\r\n
Extension Host ('traditional' VS Code API)'Test Protocol' (like DAP/LSP)Extension (like existing test explorer)
\r\n\t\t\t+ Simple to adopt for extension authors
\r\n\t\t\t+ Easier to manage state
\r\n\t\t\t+ Clear way to build 'official' test extensions
\r\n\t\t
\r\n\t\t\t+ Encourages keeping expensive work in child processes
\r\n\t\t\t+ Could be theoretically shared with VS and other editors
\r\n\t\t
\r\n\t\t\t+ Keep VS Code core slim
\r\n\t\t\t+ Unclear whether there's significant functionality we'd want that's not already possible in exthost api
\r\n\t\t
\r\n\t\t\t- The 'obvious path' is doing heavy lifting in the extension host process, which is undesirable
\r\n\t\t
\r\n\t\t\t- Additional implementation and maintainence complexity for VS Code
\r\n\t\t\t- Less friendly, additional complexity than TS APIs for extension authors
\r\n\t\t
\r\n\t\t\t- Additional extension and set of libraries to maintain+version for types and implementation
\r\n\t\t\t- Less clear there's an official pathway for test extensions
\r\n\t\t
\r\n\r\n## API Design\r\n\r\nThe following is a working draft of an API design. It should not be considered final, or anything close to final. This post will be edited as it evolves.\r\n\r\n#### Changes versus the [Test Adapter API](https://github.com/hbenl/vscode-test-adapter-api)\r\n\r\nAs mentioned, the test adapter API and this one provide a similar end user experience. Here are the notable changes we made:\r\n\r\n- The test adapter API does not distinguish between watching a workspace and watching a file. In some cases, there is an existing process that reads workspace tests (such as a language server in Java) or it's not much more expensive to get workspace tests than file tests (such as mocha, perhaps). However, some cases, like Go, providing tests for a single file can be done very cheaply and efficiently without needing to involve the workspace.\r\n\r\n\tIn this API we expect the `TestProvider` to, after activation, always provide tests for the visible text editors, and we only request tests for the entire workspace when required (i.e. when the UI needs to enumerate them).\r\n\r\n- We have modeled the test state more closely after the existing `DiagnosticCollection`, where the Test Adapter API uses only events to enumerate tests and does not have a central collection.\r\n\r\n- The Test Adapter API makes the distinction between suites and tests, we do not. They have almost identical capabilities, and in [at least one scenario](https://blog.golang.org/subtests) the 'suites' are more like tests and the leaf 'tests' cannot be run individually.\r\n\r\n- We use object identity rather than ID for referencing tests. This is in line with other items in the VS Code API, including Diagnostics.\r\n\r\n#### Ideas and Open Questions\r\n\r\n- We do not (yet) have a concept of test invalidation and auto-run, which in the test adapter API via the \"retire\" event. We are still looking into how this can best be implemented.\r\n\t\r\n\tIn a golden scenario, invalidation of tests would be done by a language server which can intelligently determine specific tests that should be invalidated when a file or a file dependency changes. Maybe this is still handled by an event on the TestProvider, but if we take a \"Test Protocol\" approach then coordination will be harder.\r\n- As marked in the `todo`, we will expose APIs for other extensions to read test state and build UI, but this is not yet included in the API design.\r\n- How should errors loading tests be handled? Emit diagnostics or have some test-specific code?\r\n\r\n- We would like to support code coverage in testing as well, but that is further down the line.\r\n\r\n- How can we let users learn about/onboard to testing from within VS Code?\r\n\r\n### API\r\n\r\nSee the current working proposal in https://github.com/microsoft/vscode/blob/master/src/vs/vscode.proposed.d.ts (ctrl+f for 107467)", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/105690", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/events", + "html_url": "https://github.com/microsoft/vscode/issues/105690", + "id": 688793797, + "node_id": "MDU6SXNzdWU2ODg3OTM3OTc=", + "number": 105690, + "title": "Extension API for Inline Values", + "user": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 291054922, + "node_id": "MDU6TGFiZWwyOTEwNTQ5MjI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/debug", + "name": "debug", + "color": "1d76db", + "default": false, + "description": "Debug viewlet, configurations, breakpoints, adapter issues" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/142", + "html_url": "https://github.com/microsoft/vscode/milestone/142", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/142/labels", + "id": 6286096, + "node_id": "MDk6TWlsZXN0b25lNjI4NjA5Ng==", + "number": 142, + "title": "February 2021", + "description": "", + "creator": { + "login": "Tyriar", + "id": 2193314, + "node_id": "MDQ6VXNlcjIxOTMzMTQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/2193314?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Tyriar", + "html_url": "https://github.com/Tyriar", + "followers_url": "https://api.github.com/users/Tyriar/followers", + "following_url": "https://api.github.com/users/Tyriar/following{/other_user}", + "gists_url": "https://api.github.com/users/Tyriar/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Tyriar/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Tyriar/subscriptions", + "organizations_url": "https://api.github.com/users/Tyriar/orgs", + "repos_url": "https://api.github.com/users/Tyriar/repos", + "events_url": "https://api.github.com/users/Tyriar/events{/privacy}", + "received_events_url": "https://api.github.com/users/Tyriar/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 232, + "closed_issues": 300, + "state": "open", + "created_at": "2021-01-07T18:23:10Z", + "updated_at": "2021-02-17T10:48:47Z", + "due_on": null, + "closed_at": null + }, + "comments": 7, + "created_at": "2020-08-30T21:21:23Z", + "updated_at": "2021-02-08T05:21:59Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Today the \"Show Inline Values\" feature of VS Code's debugger is based on a generic implementation in the VS Code core and provides neither customisability through settings, nor extensibility via extensions.\r\n\r\nAs a consequence, it is not a perfect fit for all languages (e.g. #101797) and sometimes even shows incorrect values because it doesn't understand the scope regions of the underlying language. \r\n\r\nThis features asks for an extension API that either replaces the built-in implementation completely or allows to replace parts of the implementation with custom code.\r\n", + "performed_via_github_app": null, + "score": 1 + } + ] + } + ] } ] \ No newline at end of file From c1230f88475b7b73075d81df5806141ae30e6e33 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 14:07:29 +0100 Subject: [PATCH 159/176] shared process - log errors/crashes also to active window --- .../sharedProcess/electron-main/sharedProcess.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 966a8830bf6..889a2c3b85f 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -15,6 +15,7 @@ import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedP import { Disposable } from 'vs/base/common/lifecycle'; import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { assertIsDefined } from 'vs/base/common/types'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class SharedProcess extends Disposable implements ISharedProcess { @@ -201,9 +202,11 @@ export class SharedProcess extends Disposable implements ISharedProcess { this.window.on('close', this.windowCloseListener); // Crashes & Unrsponsive & Failed to load - this.window.webContents.on('render-process-gone', (event, details) => this.logService.error(`SharedProcess: crashed (detail: ${details?.reason})`)); - this.window.on('unresponsive', () => this.logService.error('SharedProcess: detected unresponsive window')); - this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.logService.warn('SharedProcess: failed to load window, ', errorDescription)); + // We use `onUnexpectedError` explicitly because the error handler + // will send the error to the active window to log in devtools too + this.window.webContents.on('render-process-gone', (event, details) => onUnexpectedError(new Error(`SharedProcess: crashed (detail: ${details?.reason})`))); + this.window.on('unresponsive', () => onUnexpectedError(new Error('SharedProcess: detected unresponsive window'))); + this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => onUnexpectedError(new Error(`SharedProcess: failed to load window: ${errorDescription}`))); } spawn(userEnv: NodeJS.ProcessEnv): void { From f2a1ecc9a7eff37f039855a8bf3abab0aa2c422b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 17 Feb 2021 13:37:48 +0100 Subject: [PATCH 160/176] Add workspace trust banner + UI polish --- .../browser/workspaceTrustEditor.css | 32 +++++++++++-------- .../workspace/browser/workspaceTrustEditor.ts | 12 +++++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css index 371217bd035..fb9e3efbd95 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -17,7 +17,8 @@ } .workspace-trust-editor.settings-editor > .workspace-trust-header { - border-bottom: solid 1px; + padding: 14px; + border: solid 1px; } .workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-title { @@ -27,27 +28,22 @@ } /** Buttons Container */ -.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { display: flex; align-items: center; justify-content: flex-end; - padding-right: 1px; + padding: 20px 0 10px 0; overflow: hidden; /* buttons row should never overflow */ -} - -.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row { - display: flex; white-space: nowrap; - padding: 20px 10px 10px; } /** Buttons */ -.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons { +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons { display: flex; overflow: hidden; } -.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons .monaco-button { +.workspace-trust-editor.settings-editor .workspace-trust-header .workspace-trust-buttons-row .workspace-trust-buttons .monaco-button { width: fit-content; width: -moz-fit-content; padding: 5px 10px; @@ -57,17 +53,27 @@ outline-offset: 2px !important; } -.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header { +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-unknown { border-color: #cccccc; } -.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header{ +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-unknown { border-color: #3c3c3c; } +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-untrusted, +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-untrusted { + border-color: #FF0000; +} + +.monaco-workbench.vs .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-trusted, +.monaco-workbench.vs-dark .workspace-trust-editor.settings-editor > .workspace-trust-header.workspace-trust-trusted { + border-color: #327E36; +} /** Settings */ .workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents { - padding-left: 0px; + padding-left: 0; + padding-right: 0; } diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 33bc5e1d73a..47e60a806c1 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -68,6 +68,17 @@ export class WorkspaceTrustEditor extends EditorPane { this.workspaceTrustEditorModel = model; } + private getHeaderContainerClass(trustState: WorkspaceTrustState): string { + switch (trustState) { + case WorkspaceTrustState.Trusted: + return 'workspace-trust-header workspace-trust-trusted'; + case WorkspaceTrustState.Untrusted: + return 'workspace-trust-header workspace-trust-untrusted'; + case WorkspaceTrustState.Unknown: + return 'workspace-trust-header workspace-trust-unknown'; + } + } + private getHeaderTitleText(trustState: WorkspaceTrustState): string { switch (trustState) { case WorkspaceTrustState.Trusted: @@ -93,6 +104,7 @@ export class WorkspaceTrustEditor extends EditorPane { private render(model: WorkspaceTrustEditorModel): void { this.headerTitle.innerText = this.getHeaderTitleText(model.currentWorkspaceTrustState); this.headerDescription.innerText = this.getHeaderDescriptionText(model.currentWorkspaceTrustState); + this.headerContainer.className = this.getHeaderContainerClass(model.currentWorkspaceTrustState); clearNode(this.headerButtons); const buttonBar = this._register(new ButtonBar(this.headerButtons)); From 8615af08b3dbc7f92f503906d4b1e8e5fb333f48 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 17 Feb 2021 14:11:24 +0100 Subject: [PATCH 161/176] Tweak input field width --- .../contrib/workspace/browser/workspaceTrustEditor.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css index fb9e3efbd95..03bfb124126 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -77,3 +77,7 @@ padding-right: 0; } +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents .setting-list-edit-row > .setting-list-valueInput { + width: 100%; + max-width: 100%; +} From 5870204e95c504eb3c15f39295537c496bbb8765 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 14:19:40 +0100 Subject: [PATCH 162/176] make notebook and cell metadata classes, https://github.com/microsoft/vscode/issues/116333 --- .../notebook.document.test.ts | 4 +- .../src/singlefolder-tests/notebook.test.ts | 32 ++-- .../vscode-notebook-tests/src/extension.ts | 10 +- src/vs/vscode.proposed.d.ts | 144 ++++++------------ .../workbench/api/common/extHost.api.impl.ts | 6 + .../workbench/api/common/extHostNotebook.ts | 40 +++-- .../api/common/extHostTypeConverters.ts | 4 +- 7 files changed, 97 insertions(+), 143 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index 34a8218e3a1..0d5ecc671fa 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -12,8 +12,8 @@ suite('Notebook Document', function () { const contentProvider = new class implements vscode.NotebookContentProvider { async openNotebook(uri: vscode.Uri, _openContext: vscode.NotebookDocumentOpenContext): Promise { return { - cells: [{ cellKind: vscode.NotebookCellKind.Code, source: uri.toString(), language: 'javascript', metadata: {}, outputs: [] }], - metadata: {} + cells: [{ cellKind: vscode.NotebookCellKind.Code, source: uri.toString(), language: 'javascript', metadata: new vscode.NotebookCellMetadata(), outputs: [] }], + metadata: new vscode.NotebookDocumentMetadata() }; } async resolveNotebook(_document: vscode.NotebookDocument, _webview: vscode.NotebookCommunication) { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 81a03ed3250..c21be1935ab 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -90,24 +90,20 @@ suite('Notebook API tests', function () { openNotebook: async (_resource: vscode.Uri): Promise => { if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) { return { - metadata: {}, + metadata: new vscode.NotebookDocumentMetadata(), cells: [] }; } const dto: vscode.NotebookData = { - metadata: { - custom: { testMetadata: false } - }, + metadata: new vscode.NotebookDocumentMetadata().with({ custom: { testMetadata: false } }), cells: [ { source: 'test', language: 'typescript', cellKind: vscode.NotebookCellKind.Code, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) } ] }; @@ -599,7 +595,7 @@ suite('Notebook API tests', function () { await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 })); }); const document = vscode.window.activeNotebookEditor?.document!; @@ -620,7 +616,7 @@ suite('Notebook API tests', function () { const event = asPromise(vscode.notebook.onDidChangeCellMetadata); await vscode.window.activeNotebookEditor!.edit(editBuilder => { - editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 })); }); const data = await event; @@ -642,7 +638,7 @@ suite('Notebook API tests', function () { const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -661,7 +657,7 @@ suite('Notebook API tests', function () { const version = vscode.window.activeNotebookEditor!.document.version; await vscode.window.activeNotebookEditor!.edit(editBuilder => { editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]); - editBuilder.replaceCellMetadata(0, { runnable: false }); + editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ runnable: false })); }); await cellsChangeEvent; @@ -876,14 +872,14 @@ suite('Notebook API tests', function () { assert.strictEqual(cell.outputs.length, 0); let metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); - await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: false }); + await updateCellMetadata(resource, cell, cell.metadata.with({ runnable: false })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work metadataChangeEvent = asPromise(vscode.notebook.onDidChangeCellMetadata); - await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: true }); + await updateCellMetadata(resource, cell, cell.metadata.with({ runnable: true })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); @@ -904,7 +900,7 @@ suite('Notebook API tests', function () { assert.strictEqual(cell.outputs.length, 0); await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: false }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: false })); await event; }); @@ -912,7 +908,7 @@ suite('Notebook API tests', function () { assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await event; }); @@ -952,7 +948,7 @@ suite('Notebook API tests', function () { const cell = editor.document.cells[0]; await withEvent(vscode.notebook.onDidChangeNotebookDocumentMetadata, async event => { - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await event; }); @@ -993,7 +989,7 @@ suite('Notebook API tests', function () { const cell = editor.document.cells[0]; const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await metadataChangeEvent; assert.strictEqual(editor.document.metadata.runnable, true); @@ -1033,7 +1029,7 @@ suite('Notebook API tests', function () { const cell = editor.document.cells[0]; const metadataChangeEvent = asPromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); - updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true }); + updateNotebookMetadata(editor.document.uri, editor.document.metadata.with({ runnable: true })); await metadataChangeEvent; await vscode.commands.executeCommand('notebook.cell.execute'); diff --git a/extensions/vscode-notebook-tests/src/extension.ts b/extensions/vscode-notebook-tests/src/extension.ts index bf66b529bc2..55231411594 100644 --- a/extensions/vscode-notebook-tests/src/extension.ts +++ b/extensions/vscode-notebook-tests/src/extension.ts @@ -23,25 +23,21 @@ export function activate(context: vscode.ExtensionContext): any { context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookSmokeTest', { openNotebook: async (_resource: vscode.Uri) => { const dto: vscode.NotebookData = { - metadata: {}, + metadata: new vscode.NotebookDocumentMetadata(), cells: [ { source: 'code()', language: 'typescript', cellKind: vscode.NotebookCellKind.Code, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) }, { source: 'Markdown Cell', language: 'markdown', cellKind: vscode.NotebookCellKind.Markdown, outputs: [], - metadata: { - custom: { testCellMetadata: 123 } - } + metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }) } ] }; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a06d8165fab..ce9a5668450 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1162,34 +1162,44 @@ declare module 'vscode' { Idle = 2 } - // TODO@API - // make this a class, allow modified using with-pattern - export interface NotebookCellMetadata { + export class NotebookCellMetadata { /** * Controls whether a cell's editor is editable/readonly. */ - editable?: boolean; - + readonly editable?: boolean; /** * Controls if the cell has a margin to support the breakpoint UI. * This metadata is ignored for markdown cell. */ - breakpointMargin?: boolean; - + readonly breakpointMargin?: boolean; /** * Whether a code cell's editor is collapsed */ - inputCollapsed?: boolean; - + readonly outputCollapsed?: boolean; /** * Whether a code cell's outputs are collapsed */ - outputCollapsed?: boolean; - + readonly inputCollapsed?: boolean; /** * Additional attributes of a cell metadata. */ - custom?: { [key: string]: any; }; + readonly custom?: Record; + + // todo@API duplicates status bar API + readonly statusMessage?: string; + + // run related API, will be removed + readonly runnable?: boolean; + readonly hasExecutionOrder?: boolean; + readonly executionOrder?: number; + readonly runState?: NotebookCellRunState; + readonly runStartTime?: number; + readonly lastRunDuration?: number; + + constructor(editable?: boolean, breakpointMargin?: boolean, runnable?: boolean, hasExecutionOrder?: boolean, executionOrder?: number, runState?: NotebookCellRunState, runStartTime?: number, statusMessage?: string, lastRunDuration?: number, inputCollapsed?: boolean, outputCollapsed?: boolean, custom?: Record) + + // todo@API write a proper signature + with(change: Partial>): NotebookCellMetadata; } // todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md @@ -1201,39 +1211,48 @@ declare module 'vscode' { readonly document: TextDocument; readonly language: string; readonly outputs: readonly NotebookCellOutput[]; - readonly metadata: NotebookCellMetadata; - /** @deprecated use WorkspaceEdit.replaceCellOutput */ - // outputs: CellOutput[]; - // readonly outputs2: NotebookCellOutput[]; - /** @deprecated use WorkspaceEdit.replaceCellMetadata */ - // metadata: NotebookCellMetadata; + readonly metadata: NotebookCellMetadata } + export class NotebookDocumentMetadata { - export interface NotebookDocumentMetadata { /** * Controls if users can add or delete cells * Defaults to true */ - editable?: boolean; - + readonly editable: boolean; /** * Default value for [cell editable metadata](#NotebookCellMetadata.editable). * Defaults to true. */ - cellEditable?: boolean; - displayOrder?: GlobPattern[]; - + readonly cellEditable: boolean; /** * Additional attributes of the document metadata. */ - custom?: { [key: string]: any; }; - + readonly custom: { [key: string]: any; }; /** * Whether the document is trusted, default to true * When false, insecure outputs like HTML, JavaScript, SVG will not be rendered. */ - trusted?: boolean; + readonly trusted: boolean; + + // todo@API how does glob apply to mime times? + readonly displayOrder: GlobPattern[]; + + // todo@API is this a kernel property? + readonly cellHasExecutionOrder: boolean; + + // run related, remove infer from kernel, exec + // todo@API infer from kernel + // todo@API remove + readonly runnable: boolean; + readonly cellRunnable: boolean; + readonly runState: NotebookRunState; + + constructor(editable?: boolean, runnable?: boolean, cellEditable?: boolean, cellRunnable?: boolean, cellHasExecutionOrder?: boolean, displayOrder?: GlobPattern[], custom?: { [key: string]: any; }, runState?: NotebookRunState, trusted?: boolean); + + // TODO@API make this a proper signature + with(change: Partial>): NotebookDocumentMetadata; } export interface NotebookDocumentContentOptions { @@ -1640,34 +1659,6 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, NotebookKernel - export interface NotebookDocumentMetadata { - - /** - * Controls whether the full notebook can be run at once. - * Defaults to true - */ - // todo@API infer from kernel - // todo@API remove - runnable?: boolean; - - /** - * Default value for [cell runnable metadata](#NotebookCellMetadata.runnable). - * Defaults to true. - */ - cellRunnable?: boolean; - - /** - * Default value for [cell hasExecutionOrder metadata](#NotebookCellMetadata.hasExecutionOrder). - * Defaults to true. - */ - cellHasExecutionOrder?: boolean; - - /** - * The document's current run state - */ - runState?: NotebookRunState; - } - // todo@API use the NotebookCellExecution-object as a container to model and enforce // the flow of a cell execution @@ -1689,49 +1680,6 @@ declare module 'vscode' { // export const onDidStartNotebookCellExecution: Event; // export const onDidStopNotebookCellExecution: Event; - export interface NotebookCellMetadata { - - /** - * Controls if the cell is executable. - * This metadata is ignored for markdown cell. - */ - // todo@API infer from kernel - runnable?: boolean; - - /** - * Whether the [execution order](#NotebookCellMetadata.executionOrder) indicator will be displayed. - * Defaults to true. - */ - hasExecutionOrder?: boolean; - - /** - * The order in which this cell was executed. - */ - executionOrder?: number; - - /** - * A status message to be shown in the cell's status bar - */ - // todo@API duplicates status bar API - statusMessage?: string; - - /** - * The cell's current run state - */ - runState?: NotebookCellRunState; - - /** - * If the cell is running, the time at which the cell started running - */ - runStartTime?: number; - - /** - * The total duration of the cell's last run - */ - // todo@API depends on having output - lastRunDuration?: number; - } - export interface NotebookKernel { readonly id?: string; label: string; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 32d13f792e0..155c978701a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1273,6 +1273,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // checkProposedApiEnabled(extension); return extHostTypes.NotebookCellRunState; }, + get NotebookDocumentMetadata() { + return extHostTypes.NotebookDocumentMetadata; + }, + get NotebookCellMetadata() { + return extHostTypes.NotebookCellMetadata; + }, get NotebookRunState() { // checkProposedApiEnabled(extension); return extHostTypes.NotebookRunState; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 04a5cd56a2c..e6ba271e43d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -714,23 +714,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const that = this; - const document = new ExtHostNotebookDocument(this._documentsAndEditors, { - emitModelChange(event: vscode.NotebookCellsChangeEvent): void { - that._onDidChangeNotebookCells.fire(event); + const document = new ExtHostNotebookDocument( + this._documentsAndEditors, + { + emitModelChange(event: vscode.NotebookCellsChangeEvent): void { + that._onDidChangeNotebookCells.fire(event); + }, + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { + that._onDidChangeCellOutputs.fire(event); + }, + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { + that._onDidChangeCellLanguage.fire(event); + }, + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { + that._onDidChangeCellMetadata.fire(event); + }, + emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { + that._onDidChangeNotebookDocumentMetadata.fire(event); + } }, - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void { - that._onDidChangeCellOutputs.fire(event); - }, - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void { - that._onDidChangeCellLanguage.fire(event); - }, - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { - that._onDidChangeCellMetadata.fire(event); - }, - emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { - that._onDidChangeNotebookDocumentMetadata.fire(event); - } - }, viewType, modelData.contentOptions, typeConverters.NotebookDocumentMetadata.to(modelData.metadata ?? {}), uri, storageRoot); + viewType, + modelData.contentOptions, + modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(), + uri, + storageRoot + ); document.acceptModelChanged({ versionId: modelData.versionId, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 039418367df..1c0301a8ac2 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1418,7 +1418,7 @@ export namespace NotebookCellRange { export namespace NotebookCellMetadata { - export function to(data: vscode.NotebookCellMetadata): types.NotebookCellMetadata { + export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata { return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runStartTime, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom); } } @@ -1429,7 +1429,7 @@ export namespace NotebookDocumentMetadata { return data; } - export function to(data: vscode.NotebookDocumentMetadata): types.NotebookDocumentMetadata { + export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata { return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted); } From 5daa0b3b59f436b7c71e809754f0d0c32d88c27d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 17 Feb 2021 05:53:01 -0800 Subject: [PATCH 163/176] Fix right click paste Fixes #116801 Fixes #116850 --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 3e3dafe4c3c..1137b074694 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -245,10 +245,10 @@ export class TerminalViewPane extends ViewPane { await terminal.copySelection(); terminal.clearSelection(); } else { - if (!BrowserFeatures.clipboard.readText) { + if (BrowserFeatures.clipboard.readText) { terminal.paste(); } else { - this._notificationService.info('This browser doesn\'t support the clipboard.readText API needed to trigger a paste'); + this._notificationService.info(`This browser doesn\'t support the clipboard.readText API needed to trigger a paste, try ${platform.isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); } } // Clear selection after all click event bubbling is finished on Mac to prevent From 797dc143ffbb994b0b8daeb359d49603492829e3 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 17 Feb 2021 14:40:51 +0100 Subject: [PATCH 164/176] Fixed command, added spacing --- src/vs/workbench/browser/actions/workspaceActions.ts | 8 ++++---- .../contrib/workspace/browser/workspace.contribution.ts | 2 +- .../contrib/workspace/browser/workspaceTrustEditor.css | 4 ++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 713e088b96f..bc4fcc35b0e 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -258,13 +258,13 @@ class WorkspaceTrustManageAction extends Action2 { title: { value: nls.localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' }, precondition: ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true), category: nls.localize('workspacesCategory', "Workspaces"), - f1: true, + f1: true }); } - run(accessor: ServicesAccessor) { - const editorService = accessor.get(IEditorService); - editorService.openEditor({ resource: WORKSPACE_TRUST_URI, mode: 'jsonc', options: { pinned: true } }); + async run(accessor: ServicesAccessor) { + const commandService = accessor.get(ICommandService); + await commandService.executeCommand('workbench.trust.manage'); } } diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index d88325ffb9c..e2dd65d8c7e 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -308,7 +308,7 @@ registerAction2(class extends Action2 { const input = instantiationService.createInstance(WorkspaceTrustEditorInput); - editorService.openEditor(input); + editorService.openEditor(input, { pinned: true }); return; } }); diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css index 03bfb124126..45ec02a5da4 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -77,6 +77,10 @@ padding-right: 0; } +.workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents .setting-list-edit-row { + margin-top: 4px; +} + .workspace-trust-editor.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents .setting-list-edit-row > .setting-list-valueInput { width: 100%; max-width: 100%; From d7230651accb198f5936ecae34fef387ee1023a7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 17 Feb 2021 14:58:33 +0100 Subject: [PATCH 165/176] Remove old workspace trust editor --- .../browser/actions/workspaceActions.ts | 2 +- .../browser/workspace.contribution.ts | 11 +-- .../workspaceTrustFileSystemProvider.ts | 86 ------------------- .../workspaces/common/workspaceTrust.ts | 2 +- 4 files changed, 3 insertions(+), 98 deletions(-) delete mode 100644 src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index bc4fcc35b0e..8817e5fd820 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -23,7 +23,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; -import { WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust'; export class OpenFileAction extends Action { diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index e2dd65d8c7e..196a3a79711 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -20,7 +20,6 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeColor } from 'vs/workbench/api/common/extHostTypes'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkspaceTrustFileSystemProvider } from 'vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -178,14 +177,6 @@ Registry.as(WorkbenchExtensions.Workbench).regi LifecyclePhase.Starting ); -/* - * Trusted Workspace JSON Editor - */ -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - WorkspaceTrustFileSystemProvider, - LifecyclePhase.Ready -); - /** * Trusted Workspace GUI Editor */ @@ -308,7 +299,7 @@ registerAction2(class extends Action2 { const input = instantiationService.createInstance(WorkspaceTrustEditorInput); - editorService.openEditor(input, { pinned: true }); + editorService.openEditor(input, { pinned: true, revealIfOpened: true }); return; } }); diff --git a/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts b/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts deleted file mode 100644 index 4c4c49f9480..00000000000 --- a/src/vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider.ts +++ /dev/null @@ -1,86 +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 { Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileService, IStat, IWatchOptions, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { WORKSPACE_TRUST_STORAGE_KEY } from 'vs/workbench/services/workspaces/common/workspaceTrust'; - -const WORKSPACE_TRUST_SCHEMA = 'workspaceTrust'; - -const TRUSTED_WORKSPACES_STAT: IStat = { - type: FileType.File, - ctime: Date.now(), - mtime: Date.now(), - size: 0 -}; - -const PREPENDED_TEXT = `// The following file is a placeholder UX for managing trusted workspaces. It will be replaced by a rich editor and provide -// additonal information about what trust means. e.g. enabling trust will unblock automatic tasks on startup and list the tasks. It will enable certain extensions -// and list the extensions with associated functionality. -`; - -export class WorkspaceTrustFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability, IWorkbenchContribution { - readonly capabilities = FileSystemProviderCapabilities.FileReadWrite; - - readonly onDidChangeCapabilities = Event.None; - readonly onDidChangeFile = Event.None; - - constructor( - @IFileService private readonly fileService: IFileService, - @IStorageService private readonly storageService: IStorageService, - ) { - this.fileService.registerProvider(WORKSPACE_TRUST_SCHEMA, this); - } - - stat(resource: URI): Promise { - return Promise.resolve(TRUSTED_WORKSPACES_STAT); - } - - async readFile(resource: URI): Promise { - let workspacesTrustContent = this.storageService.get(WORKSPACE_TRUST_STORAGE_KEY, StorageScope.GLOBAL); - - let objectForm = {}; - try { - objectForm = JSON.parse(workspacesTrustContent || '{}'); - } catch { } - - const buffer = VSBuffer.fromString(PREPENDED_TEXT + JSON.stringify(objectForm, undefined, 2)).buffer; - return buffer; - } - - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - try { - const workspacesTrustContent = VSBuffer.wrap(content).toString().replace(PREPENDED_TEXT, ''); - this.storageService.store(WORKSPACE_TRUST_STORAGE_KEY, workspacesTrustContent, StorageScope.GLOBAL, StorageTarget.MACHINE); - } catch (err) { } - - return Promise.resolve(); - } - - watch(resource: URI, opts: IWatchOptions): IDisposable { - return { - dispose() { - return; - } - }; - } - mkdir(resource: URI): Promise { - return Promise.resolve(undefined!); - } - readdir(resource: URI): Promise<[string, FileType][]> { - return Promise.resolve(undefined!); - } - delete(resource: URI, opts: FileDeleteOptions): Promise { - return Promise.resolve(undefined!); - } - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - return Promise.resolve(undefined!); - } -} diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 192c25c7aed..b9ec07aeeeb 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -17,7 +17,7 @@ import { EditorModel } from 'vs/workbench/common/editor'; export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled'; export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key'; -export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces'); +//export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces'); export const WorkspaceTrustContext = { PendingRequest: new RawContextKey('workspaceTrustPendingRequest', false), From 9de38fc9a8058f0b42057bb0076d989455b58edc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 15:09:40 +0100 Subject: [PATCH 166/176] Disable keytar in our integration tests (#116852) * tests - extract and use ALL_PLATFORMS_API_TESTS_EXTRA_ARGS * tests - throw when accessing keytar from API tests --- scripts/test-integration.bat | 18 ++++++++++-------- scripts/test-integration.sh | 19 +++++++++++-------- src/vs/platform/environment/common/argv.ts | 1 + .../electron-main/environmentMainService.ts | 4 ++++ src/vs/platform/environment/node/argv.ts | 1 + .../electron-main/nativeHostMainService.ts | 18 +++++++++++++----- test/automation/src/code.ts | 1 + 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 3013dc04b90..3f8390b0b2b 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -45,31 +45,33 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Tests in the extension host -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-keytar --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% + +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test\unit --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test\unit %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% . if %errorlevel% neq 0 exit /b %errorlevel% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% mkdir %GITWORKSPACE% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call "%INTEGRATION_TEST_ELECTRON_PATH%" %GITWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\git --extensionTestsPath=%~dp0\..\extensions\git\out\test --enable-proposed-api=vscode.git %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS% if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in commonJS (CSS, HTML) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 537347a6dba..0eeefd80bff 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -65,28 +65,31 @@ fi after_suite # Tests in the extension host -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR + +ALL_PLATFORMS_API_TESTS_EXTRA_ARGS="--disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-keytar --disable-extensions --user-data-dir=$VSCODEUSERDATADIR" + +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/markdown-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test/unit --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test/unit $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ $ALL_PLATFORMS_API_TESTS_EXTRA_ARGS after_suite # Tests in commonJS (CSS, HTML) diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 019410e6a30..d36a3dfc49c 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -63,6 +63,7 @@ export interface NativeParsedArgs { 'export-default-configuration'?: string; 'install-source'?: string; 'disable-updates'?: boolean; + 'disable-keytar'?: boolean; 'disable-crash-reporter'?: boolean; 'crash-reporter-directory'?: string; 'crash-reporter-id'?: string; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 35b09d89d39..49acc89b969 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -36,6 +36,7 @@ export interface IEnvironmentMainService extends INativeEnvironmentService { sandbox: boolean; driverVerbose: boolean; disableUpdates: boolean; + disableKeytar: boolean; } export class EnvironmentMainService extends NativeEnvironmentService implements IEnvironmentMainService { @@ -61,6 +62,9 @@ export class EnvironmentMainService extends NativeEnvironmentService implements @memoize get disableUpdates(): boolean { return !!this._args['disable-updates']; } + @memoize + get disableKeytar(): boolean { return !!this._args['disable-keytar']; } + @memoize get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; } } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 7b6a60b5728..6f532262ae0 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -95,6 +95,7 @@ export const OPTIONS: OptionDescriptions> = { 'skip-release-notes': { type: 'boolean' }, 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, + 'disable-keytar': { type: 'boolean' }, 'disable-crash-reporter': { type: 'boolean' }, 'crash-reporter-directory': { type: 'string' }, 'crash-reporter-id': { type: 'string' }, diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index ba9d54e54fd..989cf596f6f 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -658,7 +658,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain private static readonly PASSWORD_CHUNK_SIZE = NativeHostMainService.MAX_PASSWORD_LENGTH - 100; async getPassword(windowId: number | undefined, service: string, account: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); const password = await keytar.getPassword(service, account); if (password) { @@ -686,7 +686,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async setPassword(windowId: number | undefined, service: string, account: string, password: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); if (isWindows && password.length > NativeHostMainService.MAX_PASSWORD_LENGTH) { let index = 0; @@ -714,7 +714,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async deletePassword(windowId: number | undefined, service: string, account: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); const didDelete = await keytar.deletePassword(service, account); if (didDelete) { @@ -725,17 +725,25 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async findPassword(windowId: number | undefined, service: string): Promise { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); return keytar.findPassword(service); } async findCredentials(windowId: number | undefined, service: string): Promise> { - const keytar = await import('keytar'); + const keytar = await this.withKeytar(); return keytar.findCredentials(service); } + private async withKeytar(): Promise { + if (this.environmentMainService.disableKeytar) { + throw new Error('keytar has been disabled via --disable-keytar option'); + } + + return await import('keytar'); + } + //#endregion private windowById(windowId: number | undefined): ICodeWindow | undefined { diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 0f04f08fa26..719598b188e 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -138,6 +138,7 @@ export async function spawn(options: SpawnOptions): Promise { '--disable-telemetry', '--no-cached-data', '--disable-updates', + '--disable-keytar', '--disable-crash-reporter', `--extensions-dir=${options.extensionsPath}`, `--user-data-dir=${options.userDataDir}`, From 154c94ef3b10241381b3f665abb7a52a71cf7b7a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 17 Feb 2021 06:14:36 -0800 Subject: [PATCH 167/176] Update terminalView.ts --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 1137b074694..fa6cbfcab7d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -248,7 +248,7 @@ export class TerminalViewPane extends ViewPane { if (BrowserFeatures.clipboard.readText) { terminal.paste(); } else { - this._notificationService.info(`This browser doesn\'t support the clipboard.readText API needed to trigger a paste, try ${platform.isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); + this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${platform.isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); } } // Clear selection after all click event bubbling is finished on Mac to prevent From 29f80fb60f64e1bba5087190faffc229ad117aba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 15:21:14 +0100 Subject: [PATCH 168/176] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1669245e977..2b3c52ca0bd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.54.0", - "distro": "59d93479b4a5c88173a91fec50e348126e94a818", + "distro": "1817e827de94819a2303cf0b7a17ae5cc7dfbb80", "author": { "name": "Microsoft Corporation" }, From 79f90304beb5ec2c4c90688e6b0c61e8bd80595b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 15:21:18 +0100 Subject: [PATCH 169/176] refine completion provider for context keys, https://github.com/microsoft/vscode/issues/9303 --- .../src/configurationEditingMain.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index f4c2ab34174..28bcda61df2 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -147,17 +147,22 @@ function registerContextKeyCompletions(): vscode.Disposable { { async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) { - const replacing = document.getWordRangeAtPosition(position, /[\w.-\d]/); - const inserting = replacing?.with(undefined, position); - if (!replacing || !inserting) { + const location = getLocation(document.getText(), document.offsetAt(position)); + + if (location.isAtPropertyKey) { return; } - const location = getLocation(document.getText(), document.offsetAt(position)); if (!location.matches(['*', 'when']) && !location.matches(['contributes', 'menus', '*', '*', 'when'])) { return; } + const replacing = document.getWordRangeAtPosition(position, /[^"\s]+/); + if (!replacing) { + return; + } + const inserting = replacing.with(undefined, position); + const data = await vscode.commands.executeCommand('getContextKeyInfo'); if (token.isCancellationRequested || !data) { return; From 6ecae6f5f946c925b01aec7422fdbc0cfd1c9bd3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 15:39:35 +0100 Subject: [PATCH 170/176] shared process - log errors properly --- src/vs/code/electron-main/app.ts | 23 ++++++++-- .../electron-main/sharedProcess.ts | 18 +++++--- .../platform/windows/electron-main/window.ts | 43 +++++++++++-------- .../platform/windows/electron-main/windows.ts | 18 ++++++++ 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4d17030027a..d6a819dee64 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -37,7 +37,7 @@ import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { FileProtocolHandler } from 'vs/code/electron-main/protocol'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IWindowsMainService, ICodeWindow, OpenContext } from 'vs/platform/windows/electron-main/windows'; +import { IWindowsMainService, ICodeWindow, OpenContext, WindowError } from 'vs/platform/windows/electron-main/windows'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; @@ -465,7 +465,7 @@ export class CodeApplication extends Disposable { const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, mainProcessElectronServer, fileProtocolHandler)); // Post Open Windows Tasks - appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); + appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor, sharedProcess)); // Tracing: Stop tracing after windows are ready if enabled if (this.environmentMainService.args.trace) { @@ -926,11 +926,28 @@ export class CodeApplication extends Disposable { return { fileUri: URI.file(path) }; } - private async afterWindowOpen(accessor: ServicesAccessor): Promise { + private async afterWindowOpen(accessor: ServicesAccessor, sharedProcess: SharedProcess): Promise { // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; + // Observe shared process for errors + const telemetryService = accessor.get(ITelemetryService); + this._register(sharedProcess.onDidError(e => { + + // Logging + onUnexpectedError(new Error(e.message)); + + // Telemetry + type SharedProcessErrorClassification = { + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + }; + type SharedProcessErrorEvent = { + type: WindowError; + }; + telemetryService.publicLog2('sharedprocesserror', { type: e.type }); + })); + // Windows: install mutex const win32MutexName = product.win32MutexName; if (isWindows && win32MutexName) { diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 889a2c3b85f..8c40cb0f9c3 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, ipcMain, Event, MessagePortMain } from 'electron'; +import { BrowserWindow, ipcMain, Event as ElectronEvent, MessagePortMain } from 'electron'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; @@ -15,14 +15,18 @@ import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedP import { Disposable } from 'vs/base/common/lifecycle'; import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { assertIsDefined } from 'vs/base/common/types'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { WindowError } from 'vs/platform/windows/electron-main/windows'; export class SharedProcess extends Disposable implements ISharedProcess { private readonly whenSpawnedBarrier = new Barrier(); private window: BrowserWindow | undefined = undefined; - private windowCloseListener: ((event: Event) => void) | undefined = undefined; + private windowCloseListener: ((event: ElectronEvent) => void) | undefined = undefined; + + private readonly _onDidError = this._register(new Emitter<{ type: WindowError, message: string }>()); + readonly onDidError = Event.buffer(this._onDidError.event); // buffer until we have a listener! constructor( private readonly machineId: string, @@ -187,7 +191,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { } // Prevent the window from closing - this.windowCloseListener = (e: Event) => { + this.windowCloseListener = (e: ElectronEvent) => { this.logService.trace('SharedProcess#close prevented'); // We never allow to close the shared process unless we get explicitly disposed() @@ -204,9 +208,9 @@ export class SharedProcess extends Disposable implements ISharedProcess { // Crashes & Unrsponsive & Failed to load // We use `onUnexpectedError` explicitly because the error handler // will send the error to the active window to log in devtools too - this.window.webContents.on('render-process-gone', (event, details) => onUnexpectedError(new Error(`SharedProcess: crashed (detail: ${details?.reason})`))); - this.window.on('unresponsive', () => onUnexpectedError(new Error('SharedProcess: detected unresponsive window'))); - this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => onUnexpectedError(new Error(`SharedProcess: failed to load window: ${errorDescription}`))); + this.window.webContents.on('render-process-gone', (event, details) => this._onDidError.fire({ type: WindowError.CRASHED, message: `SharedProcess: crashed (detail: ${details?.reason})` })); + this.window.on('unresponsive', () => this._onDidError.fire({ type: WindowError.UNRESPONSIVE, message: 'SharedProcess: detected unresponsive window' })); + this.window.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this._onDidError.fire({ type: WindowError.LOAD, message: `SharedProcess: failed to load window: ${errorDescription}` })); } spawn(userEnv: NodeJS.ProcessEnv): void { diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index c386a584857..a68581665d7 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { getMarks, mark } from 'vs/base/common/performance'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event, RenderProcessGoneDetails } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme, Event } from 'electron'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -19,7 +19,7 @@ import product from 'vs/platform/product/common/product'; import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { browserCodeLoadingCacheStrategy, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; +import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, WindowError, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; @@ -48,11 +48,6 @@ interface ITouchBarSegment extends SegmentedControlSegment { id: string; } -const enum WindowError { - UNRESPONSIVE = 1, - CRASHED = 2 -} - const enum ReadyState { /** @@ -403,9 +398,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { private registerListeners(): void { // Crashes & Unrsponsive & Failed to load - this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details)); this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE)); - this._win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.logService.warn('Main: failed to load workbench window, ', errorDescription)); + this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details.reason)); + this._win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.onWindowError(WindowError.LOAD, errorDescription)); // Window close this._win.on('closed', () => { @@ -550,9 +545,21 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private async onWindowError(error: WindowError.UNRESPONSIVE): Promise; - private async onWindowError(error: WindowError.CRASHED, details: RenderProcessGoneDetails): Promise; - private async onWindowError(error: WindowError, details?: RenderProcessGoneDetails): Promise { - this.logService.error(error === WindowError.CRASHED ? `Main: renderer process crashed (detail: ${details?.reason})` : 'Main: detected unresponsive'); + private async onWindowError(error: WindowError.CRASHED, details: string): Promise; + private async onWindowError(error: WindowError.LOAD, details: string): Promise; + private async onWindowError(type: WindowError, details?: string): Promise { + + switch (type) { + case WindowError.CRASHED: + this.logService.error(`CodeWindow: renderer process crashed (detail: ${details})`); + break; + case WindowError.UNRESPONSIVE: + this.logService.error('CodeWindow: detected unresponsive'); + break; + case WindowError.LOAD: + this.logService.error(`CodeWindow: failed to load workbench window: ${details}`); + break; + } // If we run extension tests from CLI, showing a dialog is not // very helpful in this case. Rather, we bring down the test run @@ -569,10 +576,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { type WindowErrorEvent = { type: WindowError; }; - this.telemetryService.publicLog2('windowerror', { type: error }); + this.telemetryService.publicLog2('windowerror', { type }); // Unresponsive - if (error === WindowError.UNRESPONSIVE) { + if (type === WindowError.UNRESPONSIVE) { if (this.isExtensionDevelopmentHost || this.isExtensionTestHost || (this._win && this._win.webContents && this._win.webContents.isDevToolsOpened())) { // TODO@bpasero Workaround for https://github.com/microsoft/vscode/issues/56994 // In certain cases the window can report unresponsiveness because a breakpoint was hit @@ -606,12 +613,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // Crashed - else { + else if (type === WindowError.CRASHED) { let message: string; - if (details && details.reason !== 'crashed') { - message = localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details?.reason); + if (details && details !== 'crashed') { + message = localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details); } else { - message = localize('appCrashed', "The window has crashed", details?.reason); + message = localize('appCrashed', "The window has crashed", details); } const result = await this.dialogMainService.showMessageBox({ diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 5a1446b4c4f..96f92d28946 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -125,6 +125,24 @@ export interface ICodeWindow extends IDisposable { serializeWindowState(): IWindowState; } +export const enum WindowError { + + /** + * Maps to the `unresponsive` event on a `BrowserWindow`. + */ + UNRESPONSIVE = 1, + + /** + * Maps to the `render-proces-gone` event on a `WebContents`. + */ + CRASHED = 2, + + /** + * Maps to the `did-fail-load` event on a `WebContents`. + */ + LOAD = 3 +} + export const IWindowsMainService = createDecorator('windowsMainService'); export interface IWindowsCountChangedEvent { From 8e003e638047cc75e6078c46b8e7611778af972f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 17 Feb 2021 06:45:01 -0800 Subject: [PATCH 171/176] Adopt description in terminal RawContextKeys Fixes #116853 --- .../contrib/terminal/common/terminal.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1c9f51ea37c..e4ea96f5b74 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -15,40 +15,40 @@ import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/comm export const TERMINAL_VIEW_ID = 'terminal'; /** A context key that is set when there is at least one opened integrated terminal. */ -export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); +export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false, true); /** A context key that is set when the integrated terminal has focus. */ -export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', false); +export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', false, 'Whether the terminal is focused'); export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY = 'terminalShellType'; /** A context key that is set to the detected shell for the most recently active terminal, this is set to the last known value when no terminals exist. */ -export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE = new RawContextKey(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, undefined); +export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE = new RawContextKey(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, undefined, { type: 'string', description: 'The shell type of the active terminal' }); -export const KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE = new RawContextKey('terminalAltBufferActive', false); +export const KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE = new RawContextKey('terminalAltBufferActive', false, true); /** A context key that is set when the integrated terminal does not have focus. */ export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); /** A context key that is set when the user is navigating the accessibility tree */ -export const KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS = new RawContextKey('terminalA11yTreeFocus', false); +export const KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS = new RawContextKey('terminalA11yTreeFocus', false, true); /** A keybinding context key that is set when the integrated terminal has text selected. */ -export const KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED = new RawContextKey('terminalTextSelected', false); +export const KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED = new RawContextKey('terminalTextSelected', false, 'Whether text is selected in the active terminal'); /** A keybinding context key that is set when the integrated terminal does not have text selected. */ export const KEYBINDING_CONTEXT_TERMINAL_TEXT_NOT_SELECTED = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.toNegated(); /** A context key that is set when the find widget in integrated terminal is visible. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE = new RawContextKey('terminalFindVisible', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE = new RawContextKey('terminalFindVisible', false, true); /** A context key that is set when the find widget in integrated terminal is not visible. */ export const KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.toNegated(); /** A context key that is set when the find widget find input in integrated terminal is focused. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED = new RawContextKey('terminalFindInputFocused', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED = new RawContextKey('terminalFindInputFocused', false, true); /** A context key that is set when the find widget in integrated terminal is focused. */ -export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalFindFocused', false); +export const KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED = new RawContextKey('terminalFindFocused', false, true); /** A context key that is set when the find widget find input in integrated terminal is not focused. */ export const KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_NOT_FOCUSED = KEYBINDING_CONTEXT_TERMINAL_FIND_INPUT_FOCUSED.toNegated(); -export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey('terminalProcessSupported', false); +export const KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED = new RawContextKey('terminalProcessSupported', false, 'Whether terminal processes can be launched'); export const IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY = 'terminal.integrated.isWorkspaceShellAllowed'; export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverMeasureRenderTime'; From 43262ab61af183de17f9b0de41bec324a0632251 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru Date: Wed, 17 Feb 2021 15:57:34 +0100 Subject: [PATCH 172/176] Add editor input factory --- .../browser/workspace.contribution.ts | 19 +++++++++++++++++++ .../workspaces/common/workspaceTrust.ts | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 196a3a79711..04237e078bd 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -29,6 +29,7 @@ import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } fro import { WorkspaceTrustEditor } from 'vs/workbench/contrib/workspace/browser/workspaceTrustEditor'; import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED } from 'vs/workbench/services/workspaces/common/workspaceTrust'; +import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, localize('workspaceTrustIcon', "Icon for workspace trust badge.")); @@ -180,6 +181,24 @@ Registry.as(WorkbenchExtensions.Workbench).regi /** * Trusted Workspace GUI Editor */ +class WorkspaceTrustEditorInputInputFactory implements IEditorInputFactory { + + canSerialize(editorInput: EditorInput): boolean { + return true; + } + + serialize(input: WorkspaceTrustEditorInput): string { + return '{}'; + } + + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): WorkspaceTrustEditorInput { + return instantiationService.createInstance(WorkspaceTrustEditorInput); + } +} + +Registry.as(EditorInputExtensions.EditorInputFactories) + .registerEditorInputFactory(WorkspaceTrustEditorInput.ID, WorkspaceTrustEditorInputInputFactory); + Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( WorkspaceTrustEditor, diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index b9ec07aeeeb..f048e4d411d 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -17,7 +17,6 @@ import { EditorModel } from 'vs/workbench/common/editor'; export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled'; export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key'; -//export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces'); export const WorkspaceTrustContext = { PendingRequest: new RawContextKey('workspaceTrustPendingRequest', false), From a94e67e5e1c3b3b46357dd92ec6ae09242d64429 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 17 Feb 2021 16:32:10 +0100 Subject: [PATCH 173/176] InlineValues provide: merge and sort segments per line --- .../debug/browser/debugEditorContribution.ts | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 346d7c395aa..57ffcf9ef3e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -50,6 +50,11 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped +class InlineSegment { + constructor(public column: number, public text: string) { + } +} + function createInlineValueDecoration(lineNumber: number, contentText: string, column = Constants.MAX_SAFE_SMALL_INTEGER): IDecorationOptions { // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { @@ -579,14 +584,14 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (InlineValuesProviderRegistry.has(model)) { - const findVariable = async (key: string): Promise => { + const findVariable = async (_key: string, caseSensitiveLookup: boolean): Promise => { const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); + const key = caseSensitiveLookup ? _key : _key.toLowerCase(); for (let scope of scopes) { const variables = await scope.getChildren(); - for (let v of variables) { - if (v.name === key) { - return v.value; - } + const found = variables.find(v => caseSensitiveLookup ? (v.name === key) : (v.name.toLowerCase() === key)); + if (found) { + return found.value; } } return undefined; @@ -601,24 +606,36 @@ export class DebugEditorContribution implements IDebugEditorContribution { allDecorations = []; + const lineDecorations = new Map(); + const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineValues(model, range, ctx, token)).then(async (result) => { if (result) { for (let iv of result) { - let text: string; + + let text: string | undefined = undefined; switch (iv.type) { case 'text': text = iv.text; break; case 'variable': - const value = await findVariable(iv.variableName); - text = `${iv.variableName} = ${value}`; + const value = await findVariable(iv.variableName, iv.caseSensitiveLookup); + if (value) { + text = `${iv.variableName} = ${value}`; + } break; case 'expression': text = `eval(${iv.expression})`; break; } + if (text) { - allDecorations.push(createInlineValueDecoration(iv.range.startLineNumber, text, iv.range.startColumn)); + const line = iv.range.startLineNumber; + let lineSegments = lineDecorations.get(line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(line, lineSegments); + } + lineSegments.push(new InlineSegment(range.startColumn, text)); } } } @@ -628,7 +645,18 @@ export class DebugEditorContribution implements IDebugEditorContribution { await Promise.all(promises); - } else { // one-size-fits-all strategy + // sort line segments and concatenate them into a decoration + + lineDecorations.forEach((segments, line) => { + if (segments.length > 0) { + segments = segments.sort((a, b) => a.column - b.column); + const text = segments.map(s => s.text).join(', '); + allDecorations.push(createInlineValueDecoration(line, text)); + } + }); + + } else { + // old "one-size-fits-all" strategy const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); // Get all top level variables in the scope chain From 83d540c4bfa345c7754f2c92a2eb47fac72aa505 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 17 Feb 2021 08:06:44 -0800 Subject: [PATCH 174/176] :lipstick: --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index f761a9fec14..ef5198afafe 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -556,8 +556,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const result: INotebookKernel[] = []; const kernelsDto = await that._proxy.$provideNotebookKernels(handle, uri, token); for (const dto of kernelsDto) { - console.log('kerneldto', dto.providerHandle); - result.push({ id: dto.id, friendlyId: dto.friendlyId, From ab84845737684fa2416e1f244b2f0e8c6f01e2e0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Feb 2021 17:44:54 +0100 Subject: [PATCH 175/176] make 'editor/title/run'-menu generally available --- src/vs/workbench/api/common/menusExtensionPoint.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 6360dadf550..7122754cfc6 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -46,8 +46,7 @@ const apiMenus: IAPIMenu[] = [ { key: 'editor/title/run', id: MenuId.EditorTitleRun, - description: localize('menus.editorTitleRun', "Run submenu inside the editor title menu"), - proposed: true + description: localize('menus.editorTitleRun', "Run submenu inside the editor title menu") }, { key: 'editor/context', From c65da0b1d6289d2247342362153b79ca770fcab3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Feb 2021 17:52:10 +0100 Subject: [PATCH 176/176] perf - init KeyboardLayoutMainService in parallel to window opening //cc @alexdima --- .../electron-main/keyboardLayoutMainService.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts index f28a15dca5c..f11db7ea10a 100644 --- a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nativeKeymap from 'native-keymap'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IKeyboardLayoutData, IKeyboardLayoutMainService as ICommonKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import * as nativeKeymap from 'native-keymap'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; export const IKeyboardLayoutMainService = createDecorator('keyboardLayoutMainService'); @@ -23,10 +24,17 @@ export class KeyboardLayoutMainService extends Disposable implements ICommonKeyb private _initPromise: Promise | null; private _keyboardLayoutData: IKeyboardLayoutData | null; - constructor() { + constructor( + @ILifecycleMainService lifecycleMainService: ILifecycleMainService + ) { super(); this._initPromise = null; this._keyboardLayoutData = null; + + // perf: automatically trigger initialize after windows + // have opened so that we can do this work in parallel + // to the window load. + lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this._initialize()); } private _initialize(): Promise {