From f063d8e32e67c853e39d0606418cac04a36a9374 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 21 Sep 2020 22:22:02 -0700 Subject: [PATCH 01/98] Add chart color tokens --- src/vs/platform/theme/common/colorRegistry.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 673f3fb3d93..ee245e0c0e0 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -443,6 +443,18 @@ export const problemsErrorIconForeground = registerColor('problemsErrorIcon.fore export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon.")); export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon.")); +/** + * Chart colors + */ +export const chartsForeground = registerColor('charts.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('chartsForeground', "The foreground color used in charts.")); +export const chartsLines = registerColor('charts.lines', { dark: transparent(foreground, .5), light: transparent(foreground, .5), hc: transparent(foreground, .5) }, nls.localize('chartsLines', "The color used for horizontal lines in charts.")); +export const chartsRed = registerColor('charts.red', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('chartsRed', "The red color used charts.")); +export const chartsBlue = registerColor('charts.blue', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoForeground }, nls.localize('chartsBlue', "The blue color used charts.")); +export const chartsYellow = registerColor('charts.yellow', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('chartsYellow', "The yellow color used charts.")); +export const chartsOrange = registerColor('charts.orange', { dark: minimapFindMatch, light: minimapFindMatch, hc: minimapFindMatch }, nls.localize('chartsOrange', "The orange color used charts.")); +export const chartsGreen = registerColor('charts.green', { dark: '#89D185', light: '#388A34', hc: '#89D185' }, nls.localize('chartsGreen', "The green color used charts.")); +export const chartsPurple = registerColor('charts.purple', { dark: '#B180D7', light: '#652D90', hc: '#B180D7' }, nls.localize('chartsPurple', "The purple color used charts.")); + // ----- color functions export function darken(colorValue: ColorValue, factor: number): ColorFunction { From 162f9a871781abc6ab17806f5c6bd8ae8612ae05 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 23 Sep 2020 17:55:21 +0200 Subject: [PATCH 02/98] When reopen code from WSL2, previous open tabs are not restored. Fixes #107111 --- src/vs/code/electron-main/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index e02f90b0f47..256f6119e5f 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -689,8 +689,8 @@ export class CodeApplication extends Disposable { }); } - // new window if "-n" or "--remote" was used without paths - if ((args['new-window'] || args.remote) && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { + // new window if "-n" + if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { return windowsMainService.open({ context, cli: args, From 143d92a2589493339fa0f1d1dd32d80d001c85b9 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 23 Sep 2020 19:30:48 +0200 Subject: [PATCH 03/98] node-debug@1.44.12 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 278b075336a..31f51151036 100644 --- a/product.json +++ b/product.json @@ -31,7 +31,7 @@ "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.11", + "version": "1.44.12", "repo": "https://github.com/microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From c8fca1b131be4441b929a5e4080e9d0abcdb0705 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Sep 2020 11:45:43 -0700 Subject: [PATCH 04/98] Add issue link --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index bdfbed3dc4c..b03ecd6d07a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2134,7 +2134,7 @@ declare module 'vscode' { } //#endregion - //#region + //#region https://github.com/microsoft/vscode/issues/91697 export interface FileSystem { /** From 74679bed5bb09bbd396b63bea30c5b27799cbfdb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Sep 2020 10:34:47 -0700 Subject: [PATCH 05/98] Fix copy/paste in electron based webviews on desktop Fixes #107309 --- .../contrib/webview/electron-browser/pre/electron-index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index 293eecf7851..3dfdad8b2b3 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -24,6 +24,7 @@ * @type {import('../../browser/pre/main').WebviewHost} */ const host = { + onElectron: true, postMessage: (channel, data) => { ipcRenderer.sendToHost(channel, data); }, From 2a18502615a0066a24137bf8c480d8e77f932baa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Sep 2020 10:35:06 -0700 Subject: [PATCH 06/98] Add platform to desktop webview url --- .../contrib/webview/electron-browser/webviewElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 9e51645077a..92dab1a41f6 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -207,7 +207,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme } this.element!.preload = require.toUrl('./pre/electron-index.js'); - this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser/index.html`; + this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser/index.html?platform=electron`; } protected createElement(options: WebviewOptions) { From 41a7b46b35e0a174193ab20024654a2ea5ccd6b8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 12:20:19 -0500 Subject: [PATCH 07/98] Clean up settings editor aria label code #106897 --- .../preferences/browser/settingsTree.ts | 101 +++--------------- .../preferences/browser/settingsWidgets.ts | 2 + 2 files changed, 16 insertions(+), 87 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index df568849718..53d1fcc26a1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -690,9 +690,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.descriptionElement.innerText = element.description; } - const baseId = (element.displayCategory + '_' + element.displayLabel).replace(/ /g, '_').toLowerCase(); - template.descriptionElement.id = baseId + '_setting_description'; - template.otherOverridesElement.innerText = ''; template.otherOverridesElement.style.display = 'none'; if (element.overriddenScopeList.length) { @@ -776,68 +773,6 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre protected abstract renderValue(dataElement: SettingsTreeSettingElement, template: ISettingItemTemplate, onChange: (value: any) => void): void; - protected setElementAriaLabels(dataElement: SettingsTreeSettingElement, templateId: string, template: ISettingItemTemplate): string { - // Create base Id for element references - const baseId = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_').toLowerCase(); - - const modifiedText = template.otherOverridesElement.textContent ? - template.otherOverridesElement.textContent : (dataElement.isConfigured ? localize('settings.Modified', ' Modified. ') : ''); - - let itemElement: HTMLElement | null = null; - - // Use '.' as reader pause - let label = dataElement.displayCategory + ' ' + dataElement.displayLabel + '. '; - - // Setup and add ARIA attributes - // Create id and label for control/input element - parent is wrapper div - - if (templateId === SETTINGS_TEXT_TEMPLATE_ID) { - if (itemElement = (template).inputBox.inputElement) { - itemElement.setAttribute('role', 'textbox'); - label += modifiedText; - } - } else if (templateId === SETTINGS_NUMBER_TEMPLATE_ID) { - if (itemElement = (template).inputBox.inputElement) { - itemElement.setAttribute('role', 'textbox'); - label += ' number. ' + modifiedText; - } - } else if (templateId === SETTINGS_BOOL_TEMPLATE_ID) { - if (itemElement = (template).checkbox.domNode) { - itemElement.setAttribute('role', 'checkbox'); - label += modifiedText; - // Add checkbox target to description clickable and able to toggle checkbox - template.descriptionElement.setAttribute('checkbox_label_target_id', baseId + '_setting_item'); - } - } else if (templateId === SETTINGS_ENUM_TEMPLATE_ID) { - if (itemElement = template.controlElement.firstElementChild) { - itemElement.setAttribute('role', 'combobox'); - label += modifiedText; - } - } else if (templateId === SETTINGS_OBJECT_TEMPLATE_ID) { - if (itemElement = (template).objectWidget.domNode) { - itemElement.setAttribute('role', 'list'); - label += modifiedText; - } - } else { - // Don't change attributes if we don't know what we areFunctions - return ''; - } - - // We don't have control element, return empty label - if (!itemElement) { - return ''; - } - - // Labels will not be read on descendent input elements of the parent treeitem - // unless defined as roles for input items - // voiceover does not seem to use labeledby correctly, set labels directly on input elements - itemElement.id = baseId + '_setting_item'; - itemElement.setAttribute('aria-label', label); - itemElement.setAttribute('aria-describedby', baseId + '_setting_description settings_aria_more_actions_shortcut_label'); - - return label; - } - disposeTemplate(template: IDisposableTemplate): void { dispose(template.toDispose); } @@ -1187,8 +1122,6 @@ export class SettingObjectRenderer extends AbstractSettingRenderer implements IT valueSuggester: createObjectValueSuggester(dataElement), }); - this.setElementAriaLabels(dataElement, this.templateId, template); - template.context = dataElement; } } @@ -1317,17 +1250,15 @@ export class SettingTextRenderer extends AbstractSettingRenderer implements ITre } protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { - const label = this.setElementAriaLabels(dataElement, SETTINGS_TEXT_TEMPLATE_ID, template); - template.onChange = undefined; template.inputBox.value = dataElement.value; template.onChange = value => { - if (!renderValidations(dataElement, template, false, label)) { + if (!renderValidations(dataElement, template, false)) { onChange(value); } }; - renderValidations(dataElement, template, true, label); + renderValidations(dataElement, template, true); } } @@ -1404,9 +1335,6 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre template.selectBox.setOptions(displayOptions); - const label = this.setElementAriaLabels(dataElement, SETTINGS_ENUM_TEMPLATE_ID, template); - template.selectBox.setAriaLabel(label); - let idx = dataElement.setting.enum!.indexOf(dataElement.value); if (idx === -1) { idx = dataElement.setting.enum!.indexOf(dataElement.defaultValue); @@ -1469,17 +1397,15 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT const nullNumParseFn = (dataElement.valueType === 'nullable-integer' || dataElement.valueType === 'nullable-number') ? ((v: string) => v === '' ? null : numParseFn(v)) : numParseFn; - const label = this.setElementAriaLabels(dataElement, SETTINGS_NUMBER_TEMPLATE_ID, template); - template.onChange = undefined; template.inputBox.value = dataElement.value; template.onChange = value => { - if (!renderValidations(dataElement, template, false, label)) { + if (!renderValidations(dataElement, template, false)) { onChange(nullNumParseFn(value)); } }; - renderValidations(dataElement, template, true, label); + renderValidations(dataElement, template, true); } } @@ -1572,9 +1498,6 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre template.onChange = undefined; template.checkbox.checked = dataElement.value; template.onChange = onChange; - - // Setup and add ARIA attributes - this.setElementAriaLabels(dataElement, SETTINGS_BOOL_TEMPLATE_ID, template); } } @@ -1693,18 +1616,18 @@ export class SettingTreeRenderers { /** * Validate and render any error message. Returns true if the value is invalid. */ -function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean, originalAriaLabel: string): boolean { +function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean): boolean { if (dataElement.setting.validator) { const errMsg = dataElement.setting.validator(template.inputBox.value); if (errMsg) { DOM.addClass(template.containerElement, 'invalid-input'); template.validationErrorMessageElement.innerText = errMsg; const validationError = localize('validationError', "Validation Error."); - template.inputBox.inputElement.parentElement!.setAttribute('aria-label', [originalAriaLabel, validationError, errMsg].join(' ')); + template.inputBox.inputElement.parentElement!.setAttribute('aria-label', [validationError, errMsg].join(' ')); if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } return true; } else { - template.inputBox.inputElement.parentElement!.setAttribute('aria-label', originalAriaLabel); + template.inputBox.inputElement.parentElement!.removeAttribute('aria-label'); } } DOM.removeClass(template.containerElement, 'invalid-input'); @@ -1923,9 +1846,13 @@ export class SettingsTree extends WorkbenchObjectTree { } }, accessibilityProvider: { - getAriaLabel() { - // TODO@roblourens https://github.com/microsoft/vscode/issues/95862 - return ''; + getAriaLabel(element: SettingsTreeElement) { + // const modifiedText = template.otherOverridesElement.textContent ? + // template.otherOverridesElement.textContent : (dataElement.isConfigured ? localize('settings.Modified', ' Modified. ') : ''); + + return (element instanceof SettingsTreeSettingElement) ? + `${element.displayCategory} ${element.displayLabel}. ${element.description}` : + element.id; }, getWidgetAriaLabel() { return localize('settings', "Settings"); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 5eaab66e56c..53c2d9b5f48 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -263,6 +263,7 @@ abstract class AbstractListSettingWidget extends Dispo super(); this.listElement = DOM.append(container, $('div')); + this.listElement.setAttribute('role', 'list'); this.getContainerClasses().forEach(c => this.listElement.classList.add(c)); this.listElement.setAttribute('tabindex', '0'); DOM.append(container, this.renderAddButton()); @@ -379,6 +380,7 @@ abstract class AbstractListSettingWidget extends Dispo actionBar.push(this.getActionsForItem(item, idx), { icon: true, label: true }); rowElement.title = this.getLocalizedRowTitle(item); + rowElement.setAttribute('aria-label', rowElement.title); if (item.selected && listFocused) { this.listDisposables.add(disposableTimeout(() => rowElement.focus())); From 8c2e1bfbcea747e85f9f0407c6247e22e3e508b4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 12:51:31 -0500 Subject: [PATCH 08/98] Add modified and "also modified in" text to setting row aria label - #106897 --- .../preferences/browser/settingsTree.ts | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 53d1fcc26a1..ef9da453cb5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -12,7 +12,7 @@ import { Button } from 'vs/base/browser/ui/button/button'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { DefaultStyleController } from 'vs/base/browser/ui/list/listWidget'; +import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; @@ -1821,6 +1821,28 @@ class NonCollapsibleObjectTreeModel extends ObjectTreeModel { } } +class SettingsTreeAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(element: SettingsTreeElement) { + if (element instanceof SettingsTreeSettingElement) { + const modifiedText = element.isConfigured ? localize('settings.Modified', 'Modified.') : ''; + + const otherOverridesStart = element.isConfigured ? + localize('alsoConfiguredIn', "Also modified in") : + localize('configuredIn', "Modified in"); + const otherOverridesList = element.overriddenScopeList.join(', '); + const otherOverridesLabel = element.overriddenScopeList.length ? `${otherOverridesStart} ${otherOverridesList}. ` : ''; + + return `${element.displayCategory} ${element.displayLabel}. ${element.description}. ${modifiedText} ${otherOverridesLabel}`; + } else { + return element.id; + } + } + + getWidgetAriaLabel() { + return localize('settings', "Settings"); + } +} + export class SettingsTree extends WorkbenchObjectTree { constructor( container: HTMLElement, @@ -1845,19 +1867,7 @@ export class SettingsTree extends WorkbenchObjectTree { return e.id; } }, - accessibilityProvider: { - getAriaLabel(element: SettingsTreeElement) { - // const modifiedText = template.otherOverridesElement.textContent ? - // template.otherOverridesElement.textContent : (dataElement.isConfigured ? localize('settings.Modified', ' Modified. ') : ''); - - return (element instanceof SettingsTreeSettingElement) ? - `${element.displayCategory} ${element.displayLabel}. ${element.description}` : - element.id; - }, - getWidgetAriaLabel() { - return localize('settings', "Settings"); - } - }, + accessibilityProvider: new SettingsTreeAccessibilityProvider(), styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), filter: instantiationService.createInstance(SettingsTreeFilter, viewState), smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), From 49225a4e9fb195b27b0e7f96506d00f8cb47457c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 12:59:58 -0500 Subject: [PATCH 09/98] Fix clicking description to toggle checkbox - #106897 --- .../contrib/preferences/browser/settingsTree.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index ef9da453cb5..912b79218cb 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1438,21 +1438,17 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre controlElement.appendChild(checkbox.domNode); toDispose.add(checkbox); toDispose.add(checkbox.onChange(() => { - if (template.onChange) { - template.onChange(checkbox.checked); - } + template.onChange!(checkbox.checked); })); // Need to listen for mouse clicks on description and toggle checkbox - use target ID for safety // Also have to ignore embedded links - too buried to stop propagation toDispose.add(DOM.addDisposableListener(descriptionElement, DOM.EventType.MOUSE_DOWN, (e) => { const targetElement = e.target; - const targetId = descriptionElement.getAttribute('checkbox_label_target_id'); - // Make sure we are not a link and the target ID matches // Toggle target checkbox - if (targetElement.tagName.toLowerCase() !== 'a' && targetId === template.checkbox.domNode.id) { - template.checkbox.checked = template.checkbox.checked ? false : true; + if (targetElement.tagName.toLowerCase() !== 'a') { + template.checkbox.checked = !template.checkbox.checked; template.onChange!(checkbox.checked); } DOM.EventHelper.stop(e); From e1b06c9a81859ba2a35a7eef8975ac3e17b6b6a5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 13:03:47 -0500 Subject: [PATCH 10/98] Remove deprecated DOM helper usages --- .../preferences/browser/settingsEditor2.ts | 4 ++-- .../preferences/browser/settingsTree.ts | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 47b1ed96b1b..32656b36c60 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -914,9 +914,9 @@ export class SettingsEditor2 extends EditorPane { } private onSearchModeToggled(): void { - DOM.removeClass(this.rootElement, 'no-toc-search'); + this.rootElement.classList.add('no-toc-search'); if (this.configurationService.getValue('workbench.settings.settingsSearchTocBehavior') === 'hide') { - DOM.toggleClass(this.rootElement, 'no-toc-search', !!this.searchResultModel); + this.rootElement.classList.toggle('no-toc-search', !!this.searchResultModel); } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 912b79218cb..3e5c89d7788 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -566,8 +566,8 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre } protected renderCommonTemplate(tree: any, _container: HTMLElement, typeClass: string): ISettingItemTemplate { - DOM.addClass(_container, 'setting-item'); - DOM.addClass(_container, 'setting-item-' + typeClass); + _container.classList.add('setting-item'); + _container.classList.add('setting-item-' + typeClass); const container = DOM.append(_container, $(AbstractSettingRenderer.CONTENTS_SELECTOR)); const titleElement = DOM.append(container, $('.setting-item-title')); @@ -788,7 +788,7 @@ export class SettingGroupRenderer implements ITreeRenderer Date: Wed, 23 Sep 2020 11:36:35 -0700 Subject: [PATCH 11/98] Enable search editor in non-HOME envs --- extensions/search-result/src/extension.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 3abaa97de56..fcc4cbba2d3 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -121,9 +121,16 @@ export function activate(context: vscode.ExtensionContext) { function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | undefined { - if (pathUtils.isAbsolute(path)) { return vscode.Uri.file(path); } + if (pathUtils.isAbsolute(path)) { + return vscode.Uri + .file(path) + .with({ scheme: process.env.HOME ? 'file' : 'vscode-userdata' }); + } + if (path.indexOf('~/') === 0) { - return vscode.Uri.file(pathUtils.join(process.env.HOME!, path.slice(2))); + return vscode.Uri + .file(pathUtils.join(process.env.HOME ?? '', path.slice(2))) + .with({ scheme: process.env.HOME ? 'file' : 'vscode-userdata' }); } const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri => From eabb747d82c45ecfb074dd4a63df2d3be23adbbf Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 23 Sep 2020 15:56:37 -0400 Subject: [PATCH 12/98] Closes #105667 - finalizes treeview description --- src/vs/vscode.d.ts | 6 ++++++ src/vs/vscode.proposed.d.ts | 11 ----------- src/vs/workbench/api/common/extHostTreeViews.ts | 2 -- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index df6fe60afc8..4186a551efc 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -8599,6 +8599,12 @@ declare module 'vscode' { */ title?: string; + /** + * An optional human-readable description which is rendered less prominently in the title of the view. + * Setting the title description to null, undefined, or empty string will remove the description from the view. + */ + description?: string; + /** * Reveals the given element in the tree view. * If the tree view is not visible then the tree view is shown and element is revealed. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b03ecd6d07a..7bd4e8fc480 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2156,17 +2156,6 @@ declare module 'vscode' { //#endregion - //#region https://github.com/microsoft/vscode/issues/105667 - - export interface TreeView { - /** - * An optional human-readable description that will be rendered in the title of the view. - * Setting the title description to null, undefined, or empty string will remove the title description from the view. - */ - description?: string | undefined; - } - //#endregion - //#region https://github.com/microsoft/vscode/issues/103120 @alexr00 export class ThemeIcon2 extends ThemeIcon { diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index bc284a4f34c..7a2ee152b24 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -102,11 +102,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { treeView.title = title; }, get description() { - checkProposedApiEnabled(extension); return treeView.description; }, set description(description: string | undefined) { - checkProposedApiEnabled(extension); treeView.description = description; }, reveal: (element: T, options?: IRevealOptions): Promise => { From 0405841876769c5b2b768ed7037e568ab9e8b7b8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 17:27:29 -0500 Subject: [PATCH 13/98] Tweak coloring and spacing in new settings editor #106897 --- .../theme-defaults/themes/dark_defaults.json | 2 -- .../themes/monokai-color-theme.json | 3 +-- .../browser/media/settingsEditor2.css | 8 +++--- .../preferences/browser/settingsTree.ts | 16 ++++------- .../preferences/browser/settingsWidgets.ts | 27 +++++++++---------- 5 files changed, 23 insertions(+), 33 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 5d20a90c62d..2c722c470dd 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -12,8 +12,6 @@ "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#BBBBBB", "input.placeholderForeground": "#A6A6A6", - "settings.textInputBackground": "#292929", - "settings.numberInputBackground": "#292929", "menu.background": "#252526", "menu.foreground": "#CCCCCC", "statusBarItem.remoteForeground": "#FFF", diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index a3050894657..51d3da920a1 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -11,8 +11,6 @@ "list.activeSelectionBackground": "#75715E", "list.focusBackground": "#414339", "dropdown.listBackground": "#1e1f1c", - "settings.textInputBackground": "#32342d", - "settings.numberInputBackground": "#32342d", "list.inactiveSelectionBackground": "#414339", "list.hoverBackground": "#3e3d32", "list.dropBackground": "#414339", @@ -46,6 +44,7 @@ "panelTitle.activeBorder": "#75715E", "panelTitle.inactiveForeground": "#75715E", "panel.border": "#414339", + "settings.focusedRowBackground": "#4143395A", "titleBar.activeBackground": "#1e1f1c", "statusBar.background": "#414339", "statusBar.noFolderBackground": "#414339", diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 8b5017293dc..a48b6290478 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -283,8 +283,8 @@ max-width: 1000px; margin: auto; box-sizing: border-box; - padding-left: 204px; - padding-right: 5px; + padding-left: 211px; + padding-right: 24px; overflow: visible; } @@ -310,7 +310,7 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item-contents { position: relative; - padding: 12px 15px 18px; + padding: 12px 14px 18px; white-space: normal; } @@ -334,7 +334,7 @@ border-left-style: solid; left: 5px; top: 15px; - bottom: 16px; + bottom: 18px; } .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents.is-configured .setting-item-modified-indicator, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 3e5c89d7788..6c99af5258e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -570,6 +570,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre _container.classList.add('setting-item-' + typeClass); const container = DOM.append(_container, $(AbstractSettingRenderer.CONTENTS_SELECTOR)); + container.classList.add('settings-row-inner-container'); const titleElement = DOM.append(container, $('.setting-item-title')); const labelCategoryContainer = DOM.append(titleElement, $('.setting-item-cat-label-container')); const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); @@ -800,7 +801,7 @@ export class SettingGroupRenderer implements ITreeRenderer, index: number, templateData: IGroupTitleTemplate): void { templateData.parent.innerText = ''; - const labelElement = DOM.append(templateData.parent, $('div.settings-group-title-label')); + const labelElement = DOM.append(templateData.parent, $('div.settings-group-title-label.settings-row-inner-container')); labelElement.classList.add(`settings-group-level-${element.element.level}`); labelElement.textContent = element.element.label; @@ -1417,6 +1418,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre _container.classList.add('setting-item-bool'); const container = DOM.append(_container, $(AbstractSettingRenderer.CONTENTS_SELECTOR)); + container.classList.add('settings-row-inner-container'); const titleElement = DOM.append(container, $('.setting-item-title')); const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); @@ -1917,14 +1919,12 @@ export class SettingsTree extends WorkbenchObjectTree { const focusedRowBackgroundColor = theme.getColor(focusedRowBackground); if (focusedRowBackgroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused .setting-item-contents, - .settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused .settings-group-title-label { background-color: ${focusedRowBackgroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.focused .settings-row-inner-container { background-color: ${focusedRowBackgroundColor}; }`); } const rowHoverBackgroundColor = theme.getColor(rowHoverBackground); if (rowHoverBackgroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row .setting-item-contents:hover, - .settings-editor > .settings-body > .settings-tree-container .monaco-list-row .settings-group-title-label:hover { background-color: ${rowHoverBackgroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row:not(.focused) .settings-row-inner-container:hover { background-color: ${rowHoverBackgroundColor}; }`); } const focusedRowBorderColor = theme.getColor(focusedRowBorder); @@ -1945,12 +1945,6 @@ export class SettingsTree extends WorkbenchObjectTree { if (focusBorderColor) { collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-markdown a:focus { outline-color: ${focusBorderColor} }`); } - - // const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground); - // if (listActiveSelectionBackgroundColor) { - // collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .setting-item-contents .setting-item-title { background-color: ${listActiveSelectionBackgroundColor}; }`); - // collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-list-row.selected .settings-group-title-label { background-color: ${listActiveSelectionBackgroundColor}; }`); - // } })); this.getHTMLElement().classList.add('settings-editor-tree'); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 53c2d9b5f48..dc57cd757cc 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -3,29 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { BrowserFeatures } from 'vs/base/browser/canIUse'; import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { IAction } from 'vs/base/common/actions'; +import { disposableTimeout } from 'vs/base/common/async'; import { Color, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { isIOS } from 'vs/base/common/platform'; +import { isDefined, isUndefinedOrNull } from 'vs/base/common/types'; import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { foreground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground, simpleCheckboxBackground, simpleCheckboxForeground, simpleCheckboxBorder, listFocusBackground, transparent, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { editorWidgetBorder, focusBorder, foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { disposableTimeout } from 'vs/base/common/async'; -import { isUndefinedOrNull, isDefined } from 'vs/base/common/types'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { preferencesEditIcon } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; -import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; -import { isIOS } from 'vs/base/common/platform'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { PANEL_BORDER } from 'vs/workbench/common/theme'; const $ = DOM.$; export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); @@ -36,7 +35,7 @@ export const modifiedItemIndicator = registerColor('settings.modifiedItemIndicat }, localize('modifiedItemForeground', "The color of the modified setting indicator.")); // Enum control colors -export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); +export const settingsSelectBackground = registerColor(`settings.dropdownBackground`, { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "Settings editor dropdown border.")); export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); @@ -47,26 +46,26 @@ export const settingsCheckboxForeground = registerColor('settings.checkboxForegr export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); // Text control colors -export const settingsTextInputBackground = settingsSelectBackground; //registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); +export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); // Number control colors -export const settingsNumberInputBackground = settingsSelectBackground; // registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); +export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); export const focusedRowBackground = registerColor('settings.focusedRowBackground', { - dark: transparent(PANEL_BORDER, .4), + dark: Color.fromHex('#808080').transparent(0.14), light: transparent(listFocusBackground, .4), hc: null -}, localize('focusedRowBackground', "The background color of a cell when the row is focused.")); +}, localize('focusedRowBackground', "The background color of a settings row when focused.")); export const rowHoverBackground = registerColor('notebook.rowHoverBackground', { dark: transparent(focusedRowBackground, .5), light: transparent(focusedRowBackground, .7), hc: null -}, localize('notebook.rowHoverBackground', "The background color of a row when the row is hovered.")); +}, localize('notebook.rowHoverBackground', "The background color of a settings row when hovered.")); export const focusedRowBorder = registerColor('notebook.focusedRowBorder', { dark: Color.white.transparent(0.12), From fde0e6f09449049fc90b4751ae99be60062379a6 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 24 Sep 2020 00:51:46 +0200 Subject: [PATCH 14/98] node-debug@1.44.14 --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 31f51151036..96307afc126 100644 --- a/product.json +++ b/product.json @@ -31,7 +31,7 @@ "builtInExtensions": [ { "name": "ms-vscode.node-debug", - "version": "1.44.12", + "version": "1.44.14", "repo": "https://github.com/microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 252bef02cea41003c0bea26f74ac53900911c6a7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 17:41:23 -0500 Subject: [PATCH 15/98] Don't hide settings tree focus border under toc #106897 --- .../contrib/preferences/browser/media/settingsEditor2.css | 1 + src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index a48b6290478..846d0e8f7b0 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -203,6 +203,7 @@ pointer-events: none; z-index: 10; position: absolute; + top: 1px; /* Leave room for the settings list focus outline above */ } .settings-editor > .settings-body .settings-toc-container .monaco-list { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 32656b36c60..63113b3427b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -1358,7 +1358,7 @@ export class SettingsEditor2 extends EditorPane { this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`; this.settingsTree.layout(settingsTreeHeight, dimension.width); - const tocTreeHeight = listHeight - 16; + const tocTreeHeight = listHeight - 17; this.tocTreeContainer.style.height = `${tocTreeHeight}px`; this.tocTree.layout(tocTreeHeight); } From 366f97bad48538856b876d580bef73a4c44371f1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 17:55:25 -0500 Subject: [PATCH 16/98] Read the setting key for "Edit in settings.json" button Fix #106975 --- .../workbench/contrib/preferences/browser/settingsTree.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 6c99af5258e..024027735eb 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -857,6 +857,8 @@ export class SettingNewExtensionsRenderer implements ITreeRenderer { + private static readonly EDIT_IN_JSON_LABEL = localize('editInSettingsJson', "Edit in settings.json"); + templateId = SETTINGS_COMPLEX_TEMPLATE_ID; renderTemplate(container: HTMLElement): ISettingComplexItemTemplate { @@ -865,7 +867,7 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: undefined, buttonHoverBackground: undefined }); common.toDispose.add(openSettingsButton); common.toDispose.add(openSettingsButton.onDidClick(() => template.onChange!())); - openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json"); + openSettingsButton.label = SettingComplexRenderer.EDIT_IN_JSON_LABEL; openSettingsButton.element.classList.add('edit-in-settings-button'); common.toDispose.add(attachButtonStyler(openSettingsButton, this._themeService, { @@ -895,6 +897,8 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingComplexItemTemplate, onChange: (value: string) => void): void { template.onChange = () => this._onDidOpenSettings.fire(dataElement.setting.key); this.renderValidations(dataElement, template); + + template.button.element.setAttribute('aria-label', `${SettingComplexRenderer.EDIT_IN_JSON_LABEL}: ${dataElement.setting.key}`); } private renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingComplexItemTemplate) { From a1932df627f36f77ad037a8d2bd46f70c0d089fa Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 23 Sep 2020 19:15:25 -0400 Subject: [PATCH 17/98] Changes moveViews command to "preserve" view state When you move views via the command, they won't get expanded or made visible Co-authored-by: SteVen Batten --- src/vs/workbench/api/common/apiCommands.ts | 4 +- src/vs/workbench/common/views.ts | 7 +- .../views/browser/viewDescriptorService.ts | 94 ++++++++++--------- 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index c50709a9d12..eaf1c90ec2d 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -16,7 +16,7 @@ import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platf import { Schemas } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewsService, ViewVisibilityState } from 'vs/workbench/common/views'; // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -279,7 +279,7 @@ CommandsRegistry.registerCommand('_workbench.action.moveViews', async function ( for (const viewId of options.viewIds) { const viewDescriptor = viewDescriptorService.getViewDescriptorById(viewId); if (viewDescriptor?.canMoveView) { - viewDescriptorService.moveViewsToContainer([viewDescriptor], destination); + viewDescriptorService.moveViewsToContainer([viewDescriptor], destination, ViewVisibilityState.Default); } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index f4b3e26f92f..c49057e2f07 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -509,6 +509,11 @@ export function getVisbileViewContextKey(viewId: string): string { return `${vie export const IViewDescriptorService = createDecorator('viewDescriptorService'); +export enum ViewVisibilityState { + Default = 0, + Expand = 1 +} + export interface IViewDescriptorService { readonly _serviceBrand: undefined; @@ -535,7 +540,7 @@ export interface IViewDescriptorService { getViewLocationById(id: string): ViewContainerLocation | null; readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>; - moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer): void; + moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer, visibilityState?: ViewVisibilityState): void; readonly onDidChangeLocation: Event<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }>; moveViewToLocation(view: IViewDescriptor, location: ViewContainerLocation): void; diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index 7e0ef661f13..103094553fa 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views'; +import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions, ViewVisibilityState } from 'vs/workbench/common/views'; import { IContextKey, RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -166,7 +166,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor // This is needed when statically-registered views are moved to // other statically registered containers as they will both try to add on startup const viewsToAdd = containerData.views.filter(view => this.getViewContainerModel(viewContainer).allViewDescriptors.filter(vd => vd.id === view.id).length === 0); - this.addViews(viewContainer, viewsToAdd); + this.addViews(viewContainer, viewsToAdd, ViewVisibilityState.Default); } } @@ -197,7 +197,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const viewContainer = this.viewsRegistry.getViewContainer(viewId); const viewDescriptor = this.getViewDescriptorById(viewId); if (viewContainer && viewDescriptor) { - this.addViews(viewContainer, [viewDescriptor]); + this.addViews(viewContainer, [viewDescriptor], ViewVisibilityState.Default); } } } @@ -330,7 +330,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor this.moveViewsToContainer([view], container); } - moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer): void { + moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer, visibilityState?: ViewVisibilityState): void { if (!views.length) { return; } @@ -339,7 +339,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const to = viewContainer; if (from && to && from !== to) { - this.moveViews(views, from, to); + this.moveViews(views, from, to, visibilityState); this.cleanUpViewContainer(from.id); } } @@ -376,9 +376,9 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor return this.isGeneratedContainerId(viewContainerId) && !this.cachedViewContainerInfo.has(viewContainerId); } - private moveViews(views: IViewDescriptor[], from: ViewContainer, to: ViewContainer, skipCacheUpdate?: boolean): void { + private moveViews(views: IViewDescriptor[], from: ViewContainer, to: ViewContainer, visibilityState: ViewVisibilityState = ViewVisibilityState.Expand): void { this.removeViews(from, views); - this.addViews(to, views, true); + this.addViews(to, views, visibilityState); const oldLocation = this.getViewContainerLocation(from); const newLocation = this.getViewContainerLocation(to); @@ -389,46 +389,44 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor this._onDidChangeContainer.fire({ views, from, to }); - if (!skipCacheUpdate) { - this.saveViewPositionsToCache(); + this.saveViewPositionsToCache(); - const containerToString = (container: ViewContainer): string => { - if (container.id.startsWith(ViewDescriptorService.COMMON_CONTAINER_ID_PREFIX)) { - return 'custom'; - } - - if (!container.extensionId) { - return container.id; - } - - return 'extension'; - }; - - // Log on cache update to avoid duplicate events in other windows - const viewCount = views.length; - const fromContainer = containerToString(from); - const toContainer = containerToString(to); - const fromLocation = oldLocation === ViewContainerLocation.Panel ? 'panel' : 'sidebar'; - const toLocation = newLocation === ViewContainerLocation.Panel ? 'panel' : 'sidebar'; - - interface ViewDescriptorServiceMoveViewsEvent { - viewCount: number; - fromContainer: string; - toContainer: string; - fromLocation: string; - toLocation: string; + const containerToString = (container: ViewContainer): string => { + if (container.id.startsWith(ViewDescriptorService.COMMON_CONTAINER_ID_PREFIX)) { + return 'custom'; } - type ViewDescriptorServiceMoveViewsClassification = { - viewCount: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - fromContainer: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - toContainer: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - fromLocation: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - toLocation: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; + if (!container.extensionId) { + return container.id; + } - this.telemetryService.publicLog2('viewDescriptorService.moveViews', { viewCount, fromContainer, toContainer, fromLocation, toLocation }); + return 'extension'; + }; + + // Log on cache update to avoid duplicate events in other windows + const viewCount = views.length; + const fromContainer = containerToString(from); + const toContainer = containerToString(to); + const fromLocation = oldLocation === ViewContainerLocation.Panel ? 'panel' : 'sidebar'; + const toLocation = newLocation === ViewContainerLocation.Panel ? 'panel' : 'sidebar'; + + interface ViewDescriptorServiceMoveViewsEvent { + viewCount: number; + fromContainer: string; + toContainer: string; + fromLocation: string; + toLocation: string; } + + type ViewDescriptorServiceMoveViewsClassification = { + viewCount: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + fromContainer: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + toContainer: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + fromLocation: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + toLocation: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + + this.telemetryService.publicLog2('viewDescriptorService.moveViews', { viewCount, fromContainer, toContainer, fromLocation, toLocation }); } private cleanUpViewContainer(viewContainerId: string): void { @@ -699,7 +697,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor // Add views that were registered prior to this view container const viewsToRegister = this.getViewsByContainer(viewContainer).filter(view => this.getDefaultContainerById(view.id) !== viewContainer); if (viewsToRegister.length) { - this.addViews(viewContainer, viewsToRegister); + this.addViews(viewContainer, viewsToRegister, ViewVisibilityState.Default); this.contextKeyService.bufferChangeEvents(() => { viewsToRegister.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); }); @@ -751,7 +749,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor }); } - private addViews(container: ViewContainer, views: IViewDescriptor[], expandViews?: boolean): void { + private addViews(container: ViewContainer, views: IViewDescriptor[], visibilityState: ViewVisibilityState = ViewVisibilityState.Default): void { // Update in memory cache this.contextKeyService.bufferChangeEvents(() => { views.forEach(view => { @@ -760,7 +758,13 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor }); }); - this.getViewContainerModel(container).add(views.map(view => { return { viewDescriptor: view, collapsed: expandViews ? false : undefined, visible: expandViews }; })); + this.getViewContainerModel(container).add(views.map(view => { + return { + viewDescriptor: view, + collapsed: visibilityState === ViewVisibilityState.Default ? undefined : false, + visible: visibilityState === ViewVisibilityState.Default ? undefined : true + }; + })); } private removeViews(container: ViewContainer, views: IViewDescriptor[]): void { From 635cfbcd0f36bf3562b2f45a4995293869c15db2 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 23 Sep 2020 21:35:23 -0400 Subject: [PATCH 18/98] Removes unneeded defaults --- .../services/views/browser/viewDescriptorService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index 103094553fa..37381602fcd 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -166,7 +166,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor // This is needed when statically-registered views are moved to // other statically registered containers as they will both try to add on startup const viewsToAdd = containerData.views.filter(view => this.getViewContainerModel(viewContainer).allViewDescriptors.filter(vd => vd.id === view.id).length === 0); - this.addViews(viewContainer, viewsToAdd, ViewVisibilityState.Default); + this.addViews(viewContainer, viewsToAdd); } } @@ -197,7 +197,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const viewContainer = this.viewsRegistry.getViewContainer(viewId); const viewDescriptor = this.getViewDescriptorById(viewId); if (viewContainer && viewDescriptor) { - this.addViews(viewContainer, [viewDescriptor], ViewVisibilityState.Default); + this.addViews(viewContainer, [viewDescriptor]); } } } @@ -697,7 +697,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor // Add views that were registered prior to this view container const viewsToRegister = this.getViewsByContainer(viewContainer).filter(view => this.getDefaultContainerById(view.id) !== viewContainer); if (viewsToRegister.length) { - this.addViews(viewContainer, viewsToRegister, ViewVisibilityState.Default); + this.addViews(viewContainer, viewsToRegister); this.contextKeyService.bufferChangeEvents(() => { viewsToRegister.forEach(viewDescriptor => this.getOrCreateMovableViewContextKey(viewDescriptor).set(!!viewDescriptor.canMoveView)); }); From 622f683870c382e29f7246b121da4bd6a479d35f Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Thu, 24 Sep 2020 01:27:35 -0500 Subject: [PATCH 19/98] Fixes #107209 --- .../contrib/outline/browser/outlinePane.css | 7 +++---- .../contrib/outline/browser/outlinePane.ts | 16 +--------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.css b/src/vs/workbench/contrib/outline/browser/outlinePane.css index bbdc8af1186..cb0f2590549 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.css +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.css @@ -31,6 +31,9 @@ display: none; padding: 10px 22px 0 22px; opacity: 0.5; + position: absolute; + pointer-events: none; + z-index: 1; } .monaco-workbench .outline-pane.message .outline-message { @@ -40,7 +43,3 @@ .monaco-workbench .outline-pane.message .outline-progress { display: none; } - -.monaco-workbench .outline-pane.message .outline-tree { - display: none; -} diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index ed53348c28a..bec21fcc8fd 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -243,7 +243,6 @@ export class OutlinePane extends ViewPane { private _requestOracle?: RequestOracle; private _domNode!: HTMLElement; private _message!: HTMLDivElement; - private _inputContainer!: HTMLDivElement; private _progressBar!: ProgressBar; private _tree!: WorkbenchDataTree; private _treeDataSource!: OutlineDataSource; @@ -288,13 +287,7 @@ export class OutlinePane extends ViewPane { focus(): void { if (this._tree) { - // focus on tree and fallback to root - // dom node when the tree cannot take focus, - // e.g. when hidden this._tree.domFocus(); - if (!this._tree.isDOMFocused()) { - this._domNode.focus(); - } } } @@ -302,12 +295,10 @@ export class OutlinePane extends ViewPane { super.renderBody(container); this._domNode = container; - this._domNode.tabIndex = 0; container.classList.add('outline-pane'); let progressContainer = dom.$('.outline-progress'); this._message = dom.$('.outline-message'); - this._inputContainer = dom.$('.outline-input'); this._progressBar = new ProgressBar(progressContainer); this._register(attachProgressBarStyler(this._progressBar, this._themeService)); @@ -315,7 +306,7 @@ export class OutlinePane extends ViewPane { let treeContainer = dom.$('.outline-tree'); dom.append( container, - progressContainer, this._message, this._inputContainer, treeContainer + progressContainer, this._message, treeContainer ); this._treeRenderer = this._instantiationService.createInstance(OutlineElementRenderer); @@ -547,11 +538,6 @@ export class OutlinePane extends ViewPane { this._tree.setInput(newModel, state); } - // transfer focus from domNode to the tree - if (this._domNode === document.activeElement) { - this._tree.domFocus(); - } - this._editorDisposables.add(toDisposable(() => this._contextKeyFiltered.reset())); // feature: reveal outline selection in editor From 0299fd433f7f10a003ff3e1428b785c84958c86a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Sep 2020 08:43:37 +0200 Subject: [PATCH 20/98] sandbox - move keytar into native host service (#107292) --- .../sharedProcess/sharedProcessMain.ts | 3 -- .../credentials/common/credentials.ts | 20 ---------- .../credentials/node/credentialsService.ts | 40 ------------------- src/vs/platform/native/common/native.ts | 7 ++++ .../electron-main/nativeHostMainService.ts | 36 +++++++++++++++++ .../workbench/api/browser/mainThreadKeytar.ts | 2 +- .../credentials/browser/credentialsService.ts | 2 +- .../credentials/common/credentials.ts | 11 ++--- .../electron-sandbox/credentialsService.ts | 37 +++++++++++++++++ .../electron-browser/workbenchTestServices.ts | 5 +++ src/vs/workbench/workbench.desktop.main.ts | 3 -- src/vs/workbench/workbench.sandbox.main.ts | 1 + src/vs/workbench/workbench.web.api.ts | 2 +- 13 files changed, 95 insertions(+), 74 deletions(-) delete mode 100644 src/vs/platform/credentials/common/credentials.ts delete mode 100644 src/vs/platform/credentials/node/credentialsService.ts create mode 100644 src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index e71a0ace313..411b06ca70d 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -55,8 +55,6 @@ import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncCha 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 { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; 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'; @@ -198,7 +196,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); - services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); services.set(IUserDataSyncAccountService, new SyncDescriptor(UserDataSyncAccountService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); diff --git a/src/vs/platform/credentials/common/credentials.ts b/src/vs/platform/credentials/common/credentials.ts deleted file mode 100644 index 06af1a01af5..00000000000 --- a/src/vs/platform/credentials/common/credentials.ts +++ /dev/null @@ -1,20 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export interface ICredentialsProvider { - getPassword(service: string, account: string): Promise; - setPassword(service: string, account: string, password: string): Promise; - deletePassword(service: string, account: string): Promise; - findPassword(service: string): Promise; - findCredentials(service: string): Promise>; -} - -export const ICredentialsService = createDecorator('ICredentialsService'); - -export interface ICredentialsService extends ICredentialsProvider { - readonly _serviceBrand: undefined; -} diff --git a/src/vs/platform/credentials/node/credentialsService.ts b/src/vs/platform/credentials/node/credentialsService.ts deleted file mode 100644 index 0960eb9a542..00000000000 --- a/src/vs/platform/credentials/node/credentialsService.ts +++ /dev/null @@ -1,40 +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 type * as keytar from 'keytar'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { IdleValue } from 'vs/base/common/async'; - -export class KeytarCredentialsService implements ICredentialsService { - - declare readonly _serviceBrand: undefined; - - private readonly _keytar = new IdleValue>(() => import('keytar')); - - async getPassword(service: string, account: string): Promise { - const keytar = await this._keytar.value; - return keytar.getPassword(service, account); - } - - async setPassword(service: string, account: string, password: string): Promise { - const keytar = await this._keytar.value; - return keytar.setPassword(service, account, password); - } - - async deletePassword(service: string, account: string): Promise { - const keytar = await this._keytar.value; - return keytar.deletePassword(service, account); - } - - async findPassword(service: string): Promise { - const keytar = await this._keytar.value; - return keytar.findPassword(service); - } - - async findCredentials(service: string): Promise> { - const keytar = await this._keytar.value; - return keytar.findCredentials(service); - } -} diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 2bbd77ee057..df1f6ee5fd9 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -141,4 +141,11 @@ export interface ICommonNativeHostService { // Registry (windows only) windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise; + + // Credentials + getPassword(service: string, account: string): Promise; + setPassword(service: string, account: string, password: string): Promise; + deletePassword(service: string, account: string): Promise; + findPassword(service: string): Promise; + findCredentials(service: string): Promise> } diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 2d154708f3a..7fa482c7e03 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -610,6 +610,42 @@ export class NativeHostMainService implements INativeHostMainService { } } + //#endregion + + //#region Credentials + + async getPassword(windowId: number | undefined, service: string, account: string): Promise { + const keytar = await import('keytar'); + + return keytar.getPassword(service, account); + } + + async setPassword(windowId: number | undefined, service: string, account: string, password: string): Promise { + const keytar = await import('keytar'); + + return keytar.setPassword(service, account, password); + } + + async deletePassword(windowId: number | undefined, service: string, account: string): Promise { + const keytar = await import('keytar'); + + return keytar.deletePassword(service, account); + } + + async findPassword(windowId: number | undefined, service: string): Promise { + const keytar = await import('keytar'); + + return keytar.findPassword(service); + } + + async findCredentials(windowId: number | undefined, service: string): Promise> { + const keytar = await import('keytar'); + + return keytar.findCredentials(service); + } + + //#endregion + private windowById(windowId: number | undefined): ICodeWindow | undefined { if (typeof windowId !== 'number') { return undefined; diff --git a/src/vs/workbench/api/browser/mainThreadKeytar.ts b/src/vs/workbench/api/browser/mainThreadKeytar.ts index 5312a9aad19..ddcf676a10c 100644 --- a/src/vs/workbench/api/browser/mainThreadKeytar.ts +++ b/src/vs/workbench/api/browser/mainThreadKeytar.ts @@ -5,7 +5,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; @extHostNamedCustomer(MainContext.MainThreadKeytar) export class MainThreadKeytar implements MainThreadKeytarShape { diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index 3d394b6f38d..aff3526121e 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICredentialsProvider, ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { ICredentialsService, ICredentialsProvider } from 'vs/workbench/services/credentials/common/credentials'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/services/credentials/common/credentials.ts b/src/vs/workbench/services/credentials/common/credentials.ts index 2799abeed19..b6dd9404b41 100644 --- a/src/vs/workbench/services/credentials/common/credentials.ts +++ b/src/vs/workbench/services/credentials/common/credentials.ts @@ -5,15 +5,16 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export const ICredentialsService = createDecorator('ICredentialsService'); - -export interface ICredentialsService { - - readonly _serviceBrand: undefined; +export const ICredentialsService = createDecorator('credentialsService'); +export interface ICredentialsProvider { getPassword(service: string, account: string): Promise; setPassword(service: string, account: string, password: string): Promise; deletePassword(service: string, account: string): Promise; findPassword(service: string): Promise; findCredentials(service: string): Promise>; } + +export interface ICredentialsService extends ICredentialsProvider { + readonly _serviceBrand: undefined; +} diff --git a/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts b/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts new file mode 100644 index 00000000000..a6e9361b42d --- /dev/null +++ b/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class KeytarCredentialsService implements ICredentialsService { + + declare readonly _serviceBrand: undefined; + + constructor(@INativeHostService private readonly nativeHostService: INativeHostService) { } + + getPassword(service: string, account: string): Promise { + return this.nativeHostService.getPassword(service, account); + } + + setPassword(service: string, account: string, password: string): Promise { + return this.nativeHostService.setPassword(service, account, password); + } + + deletePassword(service: string, account: string): Promise { + return this.nativeHostService.deletePassword(service, account); + } + + findPassword(service: string): Promise { + return this.nativeHostService.findPassword(service); + } + + findCredentials(service: string): Promise> { + return this.nativeHostService.findCredentials(service); + } +} + +registerSingleton(ICredentialsService, KeytarCredentialsService, true); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 0446d3c0cad..7989ce9a26d 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -232,6 +232,11 @@ export class TestNativeHostService implements INativeHostService { async hasClipboard(format: string, type?: 'selection' | 'clipboard' | undefined): Promise { return false; } async sendInputEvent(event: MouseInputEvent): Promise { } async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise { return undefined; } + async getPassword(service: string, account: string): Promise { return null; } + async setPassword(service: string, account: string, password: string): Promise { } + async deletePassword(service: string, account: string): Promise { return false; } + async findPassword(service: string): Promise { return null; } + async findCredentials(service: string): Promise<{ account: string; password: string; }[]> { return []; } } export function workbenchInstantiationService(): ITestInstantiationService { diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 856afbd2bf1..5f08dc5d96d 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -97,12 +97,9 @@ import 'vs/workbench/services/diagnostics/electron-browser/diagnosticsService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/platform/remote/node/tunnelService'; -registerSingleton(ICredentialsService, KeytarCredentialsService, true); registerSingleton(ITunnelService, TunnelService); //#endregion diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index 70725dd5561..65090679243 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -41,6 +41,7 @@ import 'vs/workbench/services/accessibility/electron-sandbox/accessibilityServic import 'vs/workbench/services/path/electron-sandbox/pathService'; import 'vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService'; +import 'vs/workbench/services/credentials/electron-sandbox/credentialsService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 9776a5a1453..16b5f29dd36 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -18,7 +18,7 @@ import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/brows import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IProductConfiguration } from 'vs/platform/product/common/productService'; import { mark } from 'vs/base/common/performance'; -import { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; +import { ICredentialsProvider } from 'vs/workbench/services/credentials/common/credentials'; interface IResourceUriProvider { (uri: URI): URI; From 47a6d93693f209da68d24699d66353d3c60aec9e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 09:10:14 +0200 Subject: [PATCH 21/98] Instantiate text models even if the undo / redo stack for a text model contains foreign elements (#101789) --- .../common/services/modelServiceImpl.ts | 21 ++----------------- .../services/modelUndoRedoParticipant.ts | 10 +++------ 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 576a7dd4ce8..b21a32c25dc 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -24,9 +24,9 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IUndoRedoService, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; import { StringSHA1 } from 'vs/base/common/hash'; -import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement, isEditStackElement } from 'vs/editor/common/model/editStack'; +import { EditStackElement, isEditStackElement } from 'vs/editor/common/model/editStack'; import { Schemas } from 'vs/base/common/network'; import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling'; @@ -118,23 +118,6 @@ export interface EditStackPastFutureElements { future: EditStackElement[]; } -export function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements { - return (isEditStackElements(undoElements.past) && isEditStackElements(undoElements.future)); -} - -function isEditStackElements(elements: IUndoRedoElement[]): elements is EditStackElement[] { - for (const element of elements) { - if (element instanceof SingleModelEditStackElement) { - continue; - } - if (element instanceof MultiModelEditStackElement) { - continue; - } - return false; - } - return true; -} - class DisposedModelInfo { constructor( public readonly uri: URI, diff --git a/src/vs/editor/common/services/modelUndoRedoParticipant.ts b/src/vs/editor/common/services/modelUndoRedoParticipant.ts index 0862ccf8d6e..6a03af9e80f 100644 --- a/src/vs/editor/common/services/modelUndoRedoParticipant.ts +++ b/src/vs/editor/common/services/modelUndoRedoParticipant.ts @@ -6,8 +6,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; -import { isEditStackPastFutureElements } from 'vs/editor/common/services/modelServiceImpl'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { IUndoRedoDelegate, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; export class ModelUndoRedoParticipant extends Disposable implements IUndoRedoDelegate { @@ -23,16 +22,13 @@ export class ModelUndoRedoParticipant extends Disposable implements IUndoRedoDel if (elements.past.length === 0 && elements.future.length === 0) { return; } - if (!isEditStackPastFutureElements(elements)) { - return; - } for (const element of elements.past) { - if (element.type === UndoRedoElementType.Workspace) { + if (element instanceof MultiModelEditStackElement) { element.setDelegate(this); } } for (const element of elements.future) { - if (element.type === UndoRedoElementType.Workspace) { + if (element instanceof MultiModelEditStackElement) { element.setDelegate(this); } } From d9b234e5dec1016ac76b3be5d2aa92b6981ae968 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 24 Sep 2020 10:16:59 +0200 Subject: [PATCH 22/98] fixes #107053 --- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index c91ad32759f..5dcee2af033 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -94,7 +94,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } const { actions, disposable } = DebugToolBar.getActions(this.debugToolBarMenu, this.debugService, this.instantiationService); - if (!arrays.equals(actions, this.activeActions, (first, second) => first.id === second.id)) { + if (!arrays.equals(actions, this.activeActions, (first, second) => first.id === second.id && first.enabled === second.enabled)) { this.actionBar.clear(); this.actionBar.push(actions, { icon: true, label: false }); this.activeActions = actions; From 0b0450b1fc5f319ba0bb0a63c4d3c9d23c34ccb8 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Wed, 23 Sep 2020 18:10:15 +0000 Subject: [PATCH 23/98] Fix README.md detection in welcome page --- src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 1783cf20c74..2cd1558d4bf 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -75,9 +75,10 @@ export class WelcomePageContribution implements IWorkbenchContribution { const folderUri = folder.uri; return fileService.resolve(folderUri) .then(folder => { - const files = folder.children ? folder.children.map(child => child.name) : []; + const files = folder.children ? folder.children.map(child => child.name).sort() : []; + + const file = files.find(file => file.toLowerCase() === 'readme.md') || files.find(file => file.toLowerCase().startsWith('readme')); - const file = files.sort().find(file => file.toLowerCase().startsWith('readme')); if (file) { return joinPath(folderUri, file); } From 8d6fa24ffdb55c9bf03c7e6f36e56c232729dfb1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Sep 2020 10:25:03 +0200 Subject: [PATCH 24/98] use getter - isEnabled() is accesed in the constructor --- .../services/userDataSync/browser/userDataAutoSyncService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts index fa85c6a2f54..47f86b1706d 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataAutoSyncService.ts @@ -9,7 +9,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ export class WebUserDataAutoSyncService extends UserDataAutoSyncService implements IUserDataAutoSyncService { - private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService = this.environmentService; + private get workbenchEnvironmentService(): IWorkbenchEnvironmentService { return this.environmentService; } private enabled: boolean | undefined = undefined; isEnabled(): boolean { From 73b33dcbc8204c23ae30b53ce129b32c6da98197 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Sep 2020 10:49:09 +0200 Subject: [PATCH 25/98] disable the command in web --- .../services/extensions/browser/extensionUrlHandler.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 4091dc8bb58..ed875f2e181 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -22,9 +22,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; @@ -380,7 +381,10 @@ class ManageAuthorizedExtensionURIsAction extends Action2 { id: 'workbench.extensions.action.manageAuthorizedExtensionURIs', title: { value: localize('manage', "Manage Authorized Extension URIs..."), original: 'Manage Authorized Extension URIs...' }, category: { value: localize('extensions', "Extensions"), original: 'Extensions' }, - f1: true + menu: { + id: MenuId.CommandPalette, + when: IsWebContext.toNegated() + } }); } From 459543baffe3d9b5000f768b6d3eeba1d5812c9b Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Thu, 24 Sep 2020 04:18:17 -0500 Subject: [PATCH 26/98] Fixes #107220 --- src/vs/workbench/api/common/extHostTypes.ts | 2 +- src/vs/workbench/test/browser/api/extHostTypes.test.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 43dd6f16057..dcaeaa19036 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -782,7 +782,7 @@ export class SnippetString { } appendChoice(values: string[], number: number = this._tabstop++): SnippetString { - const value = SnippetString._escape(values.toString()); + const value = values.map(s => s.replace(/\$|}|\\|,/g, '\\$&')).join(','); this.value += '${'; this.value += number; diff --git a/src/vs/workbench/test/browser/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index 08e6159df75..e4e13098f36 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -524,6 +524,10 @@ suite('ExtHostTypes', function () { string.appendChoice(['b', 'a', 'r']); assert.equal(string.value, '${1|b,a,r|}'); + string = new types.SnippetString(); + string.appendChoice(['b,1', 'a,2', 'r,3']); + assert.equal(string.value, '${1|b\\,1,a\\,2,r\\,3|}'); + string = new types.SnippetString(); string.appendChoice(['b', 'a', 'r'], 0); assert.equal(string.value, '${0|b,a,r|}'); From d9ba49fae6896b5a2f3d1ea4fd28ffcd327f2174 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Sep 2020 13:18:30 +0200 Subject: [PATCH 27/98] make enablement handler optional --- src/vs/workbench/workbench.web.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 16b5f29dd36..640931217ca 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -247,7 +247,7 @@ interface ISettingsSyncOptions { /** * Handler is being called when the user changes Settings Sync enablement. */ - enablementHandler(enablement: boolean): void; + enablementHandler?(enablement: boolean): void; } interface IWorkbenchConstructionOptions { From 392ea6dc16d2507e185dba59019e4c2318213ad8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Sep 2020 15:35:11 +0200 Subject: [PATCH 28/98] debt - adopt more of the amd.ts helper functions --- src/vs/code/electron-main/auth.ts | 4 ++-- src/vs/code/electron-main/sharedProcess.ts | 4 ++-- src/vs/code/electron-main/window.ts | 7 ++----- .../issue/electron-main/issueMainService.ts | 6 +++--- .../parts/editor/editor.contribution.ts | 5 +++-- .../browser/parts/editor/editorGroupView.ts | 3 ++- .../debug/browser/debug.contribution.ts | 19 ++++++++++--------- .../integrity/node/integrityService.ts | 3 ++- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index dd0d67ae21e..f57aef11f3e 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -6,8 +6,8 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; import { BrowserWindow, BrowserWindowConstructorOptions, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; type LoginEvent = { event: ElectronEvent; @@ -59,7 +59,7 @@ export class ProxyAuthHandler extends Disposable { show: true, title: 'VS Code', webPreferences: { - preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), sandbox: true, contextIsolation: true, enableWebSQL: false, diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index 9b7a12ac060..a40e860b384 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; import { memoize } from 'vs/base/common/decorators'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron'; @@ -14,6 +13,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class SharedProcess implements ISharedProcess { @@ -41,7 +41,7 @@ export class SharedProcess implements ISharedProcess { show: false, backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { - preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), nodeIntegration: true, enableWebSQL: false, enableRemoteModule: false, diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 5403dc9667b..1de5a852e06 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -35,6 +35,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IFileService } from 'vs/platform/files/common/files'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface IWindowCreationOptions { state: IWindowState; @@ -166,7 +167,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - preload: URI.parse(this.doGetPreloadUrl()).fsPath, + preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), enableWebSQL: false, enableRemoteModule: false, spellcheck: false, @@ -838,10 +839,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { return `${require.toUrl(workbench)}?config=${encodeURIComponent(JSON.stringify(config))}`; } - private doGetPreloadUrl(): string { - return require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js'); - } - serializeWindowState(): IWindowState { if (!this._win) { return defaultWindowState(); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index eb7da71418a..ee27d4c8e38 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -18,7 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWindowState } from 'vs/platform/windows/electron-main/windows'; import { listProcesses } from 'vs/base/node/ps'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; -import { URI } from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; @@ -195,7 +195,7 @@ export class IssueMainService implements ICommonIssueService { title: localize('issueReporter', "Issue Reporter"), backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR, webPreferences: { - preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), enableWebSQL: false, enableRemoteModule: false, spellcheck: false, @@ -261,7 +261,7 @@ export class IssueMainService implements ICommonIssueService { backgroundColor: data.styles.backgroundColor, title: localize('processExplorer', "Process Explorer"), webPreferences: { - preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, + preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), enableWebSQL: false, enableRemoteModule: false, spellcheck: false, diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 31543247012..a6a52a00e5a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -54,6 +54,7 @@ import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -422,13 +423,13 @@ editorCommands.setup(); // Touch Bar if (isMacintosh) { MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { - command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } }, + command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: getUriFromAmdModule(require, 'vs/workbench/browser/parts/editor/media/back-tb.png') } }, group: 'navigation', order: 0 }); MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { - command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } }, + command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: getUriFromAmdModule(require, 'vs/workbench/browser/parts/editor/media/forward-tb.png') } }, group: 'navigation', order: 1 }); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 7c005cfa79e..4f209f32f0a 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -52,6 +52,7 @@ import { IDialogService, IFileDialogService, ConfirmResult } from 'vs/platform/d import { ILogService } from 'vs/platform/log/common/log'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class EditorGroupView extends Themable implements IEditorGroupView { @@ -1780,7 +1781,7 @@ registerThemingParticipant((theme, collector, environment) => { const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`; collector.addRule(` .monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress { - background-image: url('${require.toUrl(letterpress)}') + background-image: url('${getPathFromAmdModule(require, letterpress)}') } `); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 610459ee1f9..a359e971221 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,6 +52,7 @@ import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debug import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); const debugCategory = nls.localize('debugCategory', "Debug"); @@ -209,15 +210,15 @@ function registerCommandsAndActions(): void { }); }; - registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); - registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png'))); - registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/continue-tb.png'))); - registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/pause-tb.png'))); - registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepover-tb.png'))); - registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepinto-tb.png'))); - registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stepout-tb.png'))); - registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/restart-tb.png'))); - registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, URI.parse(require.toUrl('vs/workbench/contrib/debug/browser/media/stop-tb.png'))); + registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-tb.png')); + registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png')); + registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-tb.png')); + registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/pause-tb.png')); + registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepover-tb.png')); + registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepinto-tb.png')); + registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepout-tb.png')); + registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/restart-tb.png')); + registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stop-tb.png')); } } diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index cc1b47000c2..cd348ebef86 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -15,6 +15,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; interface IStorageData { dontShowPrompt: boolean; @@ -141,7 +142,7 @@ export class IntegrityServiceImpl implements IIntegrityService { } private _resolve(filename: string, expected: string): Promise { - let fileUri = URI.parse(require.toUrl(filename)); + const fileUri = getUriFromAmdModule(require, filename); return new Promise((resolve, reject) => { fs.readFile(fileUri.fsPath, (err, buff) => { if (err) { From 896fb66917a47b7e90a3c7ec96f2cbd6dd539d06 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 17:59:49 -0500 Subject: [PATCH 29/98] Clean up dead aria-label code #106897 --- .../preferences/browser/settingsEditor2.ts | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 63113b3427b..030197f55f1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -28,7 +28,6 @@ import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService } f import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -47,7 +46,7 @@ import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickE import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; @@ -120,8 +119,6 @@ export class SettingsEditor2 extends EditorPane { private tocTreeContainer!: HTMLElement; private tocTree!: TOCTree; - private settingsAriaExtraLabelsContainer!: HTMLElement; - private delayedFilterLogging: Delayer; private localSearchDelayer: Delayer; private remoteSearchThrottle: ThrottledDelayer; @@ -165,7 +162,6 @@ export class SettingsEditor2 extends EditorPane { @IStorageService private readonly storageService: IStorageService, @INotificationService private readonly notificationService: INotificationService, @IEditorGroupsService protected editorGroupService: IEditorGroupsService, - @IKeybindingService private readonly keybindingService: IKeybindingService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, @IUserDataAutoSyncService private readonly userDataAutoSyncService: IUserDataAutoSyncService @@ -216,12 +212,7 @@ export class SettingsEditor2 extends EditorPane { private set searchResultModel(value: SearchResultModel | null) { this._searchResultModel = value; - DOM.toggleClass(this.rootElement, 'search-mode', !!this._searchResultModel); - } - - private get currentSettingsContextMenuKeyBindingLabel(): string { - const keybinding = this.keybindingService.lookupKeybinding(SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU); - return (keybinding && keybinding.getAriaLabel()) || ''; + this.rootElement.classList.toggle('search-mode', !!this._searchResultModel); } createEditor(parent: HTMLElement): void { @@ -351,16 +342,6 @@ export class SettingsEditor2 extends EditorPane { } focusSettings(focusSettingInput = false): void { - // TODO@roblourens is this in the right place? - // Update ARIA global labels - const labelElement = this.settingsAriaExtraLabelsContainer.querySelector('#settings_aria_more_actions_shortcut_label'); - if (labelElement) { - const settingsContextMenuShortcut = this.currentSettingsContextMenuKeyBindingLabel; - if (settingsContextMenuShortcut) { - labelElement.setAttribute('aria-label', localize('settingsContextMenuAriaShortcut', "For more actions, Press {0}.", settingsContextMenuShortcut)); - } - } - const focused = this.settingsTree.getFocus(); if (!focused.length) { this.settingsTree.focusFirst(); @@ -648,14 +629,6 @@ export class SettingsEditor2 extends EditorPane { private createSettingsTree(parent: HTMLElement): void { this.settingsTreeContainer = DOM.append(parent, $('.settings-tree-container')); - // Add ARIA extra labels div - this.settingsAriaExtraLabelsContainer = DOM.append(this.settingsTreeContainer, $('.settings-aria-extra-labels')); - this.settingsAriaExtraLabelsContainer.id = 'settings_aria_extra_labels'; - // Add global labels here - const labelDiv = DOM.append(this.settingsAriaExtraLabelsContainer, $('.settings-aria-extra-label')); - labelDiv.id = 'settings_aria_more_actions_shortcut_label'; - labelDiv.setAttribute('aria-label', ''); - this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers); this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type))); this._register(this.settingRenderers.onDidOpenSettings(settingKey => { From 65e7a941dadc8581fd3edd7598641480cc863f23 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 23 Sep 2020 18:36:40 -0500 Subject: [PATCH 30/98] Clicking setting control should focus the row #106897 --- src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts | 2 +- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 030197f55f1..4f6f8550cdb 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -637,7 +637,7 @@ export class SettingsEditor2 extends EditorPane { this._register(this.settingRenderers.onDidClickSettingLink(settingName => this.onDidClickSetting(settingName))); this._register(this.settingRenderers.onDidFocusSetting(element => { this.lastFocusedSettingElement = element.setting.key; - this.settingsTree.reveal(element); + this.settingsTree.setFocus([element]); })); this._register(this.settingRenderers.onDidClickOverrideElement((element: ISettingOverrideClickEvent) => { if (element.scope.toLowerCase() === 'workspace') { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 024027735eb..0d23b380203 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -608,7 +608,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }; // Prevent clicks from being handled by list - toDispose.add(DOM.addDisposableListener(controlElement, 'mousedown', e => e.stopPropagation())); + toDispose.add(DOM.addDisposableListener(controlElement, DOM.EventType.MOUSE_DOWN, e => e.stopPropagation())); toDispose.add(DOM.addDisposableListener(titleElement, DOM.EventType.MOUSE_ENTER, e => container.classList.add('mouseover'))); toDispose.add(DOM.addDisposableListener(titleElement, DOM.EventType.MOUSE_LEAVE, e => container.classList.remove('mouseover'))); From 3d6b92f99404647ae63e7eeff7946291c9d4321a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 24 Sep 2020 16:02:56 +0200 Subject: [PATCH 31/98] When reopen code from WSL2, previous open tabs are not restored. Fixes #107111 --- src/vs/platform/windows/electron-main/windowsMainService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 4699064afc9..40ac58b8eca 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -972,7 +972,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (pathToOpen?.folderUri) { windowsToOpen.push(pathToOpen); } - } else if (restoreWindows !== 'folders' && openedWindow.backupPath && !openedWindow.remoteAuthority) { // Local windows that were empty. Empty windows with backups will always be restored in open() + } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Empty window, potentially editors open to be restored windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } From 74c7f2b3d2a1b550de71e19a9fa6fc895f13c5fa Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 16:09:28 +0200 Subject: [PATCH 32/98] Fixes microsoft/monaco-editor#2144 --- src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts index db4cc2ecbbb..ca85c3ce869 100644 --- a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts @@ -27,7 +27,7 @@ export interface IGotoSymbolQuickPickItem extends IQuickPickItem { } export interface IGotoSymbolQuickAccessProviderOptions extends IEditorNavigationQuickAccessOptions { - openSideBySideDirection: () => undefined | 'right' | 'down' + openSideBySideDirection?: () => undefined | 'right' | 'down' } export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider { @@ -306,7 +306,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit }, strikethrough: deprecated, buttons: (() => { - const openSideBySideDirection = this.options?.openSideBySideDirection(); + const openSideBySideDirection = this.options?.openSideBySideDirection ? this.options?.openSideBySideDirection() : undefined; if (!openSideBySideDirection) { return undefined; } From 9f806960d4674ccb4e7e210c41f60bcc00491542 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 17:00:44 +0200 Subject: [PATCH 33/98] Convert `build/lib/builtInExtensions.js` to `.ts` --- ...ltInExtensions.js => builtInExtensions.ts} | 82 ++++++++++++------- 1 file changed, 51 insertions(+), 31 deletions(-) rename build/lib/{builtInExtensions.js => builtInExtensions.ts} (69%) diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.ts similarity index 69% rename from build/lib/builtInExtensions.js rename to build/lib/builtInExtensions.ts index f86414211ec..b20913f49d1 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.ts @@ -3,38 +3,54 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as rimraf from 'rimraf'; +import * as es from 'event-stream'; +import * as rename from 'gulp-rename'; +import * as vfs from 'vinyl-fs'; +import * as ext from './extensions'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; +import { Stream } from 'stream'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); const mkdirp = require('mkdirp'); -const rimraf = require('rimraf'); -const es = require('event-stream'); -const rename = require('gulp-rename'); -const vfs = require('vinyl-fs'); -const ext = require('./extensions'); -const fancyLog = require('fancy-log'); -const ansiColors = require('ansi-colors'); -const root = path.dirname(path.dirname(__dirname)); -const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); -const builtInExtensions = productjson.builtInExtensions; -const webBuiltInExtensions = productjson.webBuiltInExtensions; -const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); -const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; - -function log() { - if (ENABLE_LOGGING) { - fancyLog.apply(this, arguments); +interface IExtensionDefinition { + name: string; + version: string; + repo: string; + metadata: { + id: string; + publisherId: { + publisherId: string; + publisherName: string; + displayName: string; + flags: string; + }; + publisherDisplayName: string; } } -function getExtensionPath(extension) { +const root = path.dirname(path.dirname(__dirname)); +const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const builtInExtensions = productjson.builtInExtensions; +const webBuiltInExtensions = productjson.webBuiltInExtensions; +const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); +const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; + +function log(...messages: string[]): void { + if (ENABLE_LOGGING) { + fancyLog(...messages); + } +} + +function getExtensionPath(extension: IExtensionDefinition): string { return path.join(root, '.build', 'builtInExtensions', extension.name); } -function isUpToDate(extension) { +function isUpToDate(extension: IExtensionDefinition): boolean { const packagePath = path.join(getExtensionPath(extension), 'package.json'); if (!fs.existsSync(packagePath)) { @@ -51,7 +67,7 @@ function isUpToDate(extension) { } } -function syncMarketplaceExtension(extension) { +function syncMarketplaceExtension(extension: IExtensionDefinition): Stream { if (isUpToDate(extension)) { log(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); return es.readArray([]); @@ -65,7 +81,7 @@ function syncMarketplaceExtension(extension) { .on('end', () => log(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); } -function syncExtension(extension, controlState) { +function syncExtension(extension: IExtensionDefinition, controlState: 'disabled' | 'marketplace'): Stream { switch (controlState) { case 'disabled': log(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); @@ -89,7 +105,11 @@ function syncExtension(extension, controlState) { } } -function readControlFile() { +interface IControlFile { + [name: string]: 'disabled' | 'marketplace'; +} + +function readControlFile(): IControlFile { try { return JSON.parse(fs.readFileSync(controlFilePath, 'utf8')); } catch (err) { @@ -97,17 +117,17 @@ function readControlFile() { } } -function writeControlFile(control) { +function writeControlFile(control: IControlFile): void { mkdirp.sync(path.dirname(controlFilePath)); fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } -exports.getBuiltInExtensions = function getBuiltInExtensions() { +export function getBuiltInExtensions(): Promise { log('Syncronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); - const streams = []; + const streams: Stream[] = []; for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { let controlState = control[extension.name] || 'marketplace'; @@ -123,10 +143,10 @@ exports.getBuiltInExtensions = function getBuiltInExtensions() { .on('error', reject) .on('end', resolve); }); -}; +} if (require.main === module) { - exports.getBuiltInExtensions().then(() => process.exit(0)).catch(err => { + getBuiltInExtensions().then(() => process.exit(0)).catch(err => { console.error(err); process.exit(1); }); From 2ab5b8bd5ff134a456eab2d3c360b619859e1cc5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 17:10:39 +0200 Subject: [PATCH 34/98] Convert `build/lib/eslint/code-no-unused-expressions.js` to `.ts` --- ...sions.js => code-no-unused-expressions.ts} | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) rename build/lib/eslint/{code-no-unused-expressions.js => code-no-unused-expressions.ts} (66%) diff --git a/build/lib/eslint/code-no-unused-expressions.js b/build/lib/eslint/code-no-unused-expressions.ts similarity index 66% rename from build/lib/eslint/code-no-unused-expressions.js rename to build/lib/eslint/code-no-unused-expressions.ts index c7b17551311..d130d670da5 100644 --- a/build/lib/eslint/code-no-unused-expressions.js +++ b/build/lib/eslint/code-no-unused-expressions.ts @@ -13,6 +13,10 @@ 'use strict'; +import * as eslint from 'eslint'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; +import * as ESTree from 'estree'; + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -50,29 +54,29 @@ module.exports = { ] }, - create(context) { + create(context: eslint.Rule.RuleContext) { const config = context.options[0] || {}, allowShortCircuit = config.allowShortCircuit || false, allowTernary = config.allowTernary || false, allowTaggedTemplates = config.allowTaggedTemplates || false; // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node any node - * @returns {boolean} whether the given node structurally represents a directive - */ - function looksLikeDirective(node) { + /** + * @param node any node + * @returns whether the given node structurally represents a directive + */ + function looksLikeDirective(node: TSESTree.Node): boolean { return node.type === 'ExpressionStatement' && node.expression.type === 'Literal' && typeof node.expression.value === 'string'; } // eslint-disable-next-line jsdoc/require-description - /** - * @param {Function} predicate ([a] -> Boolean) the function used to make the determination - * @param {a[]} list the input list - * @returns {a[]} the leading sequence of members in the given list that pass the given predicate - */ - function takeWhile(predicate, list) { + /** + * @param predicate ([a] -> Boolean) the function used to make the determination + * @param list the input list + * @returns the leading sequence of members in the given list that pass the given predicate + */ + function takeWhile(predicate: (item: T) => boolean, list: T[]): T[] { for (let i = 0; i < list.length; ++i) { if (!predicate(list[i])) { return list.slice(0, i); @@ -82,21 +86,21 @@ module.exports = { } // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node a Program or BlockStatement node - * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body - */ - function directives(node) { + /** + * @param node a Program or BlockStatement node + * @returns the leading sequence of directive nodes in the given node's body + */ + function directives(node: TSESTree.Program | TSESTree.BlockStatement): TSESTree.Node[] { return takeWhile(looksLikeDirective, node.body); } // eslint-disable-next-line jsdoc/require-description - /** - * @param {ASTNode} node any node - * @param {ASTNode[]} ancestors the given node's ancestors - * @returns {boolean} whether the given node is considered a directive in its current position - */ - function isDirective(node, ancestors) { + /** + * @param node any node + * @param ancestors the given node's ancestors + * @returns whether the given node is considered a directive in its current position + */ + function isDirective(node: TSESTree.Node, ancestors: TSESTree.Node[]): boolean { const parent = ancestors[ancestors.length - 1], grandparent = ancestors[ancestors.length - 2]; @@ -105,12 +109,12 @@ module.exports = { directives(parent).indexOf(node) >= 0; } - /** - * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags. - * @param {ASTNode} node any node - * @returns {boolean} whether the given node is a valid expression - */ - function isValidExpression(node) { + /** + * Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags. + * @param node any node + * @returns whether the given node is a valid expression + */ + function isValidExpression(node: TSESTree.Node): boolean { if (allowTernary) { // Recursive check for ternary and logical expressions @@ -134,9 +138,9 @@ module.exports = { } return { - ExpressionStatement(node) { - if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { - context.report({ node, message: 'Expected an assignment or function call and instead saw an expression.' }); + ExpressionStatement(node: TSESTree.ExpressionStatement) { + if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { + context.report({ node: node, message: 'Expected an assignment or function call and instead saw an expression.' }); } } }; From 57f4c1bf74f897dd5d755af6654577c789b85795 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 17:24:24 +0200 Subject: [PATCH 35/98] Convert `build/lib/watch/` to `.ts` --- build/lib/watch/{index.js => index.ts} | 0 .../watch/{watch-win32.js => watch-win32.ts} | 61 ++++++++++--------- 2 files changed, 31 insertions(+), 30 deletions(-) rename build/lib/watch/{index.js => index.ts} (100%) rename build/lib/watch/{watch-win32.js => watch-win32.ts} (51%) diff --git a/build/lib/watch/index.js b/build/lib/watch/index.ts similarity index 100% rename from build/lib/watch/index.js rename to build/lib/watch/index.ts diff --git a/build/lib/watch/watch-win32.js b/build/lib/watch/watch-win32.ts similarity index 51% rename from build/lib/watch/watch-win32.js rename to build/lib/watch/watch-win32.ts index 91c0eae7565..4ed37277123 100644 --- a/build/lib/watch/watch-win32.js +++ b/build/lib/watch/watch-win32.ts @@ -3,16 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -var path = require('path'); -var cp = require('child_process'); -var fs = require('fs'); -var File = require('vinyl'); -var es = require('event-stream'); -var filter = require('gulp-filter'); +import * as path from 'path'; +import * as cp from 'child_process'; +import * as fs from 'fs'; +import * as File from 'vinyl'; +import * as es from 'event-stream'; +import * as filter from 'gulp-filter'; +import { Stream } from 'stream'; -var watcherPath = path.join(__dirname, 'watcher.exe'); +const watcherPath = path.join(__dirname, 'watcher.exe'); -function toChangeType(type) { +function toChangeType(type: '0' | '1' | '2'): 'change' | 'add' | 'unlink' { switch (type) { case '0': return 'change'; case '1': return 'add'; @@ -20,33 +21,33 @@ function toChangeType(type) { } } -function watch(root) { - var result = es.through(); - var child = cp.spawn(watcherPath, [root]); +function watch(root: string): Stream { + const result = es.through(); + let child: cp.ChildProcess | null = cp.spawn(watcherPath, [root]); child.stdout.on('data', function (data) { - var lines = data.toString('utf8').split('\n'); - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim(); + const lines: string[] = data.toString('utf8').split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); if (line.length === 0) { continue; } - var changeType = line[0]; - var changePath = line.substr(2); + const changeType = <'0' | '1' | '2'>line[0]; + const changePath = line.substr(2); // filter as early as possible if (/^\.git/.test(changePath) || /(^|\\)out($|\\)/.test(changePath)) { continue; } - var changePathFull = path.join(root, changePath); + const changePathFull = path.join(root, changePath); - var file = new File({ + const file = new File({ path: changePathFull, base: root }); - file.event = toChangeType(changeType); + (file).event = toChangeType(changeType); result.emit('data', file); } }); @@ -62,44 +63,44 @@ function watch(root) { process.once('SIGTERM', function () { process.exit(0); }); process.once('SIGTERM', function () { process.exit(0); }); - process.once('exit', function () { child && child.kill(); }); + process.once('exit', function () { if (child) { child.kill(); } }); return result; } -var cache = Object.create(null); +const cache: { [cwd: string]: Stream; } = Object.create(null); -module.exports = function (pattern, options) { +module.exports = function (pattern: string | string[] | filter.FileFunction, options?: { cwd?: string; base?: string; }) { options = options || {}; - var cwd = path.normalize(options.cwd || process.cwd()); - var watcher = cache[cwd]; + const cwd = path.normalize(options.cwd || process.cwd()); + let watcher = cache[cwd]; if (!watcher) { watcher = cache[cwd] = watch(cwd); } - var rebase = !options.base ? es.through() : es.mapSync(function (f) { - f.base = options.base; + const rebase = !options.base ? es.through() : es.mapSync(function (f: File) { + f.base = options!.base!; return f; }); return watcher .pipe(filter(['**', '!.git{,/**}'])) // ignore all things git .pipe(filter(pattern)) - .pipe(es.map(function (file, cb) { + .pipe(es.map(function (file: File, cb) { fs.stat(file.path, function (err, stat) { - if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err && err.code === 'ENOENT') { return cb(undefined, file); } if (err) { return cb(); } if (!stat.isFile()) { return cb(); } fs.readFile(file.path, function (err, contents) { - if (err && err.code === 'ENOENT') { return cb(null, file); } + if (err && err.code === 'ENOENT') { return cb(undefined, file); } if (err) { return cb(); } file.contents = contents; file.stat = stat; - cb(null, file); + cb(undefined, file); }); }); })) From e7c5fbb21b15f7f4c945e8291f9a551bd490915e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 17:27:52 +0200 Subject: [PATCH 36/98] Towards fixing `.gitignore` (see #107356) --- build/.gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/.gitignore b/build/.gitignore index a6c7c2852d0..ade0096d6a1 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1 +1,5 @@ -*.js +azure-pipelines/**/*.js +builtin/**/*.js +darwin/**/*.js +lib/**/*.js +monaco/**/*.js From dd6bd3ae6a81ef8453ca52bcd06a47ca6124ba0a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 17:39:48 +0200 Subject: [PATCH 37/98] Reduce the number of folders where we use TS --- build/.gitignore | 2 -- build/gulpfile.editor.js | 2 +- build/lib/compilation.ts | 2 +- build/{monaco/api.ts => lib/monaco-api.ts} | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) rename build/{monaco/api.ts => lib/monaco-api.ts} (99%) diff --git a/build/.gitignore b/build/.gitignore index ade0096d6a1..01e8737ef58 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,5 +1,3 @@ azure-pipelines/**/*.js -builtin/**/*.js darwin/**/*.js lib/**/*.js -monaco/**/*.js diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 0c0e20d1fe6..aa0282bff57 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -14,7 +14,7 @@ const i18n = require('./lib/i18n'); const standalone = require('./lib/standalone'); const cp = require('child_process'); const compilation = require('./lib/compilation'); -const monacoapi = require('./monaco/api'); +const monacoapi = require('./lib/monaco-api'); const fs = require('fs'); const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 11a0c7f2da7..96e7d59ea16 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -12,7 +12,7 @@ import * as bom from 'gulp-bom'; import * as sourcemaps from 'gulp-sourcemaps'; import * as tsb from 'gulp-tsb'; import * as path from 'path'; -import * as monacodts from '../monaco/api'; +import * as monacodts from './monaco-api'; import * as nls from './nls'; import { createReporter } from './reporter'; import * as util from './util'; diff --git a/build/monaco/api.ts b/build/lib/monaco-api.ts similarity index 99% rename from build/monaco/api.ts rename to build/lib/monaco-api.ts index f3542988a4b..6604b14d91e 100644 --- a/build/monaco/api.ts +++ b/build/lib/monaco-api.ts @@ -14,7 +14,7 @@ const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); -export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); +export const RECIPE_PATH = path.join(__dirname, '../monaco/monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message: any, ...rest: any[]): void { From 22dddaf4f7fab532cbe7b380e27832d7976d1360 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 24 Sep 2020 18:03:38 +0200 Subject: [PATCH 38/98] Remove unnecessary interfaces --- src/vs/editor/common/modes.ts | 2 ++ src/vs/monaco.d.ts | 25 ------------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 216c988d066..2a16939f8f2 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -268,6 +268,7 @@ export interface HoverProvider { /** * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. + * @internal */ export interface EvaluatableExpression { /** @@ -283,6 +284,7 @@ export interface EvaluatableExpression { /** * The hover provider interface defines the contract between extensions and * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + * @internal */ export interface EvaluatableExpressionProvider { /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3d852d5b375..a44cb3bb307 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5413,31 +5413,6 @@ declare namespace monaco.languages { provideHover(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } - /** - * An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are - * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. - */ - export interface EvaluatableExpression { - /** - * The range to which this expression applies. - */ - range: IRange; - expression?: string; - } - - /** - * The hover provider interface defines the contract between extensions and - * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ - export interface EvaluatableExpressionProvider { - /** - * Provide a hover for the given position and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - */ - provideEvaluatableExpression(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; - } - export enum CompletionItemKind { Method = 0, Function = 1, From 9ab2cad7efbe7c839e9059a6ce683431f08352bf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Sep 2020 18:17:19 +0200 Subject: [PATCH 39/98] debt - use amd.ts in a few more places --- src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts | 3 ++- .../welcome/walkThrough/browser/editor/editorWalkThrough.ts | 4 ++-- .../browser/builtinExtensionsScannerService.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 2cd1558d4bf..21731a88c9a 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -46,6 +46,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; @@ -298,7 +299,7 @@ class WelcomePage extends Disposable { const recentlyOpened = this.workspacesService.getRecentlyOpened(); const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); - const resource = URI.parse(require.toUrl('./vs_code_welcome_page')) + const resource = getUriFromAmdModule(require, './vs_code_welcome_page') .with({ scheme: Schemas.walkThrough, query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page' }) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 54886cc6d0a..c02c5aadc85 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { URI } from 'vs/base/common/uri'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; @@ -17,7 +17,7 @@ const typeId = 'workbench.editors.walkThroughInput'; const inputOptions: WalkThroughInputOptions = { typeId, name: localize('editorWalkThrough.title', "Interactive Playground"), - resource: URI.parse(require.toUrl('./vs_code_editor_walkthrough.md')) + resource: getUriFromAmdModule(require, './vs_code_editor_walkthrough.md') .with({ scheme: Schemas.walkThrough, query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough' }) diff --git a/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts index 8262f12da29..cc3cc1b9ba3 100644 --- a/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts @@ -10,6 +10,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getUriFromAmdModule } from 'vs/base/common/amd'; interface IScannedBuiltinExtension { extensionPath: string; @@ -69,7 +70,7 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne enableBuiltinExtensions = environmentService.configuration.remoteAuthority ? false : true; } if (enableBuiltinExtensions) { - return URI.parse(require.toUrl('../../../../../../extensions')); + return getUriFromAmdModule(require, '../../../../../../extensions'); } return undefined; } From 7506b2ba5ae3ee1052bff44d2f5129a61312b37b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Sep 2020 18:29:25 +0200 Subject: [PATCH 40/98] :lipstick: more use of Schemas.file for easier reference --- src/vs/editor/contrib/links/links.ts | 2 +- .../platform/webview/electron-main/webviewProtocolProvider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index ba54876ebce..cbee654068f 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -298,7 +298,7 @@ export class LinkDetector implements IEditorContribution { // Support for relative file URIs of the shape file://./relativeFile.txt or file:///./relativeFile.txt if (typeof uri === 'string' && this.editor.hasModel()) { const modelUri = this.editor.getModel().uri; - if (modelUri.scheme === Schemas.file && uri.startsWith('file:')) { + if (modelUri.scheme === Schemas.file && uri.startsWith(`${Schemas.file}:`)) { const parsedUri = URI.parse(uri); if (parsedUri.scheme === Schemas.file) { const fsPath = resources.originalFSPath(parsedUri); diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts index 32fdd04cd28..77b451c9005 100644 --- a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts @@ -136,7 +136,7 @@ export class WebviewProtocolProvider extends Disposable { } else { url = require.toUrl(`vs/workbench/contrib/webview/browser/pre/${entry}`); } - return callback(decodeURIComponent(url.replace('file://', ''))); + return callback(decodeURIComponent(url.replace(`${Schemas.file}://`, ''))); } } catch { // noop From 591c3b5a93d4e7fce46a48ea729ca17e20c502ac Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Sep 2020 11:46:08 -0700 Subject: [PATCH 41/98] Don't cache webview view title Revert #105867 --- .../contrib/webviewView/browser/webviewViewPane.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index c4b7399bdff..4c0849a8821 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -30,7 +30,6 @@ declare const ResizeObserver: any; const storageKeys = { webviewState: 'webviewState', - title: 'title' } as const; export class WebviewViewPane extends ViewPane { @@ -71,11 +70,6 @@ export class WebviewViewPane extends ViewPane { this.memento = new Memento(`webviewView.${this.id}`, storageService); this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); - const storedTitle = this.viewState[storageKeys.title]; - if (typeof storedTitle === 'string') { - this.updateTitle(storedTitle); - } - this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); this.updateTreeVisibility(); } @@ -124,7 +118,6 @@ export class WebviewViewPane extends ViewPane { if (this._webview) { this.viewState[storageKeys.webviewState] = this._webview.state; } - this.viewState[storageKeys.title] = this.setTitle; this.memento.saveMemento(); super.saveState(); From f99ff58d30e518daa111e3ac595d1909d0afd131 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Sep 2020 12:37:58 -0700 Subject: [PATCH 42/98] Remove proposed check for webview views --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 2b9b8ad2c85..9a776d1e7a9 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -479,11 +479,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return null; } - if (type === ViewType.Webview && !extension.description.enableProposedApi) { - collector.error(localize('webviewViewsRequireProposed', "Webview views are proposed api and are only supported when running out of dev or with the following command line switch: --enable-proposed-api")); - return null; - } - const viewDescriptor = { type: type, ctorDescriptor: type === ViewType.Tree ? new SyncDescriptor(TreeViewPane) : new SyncDescriptor(WebviewViewPane), From 0945f076ce227a8a305077810dab91d81b45e444 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 14:11:29 -0700 Subject: [PATCH 43/98] re #103454. notebook/comment/find. --- .../codeEditor/browser/find/simpleFindReplaceWidget.ts | 4 ++-- .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 4 ++-- src/vs/workbench/contrib/comments/browser/commentNode.ts | 2 +- .../contrib/comments/browser/commentThreadWidget.ts | 4 ++-- .../contrib/notebook/browser/notebookEditorWidget.ts | 6 +++--- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ++-- .../contrib/notebook/browser/view/renderers/codeCell.ts | 2 +- .../contrib/notebook/browser/view/renderers/markdownCell.ts | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index 088c76bdb4b..493ff8ed8cb 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -402,13 +402,13 @@ export abstract class SimpleFindReplaceWidget extends Widget { public hide(): void { if (this._isVisible) { - dom.removeClass(this._domNode, 'visible-transition'); + this._domNode.classList.remove('visible-transition'); this._domNode.setAttribute('aria-hidden', 'true'); // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list setTimeout(() => { this._isVisible = false; this.updateButtons(this.foundMatch); - dom.removeClass(this._domNode, 'visible'); + this._domNode.classList.remove('visible'); }, 200); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index cb0d5ab2d5f..18f496c77e8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -235,13 +235,13 @@ export abstract class SimpleFindWidget extends Widget { public hide(): void { if (this._isVisible) { - dom.removeClass(this._innerDomNode, 'visible-transition'); + this._innerDomNode.classList.remove('visible-transition'); this._innerDomNode.setAttribute('aria-hidden', 'true'); // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list setTimeout(() => { this._isVisible = false; this.updateButtons(this.foundMatch); - dom.removeClass(this._innerDomNode, 'visible'); + this._innerDomNode.classList.remove('visible'); }, 200); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 490a1a53d87..06a8cf9b42e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -521,7 +521,7 @@ export class CommentNode extends Disposable { if (!this._clearTimeout) { dom.addClass(this.domNode, 'focus'); this._clearTimeout = setTimeout(() => { - dom.removeClass(this.domNode, 'focus'); + this.domNode.classList.remove('focus'); }, 3000); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index d9403b06d8c..f379c44b583 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -721,8 +721,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._disposables.add(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); this._commentEditor.onDidBlurEditorWidget(() => { - if (this._commentEditor.getModel()!.getValueLength() === 0 && this._commentForm.classList.add('expand')) { - dom.removeClass(this._commentForm, 'expand'); + if (this._commentEditor.getModel()!.getValueLength() === 0 && this._commentForm.classList.contains('expand')) { + this._commentForm.classList.remove('expand'); } }); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 5fdb4c30007..b803e39d664 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1771,15 +1771,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } toggleClassName(className: string) { - DOM.toggleClass(this._overlayContainer, className); + this._overlayContainer.classList.toggle(className); } addClassName(className: string) { - DOM.addClass(this._overlayContainer, className); + this._overlayContainer.classList.add(className); } removeClassName(className: string) { - DOM.removeClass(this._overlayContainer, className); + this._overlayContainer.classList.remove(className); } 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 a30bdf629dc..22e63d148ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -1083,9 +1083,9 @@ export class ListTopCellToolbar extends Disposable { private updateClass() { if (this.notebookEditor.viewModel?.length === 0) { - DOM.addClass(this.topCellToolbar, 'emptyNotebook'); + this.topCellToolbar.classList.add('emptyNotebook'); } else { - DOM.removeClass(this.topCellToolbar, 'emptyNotebook'); + this.topCellToolbar.classList.remove('emptyNotebook'); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 8b534f2df4d..e19e4c443a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -233,7 +233,7 @@ export class CodeCell extends Disposable { e.removed.forEach(options => { if (options.className) { - DOM.removeClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.remove(options.className); } if (options.outputClassName) { 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 51857bc9edc..28d27b1f1d2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -115,7 +115,7 @@ export class StatefulMarkdownCell extends Disposable { e.removed.forEach(options => { if (options.className) { - DOM.removeClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.remove(options.className); } }); })); From de97b2e96181579f1f2fd9ade45d1177805cd3de Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 14:14:29 -0700 Subject: [PATCH 44/98] re #103454. editor. --- .../editor/contrib/parameterHints/parameterHintsWidget.ts | 8 ++++---- .../browser/suggestEnabledInput/suggestEnabledInput.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index f5a4fc0cf3d..f3fb9078df3 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -152,7 +152,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.visible = true; setTimeout(() => { if (this.domNodes) { - dom.addClass(this.domNodes.element, 'visible'); + this.domNodes.element.classList.add('visible'); } }, 100); this.editor.layoutContentWidget(this); @@ -169,7 +169,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.visible = false; this.announcedLabel = null; if (this.domNodes) { - dom.removeClass(this.domNodes.element, 'visible'); + this.domNodes.element.classList.remove('visible'); } this.editor.layoutContentWidget(this); } @@ -225,7 +225,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { documentation.textContent = activeParameter.documentation; } else { const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(activeParameter.documentation)); - dom.addClass(renderedContents.element, 'markdown-docs'); + renderedContents.element.classList.add('markdown-docs'); documentation.appendChild(renderedContents.element); } dom.append(this.domNodes.docs, $('p', {}, documentation)); @@ -237,7 +237,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { dom.append(this.domNodes.docs, $('p', {}, signature.documentation)); } else { const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(signature.documentation)); - dom.addClass(renderedContents.element, 'markdown-docs'); + renderedContents.element.classList.add('markdown-docs'); dom.append(this.domNodes.docs, renderedContents.element); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 5e809ac197e..9ae1045463c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./suggestEnabledInput'; -import { $, Dimension, addClass, append, removeClass } from 'vs/base/browser/dom'; +import { $, Dimension, append } from 'vs/base/browser/dom'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -152,11 +152,11 @@ export class SuggestEnabledInput extends Widget implements IThemable { this._register((this.inputWidget.onDidFocusEditorText(() => { if (options.focusContextKey) { options.focusContextKey.set(true); } - addClass(this.stylingContainer, 'synthetic-focus'); + this.stylingContainer.classList.add('synthetic-focus'); }))); this._register((this.inputWidget.onDidBlurEditorText(() => { if (options.focusContextKey) { options.focusContextKey.set(false); } - removeClass(this.stylingContainer, 'synthetic-focus'); + this.stylingContainer.classList.remove('synthetic-focus'); }))); const onKeyDownMonaco = Event.chain(this.inputWidget.onKeyDown); From 87dbca4eb21faf156020640cba135a9024ec0fd2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 14:18:22 -0700 Subject: [PATCH 45/98] re #103454. simple find. --- .../codeEditor/browser/find/simpleFindReplaceWidget.ts | 9 +++------ .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 6 ++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index 493ff8ed8cb..c9866a9d8df 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -345,8 +345,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.updateButtons(this.foundMatch); setTimeout(() => { - dom.addClass(this._domNode, 'visible'); - dom.addClass(this._domNode, 'visible-transition'); + this._domNode.classList.add('visible', 'visible-transition'); this._domNode.setAttribute('aria-hidden', 'false'); this._findInput.select(); }, 0); @@ -364,8 +363,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._isVisible = true; setTimeout(() => { - dom.addClass(this._domNode, 'visible'); - dom.addClass(this._domNode, 'visible-transition'); + this._domNode.classList.add('visible', 'visible-transition'); this._domNode.setAttribute('aria-hidden', 'false'); this.focus(); @@ -391,8 +389,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { } setTimeout(() => { - dom.addClass(this._domNode, 'visible'); - dom.addClass(this._domNode, 'visible-transition'); + this._domNode.classList.add('visible', 'visible-transition'); this._domNode.setAttribute('aria-hidden', 'false'); this._updateButtons(); diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 18f496c77e8..e15e3ee78fa 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -212,8 +212,7 @@ export abstract class SimpleFindWidget extends Widget { this.updateButtons(this.foundMatch); setTimeout(() => { - dom.addClass(this._innerDomNode, 'visible'); - dom.addClass(this._innerDomNode, 'visible-transition'); + this._innerDomNode.classList.add('visible', 'visible-transition'); this._innerDomNode.setAttribute('aria-hidden', 'false'); this._findInput.select(); }, 0); @@ -227,8 +226,7 @@ export abstract class SimpleFindWidget extends Widget { this._isVisible = true; setTimeout(() => { - dom.addClass(this._innerDomNode, 'visible'); - dom.addClass(this._innerDomNode, 'visible-transition'); + this._innerDomNode.classList.add('visible', 'visible-transition'); this._innerDomNode.setAttribute('aria-hidden', 'false'); }, 0); } From 56cedb2ba7aa338fa7789486c346288ea27fb7ba Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 14:26:46 -0700 Subject: [PATCH 46/98] re #103454. comments/notebook --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 2 +- .../contrib/comments/browser/commentThreadWidget.ts | 4 ++-- src/vs/workbench/contrib/comments/browser/commentsView.ts | 2 +- .../notebook/browser/diff/notebookTextDiffEditor.ts | 3 +-- .../contrib/notebook/browser/notebookEditorWidget.ts | 7 +++---- .../notebook/browser/view/renderers/cellActionView.ts | 2 +- .../contrib/notebook/browser/view/renderers/codeCell.ts | 7 +++---- .../notebook/browser/view/renderers/markdownCell.ts | 4 ++-- 8 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 06a8cf9b42e..0a1ab70e9d6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -519,7 +519,7 @@ export class CommentNode extends Disposable { focus() { this.domNode.focus(); if (!this._clearTimeout) { - dom.addClass(this.domNode, 'focus'); + this.domNode.classList.add('focus'); this._clearTimeout = setTimeout(() => { this.domNode.classList.remove('focus'); }, 3000); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index f379c44b583..70e9bfcb51e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -553,7 +553,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentForm.classList.remove('expand'); this._commentEditor.getDomNode()!.style.outline = ''; this._error.textContent = ''; - dom.addClass(this._error, 'hidden'); + this._error.classList.add('hidden'); } } })); @@ -708,7 +708,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentForm.classList.remove('expand'); this._commentEditor.getDomNode()!.style.outline = ''; this._error.textContent = ''; - dom.addClass(this._error, 'hidden'); + this._error.classList.add('hidden'); } private createReplyButton() { diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index d1a8b91f43c..026a98b0696 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -60,7 +60,7 @@ export class CommentsPanel extends ViewPane { public renderBody(container: HTMLElement): void { super.renderBody(container); - dom.addClass(container, 'comments-panel'); + container.classList.add('comments-panel'); let domContainer = dom.append(container, dom.$('.comments-panel-container')); this.treeContainer = dom.append(domContainer, dom.$('.tree-container')); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 4fa9f6575c0..883003b2a20 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -81,8 +81,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); this._overflowContainer = document.createElement('div'); - DOM.addClass(this._overflowContainer, 'notebook-overflow-widget-container'); - DOM.addClass(this._overflowContainer, 'monaco-editor'); + this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor'); DOM.append(parent, this._overflowContainer); const renderer = this.instantiationService.createInstance(CellDiffRenderer, this); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index b803e39d664..df7cf772489 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -428,18 +428,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _createBody(parent: HTMLElement): void { this._body = document.createElement('div'); - DOM.addClass(this._body, 'cell-list-container'); + this._body.classList.add('cell-list-container'); this._createCellList(); DOM.append(parent, this._body); this._overflowContainer = document.createElement('div'); - DOM.addClass(this._overflowContainer, 'notebook-overflow-widget-container'); - DOM.addClass(this._overflowContainer, 'monaco-editor'); + this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor'); DOM.append(parent, this._overflowContainer); } private _createCellList(): void { - DOM.addClass(this._body, 'cell-list-container'); + this._body.classList.add('cell-list-container'); this._dndController = this._register(new CellDragAndDropController(this, this._body)); const getScopedContextKeyService = (container?: HTMLElement) => this._list!.contextKeyService.createScoped(container); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index 66ffa090a93..55fa6e24834 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -23,7 +23,7 @@ export class VerticalSeparator extends Action { export class VerticalSeparatorViewItem extends BaseActionViewItem { render(container: HTMLElement) { - DOM.addClass(container, 'verticalSeparator'); + container.classList.add('verticalSeparator'); // const iconContainer = DOM.append(container, $('.verticalSeparator')); // DOM.addClasses(iconContainer, 'codicon', 'codicon-chrome-minimize'); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index e19e4c443a4..ca6e519dfcd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -223,7 +223,7 @@ export class CodeCell extends Disposable { this._register(viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { - DOM.addClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.add(options.className); } if (options.outputClassName) { @@ -245,7 +245,7 @@ export class CodeCell extends Disposable { viewCell.getCellDecorations().forEach(options => { if (options.className) { - DOM.addClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.add(options.className); } if (options.outputClassName) { @@ -520,8 +520,7 @@ export class CodeCell extends Disposable { this.viewCell.selfSizeMonitoring = true; this.notebookEditor.createInset(this.viewCell, result as any, this.viewCell.getOutputOffset(index)); } else { - DOM.addClass(outputItemDiv, 'foreground'); - DOM.addClass(outputItemDiv, 'output-element'); + outputItemDiv.classList.add('foreground', 'output-element'); outputItemDiv.style.position = 'absolute'; } 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 28d27b1f1d2..8265869c361 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -109,7 +109,7 @@ export class StatefulMarkdownCell extends Disposable { this._register(viewCell.onCellDecorationsChanged((e) => { e.added.forEach(options => { if (options.className) { - DOM.addClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.add(options.className); } }); @@ -124,7 +124,7 @@ export class StatefulMarkdownCell extends Disposable { viewCell.getCellDecorations().forEach(options => { if (options.className) { - DOM.addClass(templateData.rootContainer, options.className); + templateData.rootContainer.classList.add(options.className); } }); From 737d2c3361f6410ac71e25cd54d4fbc63c35f606 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 14:40:34 -0700 Subject: [PATCH 47/98] re #103454. find widget, add classes. --- src/vs/editor/contrib/find/findWidget.ts | 8 ++++---- .../browser/view/output/transforms/errorTransform.ts | 5 ++--- .../notebook/browser/view/renderers/cellActionView.ts | 1 - .../contrib/notebook/browser/view/renderers/codeCell.ts | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 6d3a2b85362..71864ba97f7 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -1314,11 +1314,11 @@ export class SimpleButton extends Widget { public setExpanded(expanded: boolean): void { this._domNode.setAttribute('aria-expanded', String(!!expanded)); if (expanded) { - dom.removeClasses(this._domNode, findCollapsedIcon.classNames); - dom.addClasses(this._domNode, findExpandedIcon.classNames); + this._domNode.classList.remove(...findCollapsedIcon.classNames.split(' ')); + this._domNode.classList.add(...findExpandedIcon.classNames.split(' ')); } else { - dom.removeClasses(this._domNode, findExpandedIcon.classNames); - dom.addClasses(this._domNode, findCollapsedIcon.classNames); + this._domNode.classList.remove(...findExpandedIcon.classNames.split(' ')); + this._domNode.classList.add(...findCollapsedIcon.classNames.split(' ')); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts index 9e0c4ba4230..10a4839b4ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform.ts @@ -5,7 +5,6 @@ import { IRenderOutput, CellOutputKind, IErrorOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookRegistry } from 'vs/workbench/contrib/notebook/browser/notebookRegistry'; -import * as DOM from 'vs/base/browser/dom'; import { RGBA, Color } from 'vs/base/common/color'; import { ansiColorIdentifiers } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -28,14 +27,14 @@ class ErrorTransform implements IOutputTransformContribution { container.appendChild(header); } const traceback = document.createElement('pre'); - DOM.addClasses(traceback, 'traceback'); + traceback.classList.add('traceback'); if (output.traceback) { for (let j = 0; j < output.traceback.length; j++) { traceback.appendChild(handleANSIOutput(output.traceback[j], this.themeService)); } } container.appendChild(traceback); - DOM.addClasses(container, 'error'); + container.classList.add('error'); return { type: RenderOutputType.None, hasDynamicHeight: false }; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts index 55fa6e24834..2e698f1ed4a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as DOM from 'vs/base/browser/dom'; import { Action, IAction, Separator } from 'vs/base/common/actions'; import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index ca6e519dfcd..8d5dbd00054 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -459,7 +459,7 @@ export class CodeCell extends Disposable { if (transformedDisplayOutput.orderedMimeTypes!.length > 1) { outputItemDiv.style.position = 'relative'; const mimeTypePicker = DOM.$('.multi-mimetype-output'); - DOM.addClasses(mimeTypePicker, 'codicon', 'codicon-code'); + mimeTypePicker.classList.add('codicon', 'codicon-code'); mimeTypePicker.tabIndex = 0; mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", transformedDisplayOutput.orderedMimeTypes!.map(mimeType => mimeType.mimeType).join(', ')); outputItemDiv.appendChild(mimeTypePicker); From 97af4fb370aef8f7bccc74594dbfccd4cbf1458b Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 15:00:18 -0700 Subject: [PATCH 48/98] re #103454. notebook/comments/find remove toggle. --- .../codeEditor/browser/find/simpleFindReplaceWidget.ts | 2 +- src/vs/workbench/contrib/comments/browser/commentsView.ts | 6 +++--- .../contrib/notebook/browser/notebookEditorWidget.ts | 4 ++-- .../browser/view/output/transforms/richTransform.ts | 4 ++-- .../contrib/notebook/browser/view/renderers/cellRenderer.ts | 4 ++-- .../contrib/notebook/browser/view/renderers/codeCell.ts | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts index c9866a9d8df..ad05b40ac5b 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -314,7 +314,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); - dom.toggleClass(this._domNode, 'replaceToggled', this._isReplaceVisible); + this._domNode.classList.toggle('replaceToggled', this._isReplaceVisible); this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); } diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index 026a98b0696..3f061246334 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -113,7 +113,7 @@ export class CommentsPanel extends ViewPane { } private async renderComments(): Promise { - dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads()); + this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); await this.tree.setInput(this.commentsModel); this.renderMessage(); } @@ -144,7 +144,7 @@ export class CommentsPanel extends ViewPane { private renderMessage(): void { this.messageBox.textContent = this.commentsModel.getMessage(); - dom.toggleClass(this.messageBoxContainer, 'hidden', this.commentsModel.hasCommentThreads()); + this.messageBoxContainer.classList.toggle('hidden', this.commentsModel.hasCommentThreads()); } private createTree(): void { @@ -237,7 +237,7 @@ export class CommentsPanel extends ViewPane { this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); } - dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads()); + this.treeContainer.classList.toggle('hidden', !this.commentsModel.hasCommentThreads()); this.tree.updateChildren().then(() => { this.renderMessage(); }, (e) => { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index df7cf772489..bb8ac657a0e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -861,8 +861,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const notebookMetadata = this.viewModel!.metadata; this._editorEditable?.set(!!notebookMetadata?.editable); this._editorRunnable?.set(!!notebookMetadata?.runnable); - DOM.toggleClass(this._overlayContainer, 'notebook-editor-editable', !!notebookMetadata?.editable); - DOM.toggleClass(this.getDomNode(), 'notebook-editor-editable', !!notebookMetadata?.editable); + this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); + this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable); this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index b7276425242..067515c53ad 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -186,7 +186,7 @@ class RichRenderer implements IOutputTransformContribution { const image = document.createElement('img'); image.src = `data:image/png;base64,${output.data['image/png']}`; const display = document.createElement('div'); - DOM.addClasses(display, 'display'); + display.classList.add('display'); display.appendChild(image); container.appendChild(display); return { type: RenderOutputType.None, hasDynamicHeight: true }; @@ -196,7 +196,7 @@ class RichRenderer implements IOutputTransformContribution { const image = document.createElement('img'); image.src = `data:image/jpeg;base64,${output.data['image/jpeg']}`; const display = document.createElement('div'); - DOM.addClasses(display, 'display'); + display.classList.add('display'); display.appendChild(image); container.appendChild(display); return { type: RenderOutputType.None, hasDynamicHeight: true }; 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 22e63d148ba..da6c7d26b13 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -785,7 +785,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende private updateForMetadata(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel!.notebookDocument.metadata); - DOM.toggleClass(templateData.container, 'runnable', !!metadata.runnable); + templateData.container.classList.toggle('runnable', !!metadata.runnable); this.updateExecutionOrder(metadata, templateData); templateData.statusBar.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; @@ -826,7 +826,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForHover(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { - DOM.toggleClass(templateData.container, 'cell-output-hover', element.outputIsHovered); + templateData.container.classList.toggle('cell-output-hover', element.outputIsHovered); } private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 8d5dbd00054..fb720668009 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -84,7 +84,7 @@ export class CodeCell extends Disposable { templateData.editor?.focus(); } - DOM.toggleClass(templateData.container, 'cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); + templateData.container.classList.toggle('cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor); }; const updateForCollapseState = () => { this.viewUpdate(); From 42e97f3c0ae8f8fe9bdac66135827ee7caa6f70d Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 15:10:37 -0700 Subject: [PATCH 49/98] fix #107003. --- src/vs/workbench/api/common/extHostNotebook.ts | 2 +- .../contrib/notebook/browser/notebookEditorWidget.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 4d88c7e7942..61f8bce094d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -349,7 +349,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN .map(pattern => typeConverters.NotebookExclusiveDocumentPattern.from(pattern)) .filter(pattern => pattern !== undefined) as (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; - if (!viewOptionsFilenamePattern) { + if (options?.viewOptions?.filenamePattern && !viewOptionsFilenamePattern) { console.warn(`Notebook content provider view options file name pattern is invalid ${options?.viewOptions?.filenamePattern}`); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index bb8ac657a0e..cc091698839 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -815,7 +815,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor || kernelsFromSameExtension.find(kernel => kernel.id === cachedKernelId) || kernelsFromSameExtension[0]; this.activeKernel = preferedKernel; - await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + if (this.activeKernel) { + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + } if (tokenSource.token.isCancellationRequested) { return; From ddc98c3f620ab1d2ecad5942ebf4736060403d67 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 15:19:31 -0700 Subject: [PATCH 50/98] add requirejs global var in rendering webview. --- .../browser/view/renderers/backLayerWebView.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index f10ef127e49..c210fc526d0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -368,7 +368,11 @@ export class BackLayerWebView extends Disposable { const baseUrl = asWebviewUri(this.environmentService, this.id, dirname(this.documentUri)); if (!isWeb) { - coreDependencies = ``; + coreDependencies = ``; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); this.initialize(htmlContent); resolveFunc!(); @@ -384,6 +388,11 @@ export class BackLayerWebView extends Disposable { + `; const htmlContent = this.generateContent(CELL_OUTPUT_PADDING, coreDependencies, baseUrl.toString()); From 5484453ae2f3a57769580c549d22ef1d2bcd493b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Sep 2020 21:04:30 -0700 Subject: [PATCH 51/98] Update to latest TS nigthly for building VS Code --- build/package.json | 2 +- build/yarn.lock | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build/package.json b/build/package.json index c55703abc59..7561ebc958c 100644 --- a/build/package.json +++ b/build/package.json @@ -45,7 +45,7 @@ "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "typescript": "^4.1.0-dev.20200918", + "typescript": "^4.1.0-dev.20200924", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index df3a7e60123..3cc284f20dc 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2535,10 +2535,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.1.0-dev.20200918: - version "4.1.0-dev.20200918" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200918.tgz#b00beedf2da8dbba09284085114e77bf8f329686" - integrity sha512-cEcXJvz55OH3k3cmpMYoVfqdAQ2YvgeccUmccmleUnQ8VqR8T/7GI761Qky/vGZO/VhiU3Y8xJF3oLkAkNrG1g== +typescript@^4.1.0-dev.20200924: + version "4.1.0-dev.20200924" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200924.tgz#d8b2aaa6f94ec22725eafcadf0b9a17aae9c32b9" + integrity sha512-AXwqVrp2AeVZ3jaZ/gcvxb0nnvqEbDFuFFjvV5/9wfcyz7KZx5KvyJENUgGoJHywCvl1PHKasQKYjzjk1QixnQ== typical@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index 1b17c439468..4f4877172c8 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "style-loader": "^1.0.0", "ts-loader": "^4.4.2", "tsec": "googleinterns/tsec", - "typescript": "^4.1.0-dev.20200918", + "typescript": "^4.1.0-dev.20200924", "typescript-formatter": "7.1.0", "underscore": "^1.8.2", "vinyl": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index 0b8a37f3147..562461edf1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9264,10 +9264,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.1.0-dev.20200918: - version "4.1.0-dev.20200918" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200918.tgz#b00beedf2da8dbba09284085114e77bf8f329686" - integrity sha512-cEcXJvz55OH3k3cmpMYoVfqdAQ2YvgeccUmccmleUnQ8VqR8T/7GI761Qky/vGZO/VhiU3Y8xJF3oLkAkNrG1g== +typescript@^4.1.0-dev.20200924: + version "4.1.0-dev.20200924" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200924.tgz#d8b2aaa6f94ec22725eafcadf0b9a17aae9c32b9" + integrity sha512-AXwqVrp2AeVZ3jaZ/gcvxb0nnvqEbDFuFFjvV5/9wfcyz7KZx5KvyJENUgGoJHywCvl1PHKasQKYjzjk1QixnQ== uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" From 528b473dba12e696213c747f3b710981cd32d30d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 08:53:37 +0200 Subject: [PATCH 52/98] debt - use network.Scheme.file in more places --- src/vs/code/electron-main/window.ts | 3 ++- src/vs/platform/diagnostics/node/diagnosticsService.ts | 5 +++-- src/vs/workbench/contrib/debug/common/debugUtils.ts | 3 ++- .../extensions/electron-browser/runtimeExtensionsEditor.ts | 2 +- src/vs/workbench/contrib/search/common/searchModel.ts | 7 ++++--- .../workbench/contrib/tasks/browser/terminalTaskSystem.ts | 2 +- .../contrib/terminal/browser/links/terminalLinkManager.ts | 2 +- .../terminal/browser/links/terminalProtocolLinkProvider.ts | 3 ++- .../workbench/contrib/terminal/browser/terminalActions.ts | 2 +- .../contrib/themes/browser/themes.test.contribution.ts | 3 ++- src/vs/workbench/contrib/timeline/browser/timelinePane.ts | 2 +- src/vs/workbench/services/path/common/pathService.ts | 4 ++-- src/vs/workbench/services/search/common/searchService.ts | 6 +++--- .../workbench/services/search/common/textSearchManager.ts | 3 ++- src/vs/workbench/test/browser/workbenchTestServices.ts | 4 ++-- 15 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 1de5a852e06..17dcfd5a97b 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -36,6 +36,7 @@ import { IStorageMainService } from 'vs/platform/storage/node/storageMainService import { IFileService } from 'vs/platform/files/common/files'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { Schemas } from 'vs/base/common/network'; export interface IWindowCreationOptions { state: IWindowState; @@ -1291,7 +1292,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] { const segments: ITouchBarSegment[] = items.map(item => { let icon: NativeImage | undefined; - if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === 'file') { + if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === Schemas.file) { icon = nativeImage.createFromPath(URI.revive(item.icon.dark).fsPath); if (icon.isEmpty()) { icon = undefined; diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index b441b1f0e98..b6f87cc08e9 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -17,6 +17,7 @@ import { IMainProcessInfo } from 'vs/platform/launch/node/launch'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Iterable } from 'vs/base/common/iterator'; +import { Schemas } from 'vs/base/common/network'; export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); @@ -450,7 +451,7 @@ export class DiagnosticsService implements IDiagnosticsService { window.folderURIs.forEach(uriComponents => { const folderUri = URI.revive(uriComponents); - if (folderUri.scheme === 'file') { + if (folderUri.scheme === Schemas.file) { const folder = folderUri.fsPath; workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(stats => { let countMessage = `${stats.fileCount} files`; @@ -518,7 +519,7 @@ export class DiagnosticsService implements IDiagnosticsService { public async reportWorkspaceStats(workspace: IWorkspaceInformation): Promise { for (const { uri } of workspace.folders) { const folderUri = URI.revive(uri); - if (folderUri.scheme !== 'file') { + if (folderUri.scheme !== Schemas.file) { continue; } diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 7e7e887a60e..9d4fa4ce1ef 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -8,6 +8,7 @@ import { IDebuggerContribution, IDebugSession, IConfigPresentation } from 'vs/wo import { URI as uri } from 'vs/base/common/uri'; import { isAbsolute } from 'vs/base/common/path'; import { deepClone } from 'vs/base/common/objects'; +import { Schemas } from 'vs/base/common/network'; const _formatPIIRegexp = /{([^}]+)}/g; @@ -145,7 +146,7 @@ function uriToString(source: PathContainer): string | undefined { if (typeof source.path === 'object') { const u = uri.revive(source.path); if (u) { - if (u.scheme === 'file') { + if (u.scheme === Schemas.file) { return u.fsPath; } else { return u.toString(); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 5b1e7777be3..1ce0f3a7aaa 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -424,7 +424,7 @@ export class RuntimeExtensionsEditor extends EditorPane { data.msgContainer.appendChild(el); } - if (element.description.extensionLocation.scheme !== 'file') { + if (element.description.extensionLocation.scheme !== Schemas.file) { const el = $('span', undefined, ...renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`)); data.msgContainer.appendChild(el); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index e6c24ce8689..0c6e0d19457 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -30,6 +30,7 @@ import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { compareFileNames, compareFileExtensions, comparePaths } from 'vs/base/common/comparers'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; +import { Schemas } from 'vs/base/common/network'; export class Match { @@ -1106,9 +1107,9 @@ export class SearchModel extends Disposable { const stats = completed && completed.stats as ITextSearchStats; - const fileSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme === 'file'); - const otherSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme !== 'file'); - const scheme = fileSchemeOnly ? 'file' : + const fileSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme === Schemas.file); + const otherSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme !== Schemas.file); + const scheme = fileSchemeOnly ? Schemas.file : otherSchemeOnly ? 'other' : 'mixed'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index d0bf98e6663..a73e2f7d2f3 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1075,7 +1075,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (options.cwd) { let cwd = options.cwd; if (!path.isAbsolute(cwd)) { - if (workspaceFolder && (workspaceFolder.uri.scheme === 'file')) { + if (workspaceFolder && (workspaceFolder.uri.scheme === Schemas.file)) { cwd = path.join(workspaceFolder.uri.fsPath, cwd); } } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index c3b990985fe..c2a23641883 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -191,7 +191,7 @@ export class TerminalLinkManager extends DisposableStore { // Check if it's a file:/// link, hand off to local link handler so to open an editor and // respect line/col attachment const uri = URI.parse(link); - if (uri.scheme === 'file') { + if (uri.scheme === Schemas.file) { this._handleLocalLink(uri.fsPath); return; } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts index 92a756b5ab9..69251bcd511 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalProtocolLinkProvider.ts @@ -10,6 +10,7 @@ import { TerminalLink, OPEN_FILE_LABEL } from 'vs/workbench/contrib/terminal/bro import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { TerminalBaseLinkProvider } from 'vs/workbench/contrib/terminal/browser/links/terminalBaseLinkProvider'; +import { Schemas } from 'vs/base/common/network'; export class TerminalProtocolLinkProvider extends TerminalBaseLinkProvider { private _linkComputerTarget: ILinkComputerTarget | undefined; @@ -51,7 +52,7 @@ export class TerminalProtocolLinkProvider extends TerminalBaseLinkProvider { const uri = link.url ? (typeof link.url === 'string' ? URI.parse(link.url) : link.url) : undefined; - const label = (uri?.scheme === 'file') ? OPEN_FILE_LABEL : undefined; + const label = (uri?.scheme === Schemas.file) ? OPEN_FILE_LABEL : undefined; return this._instantiationService.createInstance(TerminalLink, this._xterm, range, link.url?.toString() || '', this._xterm.buffer.active.viewportY, this._activateCallback, this._tooltipCallback, true, label); }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 363c3a00542..002bfbabf9c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -692,7 +692,7 @@ export function registerTerminalActions() { } const uri = editor.getModel().uri; - if (uri.scheme !== 'file') { + if (uri.scheme !== Schemas.file) { notificationService.warn(localize('workbench.action.terminal.runActiveFile.noFile', 'Only files on disk can be run in the terminal')); return; } diff --git a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts index 56d449979f5..ec65ff33089 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts @@ -17,6 +17,7 @@ import { ThemeRule, findMatchingThemeRule } from 'vs/workbench/services/textMate import { Color } from 'vs/base/common/color'; import { IFileService } from 'vs/platform/files/common/files'; import { basename } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; interface IToken { c: string; @@ -244,7 +245,7 @@ CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (acc if (!resource) { const editorService = accessor.get(IEditorService); - const file = editorService.activeEditor ? toResource(editorService.activeEditor, { filterByScheme: 'file' }) : null; + const file = editorService.activeEditor ? toResource(editorService.activeEditor, { filterByScheme: Schemas.file }) : null; if (file) { process(file).then(result => { console.log(result); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 5eab72c696f..8b451fb7281 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -352,7 +352,7 @@ export class TimelinePane extends ViewPane { if ((uri?.toString(true) === this.uri?.toString(true) && uri !== undefined) || // Fallback to match on fsPath if we are dealing with files or git schemes - (uri?.fsPath === this.uri?.fsPath && (uri?.scheme === 'file' || uri?.scheme === 'git') && (this.uri?.scheme === 'file' || this.uri?.scheme === 'git'))) { + (uri?.fsPath === this.uri?.fsPath && (uri?.scheme === Schemas.file || uri?.scheme === 'git') && (this.uri?.scheme === Schemas.file || this.uri?.scheme === 'git'))) { // If the uri hasn't changed, make sure we have valid caches for (const source of this.timelineService.getSources()) { diff --git a/src/vs/workbench/services/path/common/pathService.ts b/src/vs/workbench/services/path/common/pathService.ts index 851d03fb66c..66220d241c3 100644 --- a/src/vs/workbench/services/path/common/pathService.ts +++ b/src/vs/workbench/services/path/common/pathService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Schemas } from 'vs/base/common/network'; import { IPath, win32, posix } from 'vs/base/common/path'; import { OperatingSystem, OS } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -132,9 +133,8 @@ export abstract class AbstractPathService implements IPathService { } } - // return new _URI('file', authority, path, '', ''); return URI.from({ - scheme: 'file', + scheme: Schemas.file, authority, path: _path, query: '', diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index cb047264d05..2a6c5dc58ab 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -282,9 +282,9 @@ export class SearchService extends Disposable implements ISearchService { } private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: SearchError): void { - const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); - const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); - const scheme = fileSchemeOnly ? 'file' : + const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === Schemas.file); + const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== Schemas.file); + const scheme = fileSchemeOnly ? Schemas.file : otherSchemeOnly ? 'other' : 'mixed'; diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index 4913240ba05..03daa687908 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -13,6 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search'; import { TextSearchProvider, TextSearchResult, TextSearchMatch, TextSearchComplete, Range, TextSearchOptions, TextSearchQuery } from 'vs/workbench/services/search/common/searchExtTypes'; import { nextTick } from 'vs/base/common/process'; +import { Schemas } from 'vs/base/common/network'; export interface IFileUtils { readdir: (resource: URI) => Promise; @@ -117,7 +118,7 @@ export class TextSearchManager { return; } - const hasSibling = folderQuery.folder.scheme === 'file' ? + const hasSibling = folderQuery.folder.scheme === Schemas.file ? glob.hasSiblingPromiseFn(() => { return this.fileUtils.readdir(resources.dirname(result.uri)); }) : diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index a6acd6fcaf5..3527de197e4 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -852,10 +852,10 @@ export class TestFileService implements IFileService { } activateProvider(_scheme: string): Promise { throw new Error('not implemented'); } - canHandleResource(resource: URI): boolean { return resource.scheme === 'file' || this.providers.has(resource.scheme); } + canHandleResource(resource: URI): boolean { return resource.scheme === Schemas.file || this.providers.has(resource.scheme); } listCapabilities() { return [ - { scheme: 'file', capabilities: FileSystemProviderCapabilities.FileOpenReadWriteClose }, + { scheme: Schemas.file, capabilities: FileSystemProviderCapabilities.FileOpenReadWriteClose }, ...Iterable.map(this.providers, ([scheme, p]) => { return { scheme, capabilities: p.capabilities }; }) ]; } From dc194d005e9c97682d9276919134d340af687454 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 09:29:42 +0200 Subject: [PATCH 53/98] download service - add comment for scheme restrictions --- src/vs/platform/download/common/downloadService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/download/common/downloadService.ts b/src/vs/platform/download/common/downloadService.ts index 6442bbd301c..839e877fbe8 100644 --- a/src/vs/platform/download/common/downloadService.ts +++ b/src/vs/platform/download/common/downloadService.ts @@ -21,6 +21,7 @@ export class DownloadService implements IDownloadService { async download(resource: URI, target: URI, cancellationToken: CancellationToken = CancellationToken.None): Promise { if (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote) { + // Intentionally only support this for file|remote<->file|remote scenarios await this.fileService.copy(resource, target); return; } From 68f3e05005b3aa6f47ced29504210b04a3c113ee Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 24 Sep 2020 18:01:29 +0200 Subject: [PATCH 54/98] update references viewlet --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 96307afc126..f6943f3fca8 100644 --- a/product.json +++ b/product.json @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.63", + "version": "0.0.64", "repo": "https://github.com/microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From 1e1f14f59bcc36d8a7fcf37f1cbb31b55f313b72 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Sep 2020 09:45:26 +0200 Subject: [PATCH 55/98] debug issues to isidor --- .github/classifier.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/classifier.json b/.github/classifier.json index 33c179d3237..4e9b695a0c1 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vscode-github-triage-actions/master/classifier-deep/apply/apply-labels/deep-classifier-config.schema.json", - "vacation": ["joaomoreno"], + "vacation": ["joaomoreno"], "assignees": { "JacksonKearl": {"accuracy": 0.5} }, @@ -20,8 +20,8 @@ "context-keys": {"assign": []}, "css-less-scss": {"assign": ["aeschli"]}, "custom-editors": {"assign": ["mjbvz"]}, - "debug": {"assign": ["weinand"]}, - "debug-console": {"assign": ["weinand"]}, + "debug": {"assign": ["isidorn"]}, + "debug-console": {"assign": ["isidorn"]}, "dialogs": {"assign": ["sbatten"]}, "diff-editor": {"assign": []}, "dropdown": {"assign": []}, From 1fd7ee15d3a2c50547228b08cefc7c13a5a739d2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Sep 2020 10:03:45 +0200 Subject: [PATCH 56/98] move API to final, https://github.com/microsoft/vscode/issues/106410 --- src/vs/vscode.d.ts | 18 +++++++++++++++++- src/vs/vscode.proposed.d.ts | 24 ------------------------ 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 4186a551efc..8c190ce7ef2 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2221,7 +2221,7 @@ declare module 'vscode' { * * A code action can be any command that is [known](#commands.getCommands) to the system. */ - export interface CodeActionProvider { + export interface CodeActionProvider { /** * Provide commands for the given document and range. * @@ -2234,6 +2234,22 @@ declare module 'vscode' { * signaled by returning `undefined`, `null`, or an empty array. */ provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | CodeAction)[]>; + + /** + * Given a code action fill in its [`edit`](#CodeAction.edit)-property. Changes to + * all other properties, like title, are ignored. A code action that has an edit + * will not be resolved. + * + * *Note* that a code action provider that returns commands, not code actions, cannot successfully + * implement this function. Returning commands is deprecated and instead code actions should be + * returned. + * + * @param codeAction A code action. + * @param token A cancellation token. + * @return The resolved code action or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7bd4e8fc480..ae1b9cd29ed 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,30 +16,6 @@ declare module 'vscode' { - //#region https://github.com/microsoft/vscode/issues/106410 - - export interface CodeActionProvider { - - /** - * Given a code action fill in its [`edit`](#CodeAction.edit)-property, changes to - * all other properties, like title, are ignored. A code action that has an edit - * will not be resolved. - * - * *Note* that a code action provider that returns commands, not code actions, cannot successfully - * implement this function. Returning commands is deprecated and instead code actions should be - * returned. - * - * @param codeAction A code action. - * @param token A cancellation token. - * @return The resolved code action or a thenable that resolve to such. It is OK to return the given - * `item`. When no result is returned, the given `item` will be used. - */ - resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; - } - - //#endregion - - // #region auth provider: https://github.com/microsoft/vscode/issues/88309 /** From f57ac767b7d4668fb7cac96336c626c32f42b7b4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Sep 2020 10:09:32 +0200 Subject: [PATCH 57/98] create workspace extension config service --- .../browser/configBasedRecommendations.ts | 2 +- .../dynamicWorkspaceRecommendations.ts | 2 +- .../browser/exeBasedRecommendations.ts | 2 +- .../browser/experimentalRecommendations.ts | 2 +- .../extensions/browser/extensionEditor.ts | 2 +- .../browser/extensionRecommendations.ts | 3 +- .../extensionRecommendationsService.ts | 2 +- .../browser/extensions.contribution.ts | 3 +- .../extensions/browser/extensionsActions.ts | 6 +- .../extensions/browser/extensionsViews.ts | 3 +- .../extensions/browser/extensionsWidgets.ts | 3 +- .../browser/fileBasedRecommendations.ts | 3 +- .../browser/keymapRecommendations.ts | 2 +- .../browser/workspaceRecommendations.ts | 69 +++------------ .../contrib/extensions/common/extensions.ts | 2 - .../extensions/common/extensionsUtils.ts | 3 +- .../extensionRecommendationsService.test.ts | 2 + .../extensionsActions.test.ts | 3 +- .../electron-browser/extensionsViews.test.ts | 3 +- .../extensionsWorkbenchService.test.ts | 3 +- .../welcome/page/browser/welcomePage.ts | 3 +- .../sandbox.simpleservices.ts | 3 +- .../common/extensionManagement.ts | 60 ------------- .../common/extensionRecommendations.ts | 67 +++++++++++++++ .../common/workspaceExtensionsConfig.ts | 85 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 1 + 26 files changed, 199 insertions(+), 140 deletions(-) create mode 100644 src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts create mode 100644 src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index cfba57e4db3..e08baf5c4f5 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -6,7 +6,7 @@ import { IExtensionTipsService, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { localize } from 'vs/nls'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { Emitter } from 'vs/base/common/event'; diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index b12536d0c79..7c48c79d3ae 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -12,7 +12,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { isNumber } from 'vs/base/common/types'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; type DynamicWorkspaceRecommendationsClassification = { diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 6da238fbe74..c62466be53f 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -10,7 +10,7 @@ import { timeout } from 'vs/base/common/async'; import { localize } from 'vs/nls'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { basename } from 'vs/base/common/path'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; type ExeExtensionRecommendationsClassification = { diff --git a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts index bb8f96b0ce0..97c717ab21d 100644 --- a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts @@ -5,7 +5,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; export class ExperimentalRecommendations extends ExtensionRecommendations { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5f73348ed0b..81328757acc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -19,7 +19,7 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 931fe61cb3f..4677ab5d4af 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -9,7 +9,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { EnablementState, ExtensionRecommendationSource, IExtensionRecommendationReson, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionsConfiguration, ConfigurationKey, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index c3d8687c295..6a791f2db94 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index d24527bf41a..655685298ff 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -9,7 +9,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsLocalizedLabel, ExtensionsChannelId, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServerService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index af53a50dcd4..12a8fb36063 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -12,10 +12,11 @@ import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { dispose, Disposable } from 'vs/base/common/lifecycle'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -60,6 +61,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { Codicon } from 'vs/base/common/codicons'; import { IViewsService } from 'vs/workbench/common/views'; import { IActionViewItemOptions, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { EXTENSIONS_CONFIG } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; export function toExtensionDescription(local: ILocalExtension): IExtensionDescription { return { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 2cc1ddbebc0..68ca52c7099 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -9,7 +9,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; import { SortBy, SortOrder, IQueryOptions, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionRecommendation, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 63fcc8f02b2..330e56ed82c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -9,7 +9,8 @@ import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs import { append, $ } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; -import { IExtensionRecommendationsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 3fb5088647e..cf23079b9ac 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -6,7 +6,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { ExtensionRecommendationSource, ExtensionRecommendationReason, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts index d405c0c2a17..64b573051ad 100644 --- a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts @@ -5,7 +5,7 @@ import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; export class KeymapRecommendations extends ExtensionRecommendations { diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 7cc5c62b510..a0f8b182d33 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -4,18 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; -import { IFileService } from 'vs/platform/files/common/files'; -import { distinct, flatten, coalesce } from 'vs/base/common/arrays'; +import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; +import { distinct, flatten } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionsConfigContent, ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { parse } from 'vs/base/common/json'; -import { EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsConfigContent, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; +import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; export class WorkspaceRecommendations extends ExtensionRecommendations { @@ -31,9 +29,9 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { constructor( promptedExtensionRecommendations: PromptedExtensionRecommendations, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, - @IFileService private readonly fileService: IFileService, @INotificationService private readonly notificationService: INotificationService, ) { super(promptedExtensionRecommendations); @@ -49,26 +47,26 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { */ private async fetch(): Promise { - const extensionsConfigBySource = await this.fetchExtensionsConfigBySource(); + const extensionsConfigs = await this.workpsaceExtensionsConfigService.getExtensionsConfigs(); - const { invalidRecommendations, message } = await this.validateExtensions(extensionsConfigBySource.map(({ contents }) => contents)); + const { invalidRecommendations, message } = await this.validateExtensions(extensionsConfigs); if (invalidRecommendations.length) { this.notificationService.warn(`The ${invalidRecommendations.length} extension(s) below, in workspace recommendations have issues:\n${message}`); } this._ignoredRecommendations = []; - for (const extensionsConfig of extensionsConfigBySource) { - for (const unwantedRecommendation of extensionsConfig.contents.unwantedRecommendations) { + for (const extensionsConfig of extensionsConfigs) { + for (const unwantedRecommendation of extensionsConfig.unwantedRecommendations) { if (invalidRecommendations.indexOf(unwantedRecommendation) === -1) { this._ignoredRecommendations.push(unwantedRecommendation); } } - for (const extensionId of extensionsConfig.contents.recommendations) { + for (const extensionId of extensionsConfig.recommendations) { if (invalidRecommendations.indexOf(extensionId) === -1) { this._recommendations.push({ extensionId, - source: extensionsConfig.source, + source: this.contextService.getWorkspace(), reason: { reasonId: ExtensionRecommendationReason.Workspace, reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") @@ -79,41 +77,6 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } } - private async fetchExtensionsConfigBySource(): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }[]> { - const workspace = this.contextService.getWorkspace(); - const result = await Promise.all([ - this.resolveWorkspaceExtensionConfig(workspace), - ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)) - ]); - return coalesce(result); - } - - private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource } | null> { - try { - if (workspace.configuration) { - const content = await this.fileService.readFile(workspace.configuration); - const extensionsConfigContent = parse(content.value.toString())['extensions']; - const contents = this.parseExtensionConfig(extensionsConfigContent); - if (contents) { - return { contents, source: workspace }; - } - } - } catch (e) { /* Ignore */ } - return null; - } - - private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource } | null> { - try { - const content = await this.fileService.readFile(workspaceFolder.toResource(EXTENSIONS_CONFIG)); - const extensionsConfigContent = parse(content.value.toString()); - const contents = this.parseExtensionConfig(extensionsConfigContent); - if (contents) { - return { contents, source: workspaceFolder }; - } - } catch (e) { /* ignore */ } - return null; - } - private async validateExtensions(contents: IExtensionsConfigContent[]): Promise<{ validRecommendations: string[], invalidRecommendations: string[], message: string }> { const validExtensions: string[] = []; @@ -165,15 +128,5 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { } } - private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent | undefined): IExtensionsConfigContent | null { - if (extensionsConfigContent) { - return { - recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), - unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) - }; - } - return null; - } - } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 43b3cb97ead..7714f206f0a 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -17,8 +17,6 @@ import { IViewPaneContainer } from 'vs/workbench/common/views'; export const VIEWLET_ID = 'workbench.view.extensions'; -export const EXTENSIONS_CONFIG = '.vscode/extensions.json'; - export interface IExtensionsViewPaneContainer extends IViewPaneContainer { search(text: string, refresh?: boolean): void; } diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 9270ebca61f..448c7da4d7a 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -9,7 +9,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 5b8b04d4014..c65d660e802 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -58,6 +58,7 @@ import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspac import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkpsaceExtensionsConfigService, WorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -301,6 +302,7 @@ suite('ExtensionRecommendationsService Test', () => { const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); workspaceService = new TestContextService(myWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IWorkpsaceExtensionsConfigService, instantiationService.createInstance(WorkspaceExtensionsConfigService)); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); instantiationService.stub(IFileService, fileService); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 5b42be70e22..39e0dc6c87b 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -12,7 +12,8 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 2fe53b1048a..78ae96e3bbd 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -13,7 +13,8 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 989e89d8f3a..74a838c980f 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -13,7 +13,8 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 21731a88c9a..76f716679d0 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -23,7 +23,8 @@ import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 52ad26825ab..4dfc0b02a9d 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -38,7 +38,8 @@ import { isWindows, OS } from 'vs/base/common/platform'; import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; -import { ExtensionRecommendationReason, IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ITunnelProvider, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index e581583a0cd..74e63ab3126 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -5,11 +5,8 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { URI } from 'vs/base/common/uri'; import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IStringDictionary } from 'vs/base/common/collections'; export const IExtensionManagementServerService = createDecorator('extensionManagementServerService'); @@ -88,63 +85,6 @@ export interface IWorkbenchExtensionEnablementService { setEnablement(extensions: IExtension[], state: EnablementState): Promise; } -export interface IExtensionsConfigContent { - recommendations: string[]; - unwantedRecommendations: string[]; -} - -export type RecommendationChangeNotification = { - extensionId: string, - isRecommended: boolean -}; - -export type DynamicRecommendation = 'dynamic'; -export type ConfigRecommendation = 'config'; -export type ExecutableRecommendation = 'executable'; -export type CachedRecommendation = 'cached'; -export type ApplicationRecommendation = 'application'; -export type ExperimentalRecommendation = 'experimental'; -export type ExtensionRecommendationSource = IWorkspace | IWorkspaceFolder | URI | DynamicRecommendation | ExecutableRecommendation | CachedRecommendation | ApplicationRecommendation | ExperimentalRecommendation | ConfigRecommendation; - -export interface IExtensionRecommendation { - extensionId: string; - sources: ExtensionRecommendationSource[]; -} - -export const enum ExtensionRecommendationReason { - Workspace, - File, - Executable, - WorkspaceConfig, - DynamicWorkspace, - Experimental, - Application, -} - -export interface IExtensionRecommendationReson { - reasonId: ExtensionRecommendationReason; - reasonText: string; -} - -export const IExtensionRecommendationsService = createDecorator('extensionRecommendationsService'); - -export interface IExtensionRecommendationsService { - readonly _serviceBrand: undefined; - - getAllRecommendationsWithReason(): IStringDictionary; - getImportantRecommendations(): Promise; - getOtherRecommendations(): Promise; - getFileBasedRecommendations(): IExtensionRecommendation[]; - getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; - getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; - getWorkspaceRecommendations(): Promise; - getKeymapRecommendations(): IExtensionRecommendation[]; - - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void; - getIgnoredRecommendations(): ReadonlyArray; - onRecommendationChange: Event; -} - export const IWebExtensionsScannerService = createDecorator('IWebExtensionsScannerService'); export interface IWebExtensionsScannerService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts new file mode 100644 index 00000000000..9407b050b24 --- /dev/null +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; +import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IStringDictionary } from 'vs/base/common/collections'; + +export interface IExtensionsConfigContent { + recommendations: string[]; + unwantedRecommendations: string[]; +} + +export type RecommendationChangeNotification = { + extensionId: string, + isRecommended: boolean +}; + +export type DynamicRecommendation = 'dynamic'; +export type ConfigRecommendation = 'config'; +export type ExecutableRecommendation = 'executable'; +export type CachedRecommendation = 'cached'; +export type ApplicationRecommendation = 'application'; +export type ExperimentalRecommendation = 'experimental'; +export type ExtensionRecommendationSource = IWorkspace | IWorkspaceFolder | URI | DynamicRecommendation | ExecutableRecommendation | CachedRecommendation | ApplicationRecommendation | ExperimentalRecommendation | ConfigRecommendation; + +export interface IExtensionRecommendation { + extensionId: string; + sources: ExtensionRecommendationSource[]; +} + +export const enum ExtensionRecommendationReason { + Workspace, + File, + Executable, + WorkspaceConfig, + DynamicWorkspace, + Experimental, + Application, +} + +export interface IExtensionRecommendationReson { + reasonId: ExtensionRecommendationReason; + reasonText: string; +} + +export const IExtensionRecommendationsService = createDecorator('extensionRecommendationsService'); + +export interface IExtensionRecommendationsService { + readonly _serviceBrand: undefined; + + getAllRecommendationsWithReason(): IStringDictionary; + getImportantRecommendations(): Promise; + getOtherRecommendations(): Promise; + getFileBasedRecommendations(): IExtensionRecommendation[]; + getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; + getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; + getWorkspaceRecommendations(): Promise; + getKeymapRecommendations(): IExtensionRecommendation[]; + + toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void; + getIgnoredRecommendations(): ReadonlyArray; + onRecommendationChange: Event; +} diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts new file mode 100644 index 00000000000..8ef8e6c3ebf --- /dev/null +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { parse } from 'vs/base/common/json'; +import { IFileService } from 'vs/platform/files/common/files'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; + +export const EXTENSIONS_CONFIG = '.vscode/extensions.json'; + +export interface IExtensionsConfigContent { + recommendations: string[]; + unwantedRecommendations: string[]; +} + +export const IWorkpsaceExtensionsConfigService = createDecorator('IWorkpsaceExtensionsConfigService'); + +export interface IWorkpsaceExtensionsConfigService { + readonly _serviceBrand: undefined; + + getExtensionsConfigs(): Promise; + getUnwantedRecommendations(): Promise; + +} + +export class WorkspaceExtensionsConfigService implements IWorkpsaceExtensionsConfigService { + + declare readonly _serviceBrand: undefined; + + constructor( + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IFileService private readonly fileService: IFileService, + ) { } + + async getExtensionsConfigs(): Promise { + const workspace = this.workspaceContextService.getWorkspace(); + const result = await Promise.all([ + this.resolveWorkspaceExtensionConfig(workspace), + ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder)) + ]); + return coalesce(result); + } + + async getUnwantedRecommendations(): Promise { + const configs = await this.getExtensionsConfigs(); + return distinct(flatten(configs.map(c => c.unwantedRecommendations))); + } + + private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { + try { + if (workspace.configuration) { + const content = await this.fileService.readFile(workspace.configuration); + const extensionsConfigContent = parse(content.value.toString())['extensions']; + return this.parseExtensionConfig(extensionsConfigContent); + } + } catch (e) { /* Ignore */ } + return null; + } + + private async resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { + try { + const content = await this.fileService.readFile(workspaceFolder.toResource(EXTENSIONS_CONFIG)); + const extensionsConfigContent = parse(content.value.toString()); + return this.parseExtensionConfig(extensionsConfigContent); + } catch (e) { /* ignore */ } + return null; + } + + private parseExtensionConfig(extensionsConfigContent: IExtensionsConfigContent | undefined): IExtensionsConfigContent | null { + if (extensionsConfigContent) { + return { + recommendations: distinct((extensionsConfigContent.recommendations || []).map(e => e.toLowerCase())), + unwantedRecommendations: distinct((extensionsConfigContent.unwantedRecommendations || []).map(e => e.toLowerCase())) + }; + } + return null; + } + +} + +registerSingleton(IWorkpsaceExtensionsConfigService, WorkspaceExtensionsConfigService); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index d6c14031280..be98cac2d73 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -75,6 +75,7 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensionManagement/common/webExtensionsScannerService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import 'vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService'; +import 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; import 'vs/workbench/services/remote/common/remoteExplorerService'; From c0ae73464a9722419a7461eca6fdf2280f9126d2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 25 Sep 2020 10:24:40 +0200 Subject: [PATCH 58/98] clean up - remove unused source prop --- .../browser/configBasedRecommendations.ts | 1 - .../dynamicWorkspaceRecommendations.ts | 1 - .../browser/exeBasedRecommendations.ts | 1 - .../browser/experimentalRecommendations.ts | 1 - .../browser/extensionRecommendations.ts | 3 +- .../extensionRecommendationsService.ts | 33 +++++++------------ .../extensions/browser/extensionsViews.ts | 29 ++++++++-------- .../browser/fileBasedRecommendations.ts | 26 ++++++--------- .../browser/keymapRecommendations.ts | 1 - .../browser/workspaceRecommendations.ts | 1 - .../extensions/common/extensionsUtils.ts | 2 +- .../extensionRecommendationsService.test.ts | 14 ++++---- .../electron-browser/extensionsViews.test.ts | 20 +++++------ .../sandbox.simpleservices.ts | 8 ----- .../common/extensionRecommendations.ts | 22 ++++--------- 15 files changed, 62 insertions(+), 101 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index e08baf5c4f5..e159ddea7a5 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -73,7 +73,6 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { private toExtensionRecommendation(tip: IConfigBasedExtensionTip): ExtensionRecommendation { return { extensionId: tip.extensionId, - source: 'config', reason: { reasonId: ExtensionRecommendationReason.WorkspaceConfig, reasonText: localize('exeBasedRecommendation', "This extension is recommended because of the current workspace configuration") diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index 7c48c79d3ae..7a3c0c2f784 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -107,7 +107,6 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { private toExtensionRecommendation(extensionId: string, folder: IWorkspaceFolder): ExtensionRecommendation { return { extensionId: extensionId.toLowerCase(), - source: 'dynamic', reason: { reasonId: ExtensionRecommendationReason.DynamicWorkspace, reasonText: localize('dynamicWorkspaceRecommendation', "This extension may interest you because it's popular among users of the {0} repository.", folder.name) diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index c62466be53f..220d24318aa 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -151,7 +151,6 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { private toExtensionRecommendation(tip: IExecutableBasedExtensionTip): ExtensionRecommendation { return { extensionId: tip.extensionId.toLowerCase(), - source: 'executable', reason: { reasonId: ExtensionRecommendationReason.Executable, reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", tip.exeFriendlyName || basename(tip.windowsPath!)) diff --git a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts index 97c717ab21d..16dd2fa4e81 100644 --- a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts @@ -29,7 +29,6 @@ export class ExperimentalRecommendations extends ExtensionRecommendations { if (state === ExperimentState.Run && isNonEmptyArray(action?.properties?.recommendations) && action?.properties?.recommendationReason) { action.properties.recommendations.forEach((extensionId: string) => this._recommendations.push({ extensionId: extensionId.toLowerCase(), - source: 'experimental', reason: { reasonId: ExtensionRecommendationReason.Experimental, reasonText: action.properties.recommendationReason diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index 4677ab5d4af..d8f649fdf7c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -10,7 +10,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { localize } from 'vs/nls'; import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionRecommendationSource, IExtensionRecommendationReson } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionRecommendationReson } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionsConfiguration, ConfigurationKey, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -35,7 +35,6 @@ const choiceNever = localize('neverShowAgain', "Don't Show Again"); export type ExtensionRecommendation = { readonly extensionId: string, - readonly source: ExtensionRecommendationSource; readonly reason: IExtensionRecommendationReson; }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 6a791f2db94..d49c86b3cf2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -151,7 +151,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return output; } - async getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }> { + async getConfigBasedRecommendations(): Promise<{ important: string[], others: string[] }> { await this.configBasedRecommendations.activate(); return { important: this.toExtensionRecommendations(this.configBasedRecommendations.importantRecommendations), @@ -159,7 +159,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte }; } - async getOtherRecommendations(): Promise { + async getOtherRecommendations(): Promise { await this.activateProactiveRecommendations(); const recommendations = [ @@ -174,13 +174,10 @@ export class ExtensionRecommendationsService extends Disposable implements IExte shuffle(extensionIds, this.sessionSeed); - return extensionIds.map(extensionId => { - const sources: ExtensionRecommendationSource[] = distinct(recommendations.filter(r => r.extensionId === extensionId).map(r => r.source)); - return ({ extensionId, sources }); - }); + return extensionIds; } - async getImportantRecommendations(): Promise { + async getImportantRecommendations(): Promise { await this.activateProactiveRecommendations(); const recommendations = [ @@ -194,17 +191,14 @@ export class ExtensionRecommendationsService extends Disposable implements IExte shuffle(extensionIds, this.sessionSeed); - return extensionIds.map(extensionId => { - const sources: ExtensionRecommendationSource[] = distinct(recommendations.filter(r => r.extensionId === extensionId).map(r => r.source)); - return ({ extensionId, sources }); - }); + return extensionIds; } - getKeymapRecommendations(): IExtensionRecommendation[] { + getKeymapRecommendations(): string[] { return this.toExtensionRecommendations(this.keymapRecommendations.recommendations); } - async getWorkspaceRecommendations(): Promise { + async getWorkspaceRecommendations(): Promise { if (!this.isEnabled()) { return []; } @@ -212,14 +206,14 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return this.toExtensionRecommendations(this.workspaceRecommendations.recommendations); } - async getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }> { + async getExeBasedRecommendations(exe?: string): Promise<{ important: string[], others: string[] }> { await this.exeBasedRecommendations.activate(); const { important, others } = exe ? this.exeBasedRecommendations.getRecommendations(exe) : { important: this.exeBasedRecommendations.importantRecommendations, others: this.exeBasedRecommendations.otherRecommendations }; return { important: this.toExtensionRecommendations(important), others: this.toExtensionRecommendations(others) }; } - getFileBasedRecommendations(): IExtensionRecommendation[] { + getFileBasedRecommendations(): string[] { return this.toExtensionRecommendations(this.fileBasedRecommendations.recommendations); } @@ -264,14 +258,11 @@ export class ExtensionRecommendationsService extends Disposable implements IExte } } - private toExtensionRecommendations(recommendations: ReadonlyArray): IExtensionRecommendation[] { + private toExtensionRecommendations(recommendations: ReadonlyArray): string[] { const extensionIds = distinct(recommendations.map(e => e.extensionId)) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); - return extensionIds.map(extensionId => { - const sources: ExtensionRecommendationSource[] = distinct(recommendations.filter(r => r.extensionId === extensionId).map(r => r.source)); - return ({ extensionId, sources }); - }); + return extensionIds; } private isExtensionAllowedToBeRecommended(id: string): boolean { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 68ca52c7099..936a75ab483 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -10,7 +10,7 @@ import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; import { SortBy, SortOrder, IQueryOptions, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -593,11 +593,10 @@ export class ExtensionsListView extends ViewPane { return new PagedModel([]); } - protected async getInstallableRecommendations(recommendations: IExtensionRecommendation[], options: IQueryOptions, token: CancellationToken): Promise { + protected async getInstallableRecommendations(recommendations: string[], options: IQueryOptions, token: CancellationToken): Promise { const extensions: IExtension[] = []; if (recommendations.length) { - const names = recommendations.map(({ extensionId }) => extensionId); - const pager = await this.extensionsWorkbenchService.queryGallery({ ...options, names, pageSize: names.length }, token); + const pager = await this.extensionsWorkbenchService.queryGallery({ ...options, names: recommendations, pageSize: recommendations.length }, token); for (const extension of pager.firstPage) { if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { extensions.push(extension); @@ -607,11 +606,11 @@ export class ExtensionsListView extends ViewPane { return extensions; } - protected async getWorkspaceRecommendations(): Promise { + protected async getWorkspaceRecommendations(): Promise { const recommendations = await this.extensionRecommendationsService.getWorkspaceRecommendations(); const { important } = await this.extensionRecommendationsService.getConfigBasedRecommendations(); for (const configBasedRecommendation of important) { - if (!recommendations.find(r => r.extensionId === configBasedRecommendation.extensionId)) { + if (!recommendations.find(extensionId => extensionId === configBasedRecommendation)) { recommendations.push(configBasedRecommendation); } } @@ -624,7 +623,7 @@ export class ExtensionsListView extends ViewPane { const installableRecommendations = (await this.getInstallableRecommendations(recommendations, { ...options, source: 'recommendations-workspace' }, token)) .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); this.telemetryService.publicLog2<{ count: number }, WorkspaceRecommendationsClassification>('extensionWorkspaceRecommendations:open', { count: installableRecommendations.length }); - const result: IExtension[] = coalesce(recommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + const result: IExtension[] = coalesce(recommendations.map(id => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); return new PagedModel(result); } @@ -650,7 +649,7 @@ export class ExtensionsListView extends ViewPane { .filter(e => e.type === ExtensionType.User) .map(e => e.identifier.id.toLowerCase()); const workspaceRecommendations = (await this.getWorkspaceRecommendations()) - .map(r => r.extensionId.toLowerCase()); + .map(extensionId => extensionId.toLowerCase()); const otherRecommendations = distinct( flatten(await Promise.all([ @@ -658,13 +657,13 @@ export class ExtensionsListView extends ViewPane { this.extensionRecommendationsService.getImportantRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() - ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) - ), r => r.extensionId.toLowerCase()); + ])).filter(extensionId => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) + ), extensionId => extensionId.toLowerCase()); const installableRecommendations = (await this.getInstallableRecommendations(otherRecommendations, { ...options, source: 'recommendations-other', sortBy: undefined }, token)) .filter(extension => extension.identifier.id.toLowerCase().indexOf(value) > -1); - const result: IExtension[] = coalesce(otherRecommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + const result: IExtension[] = coalesce(otherRecommendations.map(id => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); return new PagedModel(result); } @@ -681,11 +680,11 @@ export class ExtensionsListView extends ViewPane { this.extensionRecommendationsService.getImportantRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() - ])).filter(({ extensionId }) => !local.includes(extensionId.toLowerCase()) - ), r => r.extensionId.toLowerCase()); + ])).filter(extensionId => !local.includes(extensionId.toLowerCase()) + ), extensionId => extensionId.toLowerCase()); const installableRecommendations = await this.getInstallableRecommendations(allRecommendations, { ...options, source: 'recommendations-all', sortBy: undefined }, token); - const result: IExtension[] = coalesce(allRecommendations.map(({ extensionId: id }) => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); + const result: IExtension[] = coalesce(allRecommendations.map(id => installableRecommendations.find(i => areSameExtensions(i.identifier, { id })))); return new PagedModel(result.slice(0, 8)); } @@ -1031,7 +1030,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { const installed = (await this.extensionsWorkbenchService.queryLocal()) .filter(l => l.enablementState !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind const recommendations = (await this.getWorkspaceRecommendations()) - .filter(({ extensionId }) => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); + .filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); return this.getInstallableRecommendations(recommendations, { source: 'install-all-workspace-recommendations' }, CancellationToken.None); } diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index cf23079b9ac..b66f07f64be 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -7,7 +7,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionRecommendationSource, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; @@ -42,7 +42,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private readonly fileBasedRecommendationsByPattern = new Map(); private readonly fileBasedRecommendationsByLanguage = new Map(); - private readonly fileBasedRecommendations = new Map(); + private readonly fileBasedRecommendations = new Map(); private readonly processedFileExtensions: string[] = []; private readonly processedLanguages: string[] = []; @@ -61,16 +61,13 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return this.fileBasedRecommendations.get(a)!.recommendedTime > this.fileBasedRecommendations.get(b)!.recommendedTime ? -1 : 1; }) .forEach(extensionId => { - for (const source of this.fileBasedRecommendations.get(extensionId)!.sources) { - recommendations.push({ - extensionId, - source, - reason: { - reasonId: ExtensionRecommendationReason.File, - reasonText: localize('fileBasedRecommendation', "This extension is recommended based on the files you recently opened.") - } - }); - } + recommendations.push({ + extensionId, + reason: { + reasonId: ExtensionRecommendationReason.File, + reasonText: localize('fileBasedRecommendation', "This extension is recommended based on the files you recently opened.") + } + }); }); return recommendations; } @@ -139,7 +136,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { forEach(cachedRecommendations, ({ key, value }) => { const diff = (now - value) / milliSecondsInADay; if (diff <= 7 && allRecommendations.indexOf(key) > -1) { - this.fileBasedRecommendations.set(key.toLowerCase(), { recommendedTime: value, sources: ['cached'] }); + this.fileBasedRecommendations.set(key.toLowerCase(), { recommendedTime: value }); } }); @@ -202,9 +199,6 @@ export class FileBasedRecommendations extends ExtensionRecommendations { for (const recommendation of fileBasedRecommendations) { const filedBasedRecommendation = this.fileBasedRecommendations.get(recommendation) || { recommendedTime: Date.now(), sources: [] }; filedBasedRecommendation.recommendedTime = Date.now(); - if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { - filedBasedRecommendation.sources.push(uri); - } this.fileBasedRecommendations.set(recommendation, filedBasedRecommendation); } diff --git a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts index 64b573051ad..d40178620d3 100644 --- a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts @@ -23,7 +23,6 @@ export class KeymapRecommendations extends ExtensionRecommendations { if (this.productService.keymapExtensionTips) { this._recommendations = this.productService.keymapExtensionTips.map(extensionId => ({ extensionId: extensionId.toLowerCase(), - source: 'application', reason: { reasonId: ExtensionRecommendationReason.Application, reasonText: '' diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index a0f8b182d33..0a8d055a3ad 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -66,7 +66,6 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { if (invalidRecommendations.indexOf(extensionId) === -1) { this._recommendations.push({ extensionId, - source: this.contextService.getWorkspace(), reason: { reasonId: ExtensionRecommendationReason.Workspace, reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 448c7da4d7a..6caca15aa7b 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -124,5 +124,5 @@ export async function getInstalledExtensions(accessor: ServicesAccessor): Promis export function isKeymapExtension(tipsService: IExtensionRecommendationsService, extension: IExtensionStatus): boolean { const cats = extension.local.manifest.categories; - return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(({ extensionId }) => areSameExtensions({ id: extensionId }, extension.local.identifier)); + return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(extensionId => areSameExtensions({ id: extensionId }, extension.local.identifier)); } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index c65d660e802..4f968a8c129 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -508,9 +508,9 @@ suite('ExtensionRecommendationsService Test', () => { return testObject.activationPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.some(extensionId => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.some(extensionId => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips + assert.ok(recommendations.every(extensionId => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips }); }); }); @@ -527,10 +527,10 @@ suite('ExtensionRecommendationsService Test', () => { return testObject.activationPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips - assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week + assert.ok(recommendations.some(extensionId => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.some(extensionId => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips + assert.ok(recommendations.every(extensionId => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.every(extensionId => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week }); }); }); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 78ae96e3bbd..f3db71a0425 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -14,7 +14,7 @@ import { DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; @@ -122,28 +122,28 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionRecommendationsService, >{ getWorkspaceRecommendations() { return Promise.resolve([ - { extensionId: workspaceRecommendationA.identifier.id }, - { extensionId: workspaceRecommendationB.identifier.id }]); + workspaceRecommendationA.identifier.id, + workspaceRecommendationB.identifier.id]); }, getConfigBasedRecommendations() { return Promise.resolve({ - important: [{ extensionId: configBasedRecommendationA.identifier.id }], - others: [{ extensionId: configBasedRecommendationB.identifier.id }], + important: [configBasedRecommendationA.identifier.id], + others: [configBasedRecommendationB.identifier.id], }); }, - getImportantRecommendations(): Promise { + getImportantRecommendations(): Promise { return Promise.resolve([]); }, getFileBasedRecommendations() { return [ - { extensionId: fileBasedRecommendationA.identifier.id }, - { extensionId: fileBasedRecommendationB.identifier.id } + fileBasedRecommendationA.identifier.id, + fileBasedRecommendationB.identifier.id ]; }, getOtherRecommendations() { return Promise.resolve([ - { extensionId: configBasedRecommendationB.identifier.id }, - { extensionId: otherRecommendationA.identifier.id } + configBasedRecommendationB.identifier.id, + otherRecommendationA.identifier.id ]); }, getAllRecommendationsWithReason() { diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 4dfc0b02a9d..ff37f7fb4d6 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -39,7 +39,6 @@ import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensio import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ITunnelProvider, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; @@ -766,13 +765,6 @@ class SimpleExtensionTipsService implements IExtensionTipsService { onRecommendationChange = Event.None; - getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } { return Object.create(null); } - getFileBasedRecommendations(): IExtensionRecommendation[] { return []; } - async getOtherRecommendations(): Promise { return []; } - async getWorkspaceRecommendations(): Promise { return []; } - getKeymapRecommendations(): IExtensionRecommendation[] { return []; } - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void { } - getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } { return Object.create(null); } async getConfigBasedTips(folder: URI): Promise { return []; } async getImportantExecutableBasedTips(): Promise { return []; } async getOtherExecutableBasedTips(): Promise { return []; } diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts index 9407b050b24..921a9ab8b2f 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -5,8 +5,6 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { URI } from 'vs/base/common/uri'; -import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IStringDictionary } from 'vs/base/common/collections'; export interface IExtensionsConfigContent { @@ -25,12 +23,6 @@ export type ExecutableRecommendation = 'executable'; export type CachedRecommendation = 'cached'; export type ApplicationRecommendation = 'application'; export type ExperimentalRecommendation = 'experimental'; -export type ExtensionRecommendationSource = IWorkspace | IWorkspaceFolder | URI | DynamicRecommendation | ExecutableRecommendation | CachedRecommendation | ApplicationRecommendation | ExperimentalRecommendation | ConfigRecommendation; - -export interface IExtensionRecommendation { - extensionId: string; - sources: ExtensionRecommendationSource[]; -} export const enum ExtensionRecommendationReason { Workspace, @@ -53,13 +45,13 @@ export interface IExtensionRecommendationsService { readonly _serviceBrand: undefined; getAllRecommendationsWithReason(): IStringDictionary; - getImportantRecommendations(): Promise; - getOtherRecommendations(): Promise; - getFileBasedRecommendations(): IExtensionRecommendation[]; - getExeBasedRecommendations(exe?: string): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; - getConfigBasedRecommendations(): Promise<{ important: IExtensionRecommendation[], others: IExtensionRecommendation[] }>; - getWorkspaceRecommendations(): Promise; - getKeymapRecommendations(): IExtensionRecommendation[]; + getImportantRecommendations(): Promise; + getOtherRecommendations(): Promise; + getFileBasedRecommendations(): string[]; + getExeBasedRecommendations(exe?: string): Promise<{ important: string[], others: string[] }>; + getConfigBasedRecommendations(): Promise<{ important: string[], others: string[] }>; + getWorkspaceRecommendations(): Promise; + getKeymapRecommendations(): string[]; toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void; getIgnoredRecommendations(): ReadonlyArray; From d014eb12807b7ab06662c78698052b8686555ab2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Sep 2020 01:26:53 -0700 Subject: [PATCH 59/98] Update formatting for new TS version --- .../browser/userDataSyncWorkbenchService.ts | 8 ++-- .../electron-browser/userDataSyncService.ts | 46 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index f55d666cf75..415ec7d3230 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -749,10 +749,10 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview { const newResources = this.toUserDataSyncResourceGroups( (this.manualSync?.preview || []) .map(([syncResource, syncResourcePreview]) => - ([ - syncResource, - syncResourcePreview.resourcePreviews - ])) + ([ + syncResource, + syncResourcePreview.resourcePreviews + ])) ); if (!equals(newResources, this._resources, (a, b) => isEqual(a.local, b.local) && a.mergeState === b.mergeState)) { this._resources = newResources; diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index 638cda9d60d..b4729eccf2e 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -136,16 +136,16 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private async updateConflicts(conflicts: [SyncResource, IResourcePreview[]][]): Promise { // Revive URIs this._conflicts = conflicts.map(([syncResource, conflicts]) => - ([ - syncResource, - conflicts.map(r => - ({ - ...r, - localResource: URI.revive(r.localResource), - remoteResource: URI.revive(r.remoteResource), - previewResource: URI.revive(r.previewResource), - })) - ])); + ([ + syncResource, + conflicts.map(r => + ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + })) + ])); this._onDidChangeConflicts.fire(this._conflicts); } @@ -239,19 +239,19 @@ class ManualSyncTask implements IManualSyncTask { private deserializePreviews(previews: [SyncResource, ISyncResourcePreview][]): [SyncResource, ISyncResourcePreview][] { return previews.map(([syncResource, preview]) => - ([ - syncResource, - { - isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, - resourcePreviews: preview.resourcePreviews.map(r => ({ - ...r, - localResource: URI.revive(r.localResource), - remoteResource: URI.revive(r.remoteResource), - previewResource: URI.revive(r.previewResource), - acceptedResource: URI.revive(r.acceptedResource), - })) - } - ])); + ([ + syncResource, + { + isLastSyncFromCurrentMachine: preview.isLastSyncFromCurrentMachine, + resourcePreviews: preview.resourcePreviews.map(r => ({ + ...r, + localResource: URI.revive(r.localResource), + remoteResource: URI.revive(r.remoteResource), + previewResource: URI.revive(r.previewResource), + acceptedResource: URI.revive(r.acceptedResource), + })) + } + ])); } } From ea78dd7b9c0ce378b84365407b496c654ff4ebac Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Sep 2020 10:34:13 +0200 Subject: [PATCH 60/98] fix https://github.com/microsoft/vscode/issues/107379 --- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index a5f1d7312a0..a7d058fee42 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -71,7 +71,7 @@ class CreateOperation implements IFileOperation { return new Noop(); // not overwriting, but ignoring, and the target file exists } await this._workingCopyFileService.create(this.newUri, this.contents, { overwrite: this.options.overwrite }); - return this._instaService.createInstance(DeleteOperation, this.newUri, this.options); + return this._instaService.createInstance(DeleteOperation, this.newUri, this.options, true); } } @@ -80,6 +80,7 @@ class DeleteOperation implements IFileOperation { constructor( readonly oldUri: URI, readonly options: WorkspaceFileEditOptions, + private readonly _undoesCreateOperation: boolean, @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IFileService private readonly _fileService: IFileService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -101,10 +102,12 @@ class DeleteOperation implements IFileOperation { } let contents: VSBuffer | undefined; - try { - contents = (await this._fileService.readFile(this.oldUri)).value; - } catch (err) { - this._logService.critical(err); + if (!this._undoesCreateOperation) { + try { + contents = (await this._fileService.readFile(this.oldUri)).value; + } catch (err) { + this._logService.critical(err); + } } const useTrash = this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash) && this._configurationService.getValue('files.enableTrash'); @@ -166,7 +169,7 @@ export class BulkFileEdits { op = this._instaService.createInstance(RenameOperation, edit.newResource, edit.oldResource, options); } else if (!edit.newResource && edit.oldResource) { // delete file - op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options); + op = this._instaService.createInstance(DeleteOperation, edit.oldResource, options, false); } else if (edit.newResource && !edit.oldResource) { // create file op = this._instaService.createInstance(CreateOperation, edit.newResource, options, undefined); From 47ed486348d3ebcadfba89f63097c3d26923cb68 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 25 Sep 2020 11:03:40 +0200 Subject: [PATCH 61/98] explorer: improve aria-level computation of compact folders #107235 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- .../files/browser/views/explorerViewer.ts | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9201e668832..db296ffd300 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -186,7 +186,7 @@ function asListOptions(modelProvider: () => ITreeModel options.accessibilityProvider!.getWidgetRole!() : () => 'tree', - getAriaLevel(node) { + getAriaLevel: options.accessibilityProvider && options.accessibilityProvider.getAriaLevel ? (node) => options.accessibilityProvider!.getAriaLevel!(node.element) : (node) => { return node.depth; }, getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index e06fc13446c..e9f6e0d339d 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -255,7 +255,8 @@ export class FilesRenderer implements ICompressibleTreeRenderer(); this.configListener = this.configurationService.onDidChangeConfiguration(e => { @@ -491,6 +492,22 @@ export class FilesRenderer implements ICompressibleTreeRenderer Date: Fri, 25 Sep 2020 11:35:29 +0200 Subject: [PATCH 62/98] fixes #107235 --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index e9f6e0d339d..bb47af156f8 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -163,10 +163,12 @@ export class CompressedNavigationController implements ICompressedNavigationCont private updateLabels(templateData: IFileTemplateData): void { this._labels = Array.from(templateData.container.querySelectorAll('.label-name')) as HTMLElement[]; - + let parents = ''; for (let i = 0; i < this.labels.length; i++) { - this.labels[i].setAttribute('aria-label', this.items[i].name); + const ariaLabel = parents.length ? `${this.items[i].name}, compact, ${parents}` : this.items[i].name; + this.labels[i].setAttribute('aria-label', ariaLabel); this.labels[i].setAttribute('aria-level', `${this.depth + i}`); + parents = parents.length ? `${this.items[i].name} ${parents}` : this.items[i].name; } this.updateCollapsed(this.collapsed); From dc9ef0d110a818a3d60c5275de1722452ad2c293 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 25 Sep 2020 11:45:55 +0200 Subject: [PATCH 63/98] Make sure the renderer received `$onWillActivateExtension` before proceeding to load extension code --- .../api/browser/mainThreadExtensionService.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostExtensionService.ts | 11 +++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts index e0b3986200d..5d0132e2989 100644 --- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts @@ -50,7 +50,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha $activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { return this._extensionService._activateById(extensionId, reason); } - $onWillActivateExtension(extensionId: ExtensionIdentifier): void { + async $onWillActivateExtension(extensionId: ExtensionIdentifier): Promise { this._extensionService._onWillActivateExtension(extensionId); } $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 3106330c6b5..d8c45c2afbf 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -830,7 +830,7 @@ export interface MainThreadTaskShape extends IDisposable { export interface MainThreadExtensionServiceShape extends IDisposable { $activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; - $onWillActivateExtension(extensionId: ExtensionIdentifier): void; + $onWillActivateExtension(extensionId: ExtensionIdentifier): Promise; $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; $onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise; $onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 33b062b329c..53d3612cc5d 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -303,8 +303,15 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme // --- impl - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); + private async _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { + if (!this._initData.remote.isRemote) { + // local extension host process + await this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); + } else { + // remote extension host process + // do not wait for renderer confirmation + this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); + } return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { const activationTimes = activatedExtension.activationTimes; this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, reason); From 5d8c90cdb3215615ef606dbc6bfc5a6ad7d824e1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 25 Sep 2020 13:40:08 +0200 Subject: [PATCH 64/98] Fixes #107434: `hostExtensions` should never contain remote extensions --- .../services/extensions/common/remoteExtensionHost.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index fea56076567..1d26bf01091 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -21,7 +21,7 @@ import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -205,8 +205,15 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); // Collect all identifiers for extension ids which can be considered "resolved" + const remoteExtensions = new Set(); + remoteInitData.extensions.forEach((extension) => remoteExtensions.add(ExtensionIdentifier.toKey(extension.identifier.value))); + const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier); - const hostExtensions = remoteInitData.allExtensions.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier); + const hostExtensions = ( + remoteInitData.allExtensions + .filter(extension => !remoteExtensions.has(ExtensionIdentifier.toKey(extension.identifier.value))) + .filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier) + ); const workspace = this._contextService.getWorkspace(); return { commit: this._productService.commit, From 7f035b4be73a7ce9ceafb2128c066b717eafaf87 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 14:10:45 +0200 Subject: [PATCH 65/98] debt - introduce and adopt FileAccess deprecate amd.ts and require.toUrl methods --- src/bootstrap-amd.js | 2 +- src/bootstrap-window.js | 4 +- src/bootstrap.js | 22 ++++++++--- src/typings/require.d.ts | 3 ++ src/vs/base/browser/dom.ts | 8 ++-- src/vs/base/common/amd.ts | 6 +++ src/vs/base/common/network.ts | 37 +++++++++++++++++++ src/vs/base/node/paths.ts | 4 +- src/vs/base/node/processes.ts | 4 +- src/vs/base/node/ps.ts | 6 +-- src/vs/code/electron-main/auth.ts | 8 ++-- src/vs/code/electron-main/sharedProcess.ts | 10 +++-- src/vs/code/electron-main/window.ts | 10 +++-- .../common/extensionManagement.ts | 3 +- .../node/extensionsScanner.ts | 4 +- .../files/node/watcher/nsfw/watcherService.ts | 4 +- .../files/node/watcher/unix/watcherService.ts | 4 +- .../watcher/win32/csharpWatcherService.ts | 4 +- .../issue/electron-main/issueMainService.ts | 17 +++++---- src/vs/platform/product/common/product.ts | 10 ++--- .../parts/editor/editor.contribution.ts | 6 +-- .../browser/parts/editor/editorGroupView.ts | 5 +-- .../contrib/cli/node/cli.contribution.ts | 4 +- .../debug/browser/debug.contribution.ts | 20 +++++----- .../contrib/debug/node/debugHelperService.ts | 4 +- .../browser/extensionsWorkbenchService.ts | 5 ++- .../node/externalTerminalService.ts | 4 +- .../electron-browser/webviewElement.ts | 7 +++- .../welcome/page/browser/welcomePage.ts | 5 +-- .../browser/editor/editorWalkThrough.ts | 5 +-- .../browser/webWorkerExtensionHost.ts | 5 ++- .../cachedExtensionScanner.ts | 7 ++-- .../localProcessExtensionHost.ts | 4 +- .../integrity/node/integrityService.ts | 4 +- .../search/electron-browser/searchService.ts | 7 +--- .../textMate/browser/textMateService.ts | 4 +- .../electron-sandbox/textMateService.ts | 10 ++--- .../electron-sandbox/textMateWorker.ts | 4 +- test/unit/electron/renderer.js | 2 +- 39 files changed, 172 insertions(+), 110 deletions(-) diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 8b0ec252d25..752e4ab63ed 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -14,7 +14,7 @@ const nlsConfig = bootstrap.setupNLS(); // Bootstrap: Loader loader.config({ - baseUrl: bootstrap.fileUriFromPath(__dirname, process.platform === 'win32'), + baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), catchError: true, nodeRequire: require, nodeMain: __filename, diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index d3d05e5578f..dfc7bbd12e4 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -103,7 +103,7 @@ window['MonacoEnvironment'] = {}; const loaderConfig = { - baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, safeProcess.platform === 'win32')}/out`, + baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`, 'vs/nls': nlsConfig }; @@ -241,7 +241,7 @@ } /** - * @return {{ fileUriFromPath: (path: string, isWindows: boolean) => string; }} + * @return {{ fileUriFromPath: (path: string, config: { isWindows?: boolean, scheme?: string, fallbackAuthority?: string }) => string; }} */ function bootstrap() { // @ts-ignore (defined in bootstrap.js) diff --git a/src/bootstrap.js b/src/bootstrap.js index 58c6103d9be..0cb6466aeaf 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -88,10 +88,13 @@ /** * @param {string} path - * @param {boolean} isWindows + * @param {{ isWindows?: boolean, scheme?: string, fallbackAuthority?: string }} config * @returns {string} */ - function fileUriFromPath(path, isWindows) { + function fileUriFromPath(path, config) { + + // Since we are building a URI, we normalize any backlsash + // to slashes and we ensure that the path begins with a '/'. let pathName = path.replace(/\\/g, '/'); if (pathName.length > 0 && pathName.charAt(0) !== '/') { pathName = `/${pathName}`; @@ -99,10 +102,17 @@ /** @type {string} */ let uri; - if (isWindows && pathName.startsWith('//')) { // specially handle Windows UNC paths - uri = encodeURI(`file:${pathName}`); - } else { - uri = encodeURI(`file://${pathName}`); + + // Windows: in order to support UNC paths (which start with '//') + // that have their own authority, we do not use the provided authority + // but rather preserve it. + if (config.isWindows && pathName.startsWith('//')) { + uri = encodeURI(`${config.scheme || 'file'}:${pathName}`); + } + + // Otherwise we optionally add the provided authority if specified + else { + uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`); } return uri.replace(/#/g, '%23'); diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index cc4a4043ce1..dc6c6bd10fd 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -41,6 +41,9 @@ declare const define: { }; interface NodeRequire { + /** + * @deprecated use `FileAccess.asFileUri()` for node.js contexts or `FileAccess.asBrowserUri` for browser contexts. + */ toUrl(path: string): string; (dependencies: string[], callback: (...args: any[]) => any, errorback?: (err: any) => void): any; config(data: any): any; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 8b381f96cc1..21d07830544 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -13,7 +13,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Schemas, RemoteAuthorities } from 'vs/base/common/network'; +import { Schemas, FileAccess, RemoteAuthorities } from 'vs/base/common/network'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; export function clearNode(node: HTMLElement): void { @@ -1223,10 +1223,12 @@ export function asDomUri(uri: URI): URI { if (!uri) { return uri; } - if (Schemas.vscodeRemote === uri.scheme) { + + if (uri.scheme === Schemas.vscodeRemote) { return RemoteAuthorities.rewrite(uri); } - return uri; + + return FileAccess.asBrowserUri(uri); } /** diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index ebea36794d0..d8ce68b55e2 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -5,10 +5,16 @@ import { URI } from 'vs/base/common/uri'; +/** + * @deprecated use `FileAccess.asFileUri(relativePath, requireFn).fsPath` + */ export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { return getUriFromAmdModule(requirefn, relativePath).fsPath; } +/** + * @deprecated use `FileAccess.asFileUri()` for node.js contexts or `FileAccess.asBrowserUri` for browser contexts. + */ export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { return URI.parse(requirefn.toUrl(relativePath)); } diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 4b6aebc1646..d768875629f 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -129,3 +129,40 @@ class RemoteAuthoritiesImpl { } export const RemoteAuthorities = new RemoteAuthoritiesImpl(); + +class FileAccessImpl { + + /** + * Returns a URI to use in contexts where the browser is responsible + * for loading (e.g. fetch()) or when used within the DOM. + */ + asBrowserUri(uri: URI): URI; + asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + return uri; + } + + /** + * Returns the `file` URI to use in contexts where node.js + * is responsible for loading. + */ + asFileUri(uri: URI): URI; + asFileUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI; + asFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + const uri = this.toUri(uriOrModule, moduleIdToUrl); + + return uri; + } + + private toUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + if (URI.isUri(uriOrModule)) { + return uriOrModule; + } + + return URI.parse(moduleIdToUrl!.toUrl(uriOrModule)); + } +} + +export const FileAccess = new FileAccessImpl(); diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index 66930cdaf4b..977eaf8806f 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; interface IPaths { getAppDataPath(platform: string): string; getDefaultUserDataPath(platform: string): string; } -const pathsPath = getPathFromAmdModule(require, 'paths'); +const pathsPath = FileAccess.asFileUri('paths', require).fsPath; const paths = require.__$__nodeRequire(pathsPath); export const getAppDataPath = paths.getAppDataPath; export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 6ccedd679ed..302fa75997e 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -15,7 +15,7 @@ import * as extpath from 'vs/base/common/extpath'; import * as Platform from 'vs/base/common/platform'; import { LineDecoder } from 'vs/base/node/decoder'; import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode, Executable } from 'vs/base/common/processes'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; export type ValueCallback = (value: T | Promise) => void; @@ -67,7 +67,7 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise { cp.execFile(cmd, [process.pid.toString()], { encoding: 'utf8', shell: true } as cp.ExecFileOptions, (err, stdout, stderr) => { if (err) { diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 31223d4a5d8..dd5d53d67b4 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -5,7 +5,7 @@ import { exec } from 'child_process'; import { ProcessItem } from 'vs/base/common/processes'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export function listProcesses(rootPid: number): Promise { @@ -180,7 +180,7 @@ export function listProcesses(rootPid: number): Promise { // The cpu usage value reported on Linux is the average over the process lifetime, // recalculate the usage over a one second interval // JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803 - let cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh')); + let cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/cpuUsage.sh', require).fsPath); cmd += ' ' + pids.join(' '); exec(cmd, {}, (err, stdout, stderr) => { @@ -208,7 +208,7 @@ export function listProcesses(rootPid: number): Promise { if (process.platform !== 'linux') { reject(err || new Error(stderr.toString())); } else { - const cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/ps.sh')); + const cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/ps.sh', require).fsPath); exec(cmd, {}, (err, stdout, stderr) => { if (err || stderr) { reject(err || new Error(stderr.toString())); diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index f57aef11f3e..b4096018623 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -6,8 +6,8 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { FileAccess } from 'vs/base/common/network'; import { BrowserWindow, BrowserWindowConstructorOptions, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; type LoginEvent = { event: ElectronEvent; @@ -59,7 +59,7 @@ export class ProxyAuthHandler extends Disposable { show: true, title: 'VS Code', webPreferences: { - preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, sandbox: true, contextIsolation: true, enableWebSQL: false, @@ -76,7 +76,7 @@ export class ProxyAuthHandler extends Disposable { } const win = new BrowserWindow(opts); - const url = require.toUrl('vs/code/electron-sandbox/proxy/auth.html'); + const windowUrl = FileAccess.asBrowserUri('vs/code/electron-sandbox/proxy/auth.html', require); const proxyUrl = `${authInfo.host}:${authInfo.port}`; const title = localize('authRequire', "Proxy Authentication Required"); const message = localize('proxyauth', "The proxy {0} requires authentication.", proxyUrl); @@ -97,6 +97,6 @@ export class ProxyAuthHandler extends Disposable { win.close(); } }); - win.loadURL(url); + win.loadURL(windowUrl.toString(true)); } } diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index a40e860b384..c541a2c7e08 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -13,7 +13,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export class SharedProcess implements ISharedProcess { @@ -41,7 +41,7 @@ export class SharedProcess implements ISharedProcess { show: false, backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { - preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, nodeIntegration: true, enableWebSQL: false, enableRemoteModule: false, @@ -60,8 +60,10 @@ export class SharedProcess implements ISharedProcess { windowId: this.window.id }; - const url = `${require.toUrl('vs/code/electron-browser/sharedProcess/sharedProcess.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; - this.window.loadURL(url); + const windowUrl = FileAccess + .asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }); + this.window.loadURL(windowUrl.toString(true)); // Prevent the window from dying const onClose = (e: ElectronEvent) => { diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 17dcfd5a97b..221bc958455 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -34,9 +34,8 @@ 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 { IFileService } from 'vs/platform/files/common/files'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { ColorScheme } from 'vs/platform/theme/common/theme'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { Schemas } from 'vs/base/common/network'; export interface IWindowCreationOptions { state: IWindowState; @@ -168,7 +167,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, enableWebSQL: false, enableRemoteModule: false, spellcheck: false, @@ -837,7 +836,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { workbench = 'vs/code/electron-browser/workbench/workbench.html'; } - return `${require.toUrl(workbench)}?config=${encodeURIComponent(JSON.stringify(config))}`; + return FileAccess + .asBrowserUri(workbench, require) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) + .toString(true); } serializeWindowState(): IWindowState { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index fa4e7350910..7e30f75eea8 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -10,6 +10,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { FileAccess } from 'vs/base/common/network'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); @@ -260,7 +261,7 @@ export interface IExtensionTipsService { } -export const DefaultIconPath = require.toUrl('./media/defaultIcon.png'); +export const DefaultIconPath = FileAccess.asBrowserUri('./media/defaultIcon.png', require).toString(true); export const ExtensionsLabel = localize('extensions', "Extensions"); export const ExtensionsLocalizedLabel = { value: ExtensionsLabel, original: 'Extensions' }; export const ExtensionsChannelId = 'extensions'; diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index a2c840c049b..e7342348d46 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -14,7 +14,6 @@ import { areSameExtensions, ExtensionIdentifierWithVersion, groupByExtension, ge import { Limiter, Queue } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -23,6 +22,7 @@ import { extract, ExtractError } from 'vs/base/node/zip'; import { isWindows } from 'vs/base/common/platform'; import { flatten } from 'vs/base/common/arrays'; import { IStringDictionary } from 'vs/base/common/collections'; +import { FileAccess } from 'vs/base/common/network'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -336,7 +336,7 @@ export class ExtensionsScanner extends Disposable { private _devSystemExtensionsPath: string | null = null; private get devSystemExtensionsPath(): string { if (!this._devSystemExtensionsPath) { - this._devSystemExtensionsPath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); + this._devSystemExtensionsPath = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', '.build', 'builtInExtensions')); } return this._devSystemExtensionsPath; } diff --git a/src/vs/platform/files/node/watcher/nsfw/watcherService.ts b/src/vs/platform/files/node/watcher/nsfw/watcherService.ts index 36cc3c56995..79fa3ba25f6 100644 --- a/src/vs/platform/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/platform/files/node/watcher/nsfw/watcherService.ts @@ -8,7 +8,7 @@ import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { IDiskFileChange, ILogMessage } from 'vs/platform/files/node/watcher/watcher'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWatcherRequest, IWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcher'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export class FileWatcher extends Disposable { @@ -34,7 +34,7 @@ export class FileWatcher extends Disposable { private startWatching(): void { const client = this._register(new Client( - getPathFromAmdModule(require, 'bootstrap-fork'), + FileAccess.asFileUri('bootstrap-fork', require).fsPath, { serverName: 'File Watcher (nsfw)', args: ['--type=watcherService'], diff --git a/src/vs/platform/files/node/watcher/unix/watcherService.ts b/src/vs/platform/files/node/watcher/unix/watcherService.ts index 18da926b0ef..e561cc8e513 100644 --- a/src/vs/platform/files/node/watcher/unix/watcherService.ts +++ b/src/vs/platform/files/node/watcher/unix/watcherService.ts @@ -8,7 +8,7 @@ import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { IDiskFileChange, ILogMessage } from 'vs/platform/files/node/watcher/watcher'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWatcherRequest, IWatcherOptions, IWatcherService } from 'vs/platform/files/node/watcher/unix/watcher'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export class FileWatcher extends Disposable { @@ -35,7 +35,7 @@ export class FileWatcher extends Disposable { private startWatching(): void { const client = this._register(new Client( - getPathFromAmdModule(require, 'bootstrap-fork'), + FileAccess.asFileUri('bootstrap-fork', require).fsPath, { serverName: 'File Watcher (chokidar)', args: ['--type=watcherService'], diff --git a/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts index 9eda54b3c90..4a13ec02886 100644 --- a/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts +++ b/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts @@ -8,7 +8,7 @@ import { FileChangeType } from 'vs/platform/files/common/files'; import * as decoder from 'vs/base/node/decoder'; import * as glob from 'vs/base/common/glob'; import { IDiskFileChange, ILogMessage } from 'vs/platform/files/node/watcher/watcher'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; export class OutOfProcessWin32FolderWatcher { @@ -50,7 +50,7 @@ export class OutOfProcessWin32FolderWatcher { args.push('-verbose'); } - this.handle = cp.spawn(getPathFromAmdModule(require, 'vs/platform/files/node/watcher/win32/CodeHelper.exe'), args); + this.handle = cp.spawn(FileAccess.asFileUri('vs/platform/files/node/watcher/win32/CodeHelper.exe', require).fsPath, args); const stdoutLineDecoder = new decoder.LineDecoder(); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index ee27d4c8e38..bc2b9bc4deb 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -18,9 +18,9 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWindowState } from 'vs/platform/windows/electron-main/windows'; import { listProcesses } from 'vs/base/node/ps'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; +import { FileAccess } from 'vs/base/common/network'; const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; @@ -195,7 +195,7 @@ export class IssueMainService implements ICommonIssueService { title: localize('issueReporter', "Issue Reporter"), backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR, webPreferences: { - preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, enableWebSQL: false, enableRemoteModule: false, spellcheck: false, @@ -261,7 +261,7 @@ export class IssueMainService implements ICommonIssueService { backgroundColor: data.styles.backgroundColor, title: localize('processExplorer', "Process Explorer"), webPreferences: { - preload: getPathFromAmdModule(require, 'vs/base/parts/sandbox/electron-browser/preload.js'), + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath, enableWebSQL: false, enableRemoteModule: false, spellcheck: false, @@ -294,7 +294,7 @@ export class IssueMainService implements ICommonIssueService { }; this._processExplorerWindow.loadURL( - toLauchUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration)); + toWindowUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration)); this._processExplorerWindow.on('close', () => this._processExplorerWindow = null); @@ -435,11 +435,11 @@ export class IssueMainService implements ICommonIssueService { } }; - return toLauchUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration); + return toWindowUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration); } } -function toLauchUrl(pathToHtml: string, windowConfiguration: T): string { +function toWindowUrl(modulePathToHtml: string, windowConfiguration: T): string { const environment = parseArgs(process.argv, OPTIONS); const config = Object.assign(environment, windowConfiguration); for (const keyValue of Object.keys(config)) { @@ -449,5 +449,8 @@ function toLauchUrl(pathToHtml: string, windowConfiguration: T): string { } } - return `${require.toUrl(pathToHtml)}?config=${encodeURIComponent(JSON.stringify(config))}`; + return FileAccess + .asBrowserUri(modulePathToHtml, require) + .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) + .toString(true); } diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 55567d4b537..798faa74ae8 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -5,9 +5,9 @@ import { IProductConfiguration } from 'vs/platform/product/common/productService'; import { isWeb } from 'vs/base/common/platform'; -import * as path from 'vs/base/common/path'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { env } from 'vs/base/common/process'; +import { FileAccess } from 'vs/base/common/network'; +import { dirname, joinPath } from 'vs/base/common/resources'; let product: IProductConfiguration; @@ -43,10 +43,10 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! else { // Obtain values from product.json and package.json - const rootPath = path.dirname(getPathFromAmdModule(require, '')); + const rootPath = dirname(FileAccess.asFileUri('', require)); - product = require.__$__nodeRequire(path.join(rootPath, 'product.json')); - const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; }; + product = require.__$__nodeRequire(joinPath(rootPath, 'product.json').fsPath); + const pkg = require.__$__nodeRequire(joinPath(rootPath, 'package.json').fsPath) as { version: string; }; // Running out of sources if (env['VSCODE_DEV']) { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index a6a52a00e5a..f8accd9a983 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -54,7 +54,7 @@ import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { getUriFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -423,13 +423,13 @@ editorCommands.setup(); // Touch Bar if (isMacintosh) { MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { - command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: getUriFromAmdModule(require, 'vs/workbench/browser/parts/editor/media/back-tb.png') } }, + command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: FileAccess.asFileUri('vs/workbench/browser/parts/editor/media/back-tb.png', require) } }, group: 'navigation', order: 0 }); MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { - command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: getUriFromAmdModule(require, 'vs/workbench/browser/parts/editor/media/forward-tb.png') } }, + command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: FileAccess.asFileUri('vs/workbench/browser/parts/editor/media/forward-tb.png', require) } }, group: 'navigation', order: 1 }); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 4f209f32f0a..9f5c4d3ee01 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -46,13 +46,12 @@ import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; import { hash } from 'vs/base/common/hash'; import { guessMimeTypes } from 'vs/base/common/mime'; import { extname } from 'vs/base/common/resources'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor'; import { IDialogService, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { ILogService } from 'vs/platform/log/common/log'; import { Codicon } from 'vs/base/common/codicons'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; export class EditorGroupView extends Themable implements IEditorGroupView { @@ -1781,7 +1780,7 @@ registerThemingParticipant((theme, collector, environment) => { const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`; collector.addRule(` .monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress { - background-image: url('${getPathFromAmdModule(require, letterpress)}') + background-image: url('${FileAccess.asBrowserUri(letterpress, require).fsPath}') } `); diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index a0dfd14878a..2000693fdf3 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -19,7 +19,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; function ignore(code: string, value: T): (err: any) => Promise { @@ -29,7 +29,7 @@ function ignore(code: string, value: T): (err: any) => Promise { let _source: string | null = null; function getSource(): string { if (!_source) { - const root = getPathFromAmdModule(require, ''); + const root = FileAccess.asFileUri('', require).fsPath; _source = path.resolve(root, '..', 'bin', 'code'); } return _source; diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index a359e971221..009fe56029d 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,7 +52,7 @@ import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debug import { Codicon } from 'vs/base/common/codicons'; import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; -import { getUriFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); const debugCategory = nls.localize('debugCategory', "Debug"); @@ -210,15 +210,15 @@ function registerCommandsAndActions(): void { }); }; - registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-tb.png')); - registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png')); - registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/continue-tb.png')); - registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/pause-tb.png')); - registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepover-tb.png')); - registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepinto-tb.png')); - registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stepout-tb.png')); - registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/restart-tb.png')); - registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, getUriFromAmdModule(require, 'vs/workbench/contrib/debug/browser/media/stop-tb.png')); + registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/continue-tb.png', require)); + registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/continue-without-debugging-tb.png', require)); + registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/continue-tb.png', require)); + registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/pause-tb.png', require)); + registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepover-tb.png', require)); + registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepinto-tb.png', require)); + registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepout-tb.png', require)); + registerTouchBarEntry(RESTART_SESSION_ID, RESTART_LABEL, 5, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/restart-tb.png', require)); + registerTouchBarEntry(STOP_ID, STOP_LABEL, 6, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stop-tb.png', require)); } } diff --git a/src/vs/workbench/contrib/debug/node/debugHelperService.ts b/src/vs/workbench/contrib/debug/node/debugHelperService.ts index 4089f00771a..a7bed36cf61 100644 --- a/src/vs/workbench/contrib/debug/node/debugHelperService.ts +++ b/src/vs/workbench/contrib/debug/node/debugHelperService.ts @@ -6,7 +6,7 @@ import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -24,7 +24,7 @@ export class NodeDebugHelperService implements IDebugHelperService { createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined { const client = new TelemetryClient( - getPathFromAmdModule(require, 'bootstrap-fork'), + FileAccess.asFileUri('bootstrap-fork', require).fsPath, { serverName: 'Debug Telemetry', timeout: 1000 * 60 * 5, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 3d1fbe7f7a3..7f1d1613ede 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -40,6 +40,7 @@ import { asDomUri } from 'vs/base/browser/dom'; import { getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; import { isWeb } from 'vs/base/common/platform'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { FileAccess } from 'vs/base/common/network'; interface IExtensionStateProvider { (extension: Extension): T; @@ -151,10 +152,10 @@ class Extension implements IExtension { if (this.type === ExtensionType.System && this.local) { if (this.local.manifest && this.local.manifest.contributes) { if (Array.isArray(this.local.manifest.contributes.themes) && this.local.manifest.contributes.themes.length) { - return require.toUrl('./media/theme-icon.png'); + return FileAccess.asBrowserUri('./media/theme-icon.png', require).toString(true); } if (Array.isArray(this.local.manifest.contributes.grammars) && this.local.manifest.contributes.grammars.length) { - return require.toUrl('./media/language-icon.svg'); + return FileAccess.asBrowserUri('./media/language-icon.svg', require).toString(true); } } } diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts index 9a55ddd4e97..3e0af5cc854 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts @@ -11,9 +11,9 @@ import * as pfs from 'vs/base/node/pfs'; import * as env from 'vs/base/common/platform'; import { IExternalTerminalService, IExternalTerminalConfiguration, IExternalTerminalSettings } from 'vs/workbench/contrib/externalTerminal/common/externalTerminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { DEFAULT_TERMINAL_OSX } from 'vs/workbench/contrib/externalTerminal/node/externalTerminal'; +import { FileAccess } from 'vs/base/common/network'; const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); @@ -144,7 +144,7 @@ export class MacExternalTerminalService implements IExternalTerminalService { // and then launches the program inside that window. const script = terminalApp === DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; - const scriptpath = getPathFromAmdModule(require, `vs/workbench/contrib/externalTerminal/node/${script}.scpt`); + const scriptpath = FileAccess.asFileUri(`vs/workbench/contrib/externalTerminal/node/${script}.scpt`, require).fsPath; const osaArgs = [ scriptpath, diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 92dab1a41f6..27bfc924f61 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -9,7 +9,7 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; @@ -206,7 +206,10 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme this.styledFindWidget(); } - this.element!.preload = require.toUrl('./pre/electron-index.js'); + // We must ensure to put a `file:` URI as the preload attribute + // and not the `vscode-file` URI because preload scripts are loaded + // via node.js from the main side and only allow `file:` protocol + this.element!.preload = FileAccess.asFileUri('./pre/electron-index.js', require).toString(true); this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser/index.html?platform=electron`; } diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 76f716679d0..49b2111e54b 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -19,7 +19,7 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur import { localize } from 'vs/nls'; import { Action, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -47,7 +47,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { getUriFromAmdModule } from 'vs/base/common/amd'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; @@ -300,7 +299,7 @@ class WelcomePage extends Disposable { const recentlyOpened = this.workspacesService.getRecentlyOpened(); const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); - const resource = getUriFromAmdModule(require, './vs_code_welcome_page') + const resource = FileAccess.asBrowserUri('./vs_code_welcome_page', require) .with({ scheme: Schemas.walkThrough, query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page' }) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index c02c5aadc85..04a605f1cc3 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -8,16 +8,15 @@ import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { getUriFromAmdModule } from 'vs/base/common/amd'; import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; const typeId = 'workbench.editors.walkThroughInput'; const inputOptions: WalkThroughInputOptions = { typeId, name: localize('editorWalkThrough.title', "Interactive Playground"), - resource: getUriFromAmdModule(require, './vs_code_editor_walkthrough.md') + resource: FileAccess.asBrowserUri('./vs_code_editor_walkthrough.md', require) .with({ scheme: Schemas.walkThrough, query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough' }) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 8740402bd1c..9b601619a50 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -29,6 +29,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { canceled, onUnexpectedError } from 'vs/base/common/errors'; import { WEB_WORKER_IFRAME } from 'vs/workbench/services/extensions/common/webWorkerIframe'; import { Barrier } from 'vs/base/common/async'; +import { FileAccess } from 'vs/base/common/network'; export interface IWebWorkerExtensionHostInitData { readonly autoStart: boolean; @@ -92,7 +93,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost iframe.style.display = 'none'; const vscodeWebWorkerExtHostId = generateUuid(); - const workerUrl = require.toUrl('../worker/extensionHostWorkerMain.js'); + const workerUrl = FileAccess.asBrowserUri('../worker/extensionHostWorkerMain.js', require).toString(true); const workerSrc = getWorkerBootstrapUrl(workerUrl, 'WorkerExtensionHost', true); const escapeAttribute = (value: string): string => { return value.replace(/"/g, '"'); @@ -173,7 +174,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost private async _startOutsideIframe(): Promise { const emitter = new Emitter(); - const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); + const url = getWorkerBootstrapUrl(FileAccess.asBrowserUri('../worker/extensionHostWorkerMain.js', require).toString(true), 'WorkerExtensionHost'); const worker = new Worker(url, { name: 'WorkerExtensionHost' }); const barrier = new Barrier(); diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index 600361d6275..2b68774b150 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -5,9 +5,8 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; import * as errors from 'vs/base/common/errors'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { joinPath, originalFSPath } from 'vs/base/common/resources'; @@ -30,7 +29,7 @@ interface IExtensionCacheData { let _SystemExtensionsRoot: string | null = null; function getSystemExtensionsRoot(): string { if (!_SystemExtensionsRoot) { - _SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); + _SystemExtensionsRoot = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); } return _SystemExtensionsRoot; } @@ -38,7 +37,7 @@ function getSystemExtensionsRoot(): string { let _ExtraDevSystemExtensionsRoot: string | null = null; function getExtraDevSystemExtensionsRoot(): string { if (!_ExtraDevSystemExtensionsRoot) { - _ExtraDevSystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); + _ExtraDevSystemExtensionsRoot = path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', '.build', 'builtInExtensions')); } return _ExtraDevSystemExtensionsRoot; } diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index fcb8ffa9a05..a92220fc8d6 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { ChildProcess, fork } from 'child_process'; import { Server, Socket, createServer } from 'net'; import { CrashReporterStartOptions } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; @@ -215,7 +215,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { } // Run Extension Host as fork of current process - this._extensionHostProcess = fork(getPathFromAmdModule(require, 'bootstrap-fork'), ['--type=extensionHost'], opts); + this._extensionHostProcess = fork(FileAccess.asFileUri('bootstrap-fork', require).fsPath, ['--type=extensionHost'], opts); // Catch all output coming from the extension host process type Output = { data: string, format: string[] }; diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index cd348ebef86..7d50386ce6f 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -15,7 +15,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { getUriFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; interface IStorageData { dontShowPrompt: boolean; @@ -142,7 +142,7 @@ export class IntegrityServiceImpl implements IIntegrityService { } private _resolve(filename: string, expected: string): Promise { - const fileUri = getUriFromAmdModule(require, filename); + const fileUri = FileAccess.asFileUri(filename, require); return new Promise((resolve, reject) => { fs.readFile(fileUri.fsPath, (err, buff) => { if (err) { diff --git a/src/vs/workbench/services/search/electron-browser/searchService.ts b/src/vs/workbench/services/search/electron-browser/searchService.ts index 45ed77c6a42..ea15e40b57f 100644 --- a/src/vs/workbench/services/search/electron-browser/searchService.ts +++ b/src/vs/workbench/services/search/electron-browser/searchService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; @@ -26,6 +25,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { FileAccess } from 'vs/base/common/network'; export class LocalSearchService extends SearchService { constructor( @@ -82,10 +82,7 @@ export class DiskSearch implements ISearchResultProvider { } } - const client = new Client( - getPathFromAmdModule(require, 'bootstrap-fork'), - opts); - + const client = new Client(FileAccess.asFileUri('bootstrap-fork', require).fsPath, opts); const channel = getNextTickChannel(client.getChannel('search')); this.raw = new SearchChannelClient(channel); } diff --git a/src/vs/workbench/services/textMate/browser/textMateService.ts b/src/vs/workbench/services/textMate/browser/textMateService.ts index ed781d23844..5a594121bf4 100644 --- a/src/vs/workbench/services/textMate/browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/browser/textMateService.ts @@ -14,6 +14,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { IProgressService } from 'vs/platform/progress/common/progress'; +import { FileAccess } from 'vs/base/common/network'; export class TextMateService extends AbstractTextMateService { @@ -31,8 +32,7 @@ export class TextMateService extends AbstractTextMateService { } protected async _loadVSCodeOnigurumWASM(): Promise { - const wasmPath = require.toUrl('vscode-oniguruma/../onig.wasm'); - const response = await fetch(wasmPath); + const response = await fetch(FileAccess.asBrowserUri('vscode-oniguruma/../onig.wasm', require).toString(true)); // Using the response directly only works if the server sets the MIME type 'application/wasm'. // Otherwise, a TypeError is thrown when using the streaming compiler. // We therefore use the non-streaming compiler :(. diff --git a/src/vs/workbench/services/textMate/electron-sandbox/textMateService.ts b/src/vs/workbench/services/textMate/electron-sandbox/textMateService.ts index 57f6cda6726..209e19fbb5b 100644 --- a/src/vs/workbench/services/textMate/electron-sandbox/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-sandbox/textMateService.ts @@ -26,6 +26,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProgressService } from 'vs/platform/progress/common/progress'; +import { FileAccess } from 'vs/base/common/network'; const RUN_TEXTMATE_IN_WORKER = false; @@ -182,12 +183,9 @@ export class TextMateService extends AbstractTextMateService { } protected async _loadVSCodeOnigurumWASM(): Promise { - const wasmPath = ( - this._environmentService.isBuilt - ? require.toUrl('../../../../../../node_modules.asar.unpacked/vscode-oniguruma/release/onig.wasm') - : require.toUrl('../../../../../../node_modules/vscode-oniguruma/release/onig.wasm') - ); - const response = await fetch(wasmPath); + const response = await fetch(this._environmentService.isBuilt + ? FileAccess.asBrowserUri('../../../../../../node_modules.asar.unpacked/vscode-oniguruma/release/onig.wasm', require).toString(true) + : FileAccess.asBrowserUri('../../../../../../node_modules/vscode-oniguruma/release/onig.wasm', require).toString(true)); return response; } diff --git a/src/vs/workbench/services/textMate/electron-sandbox/textMateWorker.ts b/src/vs/workbench/services/textMate/electron-sandbox/textMateWorker.ts index bf62c5393a9..5b811274fbc 100644 --- a/src/vs/workbench/services/textMate/electron-sandbox/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/electron-sandbox/textMateWorker.ts @@ -14,6 +14,7 @@ import { TokenizationStateStore } from 'vs/editor/common/model/textModelTokens'; import type { IGrammar, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { FileAccess } from 'vs/base/common/network'; export interface IValidGrammarDefinitionDTO { location: UriComponents; @@ -146,8 +147,7 @@ export class TextMateWorker { }); const vscodeTextmate = await import('vscode-textmate'); const vscodeOniguruma = await import('vscode-oniguruma'); - const wasmPath = require.toUrl('vscode-oniguruma/../onig.wasm'); - const response = await fetch(wasmPath); + const response = await fetch(FileAccess.asBrowserUri('vscode-oniguruma/../onig.wasm', require).toString(true)); // Using the response directly only works if the server sets the MIME type 'application/wasm'. // Otherwise, a TypeError is thrown when using the streaming compiler. // We therefore use the non-streaming compiler :(. diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index 3f29ac6cfa8..a1b6ef62746 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -32,7 +32,7 @@ function initLoader(opts) { nodeRequire: require, nodeMain: __filename, catchError: true, - baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src'), process.platform === 'win32'), + baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }), paths: { 'vs': `../${outdir}/vs`, 'lib': `../${outdir}/lib`, From 74842ceac1e8f38109417426b1949e909aa362a1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 14:18:39 +0200 Subject: [PATCH 66/98] debt - more FileAccess adoption --- src/vs/platform/environment/node/environmentService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 5f039341a18..16057c43a08 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -14,7 +14,7 @@ import { memoize } from 'vs/base/common/decorators'; import product from 'vs/platform/product/common/product'; import { toLocalISOString } from 'vs/base/common/date'; import { isWindows, Platform, platform } from 'vs/base/common/platform'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; export class NativeEnvironmentService implements INativeEnvironmentService { @@ -24,7 +24,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get args(): NativeParsedArgs { return this._args; } @memoize - get appRoot(): string { return path.dirname(getPathFromAmdModule(require, '')); } + get appRoot(): string { return path.dirname(FileAccess.asFileUri('', require).fsPath); } readonly logsPath: string; @@ -111,7 +111,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { if (fromArgs) { return fromArgs; } else { - return path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); + return path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions')); } } From 73084f5ccd68e0c3ab5465973f91cdc41fdea97d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 15:23:44 +0200 Subject: [PATCH 67/98] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f4877172c8..a0cbf1a3ab6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.50.0", - "distro": "20bcc93e22ceef0a6c4464ae78363429f59be797", + "distro": "f4fbb2133880d47be366fb94ea9d149862bddaf3", "author": { "name": "Microsoft Corporation" }, From b67b5e8e31fc2ff7c9baea3f223e7e1544fb197b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 25 Sep 2020 16:03:37 +0200 Subject: [PATCH 68/98] Exclude node inspect from auto port forwarding Fixes #107243 --- src/vs/workbench/contrib/remote/browser/remote.ts | 12 +++++++++++- src/vs/workbench/contrib/remote/browser/urlFinder.ts | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 069249ebda9..3f5443776f5 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -841,6 +841,8 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { } class AutomaticPortForwarding extends Disposable implements IWorkbenchContribution { + private contextServiceListener: IDisposable; + constructor( @ITerminalService private readonly terminalService: ITerminalService, @INotificationService private readonly notificationService: INotificationService, @@ -854,12 +856,20 @@ class AutomaticPortForwarding extends Disposable implements IWorkbenchContributi if (this.environmentService.configuration.remoteAuthority) { this.startUrlFinder(); } + this.contextServiceListener = this._register(this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + this.startUrlFinder(); + } + })); } + private isStarted = false; private startUrlFinder() { - if (!forwardedPortsViewEnabled.getValue(this.contextKeyService)) { + if (!this.isStarted && !forwardedPortsViewEnabled.getValue(this.contextKeyService)) { return; } + this.contextServiceListener.dispose(); + this.isStarted = true; const urlFinder = this._register(new UrlFinder(this.terminalService)); this._register(urlFinder.onDidMatchLocalUrl(async (localUrl) => { const forwarded = await this.remoteExplorerService.forward(localUrl); diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index d0ee06fd48b..2962235ee70 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -63,6 +63,10 @@ export class UrlFinder extends Disposable { if (host !== '0.0.0.0' && host !== '127.0.0.1') { host = 'localhost'; } + // Exclude node inspect, except when using defualt port + if (port !== 9229 && data.startsWith('Debugger listening on')) { + return; + } this._onDidMatchLocalUrl.fire({ port, host }); } } From 8932f62afac9b07b143e4935ed0f4de7deebe739 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Sep 2020 15:59:15 +0200 Subject: [PATCH 69/98] adress API feedback https://github.com/microsoft/vscode/issues/54938 --- extensions/git/src/decorationProvider.ts | 36 +++++++++---------- extensions/git/src/repository.ts | 10 +++--- src/vs/vscode.proposed.d.ts | 34 ++++++++---------- .../workbench/api/common/extHost.api.impl.ts | 4 +-- .../api/common/extHostDecorations.ts | 14 ++++---- src/vs/workbench/api/common/extHostTypes.ts | 21 +++++++---- .../browser/api/extHostDecorations.test.ts | 10 +++--- 7 files changed, 65 insertions(+), 64 deletions(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index ab6e5713e11..a61dd720caf 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, workspace, Uri, Disposable, Event, EventEmitter, Decoration, DecorationProvider, ThemeColor } from 'vscode'; +import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor } from 'vscode'; import * as path from 'path'; import { Repository, GitResourceGroup } from './repository'; import { Model } from './model'; @@ -11,16 +11,16 @@ import { debounce } from './decorators'; import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource } from './util'; import { GitErrorCodes, Status } from './api/git'; -class GitIgnoreDecorationProvider implements DecorationProvider { +class GitIgnoreDecorationProvider implements FileDecorationProvider { - private static Decoration: Decoration = { priority: 3, color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; + private static Decoration: FileDecoration = { priority: 3, color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; - readonly onDidChangeDecorations: Event; - private queue = new Map>; }>(); + readonly onDidChange: Event; + private queue = new Map>; }>(); private disposables: Disposable[] = []; constructor(private model: Model) { - this.onDidChangeDecorations = fireEvent(anyEvent( + this.onDidChange = fireEvent(anyEvent( filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)), model.onDidOpenRepository, model.onDidCloseRepository @@ -29,7 +29,7 @@ class GitIgnoreDecorationProvider implements DecorationProvider { this.disposables.push(window.registerDecorationProvider(this)); } - async provideDecoration(uri: Uri): Promise { + async provideFileDecoration(uri: Uri): Promise { const repository = this.model.getRepository(uri); if (!repository) { @@ -39,7 +39,7 @@ class GitIgnoreDecorationProvider implements DecorationProvider { let queueItem = this.queue.get(repository.root); if (!queueItem) { - queueItem = { repository, queue: new Map>() }; + queueItem = { repository, queue: new Map>() }; this.queue.set(repository.root, queueItem); } @@ -84,19 +84,19 @@ class GitIgnoreDecorationProvider implements DecorationProvider { } } -class GitDecorationProvider implements DecorationProvider { +class GitDecorationProvider implements FileDecorationProvider { - private static SubmoduleDecorationData: Decoration = { - title: 'Submodule', - letter: 'S', + private static SubmoduleDecorationData: FileDecoration = { + tooltip: 'Submodule', + badge: 'S', color: new ThemeColor('gitDecoration.submoduleResourceForeground') }; private readonly _onDidChangeDecorations = new EventEmitter(); - readonly onDidChangeDecorations: Event = this._onDidChangeDecorations.event; + readonly onDidChange: Event = this._onDidChangeDecorations.event; private disposables: Disposable[] = []; - private decorations = new Map(); + private decorations = new Map(); constructor(private repository: Repository) { this.disposables.push( @@ -106,7 +106,7 @@ class GitDecorationProvider implements DecorationProvider { } private onDidRunGitStatus(): void { - let newDecorations = new Map(); + let newDecorations = new Map(); this.collectSubmoduleDecorationData(newDecorations); this.collectDecorationData(this.repository.indexGroup, newDecorations); @@ -119,7 +119,7 @@ class GitDecorationProvider implements DecorationProvider { this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true))); } - private collectDecorationData(group: GitResourceGroup, bucket: Map): void { + private collectDecorationData(group: GitResourceGroup, bucket: Map): void { for (const r of group.resourceStates) { const decoration = r.resourceDecoration; @@ -134,13 +134,13 @@ class GitDecorationProvider implements DecorationProvider { } } - private collectSubmoduleDecorationData(bucket: Map): void { + private collectSubmoduleDecorationData(bucket: Map): void { for (const submodule of this.repository.submodules) { bucket.set(Uri.file(path.join(this.repository.root, submodule.path)).toString(), GitDecorationProvider.SubmoduleDecorationData); } } - provideDecoration(uri: Uri): Decoration | undefined { + provideFileDecoration(uri: Uri): FileDecoration | undefined { return this.decorations.get(uri.toString()); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index f5e8a5ccfb3..ab32edbff66 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, Decoration } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration } from 'vscode'; import * as nls from 'vscode-nls'; import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git'; import { AutoFetcher } from './autofetch'; @@ -253,11 +253,11 @@ export class Resource implements SourceControlResourceState { } } - get resourceDecoration(): Decoration { + get resourceDecoration(): FileDecoration { return { - bubble: this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED, - title: this.tooltip, - letter: this.letter, + propagte: this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED, + tooltip: this.tooltip, + badge: this.letter, color: this.color, priority: this.priority }; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index ae1b9cd29ed..22067ae3967 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -717,23 +717,18 @@ declare module 'vscode' { //#region file-decorations: https://github.com/microsoft/vscode/issues/54938 - // TODO@jrieken FileDecoration, FileDecorationProvider etc. - // TODO@jrieken Add selector notion to limit decorations to a view. - // TODO@jrieken Rename `Decoration.letter` to `short` so that it could be used for coverage et al. // TODO@jrieken priority -> DecorationSeverity.INFO,WARN,ERROR - // TODO@jrieken title -> tooltip - // TODO@jrieken bubble -> propagte - export class Decoration { + export class FileDecoration { /** - * A letter that represents this decoration. + * A very short string that represents this decoration. */ - letter?: string; + badge?: string; /** - * The human-readable title for this decoration. + * A human-readable tooltip for this decoration. */ - title?: string; + tooltip?: string; /** * The color of this decoration. @@ -747,46 +742,45 @@ declare module 'vscode' { /** * A flag expressing that this decoration should be - * propagted to its parents. + * propagated to its parents. */ - bubble?: boolean; + propagte?: boolean; /** * Creates a new decoration. * - * @param letter A letter that represents the decoration. - * @param title The title of the decoration. + * @param badge A letter that represents the decoration. + * @param tooltip The tooltip of the decoration. * @param color The color of the decoration. */ - constructor(letter?: string, title?: string, color?: ThemeColor); + constructor(badge?: string, tooltip?: string, color?: ThemeColor); } /** * The decoration provider interfaces defines the contract between extensions and * file decorations. */ - export interface DecorationProvider { + export interface FileDecorationProvider { /** * An event to signal decorations for one or many files have changed. * * @see [EventEmitter](#EventEmitter */ - onDidChangeDecorations: Event; + onDidChange: Event; /** * Provide decorations for a given uri. * - * * @param uri The uri of the file to provide a decoration for. * @param token A cancellation token. * @returns A decoration or a thenable that resolves to such. */ - provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; } export namespace window { - export function registerDecorationProvider(provider: DecorationProvider): Disposable; + export function registerDecorationProvider(provider: FileDecorationProvider): Disposable; } //#endregion diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 000b8a27af5..7917c012359 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -605,7 +605,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => { return extHostCustomEditors.registerCustomEditorProvider(extension, viewType, provider, options); }, - registerDecorationProvider(provider: vscode.DecorationProvider) { + registerDecorationProvider(provider: vscode.FileDecorationProvider) { checkProposedApiEnabled(extension); return extHostDecorations.registerDecorationProvider(provider, extension.identifier); }, @@ -1138,7 +1138,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CallHierarchyItem: extHostTypes.CallHierarchyItem, DebugConsoleMode: extHostTypes.DebugConsoleMode, DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind, - Decoration: extHostTypes.Decoration, + FileDecoration: extHostTypes.FileDecoration, UIKind: UIKind, ColorThemeKind: extHostTypes.ColorThemeKind, TimelineItem: extHostTypes.TimelineItem, diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 3e52bbcc3aa..ddc119d1842 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -6,7 +6,7 @@ import type * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { MainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/common/extHost.protocol'; -import { Disposable, Decoration } from 'vs/workbench/api/common/extHostTypes'; +import { Disposable, FileDecoration } from 'vs/workbench/api/common/extHostTypes'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -15,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { asArray } from 'vs/base/common/arrays'; interface ProviderData { - provider: vscode.DecorationProvider; + provider: vscode.FileDecorationProvider; extensionId: ExtensionIdentifier; } @@ -34,12 +34,12 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { this._proxy = extHostRpc.getProxy(MainContext.MainThreadDecorations); } - registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { + registerDecorationProvider(provider: vscode.FileDecorationProvider, extensionId: ExtensionIdentifier): vscode.Disposable { const handle = ExtHostDecorations._handlePool++; this._provider.set(handle, { provider, extensionId }); this._proxy.$registerDecorationProvider(handle, extensionId.value); - const listener = provider.onDidChangeDecorations(e => { + const listener = provider.onDidChange(e => { this._proxy.$onDidChange(handle, !e || (Array.isArray(e) && e.length > 250) ? null : asArray(e)); @@ -65,13 +65,13 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { await Promise.all(requests.map(async request => { try { const { uri, id } = request; - const data = await Promise.resolve(provider.provideDecoration(URI.revive(uri), token)); + const data = await Promise.resolve(provider.provideFileDecoration(URI.revive(uri), token)); if (!data) { return; } try { - Decoration.validate(data); - result[id] = [data.priority, data.bubble, data.title, data.letter, data.color]; + FileDecoration.validate(data); + result[id] = [data.priority, data.propagte, data.tooltip, data.badge, data.color]; } catch (e) { this._logService.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 1497c19da9c..3ba9bc36d3c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2729,22 +2729,29 @@ export enum ExtensionKind { Workspace = 2 } -export class Decoration { +export class FileDecoration { - static validate(d: Decoration): void { - if (d.letter && d.letter.length !== 1) { + static validate(d: FileDecoration): void { + if (d.badge && d.badge.length !== 1) { throw new Error(`The 'letter'-property must be undefined or a single character`); } - if (!d.bubble && !d.color && !d.letter && !d.priority && !d.title) { + if (!d.color && !d.badge && !d.tooltip) { throw new Error(`The decoration is empty`); } } - letter?: string; - title?: string; + badge?: string; + tooltip?: string; color?: vscode.ThemeColor; priority?: number; - bubble?: boolean; + propagate?: boolean; + + + constructor(badge?: string, tooltip?: string, color?: ThemeColor) { + this.badge = badge; + this.tooltip = tooltip; + this.color = color; + } } //#region Theming diff --git a/src/vs/workbench/test/browser/api/extHostDecorations.test.ts b/src/vs/workbench/test/browser/api/extHostDecorations.test.ts index 073006e636d..d086c14226e 100644 --- a/src/vs/workbench/test/browser/api/extHostDecorations.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDecorations.test.ts @@ -48,8 +48,8 @@ suite('ExtHostDecorations', function () { // never returns extHostDecorations.registerDecorationProvider({ - onDidChangeDecorations: Event.None, - provideDecoration() { + onDidChange: Event.None, + provideFileDecoration() { calledA = true; return new Promise(() => { }); } @@ -57,10 +57,10 @@ suite('ExtHostDecorations', function () { // always returns extHostDecorations.registerDecorationProvider({ - onDidChangeDecorations: Event.None, - provideDecoration() { + onDidChange: Event.None, + provideFileDecoration() { calledB = true; - return new Promise(resolve => resolve({ letter: 'H', title: 'Hello' })); + return new Promise(resolve => resolve({ badge: 'H', tooltip: 'Hello' })); } }, nullExtensionDescription.identifier); From 0a914e0e6106a87d2c18f43ce27b51ca1f03158b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Sep 2020 16:13:18 +0200 Subject: [PATCH 70/98] remove priority all togther, maybe add something like severity in the future, https://github.com/microsoft/vscode/issues/54938 --- extensions/git/src/decorationProvider.ts | 2 +- src/vs/vscode.proposed.d.ts | 7 +------ src/vs/workbench/api/browser/mainThreadDecorations.ts | 4 ++-- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostDecorations.ts | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index a61dd720caf..a4fbf0ad459 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -13,7 +13,7 @@ import { GitErrorCodes, Status } from './api/git'; class GitIgnoreDecorationProvider implements FileDecorationProvider { - private static Decoration: FileDecoration = { priority: 3, color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; + private static Decoration: FileDecoration = { color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; readonly onDidChange: Event; private queue = new Map>; }>(); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 22067ae3967..2e06d3ee301 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -717,7 +717,7 @@ declare module 'vscode' { //#region file-decorations: https://github.com/microsoft/vscode/issues/54938 - // TODO@jrieken priority -> DecorationSeverity.INFO,WARN,ERROR + export class FileDecoration { /** @@ -735,11 +735,6 @@ declare module 'vscode' { */ color?: ThemeColor; - /** - * The priority of this decoration. - */ - priority?: number; - /** * A flag expressing that this decoration should be * propagated to its parents. diff --git a/src/vs/workbench/api/browser/mainThreadDecorations.ts b/src/vs/workbench/api/browser/mainThreadDecorations.ts index e2f865dfa4e..501f1841c8f 100644 --- a/src/vs/workbench/api/browser/mainThreadDecorations.ts +++ b/src/vs/workbench/api/browser/mainThreadDecorations.ts @@ -92,9 +92,9 @@ export class MainThreadDecorations implements MainThreadDecorationsShape { if (!data) { return undefined; } - const [weight, bubble, tooltip, letter, themeColor] = data; + const [bubble, tooltip, letter, themeColor] = data; return { - weight: weight ?? 0, + weight: 10, bubble: bubble ?? false, color: themeColor?.id, tooltip, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d8c45c2afbf..79cd256ccaf 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1605,7 +1605,7 @@ export interface DecorationRequest { readonly uri: UriComponents; } -export type DecorationData = [number, boolean, string, string, ThemeColor]; +export type DecorationData = [boolean, string, string, ThemeColor]; export type DecorationReply = { [id: number]: DecorationData; }; export interface ExtHostDecorationsShape { diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index ddc119d1842..55224380b74 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -71,7 +71,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { } try { FileDecoration.validate(data); - result[id] = [data.priority, data.propagte, data.tooltip, data.badge, data.color]; + result[id] = [data.propagte, data.tooltip, data.badge, data.color]; } catch (e) { this._logService.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`); } From b241d4cd1fe455f4414fa511217d6b1283452aff Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Sep 2020 16:26:52 +0200 Subject: [PATCH 71/98] fix typo, https://github.com/microsoft/vscode/issues/54938 --- extensions/git/src/repository.ts | 10 +++------- src/vs/vscode.proposed.d.ts | 2 +- src/vs/workbench/api/common/extHostDecorations.ts | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ab32edbff66..d218e88bd6e 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -254,13 +254,9 @@ export class Resource implements SourceControlResourceState { } get resourceDecoration(): FileDecoration { - return { - propagte: this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED, - tooltip: this.tooltip, - badge: this.letter, - color: this.color, - priority: this.priority - }; + const res = new FileDecoration(this.letter, this.tooltip, this.color); + res.propagate = this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED; + return res; } constructor( diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2e06d3ee301..7184fcdc1a5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -739,7 +739,7 @@ declare module 'vscode' { * A flag expressing that this decoration should be * propagated to its parents. */ - propagte?: boolean; + propagate?: boolean; /** * Creates a new decoration. diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 55224380b74..bb32c600312 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -71,7 +71,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { } try { FileDecoration.validate(data); - result[id] = [data.propagte, data.tooltip, data.badge, data.color]; + result[id] = [data.propagate, data.tooltip, data.badge, data.color]; } catch (e) { this._logService.warn(`INVALID decoration from extension '${extensionId.value}': ${e}`); } From dab1430be0567e2ff66c1889e57524f70da1183b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 25 Sep 2020 07:31:02 -0700 Subject: [PATCH 72/98] Shutdown exthost terminal processes gracefully Fixes #107444 --- .../api/common/extHostTerminalService.ts | 43 +++++++++++-------- .../extensions/common/extensionHostMain.ts | 4 ++ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 01c4ece2795..d722f2b428e 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -13,7 +13,7 @@ import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IT import { timeout } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; @@ -22,7 +22,7 @@ import { NotSupportedError } from 'vs/base/common/errors'; import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { +export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable { readonly _serviceBrand: undefined; @@ -306,14 +306,14 @@ interface ICachedLinkEntry { link: vscode.TerminalLink; } -export abstract class BaseExtHostTerminalService implements IExtHostTerminalService, ExtHostTerminalServiceShape { +export abstract class BaseExtHostTerminalService extends Disposable implements IExtHostTerminalService, ExtHostTerminalServiceShape { readonly _serviceBrand: undefined; protected _proxy: MainThreadTerminalServiceShape; protected _activeTerminal: ExtHostTerminal | undefined; protected _terminals: ExtHostTerminal[] = []; - protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; + protected _terminalProcesses: Map = new Map(); protected _terminalProcessDisposables: { [id: number]: IDisposable } = {}; protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {}; protected _getTerminalPromises: { [id: number]: Promise } = {}; @@ -342,6 +342,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ supportsProcesses: boolean, @IExtHostRpcService extHostRpc: IExtHostRpcService ) { + super(); this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService); this._bufferer = new TerminalDataBufferer(this._proxy.$sendProcessData); this._onDidWriteTerminalData = new Emitter({ @@ -349,6 +350,13 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ onLastListenerRemove: () => this._proxy.$stopSendingDataEvents() }); this._proxy.$registerProcessSupport(supportsProcesses); + this._register({ + dispose: () => { + for (const [_, terminalProcess] of this._terminalProcesses) { + terminalProcess.shutdown(true); + } + } + }); } public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal; @@ -421,11 +429,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise { await this._getTerminalByIdEventually(id); - if (this._terminalProcesses[id]) { - // Extension pty terminal only - when virtual process resize fires it means that the - // terminal's maximum dimensions changed - this._terminalProcesses[id]?.resize(cols, rows); - } + // Extension pty terminal only - when virtual process resize fires it means that the + // terminal's maximum dimensions changed + this._terminalProcesses.get(id)?.resize(cols, rows); } public async $acceptTerminalTitleChange(id: number, name: string): Promise { @@ -497,8 +503,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ }); } - if (this._terminalProcesses[id]) { - (this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions); + const terminalProcess = this._terminalProcesses.get(id); + if (terminalProcess) { + (terminalProcess as ExtHostPseudoterminal).startSendingEvents(initialDimensions); } else { // Defer startSendingEvents call to when _setupExtHostProcessListeners is called this._extensionTerminalAwaitingStart[id] = { initialDimensions }; @@ -520,7 +527,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ if (p.onProcessOverrideDimensions) { disposables.add(p.onProcessOverrideDimensions(e => this._proxy.$sendOverrideDimensions(id, e))); } - this._terminalProcesses[id] = p; + this._terminalProcesses.set(id, p); const awaitingStart = this._extensionTerminalAwaitingStart[id]; if (awaitingStart && p instanceof ExtHostPseudoterminal) { @@ -532,12 +539,12 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } public $acceptProcessInput(id: number, data: string): void { - this._terminalProcesses[id]?.input(data); + this._terminalProcesses.get(id)?.input(data); } public $acceptProcessResize(id: number, cols: number, rows: number): void { try { - this._terminalProcesses[id]?.resize(cols, rows); + this._terminalProcesses.get(id)?.resize(cols, rows); } catch (error) { // We tried to write to a closed pipe / channel. if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') { @@ -547,15 +554,15 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ } public $acceptProcessShutdown(id: number, immediate: boolean): void { - this._terminalProcesses[id]?.shutdown(immediate); + this._terminalProcesses.get(id)?.shutdown(immediate); } public $acceptProcessRequestInitialCwd(id: number): void { - this._terminalProcesses[id]?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd)); + this._terminalProcesses.get(id)?.getInitialCwd().then(initialCwd => this._proxy.$sendProcessInitialCwd(id, initialCwd)); } public $acceptProcessRequestCwd(id: number): void { - this._terminalProcesses[id]?.getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd)); + this._terminalProcesses.get(id)?.getCwd().then(cwd => this._proxy.$sendProcessCwd(id, cwd)); } public $acceptProcessRequestLatency(id: number): number { @@ -648,7 +655,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ this._bufferer.stopBuffering(id); // Remove process reference - delete this._terminalProcesses[id]; + this._terminalProcesses.delete(id); delete this._extensionTerminalAwaitingStart[id]; // Clean up process disposables diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 552e08e1917..e6b9d6fcb17 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -21,6 +21,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtHostRpcService, ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IURITransformerService, URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IExtHostExtensionService, IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; +import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; export interface IExitFn { (code?: number): any; @@ -61,6 +62,9 @@ export class ExtensionHostMain { // todo@joh // ugly self - inject + const terminalService = instaService.invokeFunction(accessor => accessor.get(IExtHostTerminalService)); + this._disposables.add(terminalService); + const logService = instaService.invokeFunction(accessor => accessor.get(ILogService)); this._disposables.add(logService); From ee8378e8066a70092f773f589b29a4a272a41636 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Sep 2020 18:04:38 +0200 Subject: [PATCH 73/98] file access - merge asDomUri into --- src/vs/base/browser/dom.ts | 16 ++-------------- src/vs/base/browser/markdownRenderer.ts | 4 ++-- src/vs/base/common/network.ts | 4 ++++ src/vs/workbench/browser/dnd.ts | 6 +++--- .../browser/extensionsWorkbenchService.ts | 3 +-- .../contrib/files/browser/fileActions.ts | 6 +++--- .../browser/extensionResourceLoaderService.ts | 5 ++--- 7 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 21d07830544..e4606bf9dec 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -13,7 +13,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Schemas, FileAccess, RemoteAuthorities } from 'vs/base/common/network'; +import { FileAccess, RemoteAuthorities } from 'vs/base/common/network'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; export function clearNode(node: HTMLElement): void { @@ -1219,18 +1219,6 @@ export function animate(fn: () => void): IDisposable { RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? 'https' : 'http'); -export function asDomUri(uri: URI): URI { - if (!uri) { - return uri; - } - - if (uri.scheme === Schemas.vscodeRemote) { - return RemoteAuthorities.rewrite(uri); - } - - return FileAccess.asBrowserUri(uri); -} - /** * returns url('...') */ @@ -1238,7 +1226,7 @@ export function asCSSUrl(uri: URI): string { if (!uri) { return `url('')`; } - return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`; + return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`; } diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 7ccb91a83cd..98ff488bd82 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -14,7 +14,7 @@ import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; import { resolvePath } from 'vs/base/common/resources'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -70,7 +70,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende // and because of that special rewriting needs to be done // so that the URI uses a protocol that's understood by // browsers (like http or https) - return DOM.asDomUri(uri).toString(true); + return FileAccess.asBrowserUri(uri).toString(true); } if (uri.query) { uri = uri.with({ query: _uriMassage(uri.query) }); diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index d768875629f..02cc27db26d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -141,6 +141,10 @@ class FileAccessImpl { asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { const uri = this.toUri(uriOrModule, moduleIdToUrl); + if (uri.scheme === Schemas.vscodeRemote) { + return RemoteAuthorities.rewrite(uri); + } + return uri; } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 38ca25dd768..9a39382b846 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -10,7 +10,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { ITextFileService, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -22,7 +22,7 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { addDisposableListener, EventType, asDomUri } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -322,7 +322,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: // Download URL: enables support to drag a tab as file to desktop (only single file supported) // Disabled for PWA web due to: https://github.com/microsoft/vscode/issues/83441 if (!sources[0].isDirectory && (!isWeb || !isStandalone)) { - event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(sources[0].resource), asDomUri(sources[0].resource).toString()].join(':')); + event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(sources[0].resource), FileAccess.asBrowserUri(sources[0].resource).toString()].join(':')); } // Resource URLs: allows to drop multiple resources to a target in VS Code (not directories) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 7f1d1613ede..d3972e12bcd 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -36,7 +36,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IProductService } from 'vs/platform/product/common/productService'; -import { asDomUri } from 'vs/base/browser/dom'; import { getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; import { isWeb } from 'vs/base/common/platform'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; @@ -135,7 +134,7 @@ class Extension implements IExtension { private get localIconUrl(): string | null { if (this.local && this.local.manifest.icon) { - return asDomUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(true); + return FileAccess.asBrowserUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(true); } return null; } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index f03ff61f269..041deec72e9 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -30,7 +30,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { IDialogService, IConfirmationResult, getFileNamesMessage, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -39,7 +39,7 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { coalesce } from 'vs/base/common/arrays'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { getErrorMessage } from 'vs/base/common/errors'; -import { triggerDownload, asDomUri } from 'vs/base/browser/dom'; +import { triggerDownload } from 'vs/base/browser/dom'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -1015,7 +1015,7 @@ const downloadFileHandler = (accessor: ServicesAccessor) => { try { bufferOrUri = (await fileService.readFile(s.resource, { limits: { size: 1024 * 1024 /* set a limit to reduce memory pressure */ } })).value.buffer; } catch (error) { - bufferOrUri = asDomUri(s.resource); + bufferOrUri = FileAccess.asBrowserUri(s.resource); } triggerDownload(bufferOrUri, s.name); diff --git a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts index be77f2563c6..9895eb264d0 100644 --- a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts +++ b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts @@ -7,8 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader'; -import * as dom from 'vs/base/browser/dom'; -import { Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; class ExtensionResourceLoaderService implements IExtensionResourceLoaderService { @@ -19,7 +18,7 @@ class ExtensionResourceLoaderService implements IExtensionResourceLoaderService ) { } async readExtensionResource(uri: URI): Promise { - uri = dom.asDomUri(uri); + uri = FileAccess.asBrowserUri(uri); if (uri.scheme !== Schemas.http && uri.scheme !== Schemas.https) { const result = await this._fileService.readFile(uri); From c49d699aec28e6939a2b27f86dde8f4ebb7ffb2b Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Sep 2020 17:45:02 -0700 Subject: [PATCH 74/98] comment thread readonly --- src/vs/editor/common/modes.ts | 2 + src/vs/vscode.proposed.d.ts | 10 ++ .../api/browser/mainThreadComments.ts | 18 ++- .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostComments.ts | 17 +++ .../comments/browser/commentThreadWidget.ts | 140 ++++++++++-------- 6 files changed, 128 insertions(+), 60 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 2a16939f8f2..b43e821fd80 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1495,11 +1495,13 @@ export interface CommentThread { comments: Comment[] | undefined; onDidChangeComments: Event; collapsibleState?: CommentThreadCollapsibleState; + readOnly: boolean; input?: CommentInput; onDidChangeInput: Event; onDidChangeRange: Event; onDidChangeLabel: Event; onDidChangeCollasibleState: Event; + onDidChangeReadOnly: Event; isDisposed: boolean; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7184fcdc1a5..6544399ba02 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -2137,4 +2137,14 @@ declare module 'vscode' { constructor(id: string, color?: ThemeColor); } //#endregion + + //#region https://github.com/microsoft/vscode/issues/102665 Comment API @rebornix + export interface CommentThread { + /** + * Whether the thread supports reply. + * Defaults to false. + */ + readOnly: boolean; + } + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index fad1a18c266..b537782a2a3 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -84,6 +84,17 @@ export class MainThreadCommentThread implements modes.CommentThread { return this._range; } + private readonly _onDidChangeReadOnly = new Emitter(); + get onDidChangeReadOnly(): Event { return this._onDidChangeReadOnly.event; } + set readOnly(state: boolean) { + this._readOnly = state; + this._onDidChangeReadOnly.fire(this._readOnly); + } + + get readOnly() { + return this._readOnly; + } + private readonly _onDidChangeRange = new Emitter(); public onDidChangeRange = this._onDidChangeRange.event; @@ -112,7 +123,8 @@ export class MainThreadCommentThread implements modes.CommentThread { public extensionId: string, public threadId: string, public resource: string, - private _range: IRange + private _range: IRange, + private _readOnly: boolean ) { this._isDisposed = false; } @@ -126,6 +138,7 @@ export class MainThreadCommentThread implements modes.CommentThread { if (modified('contextValue')) { this._contextValue = changes.contextValue; } if (modified('comments')) { this._comments = changes.comments; } if (modified('collapseState')) { this._collapsibleState = changes.collapseState; } + if (modified('readOnly')) { this.readOnly = changes.readOnly!; } } dispose() { @@ -214,7 +227,8 @@ export class MainThreadCommentController { extensionId, threadId, URI.revive(resource).toString(), - range + range, + false ); this._threads.set(commentThreadHandle, thread); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 79cd256ccaf..3728f5602df 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -144,6 +144,7 @@ export type CommentThreadChanges = Partial<{ contextValue: string, comments: modes.Comment[], collapseState: modes.CommentThreadCollapsibleState; + readOnly: boolean; }>; export interface MainThreadCommentsShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index d6252341009..3aedb8b1e5f 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -219,6 +219,7 @@ type CommentThreadModification = Partial<{ contextValue: string | undefined, comments: vscode.Comment[], collapsibleState: vscode.CommentThreadCollapsibleState + readOnly: boolean; }>; export class ExtHostCommentThread implements vscode.CommentThread { @@ -263,6 +264,19 @@ export class ExtHostCommentThread implements vscode.CommentThread { return this._range; } + private _readonly: boolean = false; + + set readOnly(state: boolean) { + if (this._readonly !== state) { + this._readonly = state; + this.modifications.readOnly = state; + this._onDidUpdateCommentThread.fire(); + } + } + get readOnly() { + return this._readonly; + } + private _label: string | undefined; get label(): string | undefined { @@ -387,6 +401,9 @@ export class ExtHostCommentThread implements vscode.CommentThread { if (modified('collapsibleState')) { formattedModifications.collapseState = convertToCollapsibleState(this._collapseState); } + if (modified('readOnly')) { + formattedModifications.readOnly = this.readOnly; + } this.modifications = {}; this._proxy.$updateCommentThread( diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 70e9bfcb51e..4738ae56989 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -61,10 +61,10 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget protected _actionbarWidget!: ActionBar; private _bodyElement!: HTMLElement; private _parentEditor: ICodeEditor; - private _commentEditor!: ICodeEditor; + private _commentEditor?: ICodeEditor; private _commentsElement!: HTMLElement; private _commentElements: CommentNode[] = []; - private _commentForm!: HTMLElement; + private _commentForm?: HTMLElement; private _reviewThreadReplyButton!: HTMLElement; private _resizeObserver: any; private readonly _onDidClose = new Emitter(); @@ -82,7 +82,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget private _contextKeyService: IContextKeyService; private _threadIsEmpty: IContextKey; private _commentThreadContextValue: IContextKey; - private _commentEditorIsEmpty!: IContextKey; + private _commentEditorIsEmpty?: IContextKey; private _commentFormActions!: CommentFormActions; private _scopedInstatiationService: IInstantiationService; private _focusedComment: number | undefined = undefined; @@ -367,8 +367,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } - if (!this._reviewThreadReplyButton) { - this.createReplyButton(); + if (!this._reviewThreadReplyButton && this._commentEditor && this._commentForm) { + this.createReplyButton(this._commentEditor, this._commentForm); } if (this._commentThread.comments && this._commentThread.comments.length === 0) { @@ -400,11 +400,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } protected _onWidth(widthInPixel: number): void { - this._commentEditor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); + this._commentEditor?.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); } protected _doLayout(heightInPixel: number, widthInPixel: number): void { - this._commentEditor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); + this._commentEditor?.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); } display(lineNumber: number) { @@ -448,6 +448,50 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } + // create comment thread only when not readonly + if (!this._commentThread.readOnly) { + this.createCommentForm(); + } + + this._resizeObserver = new MutationObserver(this._refresh.bind(this)); + + this._resizeObserver.observe(this._bodyElement, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + + if (this._commentThread.collapsibleState === modes.CommentThreadCollapsibleState.Expanded) { + this.show({ lineNumber: lineNumber, column: 1 }, 2); + } + + // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. + // if this._commentThread.comments is undefined, it doesn't finish initialization yet, so we don't focus the editor immediately. + if (!this._commentThread.readOnly) { + if (!this._commentThread.comments || !this._commentThread.comments.length) { + this._commentEditor?.focus(); + } else if (this._commentEditor && this._commentEditor.getModel()!.getValueLength() > 0) { + this.expandReplyArea(); + } + } + + this._commentThreadDisposables.push(this._commentThread.onDidChangeReadOnly(() => { + if (this._commentForm) { + if (this._commentThread.readOnly) { + this._commentForm.style.display = 'none'; + } else { + this._commentForm.style.display = 'block'; + } + } else { + if (!this._commentThread.readOnly) { + this.createCommentForm(); + } + } + })); + } + + private createCommentForm() { const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0; this._commentForm = dom.append(this._bodyElement, dom.$('.comment-form')); this._commentEditor = this._scopedInstatiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions(), this._parentEditor, this); @@ -472,62 +516,40 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._disposables.add(this._commentEditor); this._disposables.add(this._commentEditor.getModel()!.onDidChangeContent(() => { this.setCommentEditorDecorations(); - this._commentEditorIsEmpty.set(!this._commentEditor.getValue()); + this._commentEditorIsEmpty?.set(!this._commentEditor?.getValue()); })); - this.createTextModelListener(); + this.createTextModelListener(this._commentEditor, this._commentForm); this.setCommentEditorDecorations(); // Only add the additional step of clicking a reply button to expand the textarea when there are existing comments if (hasExistingComments) { - this.createReplyButton(); + this.createReplyButton(this._commentEditor, this._commentForm); } else { if (this._commentThread.comments && this._commentThread.comments.length === 0) { this.expandReplyArea(); } } - this._error = dom.append(this._commentForm, dom.$('.validation-error.hidden')); this._formActions = dom.append(this._commentForm, dom.$('.form-actions')); this.createCommentWidgetActions(this._formActions, model); this.createCommentWidgetActionsListener(); - - this._resizeObserver = new MutationObserver(this._refresh.bind(this)); - - this._resizeObserver.observe(this._bodyElement, { - attributes: true, - childList: true, - characterData: true, - subtree: true - }); - - if (this._commentThread.collapsibleState === modes.CommentThreadCollapsibleState.Expanded) { - this.show({ lineNumber: lineNumber, column: 1 }, 2); - } - - // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. - // if this._commentThread.comments is undefined, it doesn't finish initialization yet, so we don't focus the editor immediately. - if (!this._commentThread.comments || !this._commentThread.comments.length) { - this._commentEditor.focus(); - } else if (this._commentEditor.getModel()!.getValueLength() > 0) { - this.expandReplyArea(); - } } - private createTextModelListener() { - this._commentThreadDisposables.push(this._commentEditor.onDidFocusEditorWidget(() => { + private createTextModelListener(commentEditor: ICodeEditor, commentForm: HTMLElement) { + this._commentThreadDisposables.push(commentEditor.onDidFocusEditorWidget(() => { this._commentThread.input = { - uri: this._commentEditor.getModel()!.uri, - value: this._commentEditor.getValue() + uri: commentEditor.getModel()!.uri, + value: commentEditor.getValue() }; this.commentService.setActiveCommentThread(this._commentThread); })); - this._commentThreadDisposables.push(this._commentEditor.getModel()!.onDidChangeContent(() => { - let modelContent = this._commentEditor.getValue(); - if (this._commentThread.input && this._commentThread.input.uri === this._commentEditor.getModel()!.uri && this._commentThread.input.value !== modelContent) { + this._commentThreadDisposables.push(commentEditor.getModel()!.onDidChangeContent(() => { + let modelContent = commentEditor.getValue(); + if (this._commentThread.input && this._commentThread.input.uri === commentEditor.getModel()!.uri && this._commentThread.input.value !== modelContent) { let newInput: modes.CommentInput = this._commentThread.input; newInput.value = modelContent; this._commentThread.input = newInput; @@ -538,20 +560,20 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThreadDisposables.push(this._commentThread.onDidChangeInput(input => { let thread = this._commentThread; - if (thread.input && thread.input.uri !== this._commentEditor.getModel()!.uri) { + if (thread.input && thread.input.uri !== commentEditor.getModel()!.uri) { return; } if (!input) { return; } - if (this._commentEditor.getValue() !== input.value) { - this._commentEditor.setValue(input.value); + if (commentEditor.getValue() !== input.value) { + commentEditor.setValue(input.value); if (input.value === '') { this._pendingComment = ''; - this._commentForm.classList.remove('expand'); - this._commentEditor.getDomNode()!.style.outline = ''; + commentForm.classList.remove('expand'); + commentEditor.getDomNode()!.style.outline = ''; this._error.textContent = ''; this._error.classList.add('hidden'); } @@ -639,7 +661,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } action.run({ thread: this._commentThread, - text: this._commentEditor.getValue(), + text: this._commentEditor?.getValue(), $mid: 8 }); @@ -696,23 +718,25 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private expandReplyArea() { - if (!this._commentForm.classList.contains('expand')) { - this._commentForm.classList.add('expand'); - this._commentEditor.focus(); + if (!this._commentForm?.classList.contains('expand')) { + this._commentForm?.classList.add('expand'); + this._commentEditor?.focus(); } } private hideReplyArea() { - this._commentEditor.setValue(''); + if (this._commentEditor) { + this._commentEditor.setValue(''); + this._commentEditor.getDomNode()!.style.outline = ''; + } this._pendingComment = ''; - this._commentForm.classList.remove('expand'); - this._commentEditor.getDomNode()!.style.outline = ''; + this._commentForm?.classList.remove('expand'); this._error.textContent = ''; this._error.classList.add('hidden'); } - private createReplyButton() { - this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$(`button.review-thread-reply-button.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); + private createReplyButton(commentEditor: ICodeEditor, commentForm: HTMLElement) { + this._reviewThreadReplyButton = dom.append(commentForm, dom.$(`button.review-thread-reply-button.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); this._reviewThreadReplyButton.title = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); this._reviewThreadReplyButton.textContent = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); @@ -720,9 +744,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._disposables.add(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea())); this._disposables.add(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); - this._commentEditor.onDidBlurEditorWidget(() => { - if (this._commentEditor.getModel()!.getValueLength() === 0 && this._commentForm.classList.contains('expand')) { - this._commentForm.classList.remove('expand'); + commentEditor.onDidBlurEditorWidget(() => { + if (commentEditor.getModel()!.getValueLength() === 0 && commentForm.classList.contains('expand')) { + commentForm.classList.remove('expand'); } }); } @@ -753,7 +777,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } if (!this._commentThread.comments || !this._commentThread.comments.length) { - this._commentEditor.focus(); + this._commentEditor?.focus(); } this._relayout(computedLinesNumber); @@ -785,7 +809,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } }]; - this._commentEditor.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); + this._commentEditor?.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); } } From d026ae2a79a48258934545163dd634f09eb2656d Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 25 Sep 2020 09:18:40 -0700 Subject: [PATCH 75/98] fix #102665. --- .../comments/browser/commentThreadWidget.ts | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 4738ae56989..9b87466ccf1 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -61,10 +61,13 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget protected _actionbarWidget!: ActionBar; private _bodyElement!: HTMLElement; private _parentEditor: ICodeEditor; - private _commentEditor?: ICodeEditor; private _commentsElement!: HTMLElement; private _commentElements: CommentNode[] = []; - private _commentForm?: HTMLElement; + private _commentReplyComponent?: { + editor: ICodeEditor; + form: HTMLElement; + commentEditorIsEmpty: IContextKey; + }; private _reviewThreadReplyButton!: HTMLElement; private _resizeObserver: any; private readonly _onDidClose = new Emitter(); @@ -82,7 +85,6 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget private _contextKeyService: IContextKeyService; private _threadIsEmpty: IContextKey; private _commentThreadContextValue: IContextKey; - private _commentEditorIsEmpty?: IContextKey; private _commentFormActions!: CommentFormActions; private _scopedInstatiationService: IInstantiationService; private _focusedComment: number | undefined = undefined; @@ -199,8 +201,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } public getPendingComment(): string | null { - if (this._commentEditor) { - let model = this._commentEditor.getModel(); + if (this._commentReplyComponent) { + let model = this._commentReplyComponent.editor.getModel(); if (model && model.getValueLength() > 0) { // checking length is cheap return model.getValue(); @@ -367,8 +369,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } - if (!this._reviewThreadReplyButton && this._commentEditor && this._commentForm) { - this.createReplyButton(this._commentEditor, this._commentForm); + if (!this._reviewThreadReplyButton && this._commentReplyComponent) { + this.createReplyButton(this._commentReplyComponent.editor, this._commentReplyComponent.form); } if (this._commentThread.comments && this._commentThread.comments.length === 0) { @@ -400,11 +402,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } protected _onWidth(widthInPixel: number): void { - this._commentEditor?.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); + this._commentReplyComponent?.editor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); } protected _doLayout(heightInPixel: number, widthInPixel: number): void { - this._commentEditor?.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); + this._commentReplyComponent?.editor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); } display(lineNumber: number) { @@ -468,20 +470,20 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. // if this._commentThread.comments is undefined, it doesn't finish initialization yet, so we don't focus the editor immediately. - if (!this._commentThread.readOnly) { + if (!this._commentThread.readOnly && this._commentReplyComponent) { if (!this._commentThread.comments || !this._commentThread.comments.length) { - this._commentEditor?.focus(); - } else if (this._commentEditor && this._commentEditor.getModel()!.getValueLength() > 0) { + this._commentReplyComponent.editor.focus(); + } else if (this._commentReplyComponent.editor.getModel()!.getValueLength() > 0) { this.expandReplyArea(); } } this._commentThreadDisposables.push(this._commentThread.onDidChangeReadOnly(() => { - if (this._commentForm) { + if (this._commentReplyComponent) { if (this._commentThread.readOnly) { - this._commentForm.style.display = 'none'; + this._commentReplyComponent.form.style.display = 'none'; } else { - this._commentForm.style.display = 'block'; + this._commentReplyComponent.form.style.display = 'block'; } } else { if (!this._commentThread.readOnly) { @@ -493,10 +495,16 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget private createCommentForm() { const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0; - this._commentForm = dom.append(this._bodyElement, dom.$('.comment-form')); - this._commentEditor = this._scopedInstatiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions(), this._parentEditor, this); - this._commentEditorIsEmpty = CommentContextKeys.commentIsEmpty.bindTo(this._contextKeyService); - this._commentEditorIsEmpty.set(!this._pendingComment); + const commentForm = dom.append(this._bodyElement, dom.$('.comment-form')); + const commentEditor = this._scopedInstatiationService.createInstance(SimpleCommentEditor, commentForm, SimpleCommentEditor.getEditorOptions(), this._parentEditor, this); + const commentEditorIsEmpty = CommentContextKeys.commentIsEmpty.bindTo(this._contextKeyService); + commentEditorIsEmpty.set(!this._pendingComment); + + this._commentReplyComponent = { + form: commentForm, + editor: commentEditor, + commentEditorIsEmpty + }; const modeId = generateUuid() + '-' + (hasExistingComments ? this._commentThread.threadId : ++INMEM_MODEL_ID); const params = JSON.stringify({ @@ -512,28 +520,28 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget const model = this.modelService.createModel(this._pendingComment || '', this.modeService.createByFilepathOrFirstLine(resource), resource, false); this._disposables.add(model); - this._commentEditor.setModel(model); - this._disposables.add(this._commentEditor); - this._disposables.add(this._commentEditor.getModel()!.onDidChangeContent(() => { + commentEditor.setModel(model); + this._disposables.add(commentEditor); + this._disposables.add(commentEditor.getModel()!.onDidChangeContent(() => { this.setCommentEditorDecorations(); - this._commentEditorIsEmpty?.set(!this._commentEditor?.getValue()); + commentEditorIsEmpty?.set(!commentEditor.getValue()); })); - this.createTextModelListener(this._commentEditor, this._commentForm); + this.createTextModelListener(commentEditor, commentForm); this.setCommentEditorDecorations(); // Only add the additional step of clicking a reply button to expand the textarea when there are existing comments if (hasExistingComments) { - this.createReplyButton(this._commentEditor, this._commentForm); + this.createReplyButton(commentEditor, commentForm); } else { if (this._commentThread.comments && this._commentThread.comments.length === 0) { this.expandReplyArea(); } } - this._error = dom.append(this._commentForm, dom.$('.validation-error.hidden')); + this._error = dom.append(commentForm, dom.$('.validation-error.hidden')); - this._formActions = dom.append(this._commentForm, dom.$('.form-actions')); + this._formActions = dom.append(commentForm, dom.$('.form-actions')); this.createCommentWidgetActions(this._formActions, model); this.createCommentWidgetActionsListener(); } @@ -661,7 +669,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } action.run({ thread: this._commentThread, - text: this._commentEditor?.getValue(), + text: this._commentReplyComponent?.editor.getValue(), $mid: 8 }); @@ -718,19 +726,19 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private expandReplyArea() { - if (!this._commentForm?.classList.contains('expand')) { - this._commentForm?.classList.add('expand'); - this._commentEditor?.focus(); + if (!this._commentReplyComponent?.form.classList.contains('expand')) { + this._commentReplyComponent?.form.classList.add('expand'); + this._commentReplyComponent?.editor.focus(); } } private hideReplyArea() { - if (this._commentEditor) { - this._commentEditor.setValue(''); - this._commentEditor.getDomNode()!.style.outline = ''; + if (this._commentReplyComponent) { + this._commentReplyComponent.editor.setValue(''); + this._commentReplyComponent.editor.getDomNode()!.style.outline = ''; } this._pendingComment = ''; - this._commentForm?.classList.remove('expand'); + this._commentReplyComponent?.form.classList.remove('expand'); this._error.textContent = ''; this._error.classList.add('hidden'); } @@ -777,7 +785,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } if (!this._commentThread.comments || !this._commentThread.comments.length) { - this._commentEditor?.focus(); + this._commentReplyComponent?.editor.focus(); } this._relayout(computedLinesNumber); @@ -785,7 +793,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } private setCommentEditorDecorations() { - const model = this._commentEditor && this._commentEditor.getModel(); + const model = this._commentReplyComponent && this._commentReplyComponent.editor.getModel(); if (model) { const valueLength = model.getValueLength(); const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0; @@ -809,7 +817,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } }]; - this._commentEditor?.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); + this._commentReplyComponent?.editor.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); } } From 0f56339398a02ed75f5899de7010101a5af26a54 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Sep 2020 11:11:28 -0700 Subject: [PATCH 76/98] Return a types.MarkdownString instace to extensions (#107415) Fixes #107414 --- src/vs/workbench/api/common/extHostTypeConverters.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f10f2fa7b89..7d59ed2e3c7 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -338,7 +338,9 @@ export namespace MarkdownString { } export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { - return new htmlContent.MarkdownString(value.value, { isTrusted: value.isTrusted, supportThemeIcons: value.supportThemeIcons }); + const result = new types.MarkdownString(value.value, value.supportThemeIcons); + result.isTrusted = value.isTrusted; + return result; } export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString { From efd5ffa9c91c5ea38d64f9a1874b2d732616e082 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 25 Sep 2020 11:13:07 -0700 Subject: [PATCH 77/98] fixes #107448 --- src/vs/workbench/browser/layout.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index b17344ed60d..1fd4b7435f6 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1543,6 +1543,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } setPanelHidden(hidden: boolean, skipLayout?: boolean): void { + const wasHidden = this.state.panel.hidden; this.state.panel.hidden = hidden; // Return if not initialized fully #105480 @@ -1581,21 +1582,23 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.toggleMaximizedPanel(); } - // Propagate layout changes to grid - if (!skipLayout) { - this.workbenchGrid.setViewVisible(this.panelPartView, !hidden); - // If in process of showing, toggle whether or not panel is maximized - if (!hidden) { - if (isPanelMaximized !== panelOpensMaximized) { - this.toggleMaximizedPanel(); - } - } - else { - // If in process of hiding, remember whether the panel is maximized or not - this.state.panel.wasLastMaximized = isPanelMaximized; - } + // Don't proceed if we have already done this before + if (wasHidden === hidden) { + return; } + // Propagate layout changes to grid + this.workbenchGrid.setViewVisible(this.panelPartView, !hidden); + // If in process of showing, toggle whether or not panel is maximized + if (!hidden) { + if (isPanelMaximized !== panelOpensMaximized) { + this.toggleMaximizedPanel(); + } + } + else { + // If in process of hiding, remember whether the panel is maximized or not + this.state.panel.wasLastMaximized = isPanelMaximized; + } // Remember in settings if (!hidden) { this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); From 607ee4ae725439dc4ac0f0e6e9f524011a706789 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 25 Sep 2020 12:24:08 -0700 Subject: [PATCH 78/98] Update Codicons: add 'check-all' https://github.com/microsoft/vscode-codicons/commit/010d5a32290efc22ed9d6e894d7c62444c5e8ddd --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 61820 -> 61944 bytes src/vs/base/common/codicons.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 2d45e4f0b599ac7ec696ffa0fe434a524d622f5c..d946d2bd742081f4dba18ab00a5c5fe348fb354d 100644 GIT binary patch delta 5394 zcmYM&dt6mj76qs&TqT^82xHA=scR3(y0aVXfP~Utpcl#nBzym~Xy|(GrdAl2v%7Mg*z*}|m=hipI7o@(!*Pf!n z`FtYa<1YPqzMInLFIciHh*$Fcz5=>rH7%M`|0myr%YgWo0G~|@>X$Y9%mc5N?tD@5 z!ukbsQ=W|X5-Dx8xK-?PwueF^YIXk@gM|AZ) z>wd-WQKVI(e{l=FUA`m|AS^nWQGNCm|UX&Nx4@q&C^*(;BApwyCJXhWkzQORl za+bvy4ETBA=W~+Rg!GSCNok%@lDLN@v% z7x@@~0u00;48{-?p&Vl|4iiv`$(VvFOhq-OqXsi@4QeqHvoITVsK*@4#kIH|H(;R` zi_nZ4u^2aD32w$MScY418*ayP+<`mMg1fMSuelp5aS!grYTS=M-~l{{_1J)iu@R5p zk9ZV+!ee+GoACs;;7L4X)Xo-)8D_4+amIOy z84h!;!ao`3D`q{+bzXPiPu|6Nz2Z`Uxj~_ju}N`xz${ScU|gt#i`1~tI9xn1&5Fwi z=0?Q@1+!RjX~Ep2;B}vBiQ;;Lxmj`L!7NqCVZ23gHNvn8Ib4@8w<@l5nA;TBD$MPQ zs}_b;(c$`qxkGU!!`umY-NBVHA?p7=VFe??-Fy5o2 zAEUb^#3BK6ufjaW)k=EtYU3^<31z%r33J4(RT9tmfD-p9A5@aa_>hux#`Q{iGP+-o zTx~YYEeG!X9##^`xY6<|V~42lKLG!Gn24vGl>bs#pYJb}E)bnAa2wBaF*UVu^%#U9nig>{cwB zFmJe9$pb4W%pS#B3iGC7RfXBBSYKh@QmnKv`xI*~%--0O;3+&gs@NZ3K341$Fvk>o2Fxdl-2>*hVjqF|RI#JLoKWm7FrO)Q8JLrb z{RZZ9#m>XUbBYII6N33dp^oviVq=2&Qn5wBe5Kf|VE&`nwqVXEHZYj~Dz-A1vkD6t zzgFyAFh+UV=wPg3%Y!+m*!*C=QEY=S=M@_w%(sfI5$1wolZ5$Bv0cJ^uh=+Yeo$<2kWA!~Cw; zr(rr3J2uQ^g?oAb`Qfi%8;2Feh7Rka*xF%z6`MS)pJKa*?V{NDVf__H0N4P%=-z_O4zoGD<#6{ibWkK&vG+f88$V}#=D z0UN0}g}_EB&Li;JXg(m0Ca^JzLkeuH;$4c$=}W;)n&C>F$3fKmB`*{S@ae*et~f3^rSF7K6=EoXTMPE6!)Ixr&n-Y@XuG z2Ai)qy}=Gpoa0~%6uifHGEi~2gDq4X^I!)l4t%hK6-PhVAquA$ixkH}*rAGpA#AbY zhzL7Oaae>Mt~fTrj&S#%2TqW%C5p2o>`29_5_Xj0dRangh>Rh&6t$0$ypuw{yK zC~UdnL<&1raW;h=r#Pj0VaM|UabSg=pg6k1PE;IXVJ9h$v#=G4gDq^O;)n}-wc@Y~ zJ6Yi};}j+Q5w=Q+FXL3l%U15klWHaYjMJ0^FiuzE)^LrIV8$6r+_Jq!NjPJz5)b1{ zCEXZjDT!d5tt6V!TgL-Q3}d~LSjGk=ag1}6xFy!8B#CjZl4QntN>UlGRnnJnzLHGF z>y)_UzFv97W%mtATy{4paoN2iX;I=b^e!bXLsuwnZD8+K+~mNnRNU^s-lMqjfnBAz1%ka-aWe$FT5($hd!OP4 z33d(D@W8zi?EQ+nCfKzK?zjGf;!X;7o#LJf_5sD+73_nG`z+Xp6pCAO;?nY7<)eoc z_g=6Y6?b8%O7K6!&Pbk1OuhU^grL!1#pX4i0vU;$9B+NyS|q zth=7Lzk_{Rai<5nRp9{RpA~n1u-jZs;(;4MSoducw}i0n+a|d5cvf+j2-~K(Uxanv zJaOj;yIpY)3Hvw2-6ZUDiu+2~=M{IDurDa?HM_#Peb|=B!MOam zmbeS?mGSH1&-Q5Ou_M7hp(0^d!l}fl#KOe=iD#2Sl5&z(ChbYO*mGfWLUMERu9UKr zlc{S`52me3PfkD6Ygw;7y$)xjW;A7N$T;4+wD+psJNx+eY3lP-pN_uOecSqaFJ)$B zuIyLVZ+XA-S+!YPvVF44vYWF{W_RSo=N!m6(?6_#S^pimjk$Ysf6A-MYsuS|cPQU4 ze_8(e{PP2n1~d+68*r{5qoB56UBUi=Ap?sCE-wr&Y#20e(6&LR2TvZndr0z-h9OTC z1s5e1RTXV6I$6{)G;?Uh(8I+s#cji4hP8Nyogbb$eB?Y(dnaGM!z)rLTOd$o-r9?>c*@Yvwh6DvZAuavd7AfmzS2WEEx6SFWmj zuJYW~JFdPkxjtj^;wc4F>ZV+%%Bk8`b$F_0>V~O@s}rgls@tl6o0c(c>9oz$4oy2Z zy<+-vHH|gfYIfEfs5wz{e#Z0}TV`CkChM9NwZXNuwd-q-&Mcg{V&;*V9kY^V&6>4- zcGT>`y0E&2x)<7u6B9i%{g`m@{m3^P{`~*A5JUn&(%W}XY>-cH35exi_rUj1XZP&4 z`fryHJCh|MtMk%`wAHEk?it^J-}m}RWXeUK^v*|QbVld9<8#aNhF*CJ?bX?l?J}YL i`coTxe0(Ohcb@Jk!9jE8&z*Bac70P*d*WBKL;nZpztKJb delta 5235 zcmXZg3w)2~*$437l@LkDQ4$dnhXg?qNd!RS!>qbx0+dNt(oq3`@KG&?{huRKgs{}T=#Wdw=X}A zzVT7?w%o}5fNcR#yI}dehLdkzzXNE|3TVA^$+G(vZz|mIIFMZd{2WUc&0CmNw4|KB zd!G6(G`ao4+7`-0#We|EAL+2rZ{R1pMNxvQnvkQ?vQF&ctYUxwDOcWNflq9 zW%MWEH9p*)65h%G$FHwPe-`~IFJloE9_2s&ja4`0*pKYR!|zx{^QPn$t+NwvJsqNB z5q;~dfH59umWXCiXPYWpq}z*4wJk~(Jqw4w;Sqdb-Ehj9;U^Z0J~j+rneln=L|43v zdH5!Nh(ha%S|r*~+=B(UfM4MlUbkXAh4pycdRS{LvOH^L?X0aOSbNL09BYFoEY&(T zU24%GQh`Ajj3F3?;YdR|GSCK@XovR5Mh-flBRZioa*>BF=!SfBM-TKw0gBKIB`8I2 zl%X&Bp&b2Dg;5xdYD~l=OvV&U#WYOE4AfyJZo@3hMm^?WF6Lt)7UOo@f#ngbz)CdW zF08`cSdBHf7i)1J8gW0?;Q>Uj0T1$bHsT>Xj7RYpHe(C6;z?{r6L#Qf?8LY5ZS2DT z;2C@eyYXG@!Cvgc_pl!?;2?g0L--M1!eP9OSMVxc!%vwnKf`gR)GrZv1HZ-z{2$)N zZ}AR(hu`Bp`~j!&KK_Wa_!G|I&rG+!;3IsDzv3c3!Kd)JgwJppf5#R41OLS5_yX7P zFMNp`_z!O4Ym2gIYi=>t!s0C65-rJ+EydC--7+lGvMk#=SSQQ3?h)&01y*Q9*2_w) z)OuT)^|8KIZvAb54YUdyWJ9ddhT8}mX;n7LM%x%XhwtNg96%+8VjRX}0!CP8j71H` zU?AEe3rDa7%dpM*G0l=N693D*ziM6Z7B=DExQ>(762HM2qz|ycD8`Sm6yLy$_ys;> z+QvNFDc(|XQiXd! zxM<jN8Q8Gj12lWejhMEQhg4Sajfa2*Q&;Eg{d` zPGL#flkoZ=7Gw8)VR3@nFDz4V&kG9{+yPk)5biZ$F@*c6uq?tI6&6UipM@OdfwdCun6PTXgH{5T9B^>S@VKImMov^IKy(=v6aK9Ipdbsz5MIY`DL4+?nnJ1@(odE8A zVNZbjqp&-`eIV=;aA$-a1MWj%?|?fi>>_Y~6800gbHdI7_h(^`;Q~6(1F_w}{YBV# z;64(}W&Bv!jNmQ^+Y;Pgg$)YsqOeuL{Y}`k;64$A67;FCN5MIXu%*FyVRM7KBy4kV zp9vct++|_wgZsO%3Bp|wwnMmo2pc2ZRbh*S`=_v3!hJ4mn{Z!*`~L+${r$YSChVnf z{}OgpxG#mj3ip*@1LJjJ&xQN9-1=lUcuwrYaQ_i@WVoBc-VFD(;88v)Km09h*zhK7 z-SAPuCJrAhZ0GRJgpD1(xv<5<#|WD}d<$XQhmREw0Pu0bQ2;)kB0O*^fNv?B58zt~ zCk6Nf;miP^D4ZVPlZ0~we6nz&fKL(57VxQpJ&dh|^9FpHaPmOJr}Kh1gurJA#}W88 z!odVSQ#hi)w-pX6@L9sK1-_kdfPrr>9A)6Mg+mQ|j&Qtz?{Evh|5td@Q8@F!cM?uN z@STNo5PYtL3EJlgXCwG7g5!)`h4T`8H{s+2pD%cWvAb}Xg6|RTe-D286O28D6Bc}d zaMpq^6i!|6MZ)5TKlG8pGe*#G$jVqv*0 z6w78@B-Vj(u~;X@C1UxEOU1e~-Yymr{SJv($n-nKLZ&Yh3z@!LEM)o$v5?&>#X@#B zh=uIFODtsfDzT8|5NO~MTj{Nuu{5d3DUBlsaPY1tGaFX$R!hIe*zu-tXaLWfDzG33#4}QNO zq(}I+iTgqL@NE-!hVU;4_lWQZg}X)g9|-r2@P~vuNcbNL_mc48nz*YpG`*ZPFp?L$ zDt1%s?${%-Z^vGVi-}8%E00?owPPfl#zq$R3?Js1VKyH{gILth$JFl^Vb&xh9xe|Y%u5qTrFj<`0mc;w8H>qZ`}imxiG zT2^&nRNAN+qjrxj9=&Hw&X_yKT&XUtUR`~DY{A%rW8WEjb$qk&!^YQ--#q@(gyacT z6ShsPn%FS$rHR*TI@i?JtgksbY2&2hldeskH*89?DaBKcPq{gD&D6b9&re%8ZSS;8 zwWYOSXkIu-MvA(XXZcW{$x?Ob#>yFLLpV=_;#LOGFO}_2Stb$qd zXFWGNX7=RS`(~e-eYw7 Date: Fri, 25 Sep 2020 22:23:34 +0200 Subject: [PATCH 79/98] Move `deltaExtensions` related methods up to `AbstractExtensionService` --- .../api/common/extHostExtensionService.ts | 2 +- .../api/common/shared/workspaceContains.ts | 6 +- .../extensions/browser/extensionService.ts | 15 +- .../common/abstractExtensionService.ts | 246 +++++++++++++++++- .../electron-browser/extensionService.ts | 242 ++--------------- 5 files changed, 269 insertions(+), 242 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 53d3612cc5d..311b529e5ad 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -506,7 +506,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme const host: IExtensionActivationHost = { folders: folders.map(folder => folder.uri), forceUsingSearch: localWithRemote, - exists: (path) => this._hostUtils.exists(path), + exists: (uri) => this._hostUtils.exists(uri.fsPath), checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token) }; diff --git a/src/vs/workbench/api/common/shared/workspaceContains.ts b/src/vs/workbench/api/common/shared/workspaceContains.ts index 7f883983ea7..629c9994a87 100644 --- a/src/vs/workbench/api/common/shared/workspaceContains.ts +++ b/src/vs/workbench/api/common/shared/workspaceContains.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; +import * as resources from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; @@ -19,7 +19,7 @@ export interface IExtensionActivationHost { readonly folders: readonly UriComponents[]; readonly forceUsingSearch: boolean; - exists(path: string): Promise; + exists(uri: URI): Promise; checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise; } @@ -69,7 +69,7 @@ export function checkActivateWorkspaceContainsExtension(host: IExtensionActivati async function _activateIfFileName(host: IExtensionActivationHost, fileName: string, activate: (activationEvent: string) => void): Promise { // find exact path for (const uri of host.folders) { - if (await host.exists(path.join(URI.revive(uri).fsPath, fileName))) { + if (await host.exists(resources.joinPath(URI.revive(uri), fileName))) { // the file was found activate(`workspaceContains:${fileName}`); return; diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 9e979d28691..bac3d250878 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -19,13 +19,15 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension } from 'vs/platform/extensions/common/extensions'; import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -41,6 +43,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IConfigurationService private readonly _configService: IConfigurationService, @@ -56,6 +60,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten extensionEnablementService, fileService, productService, + extensionManagementService, + contextService, ); this._runningLocation = new Map(); @@ -74,6 +80,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten super.dispose(); } + protected async _scanSingleExtension(extension: IExtension): Promise { + return null; + } + + protected async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + } + private _initFetchFileSystem(): void { const provider = new FetchFileSystemProvider(); this._disposables.add(this._fileService.registerProvider(Schemas.http, provider)); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c89897ab69c..ccd8680d38b 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Barrier } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -20,11 +21,14 @@ 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 } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension } 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'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -39,6 +43,13 @@ export function parseScannedExtension(extension: ITranslatedScannedExtension): I }; } +class DeltaExtensionsQueueItem { + constructor( + public readonly toAdd: IExtension[], + public readonly toRemove: string[] + ) { } +} + export abstract class AbstractExtensionService extends Disposable implements IExtensionService { public _serviceBrand: undefined; @@ -66,6 +77,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private readonly _proposedApiController: ProposedApiController; private readonly _isExtensionDevHost: boolean; protected readonly _isExtensionDevTestFromCli: boolean; + private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; + private _inHandleDeltaExtensions: boolean; // --- Members used per extension host process protected _extensionHostManagers: ExtensionHostManager[]; @@ -80,7 +93,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx @ITelemetryService protected readonly _telemetryService: ITelemetryService, @IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService protected readonly _fileService: IFileService, - @IProductService protected readonly _productService: IProductService + @IProductService protected readonly _productService: IProductService, + @IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, ) { super(); @@ -103,8 +118,225 @@ export abstract class AbstractExtensionService extends Disposable implements IEx const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + + this._deltaExtensionsQueue = []; + this._inHandleDeltaExtensions = false; + + this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { + let toAdd: IExtension[] = []; + let toRemove: string[] = []; + for (const extension of extensions) { + if (this._extensionEnablementService.isEnabled(extension)) { + // an extension has been enabled + toAdd.push(extension); + } else { + // an extension has been disabled + toRemove.push(extension.identifier.id); + } + } + this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); + })); + + this._register(this._extensionManagementService.onDidInstallExtension((event) => { + if (event.local) { + if (this._extensionEnablementService.isEnabled(event.local)) { + // an extension has been installed + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); + } + } + })); + + this._register(this._extensionManagementService.onDidUninstallExtension((event) => { + if (!event.error) { + // an extension has been uninstalled + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); + } + })); } + //#region deltaExtensions + + private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { + this._deltaExtensionsQueue.push(item); + if (this._inHandleDeltaExtensions) { + // Let the current item finish, the new one will be picked up + return; + } + + while (this._deltaExtensionsQueue.length > 0) { + const item = this._deltaExtensionsQueue.shift()!; + try { + this._inHandleDeltaExtensions = true; + await this._deltaExtensions(item.toAdd, item.toRemove); + } finally { + this._inHandleDeltaExtensions = false; + } + } + } + + private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { + if (this._environmentService.configuration.remoteAuthority) { + return; + } + + let toAdd: IExtensionDescription[] = []; + for (let i = 0, len = _toAdd.length; i < len; i++) { + const extension = _toAdd[i]; + + if (!this._canAddExtension(extension)) { + continue; + } + + const extensionDescription = await this._scanSingleExtension(extension); + if (!extensionDescription) { + // could not scan extension... + continue; + } + + toAdd.push(extensionDescription); + } + + let toRemove: IExtensionDescription[] = []; + for (let i = 0, len = _toRemove.length; i < len; i++) { + const extensionId = _toRemove[i]; + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + // ignore disabling/uninstalling an extension which is not running + continue; + } + + if (!this.canRemoveExtension(extensionDescription)) { + // uses non-dynamic extension point or is activated + continue; + } + + toRemove.push(extensionDescription); + } + + if (toAdd.length === 0 && toRemove.length === 0) { + return; + } + + // Update the local registry + const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); + this._onDidChangeExtensions.fire(undefined); + + toRemove = toRemove.concat(result.removedDueToLooping); + if (result.removedDueToLooping.length > 0) { + this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); + } + + // enable or disable proposed API per extension + this._checkEnableProposedApi(toAdd); + + // Update extension points + this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); + + // Update the extension host + this._updateExtensionsOnExtHosts(toAdd, toRemove.map(e => e.identifier)); + + for (let i = 0; i < toAdd.length; i++) { + this._activateAddedExtensionIfNeeded(toAdd[i]); + } + } + + public canAddExtension(extensionDescription: IExtensionDescription): boolean { + return this._canAddExtension(toExtension(extensionDescription)); + } + + protected _canAddExtension(extension: IExtension): boolean { + const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); + if (extensionDescription) { + // this extension is already running (most likely at a different version) + return false; + } + + // Check if extension is renamed + if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { + return false; + } + + return true; + } + + public canRemoveExtension(extension: IExtensionDescription): boolean { + const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + if (!extensionDescription) { + // ignore removing an extension which is not running + return false; + } + + if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extensionDescription.identifier))) { + // Extension is running, cannot remove it safely + return false; + } + + return true; + } + + private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise { + let shouldActivate = false; + let shouldActivateReason: string | null = null; + let hasWorkspaceContains = false; + if (Array.isArray(extensionDescription.activationEvents)) { + for (let activationEvent of extensionDescription.activationEvents) { + // TODO@joao: there's no easy way to contribute this + if (activationEvent === 'onUri') { + activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; + } + + if (this._allRequestedActivateEvents.has(activationEvent)) { + // This activation event was fired before the extension was added + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + + if (activationEvent === '*') { + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + + if (/^workspaceContains/.test(activationEvent)) { + hasWorkspaceContains = true; + } + + if (activationEvent === 'onStartupFinished') { + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + } + } + + if (shouldActivate) { + await Promise.all( + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) + ).then(() => { }); + } else if (hasWorkspaceContains) { + const workspace = await this._contextService.getCompleteWorkspace(); + const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority; + const host: IWorkspaceContainsActivationHost = { + folders: workspace.folders.map(folder => folder.uri), + forceUsingSearch: forceUsingSearch, + exists: (uri) => this._fileService.exists(uri), + checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)) + }; + + const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription); + if (!result) { + return; + } + + await Promise.all( + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent })) + ).then(() => { }); + } + } + + //#endregion + protected async _initialize(): Promise { perf.mark('willLoadExtensions'); this._startExtensionHosts(true, []); @@ -169,14 +401,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx //#region IExtensionService - public canAddExtension(extension: IExtensionDescription): boolean { - return false; - } - - public canRemoveExtension(extension: IExtensionDescription): boolean { - return false; - } - public restartExtensionHost(): void { this._stopExtensionHosts(); this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); @@ -463,6 +687,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[]; protected abstract _scanAndHandleExtensions(): Promise; + protected abstract _scanSingleExtension(extension: IExtension): Promise; + protected abstract _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; public abstract _onExtensionHostExit(code: number): void; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 98d13a948ed..088e023d5fe 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -25,7 +25,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; -import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -37,18 +36,10 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; -import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { exists } from 'vs/base/node/pfs'; import { ILogService } from 'vs/platform/log/common/log'; import { CATEGORIES } from 'vs/workbench/common/actions'; - -class DeltaExtensionsQueueItem { - constructor( - public readonly toAdd: IExtension[], - public readonly toRemove: string[] - ) { } -} +import { Schemas } from 'vs/base/common/network'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -56,7 +47,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten private readonly _remoteInitData: Map; private _runningLocation: Map; private readonly _extensionScanner: CachedExtensionScanner; - private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -66,7 +56,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -76,7 +67,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IHostService private readonly _hostService: IHostService, @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILogService private readonly _logService: ILogService, ) { super( @@ -86,7 +76,9 @@ export class ExtensionService extends AbstractExtensionService implements IExten telemetryService, extensionEnablementService, fileService, - productService + productService, + extensionManagementService, + contextService, ); this._enableLocalWebWorker = this._configurationService.getValue(webWorkerExtHostConfig); @@ -95,38 +87,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._runningLocation = new Map(); this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); - this._deltaExtensionsQueue = []; - - this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { - let toAdd: IExtension[] = []; - let toRemove: string[] = []; - for (const extension of extensions) { - if (this._extensionEnablementService.isEnabled(extension)) { - // an extension has been enabled - toAdd.push(extension); - } else { - // an extension has been disabled - toRemove.push(extension.identifier.id); - } - } - this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); - })); - - this._register(this._extensionManagementService.onDidInstallExtension((event) => { - if (event.local) { - if (this._extensionEnablementService.isEnabled(event.local)) { - // an extension has been installed - this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); - } - } - })); - - this._register(this._extensionManagementService.onDidUninstallExtension((event) => { - if (!event.error) { - // an extension has been uninstalled - this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); - } - })); // delay extension host creation and extension scanning // until the workbench is running. we cannot defer the @@ -163,101 +123,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return null; } - //#region deltaExtensions - - private _inHandleDeltaExtensions = false; - private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { - this._deltaExtensionsQueue.push(item); - if (this._inHandleDeltaExtensions) { - // Let the current item finish, the new one will be picked up - return; - } - - while (this._deltaExtensionsQueue.length > 0) { - const item = this._deltaExtensionsQueue.shift()!; - try { - this._inHandleDeltaExtensions = true; - await this._deltaExtensions(item.toAdd, item.toRemove); - } finally { - this._inHandleDeltaExtensions = false; - } - } - } - - private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { - if (this._environmentService.configuration.remoteAuthority) { - return; - } - - let toAdd: IExtensionDescription[] = []; - for (let i = 0, len = _toAdd.length; i < len; i++) { - const extension = _toAdd[i]; - - if (!this._canAddExtension(extension)) { - continue; - } - - const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); - if (!extensionDescription) { - // could not scan extension... - continue; - } - - toAdd.push(extensionDescription); - } - - let toRemove: IExtensionDescription[] = []; - for (let i = 0, len = _toRemove.length; i < len; i++) { - const extensionId = _toRemove[i]; - const extensionDescription = this._registry.getExtensionDescription(extensionId); - if (!extensionDescription) { - // ignore disabling/uninstalling an extension which is not running - continue; - } - - if (!this._canRemoveExtension(extensionDescription)) { - // uses non-dynamic extension point or is activated - continue; - } - - toRemove.push(extensionDescription); - } - - if (toAdd.length === 0 && toRemove.length === 0) { - return; - } - - // Update the local registry - const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); - this._onDidChangeExtensions.fire(undefined); - - toRemove = toRemove.concat(result.removedDueToLooping); - if (result.removedDueToLooping.length > 0) { - this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); - } - - // enable or disable proposed API per extension - this._checkEnableProposedApi(toAdd); - - // Update extension points - this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); - - // Update the extension host - const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); - if (localProcessExtensionHost) { - await localProcessExtensionHost.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); - } - - for (let i = 0; i < toAdd.length; i++) { - this._activateAddedExtensionIfNeeded(toAdd[i]); - } - } - - public canAddExtension(extensionDescription: IExtensionDescription): boolean { - return this._canAddExtension(toExtension(extensionDescription)); - } - - private _canAddExtension(extension: IExtension): boolean { + protected _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; } @@ -266,18 +132,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); - if (extensionDescription) { - // this extension is already running (most likely at a different version) - return false; - } - - // Check if extension is renamed - if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { - return false; - } - - return true; + return super._canAddExtension(extension); } public canRemoveExtension(extension: IExtensionDescription): boolean { @@ -289,88 +144,21 @@ export class ExtensionService extends AbstractExtensionService implements IExten return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier); - if (!extensionDescription) { - // ignore removing an extension which is not running - return false; - } - - return this._canRemoveExtension(extensionDescription); + return super.canRemoveExtension(extension); } - private _canRemoveExtension(extension: IExtensionDescription): boolean { - if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { - // Extension is running, cannot remove it safely - return false; - } - return true; + protected _scanSingleExtension(extension: IExtension): Promise { + return this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); } - private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise { - - let shouldActivate = false; - let shouldActivateReason: string | null = null; - let hasWorkspaceContains = false; - if (Array.isArray(extensionDescription.activationEvents)) { - for (let activationEvent of extensionDescription.activationEvents) { - // TODO@joao: there's no easy way to contribute this - if (activationEvent === 'onUri') { - activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; - } - - if (this._allRequestedActivateEvents.has(activationEvent)) { - // This activation event was fired before the extension was added - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - - if (activationEvent === '*') { - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - - if (/^workspaceContains/.test(activationEvent)) { - hasWorkspaceContains = true; - } - - if (activationEvent === 'onStartupFinished') { - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - } - } - - if (shouldActivate) { - await Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) - ).then(() => { }); - } else if (hasWorkspaceContains) { - const workspace = await this._contextService.getCompleteWorkspace(); - const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority; - const host: IWorkspaceContainsActivationHost = { - folders: workspace.folders.map(folder => folder.uri), - forceUsingSearch: forceUsingSearch, - exists: (path) => exists(path), - checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)) - }; - - const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription); - if (!result) { - return; - } - - await Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent })) - ).then(() => { }); + protected async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); + if (localProcessExtensionHost) { + await localProcessExtensionHost.deltaExtensions(toAdd, toRemove); } } - //#endregion - private async _scanAllLocalExtensions(): Promise { return flatten(await Promise.all([ this._extensionScanner.scannedExtensions, From fc76bf3ebcaf88c1f8ce0308f9059df6708be859 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Fri, 25 Sep 2020 13:26:58 -0700 Subject: [PATCH 80/98] Toggle window minwidth depending on panel #89721 (#107257) Change window minwidth depending on panel position. 600px if panel at sides, 400px if panel at bottom. --- src/vs/code/electron-main/window.ts | 9 +++---- src/vs/platform/native/common/native.ts | 2 ++ .../electron-main/nativeHostMainService.ts | 17 ++++++++++++ src/vs/platform/windows/common/windows.ts | 6 +++++ src/vs/workbench/browser/layout.ts | 2 +- src/vs/workbench/electron-sandbox/window.ts | 26 +++++++++++++++++-- .../electron-browser/workbenchTestServices.ts | 1 + 7 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 221bc958455..892a1ef024a 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -85,9 +85,6 @@ const enum ReadyState { export class CodeWindow extends Disposable implements ICodeWindow { - private static readonly MIN_WIDTH = 600; - private static readonly MIN_HEIGHT = 270; - private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 private readonly _onLoad = this._register(new Emitter()); @@ -162,8 +159,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { x: this.windowState.x, y: this.windowState.y, backgroundColor: this.themeMainService.getBackgroundColor(), - minWidth: CodeWindow.MIN_WIDTH, - minHeight: CodeWindow.MIN_HEIGHT, + minWidth: WindowMinimumSize.WIDTH, + minHeight: WindowMinimumSize.HEIGHT, show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index df1f6ee5fd9..e25232fe806 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -67,6 +67,8 @@ export interface ICommonNativeHostService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; + setMinimumSize(width: number | undefined, height: number | undefined): Promise; + /** * Make the window focused. * diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 7fa482c7e03..ea874cb1795 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -211,6 +211,23 @@ export class NativeHostMainService implements INativeHostMainService { } } + async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { + const window = this.windowById(windowId); + if (window) { + const [windowWidth, windowHeight] = window.win.getSize(); + const [minWindowWidth, minWindowHeight] = window.win.getMinimumSize(); + const [newMinWindowWidth, newMinWindowHeight] = [width ?? minWindowWidth, height ?? minWindowHeight]; + const [newWindowWidth, newWindowHeight] = [Math.max(windowWidth, newMinWindowWidth), Math.max(windowHeight, newMinWindowHeight)]; + + if (minWindowWidth !== newMinWindowWidth || minWindowHeight !== newMinWindowHeight) { + window.win.setMinimumSize(newMinWindowWidth, newMinWindowHeight); + } + if (windowWidth !== newWindowWidth || windowHeight !== newWindowHeight) { + window.win.setSize(newWindowWidth, newWindowHeight); + } + } + } + //#endregion //#region Dialog diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 10a0bfabe3a..edad2e03f79 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -13,6 +13,12 @@ import { LogLevel } from 'vs/platform/log/common/log'; import { ExportData } from 'vs/base/common/performance'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +export const WindowMinimumSize = { + WIDTH: 400, + WIDTH_WITH_VERTICAL_PANEL: 600, + HEIGHT: 270 +}; + export interface IBaseOpenWindowsOptions { forceReuseWindow?: boolean; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 1fd4b7435f6..014b1846fbe 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1307,7 +1307,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar].forEach((part: Part) => { this._register(part.onDidVisibilityChange((visible) => { - this._onPartVisibilityChange.fire(); if (part === sideBar) { this.setSideBarHidden(!visible, true); } else if (part === panelPart) { @@ -1315,6 +1314,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } else if (part === editorPart) { this.setEditorHidden(!visible, true); } + this._onPartVisibilityChange.fire(); })); }); diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 0ce996b5395..661ddf1841d 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -13,7 +13,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { WindowMinimumSize, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { applyZoom } from 'vs/platform/windows/electron-sandbox/window'; @@ -54,7 +54,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { posix, dirname } from 'vs/base/common/path'; import { getBaseLabel } from 'vs/base/common/labels'; import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel'; -import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; +import { IWorkbenchLayoutService, Parts, positionFromString, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; @@ -266,6 +266,12 @@ export class NativeWindow extends Disposable { )(e => this.onDidChangeMaximized(e))); this.onDidChangeMaximized(this.environmentService.configuration.maximized ?? false); + + // Detect panel position to determine minimum width + this._register(this.layoutService.onPanelPositionChange(pos => { + this.onDidPanelPositionChange(positionFromString(pos)); + })); + this.onDidPanelPositionChange(this.layoutService.getPanelPosition()); } private updateDocumentEdited(isDirty = this.workingCopyService.hasDirty): void { @@ -280,6 +286,22 @@ export class NativeWindow extends Disposable { this.layoutService.updateWindowMaximizedState(maximized); } + private getWindowMinimumWidth(panelPosition: Position = this.layoutService.getPanelPosition()): number { + // if panel is on the side, then return the larger minwidth + const panelOnSide = panelPosition === Position.LEFT || panelPosition === Position.RIGHT; + if (panelOnSide) { + return WindowMinimumSize.WIDTH_WITH_VERTICAL_PANEL; + } + else { + return WindowMinimumSize.WIDTH; + } + } + + private onDidPanelPositionChange(pos: Position): void { + const minWidth = this.getWindowMinimumWidth(pos); + this.nativeHostService.setMinimumSize(minWidth, undefined); + } + private onDidVisibleEditorsChange(): void { // Close when empty: check if we should close the window based on the setting diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 7989ce9a26d..0370252d1f4 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -187,6 +187,7 @@ export class TestNativeHostService implements INativeHostService { async maximizeWindow(): Promise { } async unmaximizeWindow(): Promise { } async minimizeWindow(): Promise { } + async setMinimumSize(width: number | undefined, height: number | undefined): Promise { } async focusWindow(options?: { windowId?: number | undefined; } | undefined): Promise { } async showMessageBox(options: Electron.MessageBoxOptions): Promise { throw new Error('Method not implemented.'); } async showSaveDialog(options: Electron.SaveDialogOptions): Promise { throw new Error('Method not implemented.'); } From cdb7b772dee8daeb33dca30201fafac34f946f64 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 25 Sep 2020 16:54:04 -0400 Subject: [PATCH 81/98] FIxes #107335 - removes hex assumption --- src/vs/base/browser/ui/dialog/dialog.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 93df3c8f22d..e2b3862aadc 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -277,8 +277,8 @@ export class Dialog extends Disposable { if (this.styles) { const style = this.styles; - const fgColor = style.dialogForeground ? `${style.dialogForeground}` : ''; - const bgColor = style.dialogBackground ? `${style.dialogBackground}` : ''; + const fgColor = style.dialogForeground; + const bgColor = style.dialogBackground; const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : ''; const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : ''; @@ -287,8 +287,8 @@ export class Dialog extends Disposable { } if (this.element) { - this.element.style.color = fgColor; - this.element.style.backgroundColor = bgColor; + this.element.style.color = fgColor?.toString() ?? ''; + this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; if (this.buttonGroup) { @@ -300,8 +300,8 @@ export class Dialog extends Disposable { } if (this.messageDetailElement && fgColor && bgColor) { - const messageDetailColor = Color.fromHex(fgColor).transparent(.9); - this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); + const messageDetailColor = fgColor.transparent(.9); + this.messageDetailElement.style.color = messageDetailColor.makeOpaque(bgColor).toString(); } if (this.iconElement) { From b9a3a4514c384d259b4206ee548397ca5be18ec3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 25 Sep 2020 23:34:36 +0200 Subject: [PATCH 82/98] Add `IScanSingleExtensionArguments` --- .../remote/common/remoteAgentEnvironmentChannel.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index 052cd072d93..c5f34dd2b75 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -22,6 +22,13 @@ export interface IScanExtensionsArguments { skipExtensions: ExtensionIdentifier[]; } +export interface IScanSingleExtensionArguments { + language: string; + remoteAuthority: string; + isBuiltin: boolean; + extensionLocation: UriComponents; +} + export interface IRemoteAgentEnvironmentDTO { pid: number; connectionToken: string; From 4b4f335f262088f4e9b0a0c9243d83dec96c35eb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 25 Sep 2020 23:40:38 +0200 Subject: [PATCH 83/98] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a0cbf1a3ab6..0f9e6a08545 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.50.0", - "distro": "f4fbb2133880d47be366fb94ea9d149862bddaf3", + "distro": "d56fa71c6a1c5f3177d01be5ed2de7c3d73edc84", "author": { "name": "Microsoft Corporation" }, From 68089c3062bc609129da54ac2b8e7a875dd27c8f Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Fri, 25 Sep 2020 15:20:52 -0700 Subject: [PATCH 84/98] Fix #106116, collectWorkspaceStats() can hang --- src/vs/platform/diagnostics/node/diagnosticsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index b6f87cc08e9..b3b4815fc75 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -86,7 +86,7 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P return; } - if (token.count > MAX_FILES) { + if (token.count >= MAX_FILES) { token.count += files.length; token.maxReached = true; resolve(); From f1e631ab83c05c4c1b2dcf9b514c8a39ce3c9627 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 25 Sep 2020 16:21:55 -0700 Subject: [PATCH 85/98] Update ts server web build --- .../src/extension.browser.ts | 2 +- .../src/utils/api.ts | 2 +- .../typescript-language-features/yarn.lock | 509 ++++-------------- 3 files changed, 108 insertions(+), 405 deletions(-) diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts index 6a0f97241ea..b6d15a34b58 100644 --- a/extensions/typescript-language-features/src/extension.browser.ts +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -53,7 +53,7 @@ export function activate( new TypeScriptVersion( TypeScriptVersionSource.Bundled, vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript-web/tsserver.web.js').toString(), - API.v400)); + API.fromSimpleString('4.0.3'))); const lazyClientHost = createLazyClientHost(context, false, { pluginManager, diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 289c091f5b1..52bcf441c42 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -9,7 +9,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); export default class API { - private static fromSimpleString(value: string): API { + public static fromSimpleString(value: string): API { return new API(value, value, value); } diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 147be55e1c8..4ef8f032788 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -30,24 +30,23 @@ dependencies: mkdirp "^1.0.4" -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@types/glob@*": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== dependencies: - "@types/events" "*" "@types/minimatch" "*" "@types/node" "*" -"@types/json-schema@^7.0.4": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" - integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json-schema@^7.0.5": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== "@types/minimatch@*": version "3.0.3" @@ -55,14 +54,14 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec" - integrity "sha1-B2Ao0LBAC+gQW4mgpVVQyGaE/+w= sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==" + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== "@types/node@^12.11.7": - version "12.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.24.tgz#d4606afd8cf6c609036b854360367d1b2c78931f" - integrity "sha1-1GBq/Yz2xgkDa4VDYDZ9Gyx4kx8= sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug==" + version "12.12.62" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.62.tgz#733923d73669188d35950253dd18a21570085d2b" + integrity sha512-qAfo81CsD7yQIM9mVyh6B/U47li5g7cfpVQEDMfQeF8pSZVwzbhwU3crc0qG4DmpsebpJPR49AKOExQyJ05Cpg== "@types/rimraf@2.0.2": version "2.0.2" @@ -84,39 +83,36 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + aggregate-error@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" - integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-keywords@^3.4.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.1.tgz#b83ca89c5d42d69031f424cad49aada0236c6957" - integrity sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA== +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.12.2: - version "6.12.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== +ajv@^6.12.4: + version "6.12.5" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" + integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -131,45 +127,11 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" - integrity "sha1-JDkOatYThrCnRyZXVNKhchnehiw= sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -200,7 +162,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -cacache@^15.0.4: +cacache@^15.0.5: version "15.0.5" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== @@ -223,11 +185,6 @@ cacache@^15.0.4: tar "^6.0.2" unique-filename "^1.1.1" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -238,13 +195,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -266,34 +216,22 @@ concat-map@0.0.1: integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= copy-webpack-plugin@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b" - integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA== + version "6.1.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.1.1.tgz#737a3ba21c16cc6ddca972f5cf8de25568ca0616" + integrity sha512-4TlkHFYkrZ3WppLA5XkPmBLI5lnEpFsXvpeqxCf5PzkratZiVklNXsvoQkLhUU43q7ZL3AOXtaHAd9jLNJoU0w== dependencies: - cacache "^15.0.4" + cacache "^15.0.5" fast-glob "^3.2.4" find-cache-dir "^3.3.1" glob-parent "^5.1.1" globby "^11.0.1" loader-utils "^2.0.0" normalize-path "^3.0.0" - p-limit "^3.0.1" - schema-utils "^2.7.0" - serialize-javascript "^4.0.0" + p-limit "^3.0.2" + schema-utils "^2.7.1" + serialize-javascript "^5.0.1" webpack-sources "^1.4.3" -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -301,6 +239,13 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -308,11 +253,6 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -337,14 +277,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -367,26 +299,6 @@ escape-string-regexp@1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -407,7 +319,7 @@ fast-glob@^3.1.1, fast-glob@^3.2.4: fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM= sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fastq@^1.6.0: version "1.8.0" @@ -440,20 +352,6 @@ find-up@^4.0.0: locate-path "^5.0.0" path-exists "^4.0.0" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -466,13 +364,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - glob-parent@^5.1.0, glob-parent@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" @@ -521,19 +412,6 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -552,14 +430,14 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + "@tootallnate/once" "1" + agent-base "6" + debug "4" https-proxy-agent@^2.2.1: version "2.2.4" @@ -569,6 +447,14 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" @@ -619,36 +505,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -657,19 +518,9 @@ json5@^2.1.2: minimist "^1.2.5" jsonc-parser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" - integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== loader-utils@^2.0.0: version "2.0.0" @@ -714,18 +565,6 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity "sha1-ChLgUCZQ5HPXNVNQUOfI9OtPrlg= sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity "sha1-nJIfwJt+FJpl39wNpNIJlyALCgY= sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==" - dependencies: - mime-db "1.43.0" - minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -758,9 +597,9 @@ minipass-flush@^1.0.5: minipass "^3.0.0" minipass-pipeline@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz#55f7839307d74859d6e8ada9c3ebe72cec216a34" - integrity sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" @@ -771,10 +610,10 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" - integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" @@ -813,7 +652,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -823,11 +662,6 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -842,7 +676,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.1: +p-limit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== @@ -883,11 +717,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -905,31 +734,11 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -psl@^1.1.24: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity "sha1-8cTEeo75cWfepda79IFtc26ISjw= sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -937,37 +746,6 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -992,29 +770,19 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -schema-utils@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== +schema-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" semver@5.5.1: version "5.5.1" @@ -1031,10 +799,10 @@ semver@^6.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== dependencies: randombytes "^2.1.0" @@ -1048,15 +816,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-support@^0.5.0: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@~0.5.12: +source-map-support@^0.5.0, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -1069,21 +829,6 @@ source-map@^0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - ssri@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" @@ -1099,14 +844,14 @@ supports-color@5.4.0: has-flag "^3.0.0" tar@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" - integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== + version "6.0.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" minipass "^3.0.0" - minizlib "^2.1.0" + minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -1126,26 +871,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - typescript-vscode-sh-plugin@^0.6.14: version "0.6.14" resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.14.tgz#a81031b502f6346a26ea49ce082438c3e353bb38" @@ -1153,7 +878,7 @@ typescript-vscode-sh-plugin@^0.6.14: "typescript-web-server@git://github.com/mjbvz/ts-server-web-build": version "0.0.0" - resolved "git://github.com/mjbvz/ts-server-web-build#1d85be25043f9b5e36a531941ea345dd5a2ca007" + resolved "git://github.com/mjbvz/ts-server-web-build#2a70d88432760118a6aab21da7b819a57e7d4e5e" unique-filename@^1.1.1: version "1.1.1" @@ -1170,34 +895,12 @@ unique-slug@^2.0.0: imurmurhash "^0.1.4" uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" -url-parse@^1.4.4: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vscode-extension-telemetry@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" @@ -1206,9 +909,9 @@ vscode-extension-telemetry@0.1.1: applicationinsights "1.0.8" vscode-nls@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" - integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== vscode-test@^0.4.1: version "0.4.3" @@ -1219,16 +922,16 @@ vscode-test@^0.4.1: https-proxy-agent "^2.2.1" vscode@^1.1.36: - version "1.1.36" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" - integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + version "1.1.37" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.37.tgz#c2a770bee4bb3fff765e2b72c7bcc813b8a6bb0a" + integrity sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg== dependencies: glob "^7.1.2" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" mocha "^5.2.0" - request "^2.88.0" semver "^5.4.1" source-map-support "^0.5.0" - url-parse "^1.4.4" vscode-test "^0.4.1" webpack-sources@^1.4.3: From 5d73637a00163f682c1056ec110f03deb6b4bf67 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 25 Sep 2020 19:38:37 -0400 Subject: [PATCH 86/98] saves SCM widget input on window reload (#107261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * saving SCM widget input * Use a better key Co-authored-by: João Moreno Co-authored-by: João Moreno --- src/vs/workbench/api/browser/mainThreadSCM.ts | 4 +++ .../contrib/scm/browser/scmViewPane.ts | 4 +-- .../contrib/scm/common/scmService.ts | 36 ++++++++++++++----- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 1b2718a458f..2f0b06a143d 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -303,6 +303,10 @@ export class MainThreadSCM implements MainThreadSCMShape { setTimeout(() => this._proxy.$setSelectedSourceControl(handle), 0); } + if (repository.input.value) { + setTimeout(() => this._proxy.$onInputBoxValueChange(handle, repository.input.value), 0); + } + this._repositoryDisposables.set(handle, disposable); } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 1aa91aacd11..0220cf2a22e 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1311,7 +1311,7 @@ class SCMInputWidget extends Disposable { if (value === textModel.getValue()) { // circuit breaker return; } - textModel.setValue(value); + textModel.setValue(input.value); this.inputEditor.setPosition(textModel.getFullModelRange().getEndPosition()); })); @@ -1381,7 +1381,7 @@ class SCMInputWidget extends Disposable { @IKeybindingService private keybindingService: IKeybindingService, @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, - @IContextViewService private readonly contextViewService: IContextViewService, + @IContextViewService private readonly contextViewService: IContextViewService ) { super(); diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 6319bf1d441..1cb479864bd 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -8,12 +8,20 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; class SCMInput implements ISCMInput { private _value = ''; get value(): string { + if (this.root) { + const key = `scm/input:${this.repository.provider.label}:${this.root.path}`; + let storedValue = this.storageService.get(key, StorageScope.WORKSPACE); + if (storedValue) { + return storedValue; + } + } return this._value; } @@ -21,11 +29,14 @@ class SCMInput implements ISCMInput { if (value === this._value) { return; } - this._value = value; + if (this.root) { + const key = `scm/input:${this.repository.provider.label}:${this.root.path}`; + this.storageService.store(key, value, StorageScope.WORKSPACE); + } this._onDidChange.fire(value); } - + private root; private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; @@ -55,7 +66,8 @@ class SCMInput implements ISCMInput { } private readonly _onDidChangeVisibility = new Emitter(); - readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + readonly onDidChangeVisibility: Event = this._onDidChangeVisibility + .event; private _validateInput: IInputValidator = () => Promise.resolve(undefined); @@ -71,7 +83,13 @@ class SCMInput implements ISCMInput { private readonly _onDidChangeValidateInput = new Emitter(); readonly onDidChangeValidateInput: Event = this._onDidChangeValidateInput.event; - constructor(readonly repository: ISCMRepository) { } + constructor( + readonly repository: ISCMRepository, + @IStorageService private storageService: IStorageService + ) { + this.root = this.repository.provider.rootUri; + this._value = this.value; + } } class SCMRepository implements ISCMRepository { @@ -84,11 +102,12 @@ class SCMRepository implements ISCMRepository { private readonly _onDidChangeSelection = new Emitter(); readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; - readonly input: ISCMInput = new SCMInput(this); + readonly input: ISCMInput = new SCMInput(this, this.storageService); constructor( public readonly provider: ISCMProvider, - private disposable: IDisposable + private disposable: IDisposable, + @IStorageService private storageService: IStorageService ) { } setSelected(selected: boolean): void { @@ -128,7 +147,8 @@ export class SCMService implements ISCMService { constructor( @ILogService private readonly logService: ILogService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IStorageService private storageService: IStorageService ) { this.providerCount = contextKeyService.createKey('scm.providerCount', 0); } @@ -161,7 +181,7 @@ export class SCMService implements ISCMService { this.providerCount.set(this._repositories.length); }); - const repository = new SCMRepository(provider, disposable); + const repository = new SCMRepository(provider, disposable, this.storageService); const selectedDisposable = Event.map(Event.filter(repository.onDidChangeSelection, selected => selected), _ => repository)(this.select, this); this._repositories.push(repository); From a9f87e7ca762b95f64c4491ebac10b8f3220c526 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 25 Sep 2020 21:44:15 -0700 Subject: [PATCH 87/98] diff outputs instead of replacing the whole outputs array. --- .../browser/viewModel/codeCellViewModel.ts | 4 +- .../common/model/notebookTextModel.ts | 38 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 06563a5b82a..9e57b51a6e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -74,7 +74,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod ) { super(viewType, model, UUID.generateUuid(), configurationService); this._register(this.model.onDidChangeOutputs((splices) => { - this._outputCollection = new Array(this.model.outputs.length); + splices.reverse().forEach(splice => { + this._outputCollection.splice(splice[0], splice[1], ...splice[2].map(() => 0)); + }); this._outputsTop = null; this._onDidChangeOutputs.fire(splices); })); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3369f353b3c..35734a6304e 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -8,13 +8,15 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent, IProcessedOutput, CellOutputKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextSnapshot } from 'vs/editor/common/model'; import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IRange } from 'vs/editor/common/core/range'; +import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; +import { hash } from 'vs/base/common/hash'; export class NotebookTextModelSnapshot implements ITextSnapshot { @@ -329,8 +331,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel //TODO@joh,@rebornix no event, no undo stop (?) this._assertIndex(edit.index); const cell = this._cells[edit.index]; - // TODO@rebornix, we should do diff first - this._spliceNotebookCellOutputs(cell.handle, [[0, cell.outputs.length, edit.outputs]], computeUndoRedo); + this._spliceNotebookCellOutputs2(cell.handle, edit.outputs, computeUndoRedo); break; case CellEditType.OutputsSplice: { @@ -645,6 +646,18 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeLanguage, index: this._cells.indexOf(cell), language: languageId, transient: false }, true); } + private _spliceNotebookCellOutputs2(cellHandle: number, outputs: IProcessedOutput[], computeUndoRedo: boolean): void { + const cell = this._mapping.get(cellHandle); + if (!cell) { + return; + } + + const diff = new LcsDiff(new OutputSequence(cell.outputs), new OutputSequence(outputs)); + const diffResult = diff.ComputeDiff(false); + const splices: NotebookCellOutputsSplice[] = diffResult.changes.map(change => [change.originalStart, change.originalLength, outputs.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength)]); + this._spliceNotebookCellOutputs(cellHandle, splices, computeUndoRedo); + } + private _spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[], computeUndoRedo: boolean): void { const cell = this._mapping.get(cellHandle); if (cell) { @@ -695,3 +708,22 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } } + +class OutputSequence implements ISequence { + constructor(readonly outputs: IProcessedOutput[]) { + } + + getElements(): Int32Array | number[] | string[] { + return this.outputs.map(output => { + switch (output.outputKind) { + case CellOutputKind.Rich: + return hash([output.outputKind, output.metadata, output.data]); + case CellOutputKind.Error: + return hash([output.outputKind, output.ename, output.evalue, output.traceback]); + case CellOutputKind.Text: + return hash([output.outputKind, output.text]); + } + }); + } + +} From b6117be33574480076fcadd8ef169a2b73a5ab1a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 27 Sep 2020 12:09:32 +0200 Subject: [PATCH 88/98] separate recommendation services - ignored recommendations service - recommendations prompt service --- .../common/extensionRecommendations.ts | 19 ++ .../browser/configBasedRecommendations.ts | 5 +- .../dynamicWorkspaceRecommendations.ts | 5 +- .../browser/exeBasedRecommendations.ts | 16 +- .../browser/experimentalRecommendations.ts | 5 +- .../extensions/browser/extensionEditor.ts | 52 ++-- ...ensionRecommendationNotificationService.ts | 239 ++++++++++++++++++ .../browser/extensionRecommendations.ts | 223 ---------------- .../extensionRecommendationsService.ts | 124 ++------- .../browser/extensions.contribution.ts | 3 + .../extensions/browser/extensionsActions.ts | 10 +- .../extensions/browser/extensionsViews.ts | 6 +- .../extensions/browser/extensionsWidgets.ts | 2 +- .../browser/fileBasedRecommendations.ts | 21 +- .../browser/keymapRecommendations.ts | 5 +- .../browser/workspaceRecommendations.ts | 23 +- .../extensionRecommendationsService.test.ts | 90 ++++--- .../extensionIgnoredRecommendationsService.ts | 114 +++++++++ .../common/extensionRecommendations.ts | 33 ++- .../common/workspaceExtensionsConfig.ts | 15 +- src/vs/workbench/workbench.common.main.ts | 1 + 21 files changed, 554 insertions(+), 457 deletions(-) create mode 100644 src/vs/platform/extensionRecommendations/common/extensionRecommendations.ts create mode 100644 src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts create mode 100644 src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts diff --git a/src/vs/platform/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/platform/extensionRecommendations/common/extensionRecommendations.ts new file mode 100644 index 00000000000..21d3716dd25 --- /dev/null +++ b/src/vs/platform/extensionRecommendations/common/extensionRecommendations.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IExtensionRecommendationNotificationService = createDecorator('IExtensionRecommendationNotificationService'); + +export interface IExtensionRecommendationNotificationService { + readonly _serviceBrand: undefined; + + readonly ignoredRecommendations: string[]; + hasToIgnoreRecommendationNotifications(): boolean; + + promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): Promise; + promptWorkspaceRecommendations(recommendations: string[]): Promise; +} + diff --git a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts index e159ddea7a5..c8e16822555 100644 --- a/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/configBasedRecommendations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IExtensionTipsService, IConfigBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { localize } from 'vs/nls'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; @@ -27,11 +27,10 @@ export class ConfigBasedRecommendations extends ExtensionRecommendations { get recommendations(): ReadonlyArray { return [...this.importantRecommendations, ...this.otherRecommendations]; } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, ) { - super(promptedExtensionRecommendations); + super(); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index 7a3c0c2f784..87780840a9c 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { isNumber } from 'vs/base/common/types'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; @@ -30,7 +30,6 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { get recommendations(): ReadonlyArray { return this._recommendations; } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, @IWorkspaceTagsService private readonly workspaceTagsService: IWorkspaceTagsService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -38,7 +37,7 @@ export class DynamicWorkspaceRecommendations extends ExtensionRecommendations { @ITelemetryService private readonly telemetryService: ITelemetryService, @IStorageService private readonly storageService: IStorageService, ) { - super(promptedExtensionRecommendations); + super(); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index 220d24318aa..cccecbcfe65 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -5,13 +5,14 @@ import { IExtensionTipsService, IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { timeout } from 'vs/base/common/async'; import { localize } from 'vs/nls'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { basename } from 'vs/base/common/path'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -31,13 +32,13 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { private readonly tasExperimentService: ITASExperimentService | undefined; constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, ) { - super(promptedExtensionRecommendations); + super(); this.tasExperimentService = tasExperimentService; /* @@ -103,14 +104,9 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { } private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: Map): Promise { - if (this.promptedExtensionRecommendations.hasToIgnoreRecommendationNotifications()) { + if (this.extensionRecommendationNotificationService.hasToIgnoreRecommendationNotifications()) { return; } - recommendations = this.promptedExtensionRecommendations.filterIgnoredOrNotAllowed(recommendations); - if (recommendations.length === 0) { - return; - } - const recommendationsByExe = new Map(); for (const extensionId of recommendations) { const tip = importantExeBasedRecommendations.get(extensionId); @@ -131,7 +127,7 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { } const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName); - this.promptedExtensionRecommendations.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); + this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); } } diff --git a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts index 16dd2fa4e81..cc0dda953fb 100644 --- a/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/experimentalRecommendations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; @@ -14,10 +14,9 @@ export class ExperimentalRecommendations extends ExtensionRecommendations { get recommendations(): ReadonlyArray { return this._recommendations; } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IExperimentService private readonly experimentService: IExperimentService, ) { - super(promptedExtensionRecommendations); + super(); } /** diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 81328757acc..5749ad885ee 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -19,7 +19,7 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; @@ -189,6 +189,7 @@ export class ExtensionEditor extends EditorPane { @INotificationService private readonly notificationService: INotificationService, @IOpenerService private readonly openerService: IOpenerService, @IExtensionRecommendationsService private readonly extensionRecommendationsService: IExtensionRecommendationsService, + @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @@ -471,36 +472,27 @@ export class ExtensionEditor extends EditorPane { this.transientDisposables.add(ignoreAction); this.transientDisposables.add(undoIgnoreAction); - const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); - if (extRecommendations[extension.identifier.id.toLowerCase()]) { - ignoreAction.enabled = true; - template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; - show(template.subtextContainer); - } else if (this.extensionRecommendationsService.getIgnoredRecommendations().indexOf(extension.identifier.id.toLowerCase()) !== -1) { - undoIgnoreAction.enabled = true; - template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); - show(template.subtextContainer); - } - else { - template.subtext.textContent = ''; - } - - this.extensionRecommendationsService.onRecommendationChange(change => { - if (change.extensionId.toLowerCase() === extension.identifier.id.toLowerCase()) { - if (change.isRecommended) { - undoIgnoreAction.enabled = false; - const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); - if (extRecommendations[extension.identifier.id.toLowerCase()]) { - ignoreAction.enabled = true; - template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; - } - } else { - undoIgnoreAction.enabled = true; - ignoreAction.enabled = false; - template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); - } + const updateRecommendationFn = () => { + const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); + if (extRecommendations[extension.identifier.id.toLowerCase()]) { + ignoreAction.enabled = true; + undoIgnoreAction.enabled = false; + template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; + show(template.subtextContainer); + } else if (this.extensionIgnoredRecommendationsService.globalIgnoredRecommendations.indexOf(extension.identifier.id.toLowerCase()) !== -1) { + ignoreAction.enabled = false; + undoIgnoreAction.enabled = true; + template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); + show(template.subtextContainer); + } else { + ignoreAction.enabled = false; + undoIgnoreAction.enabled = false; + template.subtext.textContent = ''; + hide(template.subtextContainer); } - }); + }; + updateRecommendationFn(); + this.transientDisposables.add(this.extensionRecommendationsService.onDidChangeRecommendations(() => updateRecommendationFn())); this.transientDisposables.add(reloadAction.onDidChange(e => { if (e.tooltip) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts new file mode 100644 index 00000000000..d88576ce36c --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IAction } from 'vs/base/common/actions'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; + +interface IExtensionsConfiguration { + autoUpdate: boolean; + autoCheckUpdates: boolean; + ignoreRecommendations: boolean; + showRecommendationsOnlyOnDemand: boolean; + closeExtensionDetailsOnViewChange: boolean; +} + +type ExtensionRecommendationsNotificationClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + extensionId?: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; +}; + +type ExtensionWorkspaceRecommendationsNotificationClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + +const ignoreImportantExtensionRecommendation = 'extensionsAssistant/importantRecommendationsIgnore'; +const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; +const choiceNever = localize('neverShowAgain', "Don't Show Again"); + +export class ExtensionRecommendationNotificationService extends Disposable implements IExtensionRecommendationNotificationService { + + declare readonly _serviceBrand: undefined; + + // Ignored Important Recommendations + get ignoredRecommendations(): string[] { + return [...(JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')))].map(i => i.toLowerCase()); + } + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IStorageService private readonly storageService: IStorageService, + @INotificationService private readonly notificationService: INotificationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, + @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, + @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + ) { + super(); + storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendation, version: 1 }); + } + + hasToIgnoreRecommendationNotifications(): boolean { + const config = this.configurationService.getValue('extensions'); + return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand; + } + + async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): Promise { + if (this.hasToIgnoreRecommendationNotifications()) { + return false; + } + + extensionIds = this.filterIgnoredOrNotAllowed(extensionIds); + if (!extensionIds.length) { + return false; + } + + const extensions = await this.getInstallableExtensions(extensionIds); + if (!extensions.length) { + return false; + } + + this.notificationService.prompt(Severity.Info, message, + [{ + label: localize('install', "Install"), + run: async () => { + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + await Promise.all(extensions.map(async extension => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); + } + }, { + label: localize('show recommendations', "Show Recommendations"), + run: async () => { + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); + } + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + for (const extension of extensions) { + this.addToImportantRecommendationsIgnore(extension.identifier.id); + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); + } + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); + } + }], + { + sticky: true, + onCancel: () => { + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); + } + } + } + ); + + return true; + } + + async promptWorkspaceRecommendations(recommendations: string[]): Promise { + if (this.hasToIgnoreWorkspaceRecommendationNotifications()) { + return false; + } + + let installed = await this.extensionManagementService.getInstalled(); + installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind + recommendations = recommendations.filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); + + if (!recommendations.length) { + return false; + } + + const extensions = await this.getInstallableExtensions(recommendations); + if (!extensions.length) { + return false; + } + + const searchValue = '@recommended '; + this.notificationService.prompt( + Severity.Info, + localize('workspaceRecommended', "Do you want to install the recommended extensions for this repository?"), + [{ + label: localize('install', "Install"), + run: async () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); + await Promise.all(extensions.map(async extension => { + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: async () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + } + }, { + label: localize('neverShowAgain', "Don't Show Again"), + isSecondary: true, + run: () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + }], + { + sticky: true, + onCancel: () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + } + } + ); + + return true; + } + + private hasToIgnoreWorkspaceRecommendationNotifications(): boolean { + return this.hasToIgnoreRecommendationNotifications() || this.storageService.getBoolean(ignoreWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false); + } + + private async getInstallableExtensions(extensionIds: string[]): Promise { + const extensions: IExtension[] = []; + if (extensionIds.length) { + const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); + for (const extension of pager.firstPage) { + if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { + extensions.push(extension); + } + } + } + return extensions; + } + + private filterIgnoredOrNotAllowed(recommendationsToSuggest: string[]): string[] { + const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.ignoredRecommendations]; + return recommendationsToSuggest.filter(id => !ignoredRecommendations.includes(id)); + } + + private async runAction(action: IAction): Promise { + try { + await action.run(); + } finally { + action.dispose(); + } + } + + private addToImportantRecommendationsIgnore(id: string) { + const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')); + importantRecommendationsIgnoreList.push(id.toLowerCase()); + this.storageService.store(ignoreImportantExtensionRecommendation, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); + } + + private setIgnoreRecommendationsConfig(configVal: boolean) { + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts index d8f649fdf7c..5c6ee68a963 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendations.ts @@ -4,34 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { localize } from 'vs/nls'; -import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationReson } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { IExtensionsConfiguration, ConfigurationKey, IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; -import { IAction } from 'vs/base/common/actions'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; - -type ExtensionRecommendationsNotificationClassification = { - userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - extensionId?: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; -}; - -type ExtensionWorkspaceRecommendationsNotificationClassification = { - userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; -}; - -const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; -const ignoreImportantExtensionRecommendation = 'extensionsAssistant/importantRecommendationsIgnore'; -const choiceNever = localize('neverShowAgain', "Don't Show Again"); export type ExtensionRecommendation = { readonly extensionId: string, @@ -43,12 +16,6 @@ export abstract class ExtensionRecommendations extends Disposable { readonly abstract recommendations: ReadonlyArray; protected abstract doActivate(): Promise; - constructor( - protected readonly promptedExtensionRecommendations: PromptedExtensionRecommendations, - ) { - super(); - } - private _activationPromise: Promise | null = null; get activated(): boolean { return this._activationPromise !== null; } activate(): Promise { @@ -59,193 +26,3 @@ export abstract class ExtensionRecommendations extends Disposable { } } - -export class PromptedExtensionRecommendations extends Disposable { - - constructor( - private readonly isExtensionAllowedToBeRecommended: (extensionId: string) => boolean, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @INotificationService private readonly notificationService: INotificationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IStorageService private readonly storageService: IStorageService, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, - ) { - super(); - storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendation, version: 1 }); - } - - async promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): Promise { - if (this.hasToIgnoreRecommendationNotifications()) { - return; - } - - const extensions = await this.getInstallableExtensions(extensionIds); - if (!extensions.length) { - return; - } - - this.notificationService.prompt(Severity.Info, message, - [{ - label: localize('install', "Install"), - run: async () => { - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - await Promise.all(extensions.map(async extension => { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); - await this.extensionManagementService.installFromGallery(extension.gallery!); - })); - } - }, { - label: localize('show recommendations', "Show Recommendations"), - run: async () => { - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); - } - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - for (const extension of extensions) { - this.addToImportantRecommendationsIgnore(extension.identifier.id); - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); - } - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); - } - }], - { - sticky: true, - onCancel: () => { - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); - } - } - } - ); - } - - async promptWorkspaceRecommendations(recommendations: string[]): Promise { - if (this.hasToIgnoreWorkspaceRecommendationNotifications()) { - return; - } - - let installed = await this.extensionManagementService.getInstalled(); - installed = installed.filter(l => this.extensionEnablementService.getEnablementState(l) !== EnablementState.DisabledByExtensionKind); // Filter extensions disabled by kind - recommendations = recommendations.filter(extensionId => installed.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); - - if (!recommendations.length) { - return; - } - - const extensions = await this.getInstallableExtensions(recommendations); - if (!extensions.length) { - return; - } - - const searchValue = '@recommended '; - this.notificationService.prompt( - Severity.Info, - localize('workspaceRecommended', "Do you want to install the recommended extensions for this repository?"), - [{ - label: localize('install', "Install"), - run: async () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - await Promise.all(extensions.map(async extension => { - this.extensionsWorkbenchService.open(extension, { pinned: true }); - await this.extensionManagementService.installFromGallery(extension.gallery!); - })); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: async () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - } - }, { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); - this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); - } - }], - { - sticky: true, - onCancel: () => { - this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); - } - } - ); - } - - hasToIgnoreRecommendationNotifications(): boolean { - const config = this.configurationService.getValue(ConfigurationKey); - return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand; - } - - hasToIgnoreWorkspaceRecommendationNotifications(): boolean { - return this.hasToIgnoreRecommendationNotifications() || this.storageService.getBoolean(ignoreWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, false); - } - - filterIgnoredOrNotAllowed(recommendationsToSuggest: string[]): string[] { - const importantRecommendationsIgnoreList = (JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]'))).map(e => e.toLowerCase()); - return recommendationsToSuggest.filter(id => { - if (importantRecommendationsIgnoreList.indexOf(id) !== -1) { - return false; - } - if (!this.isExtensionAllowedToBeRecommended(id)) { - return false; - } - return true; - }); - } - - private async getInstallableExtensions(extensionIds: string[]): Promise { - const extensions: IExtension[] = []; - if (extensionIds.length) { - const pager = await this.extensionsWorkbenchService.queryGallery({ names: extensionIds, pageSize: extensionIds.length, source: 'install-recommendations' }, CancellationToken.None); - for (const extension of pager.firstPage) { - if (extension.gallery && (await this.extensionManagementService.canInstall(extension.gallery))) { - extensions.push(extension); - } - } - } - return extensions; - } - - private async runAction(action: IAction): Promise { - try { - await action.run(); - } finally { - action.dispose(); - } - } - - private addToImportantRecommendationsIgnore(id: string) { - const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')); - importantRecommendationsIgnoreList.push(id.toLowerCase()); - this.storageService.store(ignoreImportantExtensionRecommendation, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); - } - - private setIgnoreRecommendationsConfig(configVal: boolean) { - this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); - } - -} - diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index d49c86b3cf2..02e53565da8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -5,8 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionGalleryService, InstallOperation, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, ExtensionRecommendationReason, RecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -21,23 +20,19 @@ import { ExperimentalRecommendations } from 'vs/workbench/contrib/extensions/bro import { WorkspaceRecommendations } from 'vs/workbench/contrib/extensions/browser/workspaceRecommendations'; import { FileBasedRecommendations } from 'vs/workbench/contrib/extensions/browser/fileBasedRecommendations'; import { KeymapRecommendations } from 'vs/workbench/contrib/extensions/browser/keymapRecommendations'; -import { ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; -import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { ConfigBasedRecommendations } from 'vs/workbench/contrib/extensions/browser/configBasedRecommendations'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; type IgnoreRecommendationClassification = { recommendationReason: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; -const ignoredRecommendationsStorageKey = 'extensionsAssistant/ignored_recommendations'; - export class ExtensionRecommendationsService extends Disposable implements IExtensionRecommendationsService { declare readonly _serviceBrand: undefined; - private readonly promptedExtensionRecommendations: PromptedExtensionRecommendations; - // Recommendations private readonly fileBasedRecommendations: FileBasedRecommendations; private readonly workspaceRecommendations: WorkspaceRecommendations; @@ -47,39 +42,32 @@ export class ExtensionRecommendationsService extends Disposable implements IExte private readonly dynamicWorkspaceRecommendations: DynamicWorkspaceRecommendations; private readonly keymapRecommendations: KeymapRecommendations; - // Ignored Recommendations - private globallyIgnoredRecommendations: string[] = []; - public readonly activationPromise: Promise; private sessionSeed: number; - private readonly _onRecommendationChange = this._register(new Emitter()); - onRecommendationChange: Event = this._onRecommendationChange.event; + private _onDidChangeRecommendations = this._register(new Emitter()); + readonly onDidChangeRecommendations = this._onDidChangeRecommendations.event; constructor( @IInstantiationService instantiationService: IInstantiationService, @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, - @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService, + @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, ) { super(); - storageKeysSyncRegistryService.registerStorageKey({ key: ignoredRecommendationsStorageKey, version: 1 }); - - const isExtensionAllowedToBeRecommended = (extensionId: string) => this.isExtensionAllowedToBeRecommended(extensionId); - this.promptedExtensionRecommendations = instantiationService.createInstance(PromptedExtensionRecommendations, isExtensionAllowedToBeRecommended); - this.workspaceRecommendations = instantiationService.createInstance(WorkspaceRecommendations, this.promptedExtensionRecommendations); - this.fileBasedRecommendations = instantiationService.createInstance(FileBasedRecommendations, this.promptedExtensionRecommendations); - this.experimentalRecommendations = instantiationService.createInstance(ExperimentalRecommendations, this.promptedExtensionRecommendations); - this.configBasedRecommendations = instantiationService.createInstance(ConfigBasedRecommendations, this.promptedExtensionRecommendations); - this.exeBasedRecommendations = instantiationService.createInstance(ExeBasedRecommendations, this.promptedExtensionRecommendations); - this.dynamicWorkspaceRecommendations = instantiationService.createInstance(DynamicWorkspaceRecommendations, this.promptedExtensionRecommendations); - this.keymapRecommendations = instantiationService.createInstance(KeymapRecommendations, this.promptedExtensionRecommendations); + this.workspaceRecommendations = instantiationService.createInstance(WorkspaceRecommendations); + this.fileBasedRecommendations = instantiationService.createInstance(FileBasedRecommendations); + this.experimentalRecommendations = instantiationService.createInstance(ExperimentalRecommendations); + this.configBasedRecommendations = instantiationService.createInstance(ConfigBasedRecommendations); + this.exeBasedRecommendations = instantiationService.createInstance(ExeBasedRecommendations); + this.dynamicWorkspaceRecommendations = instantiationService.createInstance(DynamicWorkspaceRecommendations); + this.keymapRecommendations = instantiationService.createInstance(KeymapRecommendations); if (!this.isEnabled()) { this.sessionSeed = 0; @@ -88,13 +76,11 @@ export class ExtensionRecommendationsService extends Disposable implements IExte } this.sessionSeed = +new Date(); - this.globallyIgnoredRecommendations = this.getCachedIgnoredRecommendations(); // Activation this.activationPromise = this.activate(); this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e))); - this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); } private async activate(): Promise { @@ -114,6 +100,16 @@ export class ExtensionRecommendationsService extends Disposable implements IExte }) ]); + this._register(this.extensionRecommendationsManagementService.onDidChangeIgnoredRecommendations(() => this._onDidChangeRecommendations.fire())); + this._register(this.extensionRecommendationsManagementService.onDidChangeGlobalIgnoredRecommendation(({ extensionId, isRecommended }) => { + if (!isRecommended) { + const reason = this.getAllRecommendationsWithReason()[extensionId]; + if (reason && reason.reasonId) { + this.telemetryService.publicLog2<{ extensionId: string, recommendationReason: ExtensionRecommendationReason }, IgnoreRecommendationClassification>('extensionsRecommendations:ignoreRecommendation', { extensionId, recommendationReason: reason.reasonId }); + } + } + })); + await this.promptWorkspaceRecommendations(); this._register(Event.any(this.workspaceRecommendations.onDidChangeRecommendations, this.configBasedRecommendations.onDidChangeRecommendations)(() => this.promptWorkspaceRecommendations())); } @@ -217,29 +213,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return this.toExtensionRecommendations(this.fileBasedRecommendations.recommendations); } - getIgnoredRecommendations(): ReadonlyArray { - return this.globallyIgnoredRecommendations; - } - - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean) { - extensionId = extensionId.toLowerCase(); - const ignored = this.globallyIgnoredRecommendations.indexOf(extensionId) !== -1; - if (ignored === shouldIgnore) { - return; - } - - if (shouldIgnore) { - const reason = this.getAllRecommendationsWithReason()[extensionId]; - if (reason && reason.reasonId) { - this.telemetryService.publicLog2<{ extensionId: string, recommendationReason: ExtensionRecommendationReason }, IgnoreRecommendationClassification>('extensionsRecommendations:ignoreRecommendation', { extensionId, recommendationReason: reason.reasonId }); - } - } - - this.globallyIgnoredRecommendations = shouldIgnore ? [...this.globallyIgnoredRecommendations, extensionId] : this.globallyIgnoredRecommendations.filter(id => id !== extensionId); - this.storeCachedIgnoredRecommendations(this.globallyIgnoredRecommendations); - this._onRecommendationChange.fire({ extensionId, isRecommended: !shouldIgnore }); - } - private onDidInstallExtension(e: DidInstallExtensionEvent): void { if (e.gallery && e.operation === InstallOperation.Install) { const extRecommendations = this.getAllRecommendationsWithReason() || {}; @@ -265,12 +238,8 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return extensionIds; } - private isExtensionAllowedToBeRecommended(id: string): boolean { - const allIgnoredRecommendations = [ - ...this.globallyIgnoredRecommendations, - ...this.workspaceRecommendations.ignoredRecommendations - ]; - return allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; + private isExtensionAllowedToBeRecommended(extensionId: string): boolean { + return !this.extensionRecommendationsManagementService.ignoredRecommendations.includes(extensionId.toLowerCase()); } private async promptWorkspaceRecommendations(): Promise { @@ -279,49 +248,10 @@ export class ExtensionRecommendationsService extends Disposable implements IExte .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); if (allowedRecommendations.length) { - await this.promptedExtensionRecommendations.promptWorkspaceRecommendations(allowedRecommendations); + await this.extensionRecommendationNotificationService.promptWorkspaceRecommendations(allowedRecommendations); } } - private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { - if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL - && this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) { - this._ignoredRecommendationsValue = undefined; - this.globallyIgnoredRecommendations = this.getCachedIgnoredRecommendations(); - } - } - private getCachedIgnoredRecommendations(): string[] { - const ignoredRecommendations: string[] = JSON.parse(this.ignoredRecommendationsValue); - return ignoredRecommendations.map(e => e.toLowerCase()); - } - - private storeCachedIgnoredRecommendations(ignoredRecommendations: string[]): void { - this.ignoredRecommendationsValue = JSON.stringify(ignoredRecommendations); - } - - private _ignoredRecommendationsValue: string | undefined; - private get ignoredRecommendationsValue(): string { - if (!this._ignoredRecommendationsValue) { - this._ignoredRecommendationsValue = this.getStoredIgnoredRecommendationsValue(); - } - - return this._ignoredRecommendationsValue; - } - - private set ignoredRecommendationsValue(ignoredRecommendationsValue: string) { - if (this.ignoredRecommendationsValue !== ignoredRecommendationsValue) { - this._ignoredRecommendationsValue = ignoredRecommendationsValue; - this.setStoredIgnoredRecommendationsValue(ignoredRecommendationsValue); - } - } - - private getStoredIgnoredRecommendationsValue(): string { - return this.storageService.get(ignoredRecommendationsStorageKey, StorageScope.GLOBAL, '[]'); - } - - private setStoredIgnoredRecommendationsValue(value: string): void { - this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL); - } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 655685298ff..a6f01f0dfc3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -56,9 +56,12 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); +registerSingleton(IExtensionRecommendationNotificationService, ExtensionRecommendationNotificationService); registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService); Registry.as(OutputExtensions.OutputChannels) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 12a8fb36063..17b2b8a5093 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -16,7 +16,7 @@ import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IE import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IExtensionRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionIgnoredRecommendationsService, IExtensionsConfigContent } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -1912,7 +1912,7 @@ export class IgnoreExtensionRecommendationAction extends Action { constructor( private readonly extension: IExtension, - @IExtensionRecommendationsService private readonly extensionsTipsService: IExtensionRecommendationsService, + @IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService, ) { super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation'); @@ -1922,7 +1922,7 @@ export class IgnoreExtensionRecommendationAction extends Action { } public run(): Promise { - this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, true); + this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, true); return Promise.resolve(); } } @@ -1935,7 +1935,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { constructor( private readonly extension: IExtension, - @IExtensionRecommendationsService private readonly extensionsTipsService: IExtensionRecommendationsService, + @IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService, ) { super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo'); @@ -1945,7 +1945,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { } public run(): Promise { - this.extensionsTipsService.toggleIgnoredRecommendation(this.extension.identifier.id, false); + this.extensionRecommendationsManagementService.toggleGlobalIgnoredRecommendation(this.extension.identifier.id, false); return Promise.resolve(); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 936a75ab483..c5979ca03c3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -955,7 +955,7 @@ export class DefaultRecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.extensionRecommendationsService.onRecommendationChange(() => { + this._register(this.extensionRecommendationsService.onDidChangeRecommendations(() => { this.show(''); })); } @@ -980,7 +980,7 @@ export class RecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.extensionRecommendationsService.onRecommendationChange(() => { + this._register(this.extensionRecommendationsService.onDidChangeRecommendations(() => { this.show(''); })); } @@ -997,7 +997,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { renderBody(container: HTMLElement): void { super.renderBody(container); - this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.show(this.recommendedExtensionsQuery))); + this._register(this.extensionRecommendationsService.onDidChangeRecommendations(() => this.show(this.recommendedExtensionsQuery))); this._register(this.contextService.onDidChangeWorkbenchState(() => this.show(this.recommendedExtensionsQuery))); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 330e56ed82c..8718d0b9b03 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -199,7 +199,7 @@ export class RecommendationWidget extends ExtensionWidget { super(); this.render(); this._register(toDisposable(() => this.clear())); - this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.render())); + this._register(this.extensionRecommendationsService.onDidChangeRecommendations(() => this.render())); } private clear(): void { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index b66f07f64be..7b03d99e1e2 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationReason, IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { IExtensionsViewPaneContainer, IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; @@ -25,6 +25,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IModelService } from 'vs/editor/common/services/modelService'; import { setImmediate } from 'vs/base/common/platform'; import { IModeService } from 'vs/editor/common/services/modeService'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; type FileExtensionSuggestionClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; @@ -81,7 +82,6 @@ export class FileBasedRecommendations extends ExtensionRecommendations { } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionService private readonly extensionService: IExtensionService, @IViewletService private readonly viewletService: IViewletService, @@ -91,8 +91,10 @@ export class FileBasedRecommendations extends ExtensionRecommendations { @INotificationService private readonly notificationService: INotificationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IStorageService private readonly storageService: IStorageService, + @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, + @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, ) { - super(promptedExtensionRecommendations); + super(); if (productService.extensionTips) { forEach(productService.extensionTips, ({ key, value }) => this.extensionTips.set(key.toLowerCase(), value)); @@ -204,7 +206,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.storeCachedRecommendations(); - if (this.promptedExtensionRecommendations.hasToIgnoreRecommendationNotifications()) { + if (this.extensionRecommendationNotificationService.hasToIgnoreRecommendationNotifications()) { return; } @@ -229,7 +231,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { private async promptRecommendedExtensionForFileType(name: string, recommendations: string[], installed: IExtension[]): Promise { - recommendations = this.promptedExtensionRecommendations.filterIgnoredOrNotAllowed(recommendations); + recommendations = this.filterIgnoredOrNotAllowed(recommendations); if (recommendations.length === 0) { return false; } @@ -245,7 +247,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.promptedExtensionRecommendations.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", name), `@id:${extensionId}`); + this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", name), `@id:${extensionId}`); return true; } @@ -301,6 +303,11 @@ export class FileBasedRecommendations extends ExtensionRecommendations { ); } + private filterIgnoredOrNotAllowed(recommendationsToSuggest: string[]): string[] { + const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.extensionRecommendationNotificationService.ignoredRecommendations]; + return recommendationsToSuggest.filter(id => !ignoredRecommendations.includes(id)); + } + private filterInstalled(recommendationsToSuggest: string[], installed: IExtension[]): string[] { const installedExtensionsIds = installed.reduce((result, i) => { if (i.enablementState !== EnablementState.DisabledByExtensionKind) { diff --git a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts index d40178620d3..a40eed0f23f 100644 --- a/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/keymapRecommendations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { IProductService } from 'vs/platform/product/common/productService'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; @@ -13,10 +13,9 @@ export class KeymapRecommendations extends ExtensionRecommendations { get recommendations(): ReadonlyArray { return this._recommendations; } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, @IProductService private readonly productService: IProductService, ) { - super(promptedExtensionRecommendations); + super(); } protected async doActivate(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index 0a8d055a3ad..231aa8f4b47 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { EXTENSION_IDENTIFIER_PATTERN, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { distinct, flatten } from 'vs/base/common/arrays'; -import { ExtensionRecommendations, ExtensionRecommendation, PromptedExtensionRecommendations } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; +import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IExtensionsConfigContent, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { ILogService } from 'vs/platform/log/common/log'; @@ -27,19 +26,17 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { get ignoredRecommendations(): ReadonlyArray { return this._ignoredRecommendations; } constructor( - promptedExtensionRecommendations: PromptedExtensionRecommendations, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, @INotificationService private readonly notificationService: INotificationService, ) { - super(promptedExtensionRecommendations); + super(); } protected async doActivate(): Promise { await this.fetch(); - this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); + this._register(this.workpsaceExtensionsConfigService.onDidChangeExtensionsConfigs(() => this.onDidChangeExtensionsConfigs())); } /** @@ -116,14 +113,12 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { return { validRecommendations: validExtensions, invalidRecommendations: invalidExtensions, message }; } - private async onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): Promise { - if (event.added.length) { - const oldWorkspaceRecommended = this._recommendations; - await this.fetch(); - // Suggest only if at least one of the newly added recommendations was not suggested before - if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { - this._onDidChangeRecommendations.fire(); - } + private async onDidChangeExtensionsConfigs(): Promise { + const oldWorkspaceRecommended = this._recommendations; + await this.fetch(); + // Suggest only if at least one of the newly added recommendations was not suggested before + if (this._recommendations.some(current => oldWorkspaceRecommended.every(old => current.extensionId !== old.extensionId))) { + this._onDidChangeRecommendations.fire(); } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 4f968a8c129..4b96ba059e7 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -59,6 +59,10 @@ import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkpsaceExtensionsConfigService, WorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; +import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -303,6 +307,8 @@ suite('ExtensionRecommendationsService Test', () => { workspaceService = new TestContextService(myWorkspace); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IWorkpsaceExtensionsConfigService, instantiationService.createInstance(WorkspaceExtensionsConfigService)); + instantiationService.stub(IExtensionIgnoredRecommendationsService, instantiationService.createInstance(ExtensionIgnoredRecommendationsService)); + instantiationService.stub(IExtensionRecommendationNotificationService, instantiationService.createInstance(ExtensionRecommendationNotificationService)); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); instantiationService.stub(IFileService, fileService); @@ -426,73 +432,73 @@ suite('ExtensionRecommendationsService Test', () => { }); }); - test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', () => { + test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { + const storageService = instantiationService.get(IStorageService); const workspaceIgnoredRecommendations = ['ms-dotnettools.csharp']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); - return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations).then(() => { - testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.activationPromise.then(() => { - const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(recommendations['ms-python.python']); + await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + await testObject.activationPromise; - assert.ok(!recommendations['mockpublisher2.mockextension2']); - assert.ok(!recommendations['ms-dotnettools.csharp']); - }); - }); + const recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(recommendations['ms-python.python']); + assert.ok(!recommendations['mockpublisher2.mockextension2']); + assert.ok(!recommendations['ms-dotnettools.csharp']); }); - test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', () => { + test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', async () => { + const storageService = instantiationService.get(IStorageService); + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); - instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); + storageService.store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.GLOBAL); + storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.GLOBAL); - return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionRecommendationsService); - return testObject.activationPromise.then(() => { - const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(recommendations['ms-python.python']); - assert.ok(recommendations['mockpublisher1.mockextension1']); + await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions); + const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + await testObject.activationPromise; - assert.ok(!recommendations['mockpublisher2.mockextension2']); + let recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(recommendations['ms-python.python']); + assert.ok(recommendations['mockpublisher1.mockextension1']); + assert.ok(!recommendations['mockpublisher2.mockextension2']); - return testObject.toggleIgnoredRecommendation('mockpublisher1.mockextension1', true); - }).then(() => { - const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(recommendations['ms-python.python']); + extensionIgnoredRecommendationsService.toggleGlobalIgnoredRecommendation('mockpublisher1.mockextension1', true); - assert.ok(!recommendations['mockpublisher1.mockextension1']); - assert.ok(!recommendations['mockpublisher2.mockextension2']); + recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(recommendations['ms-python.python']); + assert.ok(!recommendations['mockpublisher1.mockextension1']); + assert.ok(!recommendations['mockpublisher2.mockextension2']); - return testObject.toggleIgnoredRecommendation('mockpublisher1.mockextension1', false); - }).then(() => { - const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(recommendations['ms-python.python']); + extensionIgnoredRecommendationsService.toggleGlobalIgnoredRecommendation('mockpublisher1.mockextension1', false); - assert.ok(recommendations['mockpublisher1.mockextension1']); - assert.ok(!recommendations['mockpublisher2.mockextension2']); - }); - }); + recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(recommendations['ms-python.python']); + assert.ok(recommendations['mockpublisher1.mockextension1']); + assert.ok(!recommendations['mockpublisher2.mockextension2']); }); test('test global extensions are modified and recommendation change event is fired when an extension is ignored', async () => { + const storageService = instantiationService.get(IStorageService); const changeHandlerTarget = sinon.spy(); const ignoredExtensionId = 'Some.Extension'; - instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); - instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL); + storageService.store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE); + storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.GLOBAL); await setUpFolderWorkspace('myFolder', []); testObject = instantiationService.createInstance(ExtensionRecommendationsService); - testObject.onRecommendationChange(changeHandlerTarget); - testObject.toggleIgnoredRecommendation(ignoredExtensionId, true); + const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); + extensionIgnoredRecommendationsService.onDidChangeGlobalIgnoredRecommendation(changeHandlerTarget); + extensionIgnoredRecommendationsService.toggleGlobalIgnoredRecommendation(ignoredExtensionId, true); await testObject.activationPromise; assert.ok(changeHandlerTarget.calledOnce); diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts new file mode 100644 index 00000000000..ce9782b3a20 --- /dev/null +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { distinct } from 'vs/base/common/arrays'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { IExtensionIgnoredRecommendationsService, IgnoredRecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; + +const ignoredRecommendationsStorageKey = 'extensionsAssistant/ignored_recommendations'; + +export class ExtensionIgnoredRecommendationsService extends Disposable implements IExtensionIgnoredRecommendationsService { + + declare readonly _serviceBrand: undefined; + + private _onDidChangeIgnoredRecommendations = this._register(new Emitter()); + readonly onDidChangeIgnoredRecommendations = this._onDidChangeIgnoredRecommendations.event; + + // Global Ignored Recommendations + private _globalIgnoredRecommendations: string[] = []; + get globalIgnoredRecommendations(): string[] { return [...this._globalIgnoredRecommendations]; } + private _onDidChangeGlobalIgnoredRecommendation = this._register(new Emitter()); + readonly onDidChangeGlobalIgnoredRecommendation = this._onDidChangeGlobalIgnoredRecommendation.event; + + // Ignored Workspace Recommendations + private ignoredWorkspaceRecommendations: string[] = []; + + get ignoredRecommendations(): string[] { return distinct([...this.globalIgnoredRecommendations, ...this.ignoredWorkspaceRecommendations]); } + + constructor( + @IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService, + @IStorageService private readonly storageService: IStorageService, + @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + ) { + super(); + storageKeysSyncRegistryService.registerStorageKey({ key: ignoredRecommendationsStorageKey, version: 1 }); + this._globalIgnoredRecommendations = this.getCachedIgnoredRecommendations(); + this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e))); + + this.initIgnoredWorkspaceRecommendations(); + } + + private async initIgnoredWorkspaceRecommendations(): Promise { + this.ignoredWorkspaceRecommendations = await this.workpsaceExtensionsConfigService.getUnwantedRecommendations(); + this._onDidChangeIgnoredRecommendations.fire(); + this._register(this.workpsaceExtensionsConfigService.onDidChangeExtensionsConfigs(async () => { + this.ignoredWorkspaceRecommendations = await this.workpsaceExtensionsConfigService.getUnwantedRecommendations(); + this._onDidChangeIgnoredRecommendations.fire(); + })); + } + + toggleGlobalIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void { + extensionId = extensionId.toLowerCase(); + const ignored = this._globalIgnoredRecommendations.indexOf(extensionId) !== -1; + if (ignored === shouldIgnore) { + return; + } + + this._globalIgnoredRecommendations = shouldIgnore ? [...this._globalIgnoredRecommendations, extensionId] : this._globalIgnoredRecommendations.filter(id => id !== extensionId); + this.storeCachedIgnoredRecommendations(this._globalIgnoredRecommendations); + this._onDidChangeGlobalIgnoredRecommendation.fire({ extensionId, isRecommended: !shouldIgnore }); + this._onDidChangeIgnoredRecommendations.fire(); + } + + private getCachedIgnoredRecommendations(): string[] { + const ignoredRecommendations: string[] = JSON.parse(this.ignoredRecommendationsValue); + return ignoredRecommendations.map(e => e.toLowerCase()); + } + + private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void { + if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL + && this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) { + this._ignoredRecommendationsValue = undefined; + this._globalIgnoredRecommendations = this.getCachedIgnoredRecommendations(); + this._onDidChangeIgnoredRecommendations.fire(); + } + } + + private storeCachedIgnoredRecommendations(ignoredRecommendations: string[]): void { + this.ignoredRecommendationsValue = JSON.stringify(ignoredRecommendations); + } + + private _ignoredRecommendationsValue: string | undefined; + private get ignoredRecommendationsValue(): string { + if (!this._ignoredRecommendationsValue) { + this._ignoredRecommendationsValue = this.getStoredIgnoredRecommendationsValue(); + } + + return this._ignoredRecommendationsValue; + } + + private set ignoredRecommendationsValue(ignoredRecommendationsValue: string) { + if (this.ignoredRecommendationsValue !== ignoredRecommendationsValue) { + this._ignoredRecommendationsValue = ignoredRecommendationsValue; + this.setStoredIgnoredRecommendationsValue(ignoredRecommendationsValue); + } + } + + private getStoredIgnoredRecommendationsValue(): string { + return this.storageService.get(ignoredRecommendationsStorageKey, StorageScope.GLOBAL, '[]'); + } + + private setStoredIgnoredRecommendationsValue(value: string): void { + this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL); + } + +} + +registerSingleton(IExtensionIgnoredRecommendationsService, ExtensionIgnoredRecommendationsService); diff --git a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts index 921a9ab8b2f..21fb2dcf206 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/extensionRecommendations.ts @@ -3,20 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStringDictionary } from 'vs/base/common/collections'; +import { Event } from 'vs/base/common/event'; export interface IExtensionsConfigContent { recommendations: string[]; unwantedRecommendations: string[]; } -export type RecommendationChangeNotification = { - extensionId: string, - isRecommended: boolean -}; - export type DynamicRecommendation = 'dynamic'; export type ConfigRecommendation = 'config'; export type ExecutableRecommendation = 'executable'; @@ -44,7 +39,9 @@ export const IExtensionRecommendationsService = createDecorator; getAllRecommendationsWithReason(): IStringDictionary; + getImportantRecommendations(): Promise; getOtherRecommendations(): Promise; getFileBasedRecommendations(): string[]; @@ -52,8 +49,24 @@ export interface IExtensionRecommendationsService { getConfigBasedRecommendations(): Promise<{ important: string[], others: string[] }>; getWorkspaceRecommendations(): Promise; getKeymapRecommendations(): string[]; - - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void; - getIgnoredRecommendations(): ReadonlyArray; - onRecommendationChange: Event; } + +export type IgnoredRecommendationChangeNotification = { + extensionId: string, + isRecommended: boolean +}; + +export const IExtensionIgnoredRecommendationsService = createDecorator('IExtensionIgnoredRecommendationsService'); + +export interface IExtensionIgnoredRecommendationsService { + readonly _serviceBrand: undefined; + + onDidChangeIgnoredRecommendations: Event; + readonly ignoredRecommendations: string[]; + + onDidChangeGlobalIgnoredRecommendation: Event; + readonly globalIgnoredRecommendations: string[]; + toggleGlobalIgnoredRecommendation(extensionId: string, ignore: boolean): void; +} + + diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index 8ef8e6c3ebf..dfcdaf50147 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -22,19 +24,26 @@ export const IWorkpsaceExtensionsConfigService = createDecorator; getExtensionsConfigs(): Promise; getUnwantedRecommendations(): Promise; } -export class WorkspaceExtensionsConfigService implements IWorkpsaceExtensionsConfigService { +export class WorkspaceExtensionsConfigService extends Disposable implements IWorkpsaceExtensionsConfigService { declare readonly _serviceBrand: undefined; + private readonly _onDidChangeExtensionsConfigs = this._register(new Emitter()); + readonly onDidChangeExtensionsConfigs = this._onDidChangeExtensionsConfigs.event; + constructor( @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileService private readonly fileService: IFileService, - ) { } + ) { + super(); + this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(e => this._onDidChangeExtensionsConfigs.fire())); + } async getExtensionsConfigs(): Promise { const workspace = this.workspaceContextService.getWorkspace(); @@ -47,7 +56,7 @@ export class WorkspaceExtensionsConfigService implements IWorkpsaceExtensionsCon async getUnwantedRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.unwantedRecommendations))); + return distinct(flatten(configs.map(c => c.unwantedRecommendations.map(c => c.toLowerCase())))); } private async resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index be98cac2d73..5adea5d1a72 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -75,6 +75,7 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensionManagement/common/webExtensionsScannerService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import 'vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService'; +import 'vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService'; import 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; From 3a0d27441c1e70ff9a1d6c640e059021e40d1b89 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 27 Sep 2020 15:31:18 +0200 Subject: [PATCH 89/98] Fix #105914 --- .../sharedProcess/sharedProcessMain.ts | 6 ++ .../electron-sandbox/extensionTipsService.ts | 77 +++++++++++++++- .../extensionRecommendationsIpc.ts | 48 ++++++++++ .../windows/electron-sandbox/windowTracker.ts | 51 +++++++++++ .../browser/exeBasedRecommendations.ts | 89 +------------------ ...ensionRecommendationNotificationService.ts | 15 +++- .../extensions.contribution.ts | 8 +- 7 files changed, 199 insertions(+), 95 deletions(-) create mode 100644 src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts create mode 100644 src/vs/platform/windows/electron-sandbox/windowTracker.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 411b06ca70d..bd60d1fe56d 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -66,6 +66,9 @@ import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; +import { ActiveWindowManager } from 'vs/platform/windows/electron-sandbox/windowTracker'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -156,8 +159,11 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const nativeHostService = createChannelSender(mainProcessService.getChannel('nativeHost'), { context: configuration.windowId }); services.set(INativeHostService, nativeHostService); + const activeWindowManager = new ActiveWindowManager(nativeHostService); + const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); services.set(IDownloadService, new SyncDescriptor(DownloadService)); + services.set(IExtensionRecommendationNotificationService, new ExtensionRecommendationNotificationServiceChannelClient(server.getChannel('IExtensionRecommendationNotificationService', activeWindowRouter))); const instantiationService = new InstantiationService(services); diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index 4366dc4fdfc..a335ec9397d 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -4,18 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { join, } from 'vs/base/common/path'; +import { basename, join, } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IFileService } from 'vs/platform/files/common/files'; import { isWindows } from 'vs/base/common/platform'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { IExecutableBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { forEach } from 'vs/base/common/collections'; import { IRequestService } from 'vs/platform/request/common/request'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; +import { timeout } from 'vs/base/common/async'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { localize } from 'vs/nls'; + +type ExeExtensionRecommendationsClassification = { + extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + exeName: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; +}; type IExeBasedExtensionTips = { readonly exeFriendlyName: string, @@ -32,6 +41,9 @@ export class ExtensionTipsService extends BaseExtensionTipsService { constructor( @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, @IFileService fileService: IFileService, @IProductService productService: IProductService, @IRequestService requestService: IRequestService, @@ -57,6 +69,12 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } }); } + + /* + 3s has come out to be the good number to fetch and prompt important exe based recommendations + Also fetch important exe based recommendations for reporting telemetry + */ + timeout(3000).then(() => this.promptImportantExeBasedRecommendations()); } getImportantExecutableBasedTips(): Promise { @@ -67,6 +85,61 @@ export class ExtensionTipsService extends BaseExtensionTipsService { return this.getValidExecutableBasedExtensionTips(this.allOtherExecutableTips); } + private async promptImportantExeBasedRecommendations(): Promise { + const importantExeBasedRecommendations = new Map(); + const importantExeBasedTips = await this.getImportantExecutableBasedTips(); + importantExeBasedTips.forEach(tip => importantExeBasedRecommendations.set(tip.extensionId.toLowerCase(), tip)); + + const local = await this.extensionManagementService.getInstalled(); + const { installed, uninstalled: recommendations } = this.groupByInstalled([...importantExeBasedRecommendations.keys()], local); + + /* Log installed and uninstalled exe based recommendations */ + for (const extensionId of installed) { + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } + } + for (const extensionId of recommendations) { + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } + } + + const recommendationsByExe = new Map(); + for (const extensionId of recommendations) { + const tip = importantExeBasedRecommendations.get(extensionId); + if (tip) { + let tips = recommendationsByExe.get(tip.exeFriendlyName); + if (!tips) { + tips = []; + recommendationsByExe.set(tip.exeFriendlyName, tips); + } + tips.push(tip); + } + } + + for (const [, tips] of recommendationsByExe) { + const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase()); + const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName); + this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); + } + } + + private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { + const installed: string[] = [], uninstalled: string[] = []; + const installedExtensionsIds = local.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); + recommendationsToSuggest.forEach(id => { + if (installedExtensionsIds.has(id.toLowerCase())) { + installed.push(id); + } else { + uninstalled.push(id); + } + }); + return { installed, uninstalled }; + } + private async getValidExecutableBasedExtensionTips(executableTips: Map): Promise { const result: IExecutableBasedExtensionTip[] = []; diff --git a/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts b/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts new file mode 100644 index 00000000000..c5896d18eda --- /dev/null +++ b/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; + +export class ExtensionRecommendationNotificationServiceChannelClient implements IExtensionRecommendationNotificationService { + + declare readonly _serviceBrand: undefined; + + constructor(private readonly channel: IChannel) { } + + get ignoredRecommendations(): string[] { throw new Error('not supported'); } + + promptImportantExtensionsInstallNotification(extensionIds: string[], message: string, searchValue: string): Promise { + return this.channel.call('promptImportantExtensionsInstallNotification', [extensionIds, message, searchValue]); + } + + promptWorkspaceRecommendations(recommendations: string[]): Promise { + throw new Error('not supported'); + } + + hasToIgnoreRecommendationNotifications(): boolean { + throw new Error('not supported'); + } + +} + +export class ExtensionRecommendationNotificationServiceChannel implements IServerChannel { + + constructor(private service: IExtensionRecommendationNotificationService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + call(_: unknown, command: string, args?: any): Promise { + switch (command) { + case 'promptImportantExtensionsInstallNotification': this.service.promptImportantExtensionsInstallNotification(args[0], args[1], args[2]); return Promise.resolve(); + } + + throw new Error(`Call not found: ${command}`); + } +} + diff --git a/src/vs/platform/windows/electron-sandbox/windowTracker.ts b/src/vs/platform/windows/electron-sandbox/windowTracker.ts new file mode 100644 index 00000000000..92d3e21c4b6 --- /dev/null +++ b/src/vs/platform/windows/electron-sandbox/windowTracker.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; + +export class ActiveWindowManager extends Disposable { + + private readonly disposables = this._register(new DisposableStore()); + private firstActiveWindowIdPromise: CancelablePromise | undefined; + + private activeWindowId: number | undefined; + + constructor(@INativeHostService nativeHostService: INativeHostService) { + super(); + + // remember last active window id upon events + const onActiveWindowChange = Event.latch(Event.any(nativeHostService.onWindowOpen, nativeHostService.onWindowFocus)); + onActiveWindowChange(this.setActiveWindow, this, this.disposables); + + // resolve current active window + this.firstActiveWindowIdPromise = createCancelablePromise(() => nativeHostService.getActiveWindowId()); + (async () => { + try { + const windowId = await this.firstActiveWindowIdPromise; + this.activeWindowId = (typeof this.activeWindowId === 'number') ? this.activeWindowId : windowId; + } finally { + this.firstActiveWindowIdPromise = undefined; + } + })(); + } + + private setActiveWindow(windowId: number | undefined) { + if (this.firstActiveWindowIdPromise) { + this.firstActiveWindowIdPromise.cancel(); + this.firstActiveWindowIdPromise = undefined; + } + + this.activeWindowId = windowId; + } + + async getActiveClientId(): Promise { + const id = this.firstActiveWindowIdPromise ? (await this.firstActiveWindowIdPromise) : this.activeWindowId; + + return `window:${id}`; + } +} diff --git a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts index cccecbcfe65..6afbdc7f903 100644 --- a/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/exeBasedRecommendations.ts @@ -3,21 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IExtensionTipsService, IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionTipsService, IExecutableBasedExtensionTip } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; -import { timeout } from 'vs/base/common/async'; import { localize } from 'vs/nls'; -import { optional } from 'vs/platform/instantiation/common/instantiation'; import { basename } from 'vs/base/common/path'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; -import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; - -type ExeExtensionRecommendationsClassification = { - extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - exeName: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; -}; export class ExeBasedRecommendations extends ExtensionRecommendations { @@ -29,23 +19,10 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { get recommendations(): ReadonlyArray { return [...this.importantRecommendations, ...this.otherRecommendations]; } - private readonly tasExperimentService: ITASExperimentService | undefined; - constructor( @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, ) { super(); - this.tasExperimentService = tasExperimentService; - - /* - 3s has come out to be the good number to fetch and prompt important exe based recommendations - Also fetch important exe based recommendations for reporting telemetry - */ - timeout(3000).then(() => this.fetchAndPromptImportantExeBasedRecommendations()); } getRecommendations(exe: string): { important: ExtensionRecommendation[], others: ExtensionRecommendation[] } { @@ -80,70 +57,6 @@ export class ExeBasedRecommendations extends ExtensionRecommendations { return importantExeBasedRecommendations; } - private async fetchAndPromptImportantExeBasedRecommendations(): Promise { - const importantExeBasedRecommendations = await this.fetchImportantExeBasedRecommendations(); - - const local = await this.extensionManagementService.getInstalled(); - const { installed, uninstalled } = this.groupByInstalled([...importantExeBasedRecommendations.keys()], local); - - /* Log installed and uninstalled exe based recommendations */ - for (const extensionId of installed) { - const tip = importantExeBasedRecommendations.get(extensionId); - if (tip) { - this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); - } - } - for (const extensionId of uninstalled) { - const tip = importantExeBasedRecommendations.get(extensionId); - if (tip) { - this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); - } - } - - this.promptImportantExeBasedRecommendations(uninstalled, importantExeBasedRecommendations); - } - - private async promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: Map): Promise { - if (this.extensionRecommendationNotificationService.hasToIgnoreRecommendationNotifications()) { - return; - } - const recommendationsByExe = new Map(); - for (const extensionId of recommendations) { - const tip = importantExeBasedRecommendations.get(extensionId); - if (tip) { - let tips = recommendationsByExe.get(tip.exeFriendlyName); - if (!tips) { - tips = []; - recommendationsByExe.set(tip.exeFriendlyName, tips); - } - tips.push(tip); - } - } - - for (const [, tips] of recommendationsByExe) { - const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase()); - if (this.tasExperimentService && extensionIds.indexOf('ms-vscode-remote.remote-wsl') !== -1) { - await this.tasExperimentService.getTreatment('wslpopupaa'); - } - - const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName); - this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); - } - } - - private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { - const installed: string[] = [], uninstalled: string[] = []; - const installedExtensionsIds = local.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); - recommendationsToSuggest.forEach(id => { - if (installedExtensionsIds.has(id.toLowerCase())) { - installed.push(id); - } else { - uninstalled.push(id); - } - }); - return { installed, uninstalled }; - } - private toExtensionRecommendation(tip: IExecutableBasedExtensionTip): ExtensionRecommendation { return { extensionId: tip.extensionId.toLowerCase(), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index d88576ce36c..8aa220ac14f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -5,19 +5,19 @@ import { IAction } from 'vs/base/common/actions'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; @@ -42,10 +42,12 @@ const ignoreImportantExtensionRecommendation = 'extensionsAssistant/importantRec const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; const choiceNever = localize('neverShowAgain', "Don't Show Again"); -export class ExtensionRecommendationNotificationService extends Disposable implements IExtensionRecommendationNotificationService { +export class ExtensionRecommendationNotificationService implements IExtensionRecommendationNotificationService { declare readonly _serviceBrand: undefined; + private readonly tasExperimentService: ITASExperimentService | undefined; + // Ignored Important Recommendations get ignoredRecommendations(): string[] { return [...(JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')))].map(i => i.toLowerCase()); @@ -62,9 +64,10 @@ export class ExtensionRecommendationNotificationService extends Disposable imple @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { - super(); storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendation, version: 1 }); + this.tasExperimentService = tasExperimentService; } hasToIgnoreRecommendationNotifications(): boolean { @@ -87,6 +90,10 @@ export class ExtensionRecommendationNotificationService extends Disposable imple return false; } + if (this.tasExperimentService && extensionIds.indexOf('ms-vscode-remote.remote-wsl') !== -1) { + await this.tasExperimentService.getTreatment('wslpopupaa'); + } + this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', "Install"), diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 9e6a7b2e302..21d2f7844d3 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -23,6 +23,9 @@ import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-sandbox/extensionsActions'; import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { ExtensionRecommendationNotificationServiceChannel } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; // Singletons registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); @@ -64,8 +67,11 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.from(ShowRuntimeExte class ExtensionsContributions implements IWorkbenchContribution { constructor( - @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService + @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, + @IExtensionRecommendationNotificationService extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, + @ISharedProcessService sharedProcessService: ISharedProcessService, ) { + sharedProcessService.registerChannel('IExtensionRecommendationNotificationService', new ExtensionRecommendationNotificationServiceChannel(extensionRecommendationNotificationService)); if (environmentService.extensionsPath) { const openExtensionsFolderActionDescriptor = SyncActionDescriptor.from(OpenExtensionsFolderAction); actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); From f5e4c2d1f6010046badc1ece12e73d02199f3dc0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 26 Sep 2020 11:48:58 -0500 Subject: [PATCH 90/98] Fix settings editor toc disappearing during search --- src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 4f6f8550cdb..a7c67100ab2 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -887,7 +887,7 @@ export class SettingsEditor2 extends EditorPane { } private onSearchModeToggled(): void { - this.rootElement.classList.add('no-toc-search'); + this.rootElement.classList.remove('no-toc-search'); if (this.configurationService.getValue('workbench.settings.settingsSearchTocBehavior') === 'hide') { this.rootElement.classList.toggle('no-toc-search', !!this.searchResultModel); } From 2ecd0c61cb5fc7dfadac59811057cfb971d1ffb4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 26 Sep 2020 12:50:48 -0500 Subject: [PATCH 91/98] Fix showing setting context menu with keybinding #106897 --- .../preferences/browser/settingsEditor2.ts | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index a7c67100ab2..8ec070e6394 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -362,25 +362,13 @@ export class SettingsEditor2 extends EditorPane { } showContextMenu(): void { - const activeElement = this.getActiveElementInSettingsTree(); - if (!activeElement) { + const focused = this.settingsTree.getFocus()[0]; + if (!(focused instanceof SettingsTreeSettingElement)) { return; } - const settingDOMElement = this.settingRenderers.getSettingDOMElementForDOMElement(activeElement); - if (!settingDOMElement) { - return; - } - - const focusedKey = this.settingRenderers.getKeyForDOMElementInSetting(settingDOMElement); - if (!focusedKey) { - return; - } - - const elements = this.currentSettingsModel.getElementsByName(focusedKey); - if (elements && elements[0]) { - this.settingRenderers.showContextMenu(elements[0], settingDOMElement); - } + const rowElement = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), focused.setting.key)[0]; + this.settingRenderers.showContextMenu(focused, rowElement); } focusSearch(filter?: string, selectAll = true): void { @@ -984,7 +972,7 @@ export class SettingsEditor2 extends EditorPane { } } - private getActiveElementInSettingsTree(): HTMLElement | null { + private getActiveControlInSettingsTree(): HTMLElement | null { return (document.activeElement && DOM.isAncestor(document.activeElement, this.settingsTree.getHTMLElement())) ? document.activeElement : null; @@ -1006,7 +994,7 @@ export class SettingsEditor2 extends EditorPane { } // If a setting control is currently focused, schedule a refresh for later - const activeElement = this.getActiveElementInSettingsTree(); + const activeElement = this.getActiveControlInSettingsTree(); const focusedSetting = activeElement && this.settingRenderers.getSettingDOMElementForDOMElement(activeElement); if (focusedSetting && !force) { // If a single setting is being refreshed, it's ok to refresh now if that is not the focused setting @@ -1067,7 +1055,7 @@ export class SettingsEditor2 extends EditorPane { const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key); if (elements && elements[0]) { - DOM.toggleClass(elements[0], 'is-configured', !!isModified); + elements[0].classList.toggle('is-configured', !!isModified); } } From e96516c852323d752b0742f4ab3d341155d65111 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 26 Sep 2020 17:17:10 -0500 Subject: [PATCH 92/98] Fix restoring focus to setting row vs control #106897 --- .../preferences/browser/settingsEditor2.ts | 66 +++++++++++++------ 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 8ec070e6394..11ef90e1ea1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -53,6 +53,13 @@ import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/p import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; +const enum SettingsFocusContext { + Search, + TableOfContents, + SettingTree, + SettingControl +} + function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { return Iterable.map(group.children, g => { return { @@ -138,7 +145,7 @@ export class SettingsEditor2 extends EditorPane { private searchFocusContextKey: IContextKey; private scheduledRefreshes: Map; - private lastFocusedSettingElement: string | null = null; + private lastFocusContext: SettingsFocusContext = SettingsFocusContext.Search; /** Don't spam warnings */ private hasWarnedMissingSettings = false; @@ -215,6 +222,15 @@ export class SettingsEditor2 extends EditorPane { this.rootElement.classList.toggle('search-mode', !!this._searchResultModel); } + private get focusedSettingDOMElement(): HTMLElement | undefined { + const focused = this.settingsTree.getFocus()[0]; + if (!(focused instanceof SettingsTreeSettingElement)) { + return; + } + + return this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), focused.setting.key)[0]; + } + createEditor(parent: HTMLElement): void { parent.setAttribute('tabindex', '-1'); this.rootElement = DOM.append(parent, $('.settings-editor', { tabindex: '-1' })); @@ -311,23 +327,27 @@ export class SettingsEditor2 extends EditorPane { const monacoWidth = innerWidth - 10 - this.countElement.clientWidth - this.controlsElement.clientWidth - 12; this.searchWidget.layout({ height: 20, width: monacoWidth }); - DOM.toggleClass(this.rootElement, 'mid-width', dimension.width < 1000 && dimension.width >= 600); - DOM.toggleClass(this.rootElement, 'narrow-width', dimension.width < 600); + this.rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); + this.rootElement.classList.toggle('narrow-width', dimension.width < 600); } focus(): void { - if (this.lastFocusedSettingElement) { - const elements = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), this.lastFocusedSettingElement); - if (elements.length) { - const control = elements[0].querySelector(AbstractSettingRenderer.CONTROL_SELECTOR); + if (this.lastFocusContext === SettingsFocusContext.Search) { + this.focusSearch(); + } else if (this.lastFocusContext === SettingsFocusContext.SettingControl) { + const element = this.focusedSettingDOMElement; + if (element) { + const control = element.querySelector(AbstractSettingRenderer.CONTROL_SELECTOR); if (control) { (control).focus(); return; } } + } else if (this.lastFocusContext === SettingsFocusContext.SettingTree) { + this.settingsTree.domFocus(); + } else if (this.lastFocusContext === SettingsFocusContext.TableOfContents) { + this.tocTree.domFocus(); } - - this.focusSearch(); } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { @@ -363,12 +383,10 @@ export class SettingsEditor2 extends EditorPane { showContextMenu(): void { const focused = this.settingsTree.getFocus()[0]; - if (!(focused instanceof SettingsTreeSettingElement)) { - return; + const rowElement = this.focusedSettingDOMElement; + if (rowElement && focused instanceof SettingsTreeSettingElement) { + this.settingRenderers.showContextMenu(focused, rowElement); } - - const rowElement = this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), focused.setting.key)[0]; - this.settingRenderers.showContextMenu(focused, rowElement); } focusSearch(filter?: string, selectAll = true): void { @@ -417,11 +435,9 @@ export class SettingsEditor2 extends EditorPane { placeholderText: searchBoxLabel, focusContextKey: this.searchFocusContextKey, // TODO: Aria-live - }) - ); - + })); this._register(this.searchWidget.onFocus(() => { - this.lastFocusedSettingElement = ''; + this.lastFocusContext = SettingsFocusContext.Search; })); this._register(attachSuggestEnabledInputBoxStyler(this.searchWidget, this.themeService, { @@ -585,6 +601,10 @@ export class SettingsEditor2 extends EditorPane { })), this.viewState)); + this._register(this.tocTree.onDidFocus(() => { + this.lastFocusContext = SettingsFocusContext.TableOfContents; + })); + this._register(this.tocTree.onDidChangeFocus(e => { const element: SettingsTreeGroupElement | null = e.elements[0]; if (this.tocFocusedElement === element) { @@ -624,8 +644,8 @@ export class SettingsEditor2 extends EditorPane { })); this._register(this.settingRenderers.onDidClickSettingLink(settingName => this.onDidClickSetting(settingName))); this._register(this.settingRenderers.onDidFocusSetting(element => { - this.lastFocusedSettingElement = element.setting.key; this.settingsTree.setFocus([element]); + this.lastFocusContext = SettingsFocusContext.SettingControl; })); this._register(this.settingRenderers.onDidClickOverrideElement((element: ISettingOverrideClickEvent) => { if (element.scope.toLowerCase() === 'workspace') { @@ -659,6 +679,10 @@ export class SettingsEditor2 extends EditorPane { }, 0); })); + this._register(this.settingsTree.onDidFocus(() => { + this.lastFocusContext = SettingsFocusContext.SettingTree; + })); + // There is no different select state in the settings tree this._register(this.settingsTree.onDidChangeFocus(e => { const element = e.elements[0]; @@ -1270,7 +1294,7 @@ export class SettingsEditor2 extends EditorPane { this.layout(this.dimension); } - DOM.removeClass(this.rootElement, 'no-results'); + this.rootElement.classList.remove('no-results'); return; } @@ -1286,7 +1310,7 @@ export class SettingsEditor2 extends EditorPane { this.countElement.style.display = 'block'; this.layout(this.dimension); } - DOM.toggleClass(this.rootElement, 'no-results', count === 0); + this.rootElement.classList.toggle('no-results', count === 0); } } From e79e1cfb2fccaeb9bc715aa0c9c4c811d2a75f50 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 26 Sep 2020 17:55:53 -0500 Subject: [PATCH 93/98] Prefs actions clean up, add to command palette --- .../browser/preferences.contribution.ts | 77 ++++++------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index d669a204251..11f3a059496 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -51,7 +51,6 @@ const SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING = 'settings.action.editFocuse const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focusSettingsFromSearch'; const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; const SETTINGS_EDITOR_COMMAND_FOCUS_TOC = 'settings.action.focusTOC'; -const SETTINGS_EDITOR_COMMAND_FOCUS_TOC2 = 'settings.action.focusTOC2'; const SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL = 'settings.action.focusSettingControl'; const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; @@ -168,6 +167,8 @@ Registry.as(EditorInputExtensions.EditorInputFactor const OPEN_SETTINGS2_ACTION_TITLE = { value: nls.localize('openSettings2', "Open Settings (UI)"), original: 'Open Settings (UI)' }; +const category = { value: nls.localize('preferences', "Preferences"), original: 'Preferences' }; + class PreferencesActionsContribution extends Disposable implements IWorkbenchContribution { constructor( @@ -189,7 +190,6 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon private registerSettingsActions() { const that = this; - const category = { value: nls.localize('preferences', "Preferences"), original: 'Preferences' }; registerAction2(class extends Action2 { constructor() { super({ @@ -226,7 +226,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openSettings2', title: { value: nls.localize('openSettings2', "Open Settings (UI)"), original: 'Open Settings (UI)' }, category, - menu: { id: MenuId.CommandPalette } + f1: true, }); } run(accessor: ServicesAccessor) { @@ -239,7 +239,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openSettingsJson', title: { value: nls.localize('openSettingsJson', "Open Settings (JSON)"), original: 'Open Settings (JSON)' }, category, - menu: { id: MenuId.CommandPalette } + f1: true, }); } run(accessor: ServicesAccessor) { @@ -252,7 +252,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openGlobalSettings', title: { value: nls.localize('openGlobalSettings', "Open User Settings"), original: 'Open User Settings' }, category, - menu: { id: MenuId.CommandPalette } + f1: true, }); } run(accessor: ServicesAccessor) { @@ -265,7 +265,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: 'workbench.action.openRawDefaultSettings', title: { value: nls.localize('openRawDefaultSettings', "Open Default Settings (JSON)"), original: 'Open Default Settings (JSON)' }, category, - menu: { id: MenuId.CommandPalette } + f1: true, }); } run(accessor: ServicesAccessor) { @@ -318,7 +318,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon id: ConfigureLanguageBasedSettingsAction.ID, title: ConfigureLanguageBasedSettingsAction.LABEL, category, - menu: { id: MenuId.CommandPalette } + f1: true, }); } run(accessor: ServicesAccessor) { @@ -522,30 +522,15 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_SEARCH, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR), + precondition: CONTEXT_SETTINGS_EDITOR, keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F, weight: KeybindingWeight.EditorContrib, when: null }, - title: nls.localize('settings.focusSearch', "Focus settings search") - }); - } - - run(accessor: ServicesAccessor) { settingsEditorFocusSearch(accessor); } - }); - - registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_SEARCH, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS), - keybinding: { - primary: KeyCode.Escape, - weight: KeybindingWeight.WorkbenchContrib, - when: null - }, - title: nls.localize('settings.focusSearch', "Focus settings search") + category, + f1: true, + title: nls.localize('settings.focusSearch', "Focus Settings Search") }); } @@ -556,13 +541,15 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, - precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + precondition: CONTEXT_SETTINGS_EDITOR, keybinding: { primary: KeyCode.Escape, weight: KeybindingWeight.EditorContrib, - when: null + when: CONTEXT_SETTINGS_SEARCH_FOCUS }, - title: nls.localize('settings.clearResults', "Clear settings search results") + category, + f1: true, + title: nls.localize('settings.clearResults', "Clear Settings Search Results") }); } @@ -714,6 +701,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FOCUS_TOC, + precondition: CONTEXT_SETTINGS_EDITOR, + f1: true, keybinding: [ { primary: KeyCode.Escape, @@ -725,7 +714,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate(), InputFocusedContext.negate()) }], - title: nls.localize('settings.focusSettingsTOC', "Focus settings TOC tree") + category, + title: nls.localize('settings.focusSettingsTOC', "Focus Settings Table of Contents") }); } @@ -752,7 +742,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon primary: KeyCode.Enter, weight: KeybindingWeight.WorkbenchContrib, }, - title: nls.localize('settings.focusSettingControl', "Focus setting control") + title: nls.localize('settings.focusSettingControl', "Focus Setting Control") }); } @@ -768,36 +758,19 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } }); - registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_FOCUS_TOC2, - - title: nls.localize('settings.focusSettingsTOC', "Focus settings TOC tree") - }); - } - - run(accessor: ServicesAccessor): void { - const preferencesEditor = getPreferencesEditor(accessor); - if (!(preferencesEditor instanceof SettingsEditor2)) { - return; - } - - preferencesEditor.focusTOC(); - } - }); - registerAction2(class extends Action2 { constructor() { super({ id: SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR), + precondition: CONTEXT_SETTINGS_EDITOR, keybinding: { primary: KeyMod.Shift | KeyCode.F9, weight: KeybindingWeight.WorkbenchContrib, when: null }, - title: nls.localize('settings.showContextMenu', "Show context menu") + f1: true, + category, + title: nls.localize('settings.showContextMenu', "Show Setting Context Menu") }); } From c5bbe573d8109144cdf89e1282e7f2baf0996eae Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Sep 2020 11:15:42 -0700 Subject: [PATCH 94/98] Fix "enter" to focus control on complex settings #106897 --- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 0d23b380203..52653f049d0 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -869,6 +869,7 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I common.toDispose.add(openSettingsButton.onDidClick(() => template.onChange!())); openSettingsButton.label = SettingComplexRenderer.EDIT_IN_JSON_LABEL; openSettingsButton.element.classList.add('edit-in-settings-button'); + openSettingsButton.element.classList.add(AbstractSettingRenderer.CONTROL_CLASS); common.toDispose.add(attachButtonStyler(openSettingsButton, this._themeService, { buttonBackground: Color.transparent.toString(), From 832725b9dc98448d42e2fd87f442d276001f3a6a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Sep 2020 11:16:17 -0700 Subject: [PATCH 95/98] Add "focus level up" command to clean up "escape" behavior on settings editor #106897 --- .../browser/preferences.contribution.ts | 59 +++++++++++++------ .../preferences/browser/settingsEditor2.ts | 34 +++++++---- .../contrib/preferences/common/preferences.ts | 1 + 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 11f3a059496..6c013fb1efc 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -5,6 +5,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/preferences'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; @@ -12,18 +13,18 @@ import * as nls from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContext, IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Schemas } from 'vs/base/common/network'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { RemoteNameContext, WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { AbstractSideBySideEditorInputFactory } from 'vs/workbench/browser/parts/editor/editor.contribution'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { ResourceContextKey } from 'vs/workbench/common/resources'; @@ -31,16 +32,14 @@ import { ExplorerFolderContext, ExplorerRootContext } from 'vs/workbench/contrib import { KeybindingsEditor } from 'vs/workbench/contrib/preferences/browser/keybindingsEditor'; import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; -import { SettingsEditor2 } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, MODIFIED_SETTING_TAG, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { AbstractSideBySideEditorInputFactory } from 'vs/workbench/browser/parts/editor/editor.contribution'; -import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -52,6 +51,7 @@ const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focu const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; const SETTINGS_EDITOR_COMMAND_FOCUS_TOC = 'settings.action.focusTOC'; const SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL = 'settings.action.focusSettingControl'; +const SETTINGS_EDITOR_COMMAND_FOCUS_UP = 'settings.action.focusLevelUp'; const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; @@ -704,15 +704,10 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon precondition: CONTEXT_SETTINGS_EDITOR, f1: true, keybinding: [ - { - primary: KeyCode.Escape, - weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate()), - }, { primary: KeyCode.LeftArrow, weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate(), InputFocusedContext.negate()) + when: CONTEXT_SETTINGS_ROW_FOCUS }], category, title: nls.localize('settings.focusSettingsTOC', "Focus Settings Table of Contents") @@ -725,11 +720,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon return; } - if (document.activeElement?.classList.contains('monaco-list')) { - preferencesEditor.focusTOC(); - } else { - preferencesEditor.focusSettings(); - } + preferencesEditor.focusTOC(); } }); @@ -737,7 +728,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS.negate(), WorkbenchListFocusContextKey), + precondition: CONTEXT_SETTINGS_ROW_FOCUS, keybinding: { primary: KeyCode.Enter, weight: KeybindingWeight.WorkbenchContrib, @@ -781,6 +772,38 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } } }); + + registerAction2(class extends Action2 { + constructor() { + super({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_UP, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS.toNegated()), + keybinding: { + primary: KeyCode.Escape, + weight: KeybindingWeight.WorkbenchContrib, + when: null + }, + f1: true, + category, + title: nls.localize('settings.focusLevelUp', "Move Focus Up One Level") + }); + } + + run(accessor: ServicesAccessor): void { + const preferencesEditor = getPreferencesEditor(accessor); + if (!(preferencesEditor instanceof SettingsEditor2)) { + return; + } + + if (preferencesEditor.currentFocusContext === SettingsFocusContext.SettingControl) { + preferencesEditor.focusSettings(); + } else if (preferencesEditor.currentFocusContext === SettingsFocusContext.SettingTree) { + preferencesEditor.focusTOC(); + } else if (preferencesEditor.currentFocusContext === SettingsFocusContext.TableOfContents) { + preferencesEditor.focusSearch(); + } + } + }); } private registerKeybindingsActions() { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 11ef90e1ea1..955b234343c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -46,14 +46,14 @@ import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickE import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, EXTENSION_SETTING_TAG, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/contrib/preferences/common/preferences'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; -const enum SettingsFocusContext { +export const enum SettingsFocusContext { Search, TableOfContents, SettingTree, @@ -141,11 +141,12 @@ export class SettingsEditor2 extends EditorPane { private _searchResultModel: SearchResultModel | null = null; private tocRowFocused: IContextKey; + private settingRowFocused: IContextKey; private inSettingsEditorContextKey: IContextKey; private searchFocusContextKey: IContextKey; private scheduledRefreshes: Map; - private lastFocusContext: SettingsFocusContext = SettingsFocusContext.Search; + private _currentFocusContext: SettingsFocusContext = SettingsFocusContext.Search; /** Don't spam warnings */ private hasWarnedMissingSettings = false; @@ -187,6 +188,7 @@ export class SettingsEditor2 extends EditorPane { this.inSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(contextKeyService); this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(contextKeyService); this.tocRowFocused = CONTEXT_TOC_ROW_FOCUS.bindTo(contextKeyService); + this.settingRowFocused = CONTEXT_SETTINGS_ROW_FOCUS.bindTo(contextKeyService); this.scheduledRefreshes = new Map(); @@ -231,6 +233,10 @@ export class SettingsEditor2 extends EditorPane { return this.settingRenderers.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), focused.setting.key)[0]; } + get currentFocusContext() { + return this._currentFocusContext; + } + createEditor(parent: HTMLElement): void { parent.setAttribute('tabindex', '-1'); this.rootElement = DOM.append(parent, $('.settings-editor', { tabindex: '-1' })); @@ -332,9 +338,9 @@ export class SettingsEditor2 extends EditorPane { } focus(): void { - if (this.lastFocusContext === SettingsFocusContext.Search) { + if (this._currentFocusContext === SettingsFocusContext.Search) { this.focusSearch(); - } else if (this.lastFocusContext === SettingsFocusContext.SettingControl) { + } else if (this._currentFocusContext === SettingsFocusContext.SettingControl) { const element = this.focusedSettingDOMElement; if (element) { const control = element.querySelector(AbstractSettingRenderer.CONTROL_SELECTOR); @@ -343,9 +349,9 @@ export class SettingsEditor2 extends EditorPane { return; } } - } else if (this.lastFocusContext === SettingsFocusContext.SettingTree) { + } else if (this._currentFocusContext === SettingsFocusContext.SettingTree) { this.settingsTree.domFocus(); - } else if (this.lastFocusContext === SettingsFocusContext.TableOfContents) { + } else if (this._currentFocusContext === SettingsFocusContext.TableOfContents) { this.tocTree.domFocus(); } } @@ -437,7 +443,7 @@ export class SettingsEditor2 extends EditorPane { // TODO: Aria-live })); this._register(this.searchWidget.onFocus(() => { - this.lastFocusContext = SettingsFocusContext.Search; + this._currentFocusContext = SettingsFocusContext.Search; })); this._register(attachSuggestEnabledInputBoxStyler(this.searchWidget, this.themeService, { @@ -602,7 +608,7 @@ export class SettingsEditor2 extends EditorPane { this.viewState)); this._register(this.tocTree.onDidFocus(() => { - this.lastFocusContext = SettingsFocusContext.TableOfContents; + this._currentFocusContext = SettingsFocusContext.TableOfContents; })); this._register(this.tocTree.onDidChangeFocus(e => { @@ -645,7 +651,8 @@ export class SettingsEditor2 extends EditorPane { this._register(this.settingRenderers.onDidClickSettingLink(settingName => this.onDidClickSetting(settingName))); this._register(this.settingRenderers.onDidFocusSetting(element => { this.settingsTree.setFocus([element]); - this.lastFocusContext = SettingsFocusContext.SettingControl; + this._currentFocusContext = SettingsFocusContext.SettingControl; + this.settingRowFocused.set(false); })); this._register(this.settingRenderers.onDidClickOverrideElement((element: ISettingOverrideClickEvent) => { if (element.scope.toLowerCase() === 'workspace') { @@ -680,7 +687,12 @@ export class SettingsEditor2 extends EditorPane { })); this._register(this.settingsTree.onDidFocus(() => { - this.lastFocusContext = SettingsFocusContext.SettingTree; + this._currentFocusContext = SettingsFocusContext.SettingTree; + this.settingRowFocused.set(true); + })); + + this._register(this.settingsTree.onDidBlur(() => { + this.settingRowFocused.set(false); })); // There is no different select state in the settings tree diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index b318d190d27..62f57e04ada 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -73,6 +73,7 @@ export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEdi export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey('inSettingsJSONEditor', false); export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); export const CONTEXT_TOC_ROW_FOCUS = new RawContextKey('settingsTocRowFocus', false); +export const CONTEXT_SETTINGS_ROW_FOCUS = new RawContextKey('settingRowFocus', false); export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKeybindingsSearch', false); export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); From ce74b1a82444d47d6db363e57e95d0bdb6988188 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 27 Sep 2020 23:48:47 +0200 Subject: [PATCH 96/98] Handle delta extensions on the web --- .../sandbox.simpleservices.ts | 1 + .../common/extensionManagement.ts | 2 + .../common/webExtensionsScannerService.ts | 28 +++++ .../extensions/browser/extensionService.ts | 104 ++++++++++++++---- .../common/abstractExtensionService.ts | 17 ++- .../electron-browser/extensionService.ts | 10 -- .../common/abstractRemoteAgentService.ts | 8 ++ .../common/remoteAgentEnvironmentChannel.ts | 15 +++ .../remote/common/remoteAgentService.ts | 5 + 9 files changed, 153 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index ff37f7fb4d6..d57009a5a25 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -443,6 +443,7 @@ export class SimpleRemoteAgentService implements IRemoteAgentService { async flushTelemetry(): Promise { } async getRawEnvironment(): Promise { return null; } async scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise { return []; } + async scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise { return null; } } //#endregion diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 74e63ab3126..865593c3386 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -7,6 +7,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { URI } from 'vs/base/common/uri'; export const IExtensionManagementServerService = createDecorator('extensionManagementServerService'); @@ -90,6 +91,7 @@ export interface IWebExtensionsScannerService { readonly _serviceBrand: undefined; scanExtensions(type?: ExtensionType): Promise; scanAndTranslateExtensions(type?: ExtensionType): Promise; + scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise; canAddExtension(galleryExtension: IGalleryExtension): Promise; addExtension(galleryExtension: IGalleryExtension): Promise; removeExtension(identifier: IExtensionIdentifier, version?: string): Promise; diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts index b8c68ec3fa5..6ce0b19c8fd 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionsScannerService.ts @@ -170,6 +170,34 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten return Promise.all(extensions.map((ext) => this._translateScannedExtension(ext))); } + async scanAndTranslateSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise { + const extension = await this._scanSingleExtension(extensionLocation, extensionType); + if (extension) { + return this._translateScannedExtension(extension); + } + return null; + } + + private async _scanSingleExtension(extensionLocation: URI, extensionType: ExtensionType): Promise { + if (extensionType === ExtensionType.System) { + const systemExtensions = await this.systemExtensionsPromise; + return this._findScannedExtension(systemExtensions, extensionLocation); + } + + const staticExtensions = await this.defaultExtensionsPromise; + const userExtensions = await this.scanUserExtensions(); + return this._findScannedExtension(staticExtensions.concat(userExtensions), extensionLocation); + } + + private _findScannedExtension(candidates: IScannedExtension[], extensionLocation: URI): IScannedExtension | null { + for (const candidate of candidates) { + if (candidate.location.toString() === extensionLocation.toString()) { + return candidate; + } + } + return null; + } + private async _translateScannedExtension(scannedExtension: IScannedExtension): Promise { let manifest = scannedExtension.packageJSON; if (scannedExtension.packageNLS) { diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index bac3d250878..08082e17fd1 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -9,7 +9,7 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IExtensionService, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, IExtensionHost, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -19,7 +19,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -80,11 +80,69 @@ export class ExtensionService extends AbstractExtensionService implements IExten super.dispose(); } + protected _canAddExtension(extension: IExtension): boolean { + const extensionKind = getExtensionKind(extension.manifest, this._productService, this._configService); + const isRemote = extension.location.scheme === Schemas.vscodeRemote; + const runningLocation = pickRunningLocation(extensionKind, !isRemote, isRemote); + if (runningLocation === ExtensionRunningLocation.None) { + return false; + } + return super._canAddExtension(extension); + } + protected async _scanSingleExtension(extension: IExtension): Promise { + if (extension.location.scheme === Schemas.vscodeRemote) { + return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System); + } + + const scannedExtension = await this._webExtensionsScannerService.scanAndTranslateSingleExtension(extension.location, extension.type); + if (scannedExtension) { + return parseScannedExtension(scannedExtension); + } + return null; } protected async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + + let localToAdd: IExtensionDescription[] = []; + let remoteToAdd: IExtensionDescription[] = []; + for (const extension of toAdd) { + const extensionKind = getExtensionKind(extension, this._productService, this._configService); + const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote; + const runningLocation = pickRunningLocation(extensionKind, !isRemote, isRemote); + this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation); + if (runningLocation === ExtensionRunningLocation.LocalWebWorker) { + localToAdd.push(extension); + } else if (runningLocation === ExtensionRunningLocation.Remote) { + remoteToAdd.push(extension); + } + } + + let localToRemove: ExtensionIdentifier[] = []; + let remoteToRemove: ExtensionIdentifier[] = []; + for (const extensionId of toRemove) { + const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extensionId)); + this._runningLocation.delete(ExtensionIdentifier.toKey(extensionId)); + if (runningLocation === ExtensionRunningLocation.LocalWebWorker) { + localToRemove.push(extensionId); + } else if (runningLocation === ExtensionRunningLocation.Remote) { + remoteToRemove.push(extensionId); + } + } + + if (localToAdd.length > 0 || localToRemove.length > 0) { + const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker); + if (localWebWorkerExtensionHost) { + await localWebWorkerExtensionHost.deltaExtensions(localToAdd, localToRemove); + } + } + if (remoteToAdd.length > 0 || remoteToRemove.length > 0) { + const remoteExtensionHost = this._getExtensionHostManager(ExtensionHostKind.Remote); + if (remoteExtensionHost) { + await remoteExtensionHost.deltaExtensions(remoteToAdd, remoteToRemove); + } + } } private _initFetchFileSystem(): void { @@ -183,37 +241,41 @@ const enum ExtensionRunningLocation { Remote } -export function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map, hasRemote: boolean): Map { +function pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean): ExtensionRunningLocation { + for (const extensionKind of extensionKinds) { + if (extensionKind === 'ui' && isInstalledRemotely) { + // ui extensions run remotely if possible + return ExtensionRunningLocation.Remote; + } + if (extensionKind === 'workspace' && isInstalledRemotely) { + // workspace extensions run remotely if possible + return ExtensionRunningLocation.Remote; + } + if (extensionKind === 'web' && isInstalledLocally) { + // web worker extensions run in the local web worker if possible + return ExtensionRunningLocation.LocalWebWorker; + } + } + return ExtensionRunningLocation.None; +} + +function determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], allExtensionKinds: Map, hasRemote: boolean): Map { const localExtensionsSet = new Set(); localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); const remoteExtensionsSet = new Set(); remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); - const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => { + const _pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => { const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier)); const extensionKinds = allExtensionKinds.get(ExtensionIdentifier.toKey(extension.identifier)) || []; - for (const extensionKind of extensionKinds) { - if (extensionKind === 'ui' && isInstalledRemotely) { - // ui extensions run remotely if possible - return ExtensionRunningLocation.Remote; - } - if (extensionKind === 'workspace' && isInstalledRemotely) { - // workspace extensions run remotely if possible - return ExtensionRunningLocation.Remote; - } - if (extensionKind === 'web' && isInstalledLocally) { - // web worker extensions run in the local web worker if possible - return ExtensionRunningLocation.LocalWebWorker; - } - } - return ExtensionRunningLocation.None; + return pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely); }; const runningLocation = new Map(); - localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); - remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), _pickRunningLocation(ext))); + remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), _pickRunningLocation(ext))); return runningLocation; } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index ccd8680d38b..796363c64a9 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -16,7 +16,7 @@ import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionM import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; @@ -154,6 +154,15 @@ export abstract class AbstractExtensionService extends Disposable implements IEx })); } + protected _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null { + for (const extensionHostManager of this._extensionHostManagers) { + if (extensionHostManager.kind === kind) { + return extensionHostManager; + } + } + return null; + } + //#region deltaExtensions private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { @@ -175,10 +184,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { - if (this._environmentService.configuration.remoteAuthority) { - return; - } - let toAdd: IExtensionDescription[] = []; for (let i = 0, len = _toAdd.length; i < len; i++) { const extension = _toAdd[i]; @@ -233,7 +238,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); // Update the extension host - this._updateExtensionsOnExtHosts(toAdd, toRemove.map(e => e.identifier)); + await this._updateExtensionsOnExtHosts(toAdd, toRemove.map(e => e.identifier)); for (let i = 0; i < toAdd.length; i++) { this._activateAddedExtensionIfNeeded(toAdd[i]); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 088e023d5fe..36fd3503827 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -114,15 +114,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten } } - private _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null { - for (const extensionHostManager of this._extensionHostManagers) { - if (extensionHostManager.kind === kind) { - return extensionHostManager; - } - } - return null; - } - protected _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; @@ -147,7 +138,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten return super.canRemoveExtension(extension); } - protected _scanSingleExtension(extension: IExtension): Promise { return this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); } diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 61b727c08fa..12c79e8612d 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -24,6 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; +import { URI } from 'vs/base/common/uri'; export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService { @@ -80,6 +81,13 @@ export abstract class AbstractRemoteAgentService extends Disposable implements I ).then(undefined, () => []); } + scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise { + return this._withChannel( + (channel, connection) => RemoteExtensionEnvironmentChannelClient.scanSingleExtension(channel, connection.remoteAuthority, isBuiltin, extensionLocation), + null + ).then(undefined, () => null); + } + getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise { return this._withChannel( channel => RemoteExtensionEnvironmentChannelClient.getDiagnosticInfo(channel, options), diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index c5f34dd2b75..5ed9d1e29bf 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -81,6 +81,21 @@ export class RemoteExtensionEnvironmentChannelClient { return extensions; } + static async scanSingleExtension(channel: IChannel, remoteAuthority: string, isBuiltin: boolean, extensionLocation: URI): Promise { + const args: IScanSingleExtensionArguments = { + language: platform.language, + remoteAuthority, + isBuiltin, + extensionLocation + }; + + const extension = await channel.call('scanSingleExtension', args); + if (extension) { + (extension).extensionLocation = URI.revive(extension.extensionLocation); + } + return extension; + } + static getDiagnosticInfo(channel: IChannel, options: IDiagnosticInfoOptions): Promise { return channel.call('getDiagnosticInfo', options); } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 2b1a17c2aca..c5a953b0f08 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -11,6 +11,7 @@ import { Event } from 'vs/base/common/event'; import { PersistenConnectionEvent as PersistentConnectionEvent, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; export const RemoteExtensionLogFileName = 'remoteagent'; @@ -34,6 +35,10 @@ export interface IRemoteAgentService { * Scan remote extensions. */ scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise; + /** + * Scan a single remote extension. + */ + scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise; getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise; disableTelemetry(): Promise; logTelemetry(eventName: string, data?: ITelemetryData): Promise; From b03cf79c19de86449580ef03a9ee387a4f993f3e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 27 Sep 2020 23:06:34 +0200 Subject: [PATCH 97/98] :lipstick: --- ...ensionRecommendationNotificationService.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 8aa220ac14f..a3e55134b19 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction } from 'vs/base/common/actions'; +import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -38,7 +39,7 @@ type ExtensionWorkspaceRecommendationsNotificationClassification = { userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; }; -const ignoreImportantExtensionRecommendation = 'extensionsAssistant/importantRecommendationsIgnore'; +const ignoreImportantExtensionRecommendationStorageKey = 'extensionsAssistant/importantRecommendationsIgnore'; const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -50,7 +51,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec // Ignored Important Recommendations get ignoredRecommendations(): string[] { - return [...(JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')))].map(i => i.toLowerCase()); + return distinct([...(JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendationStorageKey, StorageScope.GLOBAL, '[]')))].map(i => i.toLowerCase())); } constructor( @@ -66,7 +67,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { - storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendation, version: 1 }); + storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendationStorageKey, version: 1 }); this.tasExperimentService = tasExperimentService; } @@ -80,7 +81,8 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec return false; } - extensionIds = this.filterIgnoredOrNotAllowed(extensionIds); + const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.ignoredRecommendations]; + extensionIds = extensionIds.filter(id => !ignoredRecommendations.includes(id)); if (!extensionIds.length) { return false; } @@ -221,11 +223,6 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec return extensions; } - private filterIgnoredOrNotAllowed(recommendationsToSuggest: string[]): string[] { - const ignoredRecommendations = [...this.extensionIgnoredRecommendationsService.ignoredRecommendations, ...this.ignoredRecommendations]; - return recommendationsToSuggest.filter(id => !ignoredRecommendations.includes(id)); - } - private async runAction(action: IAction): Promise { try { await action.run(); @@ -235,9 +232,11 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec } private addToImportantRecommendationsIgnore(id: string) { - const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get(ignoreImportantExtensionRecommendation, StorageScope.GLOBAL, '[]')); - importantRecommendationsIgnoreList.push(id.toLowerCase()); - this.storageService.store(ignoreImportantExtensionRecommendation, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); + const importantRecommendationsIgnoreList = [...this.ignoredRecommendations]; + if (!importantRecommendationsIgnoreList.includes(id.toLowerCase())) { + importantRecommendationsIgnoreList.push(id.toLowerCase()); + this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL); + } } private setIgnoreRecommendationsConfig(configVal: boolean) { From 772aa0723f7f6b04b710bc3a92b7a904b60ef439 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Sep 2020 01:00:40 +0200 Subject: [PATCH 98/98] Fix #105947 --- .../electron-sandbox/extensionTipsService.ts | 30 +++++- .../extensionRecommendationsIpc.ts | 2 +- ...ensionRecommendationNotificationService.ts | 102 +++++++++--------- .../browser/fileBasedRecommendations.ts | 27 ++++- 4 files changed, 104 insertions(+), 57 deletions(-) diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index a335ec9397d..df30746fabd 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -12,7 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { isWindows } from 'vs/base/common/platform'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IExecutableBasedExtensionTip, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { forEach } from 'vs/base/common/collections'; +import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { IRequestService } from 'vs/platform/request/common/request'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionTipsService as BaseExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; @@ -20,6 +20,7 @@ import { timeout } from 'vs/base/common/async'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { localize } from 'vs/nls'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; type ExeExtensionRecommendationsClassification = { extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; @@ -32,6 +33,8 @@ type IExeBasedExtensionTips = { readonly recommendations: { extensionId: string, extensionName: string, isExtensionPack: boolean }[]; }; +const promptedExecutableTipsStorageKey = 'extensionTips/promptedExecutableTips'; + export class ExtensionTipsService extends BaseExtensionTipsService { _serviceBrand: any; @@ -43,6 +46,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IStorageService private readonly storageService: IStorageService, @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, @IFileService fileService: IFileService, @IProductService productService: IProductService, @@ -108,13 +112,14 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } const recommendationsByExe = new Map(); + const promptedExecutableTips = this.getPromptedExecutableTips(); for (const extensionId of recommendations) { const tip = importantExeBasedRecommendations.get(extensionId); - if (tip) { - let tips = recommendationsByExe.get(tip.exeFriendlyName); + if (tip && (!promptedExecutableTips[tip.exeName] || !promptedExecutableTips[tip.exeName].includes(tip.extensionId))) { + let tips = recommendationsByExe.get(tip.exeName); if (!tips) { tips = []; - recommendationsByExe.set(tip.exeFriendlyName, tips); + recommendationsByExe.set(tip.exeName, tips); } tips.push(tip); } @@ -123,10 +128,25 @@ export class ExtensionTipsService extends BaseExtensionTipsService { for (const [, tips] of recommendationsByExe) { const extensionIds = tips.map(({ extensionId }) => extensionId.toLowerCase()); const message = localize('exeRecommended', "You have {0} installed on your system. Do you want to install the recommended extensions for it?", tips[0].exeFriendlyName); - this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`); + this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification(extensionIds, message, `@exe:"${tips[0].exeName}"`) + .then(result => { + if (result) { + this.addToRecommendedExecutables(tips[0].exeName, extensionIds); + } + }); } } + private getPromptedExecutableTips(): IStringDictionary { + return JSON.parse(this.storageService.get(promptedExecutableTipsStorageKey, StorageScope.GLOBAL, '{}')); + } + + private addToRecommendedExecutables(exeName: string, extensions: string[]) { + const promptedExecutableTips = this.getPromptedExecutableTips(); + promptedExecutableTips[exeName] = extensions; + this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL); + } + private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { const installed: string[] = [], uninstalled: string[] = []; const installedExtensionsIds = local.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); diff --git a/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts b/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts index c5896d18eda..b70b75dee0d 100644 --- a/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts +++ b/src/vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc.ts @@ -39,7 +39,7 @@ export class ExtensionRecommendationNotificationServiceChannel implements IServe call(_: unknown, command: string, args?: any): Promise { switch (command) { - case 'promptImportantExtensionsInstallNotification': this.service.promptImportantExtensionsInstallNotification(args[0], args[1], args[2]); return Promise.resolve(); + case 'promptImportantExtensionsInstallNotification': return this.service.promptImportantExtensionsInstallNotification(args[0], args[1], args[2]); } throw new Error(`Call not found: ${command}`); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index a3e55134b19..335383064e4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -96,58 +96,64 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec await this.tasExperimentService.getTreatment('wslpopupaa'); } - this.notificationService.prompt(Severity.Info, message, - [{ - label: localize('install', "Install"), - run: async () => { - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - await Promise.all(extensions.map(async extension => { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); - await this.extensionManagementService.installFromGallery(extension.gallery!); - })); - } - }, { - label: localize('show recommendations', "Show Recommendations"), - run: async () => { - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); - this.extensionsWorkbenchService.open(extension, { pinned: true }); + return new Promise((c, e) => { + let cancelled: boolean = false; + const handle = this.notificationService.prompt(Severity.Info, message, + [{ + label: localize('install', "Install"), + run: async () => { + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); + await Promise.all(extensions.map(async extension => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); + await this.extensionManagementService.installFromGallery(extension.gallery!); + })); } - this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - for (const extension of extensions) { - this.addToImportantRecommendationsIgnore(extension.identifier.id); - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); + }, { + label: localize('show recommendations', "Show Recommendations"), + run: async () => { + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId: extension.identifier.id }); + this.extensionsWorkbenchService.open(extension, { pinned: true }); + } + this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); } - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); - } - }], - { - sticky: true, - onCancel: () => { - for (const extension of extensions) { - this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); + }, { + label: choiceNever, + isSecondary: true, + run: () => { + for (const extension of extensions) { + this.addToImportantRecommendationsIgnore(extension.identifier.id); + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: extension.identifier.id }); + } + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); + } + }], + { + sticky: true, + onCancel: () => { + cancelled = true; + for (const extension of extensions) { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: extension.identifier.id }); + } } } - } - ); - - return true; + ); + const disposable = handle.onDidClose(() => { + disposable.dispose(); + c(!cancelled); + }); + }); } async promptWorkspaceRecommendations(recommendations: string[]): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 7b03d99e1e2..8f5cdc37d8c 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -32,6 +32,7 @@ type FileExtensionSuggestionClassification = { fileExtension: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; }; +const promptedRecommendationsStorageKey = 'fileBasedRecommendations/promptedRecommendations'; const recommendationsStorageKey = 'extensionsAssistant/recommendations'; const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); const milliSecondsInADay = 1000 * 60 * 60 * 24; @@ -212,7 +213,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { const installed = await this.extensionsWorkbenchService.queryLocal(); if (importantRecommendations.length && - await this.promptRecommendedExtensionForFileType(languageName || basename(uri), importantRecommendations, installed)) { + await this.promptRecommendedExtensionForFileType(languageName || basename(uri), language, importantRecommendations, installed)) { return; } @@ -229,7 +230,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { this.promptRecommendedExtensionForFileExtension(fileExtension, installed); } - private async promptRecommendedExtensionForFileType(name: string, recommendations: string[], installed: IExtension[]): Promise { + private async promptRecommendedExtensionForFileType(name: string, language: string, recommendations: string[], installed: IExtension[]): Promise { recommendations = this.filterIgnoredOrNotAllowed(recommendations); if (recommendations.length === 0) { @@ -247,10 +248,30 @@ export class FileBasedRecommendations extends ExtensionRecommendations { return false; } - this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", name), `@id:${extensionId}`); + const promptedRecommendations = this.getPromptedRecommendations(); + if (promptedRecommendations[language] && promptedRecommendations[language].includes(extensionId)) { + return false; + } + + this.extensionRecommendationNotificationService.promptImportantExtensionsInstallNotification([extensionId], localize('reallyRecommended', "Do you want to install the recommended extensions for {0}?", name), `@id:${extensionId}`) + .then(result => { + if (result) { + this.addToPromptedRecommendations(language, [extensionId]); + } + }); return true; } + private getPromptedRecommendations(): IStringDictionary { + return JSON.parse(this.storageService.get(promptedRecommendationsStorageKey, StorageScope.GLOBAL, '{}')); + } + + private addToPromptedRecommendations(exeName: string, extensions: string[]) { + const promptedRecommendations = this.getPromptedRecommendations(); + promptedRecommendations[exeName] = extensions; + this.storageService.store(promptedRecommendationsStorageKey, JSON.stringify(promptedRecommendations), StorageScope.GLOBAL); + } + private async promptRecommendedExtensionForFileExtension(fileExtension: string, installed: IExtension[]): Promise { const fileExtensionSuggestionIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/fileExtensionsSuggestionIgnore', StorageScope.GLOBAL, '[]')); if (fileExtensionSuggestionIgnoreList.indexOf(fileExtension) > -1) {