From aabd703135705e5714f79a142c145f04161ebbfe Mon Sep 17 00:00:00 2001 From: Christopher Leidigh Date: Tue, 17 Jul 2018 18:16:09 -0400 Subject: [PATCH 01/89] Settings: Utilize selectBox.setAriaLabel Related: #53821 --- src/vs/workbench/parts/preferences/browser/settingsTree.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 55c7c3a841b7e..fca2a30f700d6 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -908,6 +908,9 @@ export class SettingsRenderer implements IRenderer { const displayOptions = dataElement.setting.enum.map(escapeInvisibleChars); template.selectBox.setOptions(displayOptions); + const label = dataElement.displayCategory + ' ' + dataElement.displayLabel; + template.selectBox.setAriaLabel(label); + const idx = dataElement.setting.enum.indexOf(dataElement.value); template.onChange = null; template.selectBox.select(idx); From 151948783814664e2087f39c817c00c27262227e Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 18 Jul 2018 10:47:50 -0700 Subject: [PATCH 02/89] fix #54156 --- .../browser/parts/menubar/media/menubarpart.css | 4 ++-- src/vs/workbench/browser/parts/menubar/menubarPart.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css index e1ee844a3356d..5c2bd38576fbe 100644 --- a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css +++ b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css @@ -39,9 +39,9 @@ } .menubar-menu-items-holder.monaco-menu-container { - box-shadow: 0 2px 8px #A8A8A8; + box-shadow: 0 2px 4px; } .vs-dark .menubar-menu-items-holder.monaco-menu-container { - box-shadow: 0 2px 8px #000; + box-shadow: 0 2px 4px; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index 8bc34ec91f593..2e5abcdb3f898 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -1037,6 +1037,15 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { `); } + const menuShadow = theme.getColor('widget.shadow'); + if (menuShadow) { + collector.addRule(` + .monaco-shell .monaco-workbench .monaco-menu-container { + box-shadow: 0 2px 4px ${menuShadow}; + } + `); + } + const menuBgColor = theme.getColor(MENU_BACKGROUND); if (menuBgColor) { collector.addRule(` From c48de0a981cfa03d805d88f2075f1e5642e4fcf6 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 18 Jul 2018 10:58:36 -0700 Subject: [PATCH 03/89] Adjust comment glyph height --- .../parts/comments/electron-browser/media/review.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index b39b972ab61eb..760cf0af66f92 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -11,10 +11,7 @@ } .monaco-editor .comment-hint{ - display: flex; - align-items: center; - justify-content: center; - height: 16px; + height: 20px; width: 20px; padding-left: 2px; background: url('comment.svg') center center no-repeat; From 9c2084b9ee8c3a7ac0320772bd201cd341d08e5f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Jul 2018 11:30:51 -0700 Subject: [PATCH 04/89] Fix #54603 --- .../parts/preferences/browser/settingsTree.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 55c7c3a841b7e..3df07a2038f6e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -20,18 +20,19 @@ import * as objects from 'vs/base/common/objects'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IAccessibilityProvider, IDataSource, IFilter, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; +import { IAccessibilityProvider, IDataSource, IFilter, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { inputBackground, inputBorder, inputForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { attachInputBoxStyler, attachSelectBoxStyler, attachButtonStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +import { Color } from 'vs/base/common/color'; const $ = DOM.$; @@ -788,10 +789,17 @@ export class SettingsRenderer implements IRenderer { const common = this.renderCommonTemplate(tree, container, 'complex'); const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: null, buttonHoverBackground: null }); - openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire()); + common.toDispose.push(openSettingsButton); + common.toDispose.push(openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire())); openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json"); openSettingsButton.element.classList.add('edit-in-settings-button'); + common.toDispose.push(attachButtonStyler(openSettingsButton, this.themeService, { + buttonBackground: Color.transparent.toString(), + buttonHoverBackground: Color.transparent.toString(), + buttonForeground: 'foreground' + })); + const template: ISettingComplexItemTemplate = { ...common, button: openSettingsButton From cf54770f2598c65a181be25569dd8b4c7c054187 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 18 Jul 2018 11:42:33 -0700 Subject: [PATCH 05/89] fixes #54479 --- src/vs/workbench/browser/parts/menubar/menubarPart.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index 2e5abcdb3f898..f1c8b0bba5010 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -1150,6 +1150,10 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.lastKeyReleased = undefined; } + if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) { + this._keyStatus.lastKeyPressed = undefined; + } + this._keyStatus.altKey = e.altKey; this._keyStatus.ctrlKey = e.ctrlKey; this._keyStatus.shiftKey = e.shiftKey; From e90b3669a8eb86e13a9257f615a6578d903fdb4b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 18 Jul 2018 13:31:32 -0700 Subject: [PATCH 06/89] Add precondition for terminal menu items --- .../parts/terminal/common/terminal.ts | 6 ++-- .../parts/terminal/common/terminalMenu.ts | 28 +++++++++++++------ .../parts/terminal/common/terminalService.ts | 14 +++++++++- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index ffcd27e8f4672..3e637c51d8fd9 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -14,9 +14,11 @@ export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; export const TERMINAL_SERVICE_ID = 'terminalService'; -/** A context key that is set when the integrated terminal has focus. */ +/** A context key that is set when there is at least one opened integrated terminal. */ +export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); +/** A context key that is set when the integrated terminal has focus. */ export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', undefined); -/** A context key that is set when the integrated terminal does not have focus. */ +/** A context key that is set when the integrated terminal does not have focus. */ export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); /** A keybinding context key that is set when the integrated terminal has text selected. */ diff --git a/src/vs/workbench/parts/terminal/common/terminalMenu.ts b/src/vs/workbench/parts/terminal/common/terminalMenu.ts index df51b81f073cb..6a2513a9ae69d 100644 --- a/src/vs/workbench/parts/terminal/common/terminalMenu.ts +++ b/src/vs/workbench/parts/terminal/common/terminalMenu.ts @@ -6,6 +6,7 @@ import * as nls from 'vs/nls'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export function setupTerminalMenu() { // Manage @@ -22,7 +23,8 @@ export function setupTerminalMenu() { group: manageGroup, command: { id: TERMINAL_COMMAND_ID.SPLIT, - title: nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal") + title: nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 2 }); @@ -31,7 +33,8 @@ export function setupTerminalMenu() { group: manageGroup, command: { id: TERMINAL_COMMAND_ID.KILL, - title: nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal") + title: nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 3 }); @@ -42,7 +45,8 @@ export function setupTerminalMenu() { group: runGroup, command: { id: TERMINAL_COMMAND_ID.CLEAR, - title: nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear") + title: nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 1 }); @@ -50,7 +54,8 @@ export function setupTerminalMenu() { group: runGroup, command: { id: TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, - title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File") + title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 2 }); @@ -58,7 +63,8 @@ export function setupTerminalMenu() { group: runGroup, command: { id: TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, - title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text") + title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 3 }); @@ -69,7 +75,8 @@ export function setupTerminalMenu() { group: navigationGroup, command: { id: TERMINAL_COMMAND_ID.SCROLL_TO_PREVIOUS_COMMAND, - title: nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command") + title: nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 1 }); @@ -77,7 +84,8 @@ export function setupTerminalMenu() { group: navigationGroup, command: { id: TERMINAL_COMMAND_ID.SCROLL_TO_NEXT_COMMAND, - title: nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command") + title: nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 2 }); @@ -85,7 +93,8 @@ export function setupTerminalMenu() { group: navigationGroup, command: { id: TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_COMMAND, - title: nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command") + title: nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 3 }); @@ -93,7 +102,8 @@ export function setupTerminalMenu() { group: navigationGroup, command: { id: TERMINAL_COMMAND_ID.SELECT_TO_NEXT_COMMAND, - title: nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command") + title: nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') }, order: 4 }); diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 20383cf4d66e0..2c28f173a594f 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -9,7 +9,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN } from 'vs/workbench/parts/terminal/common/terminal'; import { TPromise } from 'vs/base/common/winjs.base'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -71,6 +71,18 @@ export abstract class TerminalService implements ITerminalService { this.onTabDisposed(tab => this._removeTab(tab)); lifecycleService.when(LifecyclePhase.Restoring).then(() => this._restoreTabs()); + + this._handleContextKeys(); + } + + private _handleContextKeys(): void { + const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); + + const updateTerminalContextKeys = () => { + terminalIsOpenContext.set(this.terminalInstances.length > 0); + }; + + this.onInstancesChanged(() => updateTerminalContextKeys()); } protected abstract _showTerminalCloseConfirmation(): TPromise; From d3ff1b2ff326b8df86dc497d185b1a66113ee40e Mon Sep 17 00:00:00 2001 From: Ramya Rao Date: Wed, 18 Jul 2018 13:36:49 -0700 Subject: [PATCH 07/89] Open release notes in product from changelog in built in extensions (#54522) * Open release notes in product from changelog in built in extensions * Allow only the release notes command from webview * Localized text may invalidate markdown, skip it --- .../parts/extensions/electron-browser/extensionEditor.ts | 6 +++++- .../parts/extensions/node/extensionsWorkbenchService.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 997b386f29b27..d7f89ad89158b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -49,6 +49,7 @@ import { assign } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionsTree, IExtensionData } from 'vs/workbench/parts/extensions/browser/extensionsViewer'; +import { ShowCurrentReleaseNotesAction } from 'vs/workbench/parts/update/electron-browser/update'; /** A context key that is set when an extension editor webview has focus. */ export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey('extensionEditorWebviewFocus', undefined); @@ -493,8 +494,11 @@ export class ExtensionEditor extends BaseEditor { this.activeWebview.contents = body; this.activeWebview.onDidClickLink(link => { + if (!link) { + return; + } // Whitelist supported schemes for links - if (link && ['http', 'https', 'mailto'].indexOf(link.scheme) >= 0) { + if (['http', 'https', 'mailto'].indexOf(link.scheme) >= 0 || (link.scheme === 'command' && link.path === ShowCurrentReleaseNotesAction.ID)) { this.openerService.open(link); } }, null, this.contentDisposables); diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index e7ccaedb3f64e..a492f570789d3 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -276,7 +276,7 @@ ${this.description} if (!changelogUrl) { if (this.type === LocalExtensionType.System) { - return TPromise.as(nls.localize('checkReleaseNotes', 'Please check the [VS Code Release Notes](https://code.visualstudio.com/updates) for changes to the built-in extensions.')); + return TPromise.as('Please check the [VS Code Release Notes](command:update.showCurrentReleaseNotes) for changes to the built-in extensions.'); } return TPromise.wrapError(new Error('not available')); From 630744a905c3ffec40d702734dcfb35b4353bf88 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Jul 2018 13:58:55 -0700 Subject: [PATCH 08/89] Add unit test helper, testRepeatOnly --- src/vs/base/test/common/utils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index ada86e61b63a8..ececd9b461c14 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -54,3 +54,7 @@ export function testRepeat(n: number, description: string, callback: (this: any, test(`${description} (iteration ${i})`, callback); } } + +export function testRepeatOnly(n: number, description: string, callback: (this: any, done: MochaDone) => any): void { + suite.only('repeat', () => testRepeat(n, description, callback)); +} From 831a5c8630e1ebc0c3771151712674cafe8c6aa3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 18 Jul 2018 13:59:50 -0700 Subject: [PATCH 09/89] Remove precondition from run terminal commands --- src/vs/workbench/parts/terminal/common/terminalMenu.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminalMenu.ts b/src/vs/workbench/parts/terminal/common/terminalMenu.ts index 6a2513a9ae69d..ae73b0d3a7d82 100644 --- a/src/vs/workbench/parts/terminal/common/terminalMenu.ts +++ b/src/vs/workbench/parts/terminal/common/terminalMenu.ts @@ -54,8 +54,7 @@ export function setupTerminalMenu() { group: runGroup, command: { id: TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, - title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File"), - precondition: ContextKeyExpr.has('terminalIsOpen') + title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File") }, order: 2 }); @@ -63,8 +62,7 @@ export function setupTerminalMenu() { group: runGroup, command: { id: TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, - title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text"), - precondition: ContextKeyExpr.has('terminalIsOpen') + title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text") }, order: 3 }); From 290af21d0f34edd58c9f085122e0ab1f2ee87196 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Jul 2018 14:19:23 -0700 Subject: [PATCH 10/89] Fix flaky SearchModel test --- .../search/test/common/searchModel.test.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index e0be7f5547196..589973b27a612 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -6,20 +6,20 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; +import { timeout } from 'vs/base/common/async'; import URI from 'vs/base/common/uri'; -import { IFileMatch, IFolderQuery, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats, ISearchQuery } from 'vs/platform/search/common/search'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { DeferredTPromise } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { timeout } from 'vs/base/common/async'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IFileMatch, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; const nullEvent = new class { @@ -158,7 +158,7 @@ suite('SearchModel', () => { }); }); - test.skip('Search Model: Search reports timed telemetry on search when progress is called', () => { + test('Search Model: Search reports timed telemetry on search when progress is called', () => { let target2 = sinon.spy(); stub(nullEvent, 'stop', target2); let target1 = sinon.stub().returns(nullEvent); @@ -171,8 +171,10 @@ suite('SearchModel', () => { let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - return timeout(1).then(() => { - return result.then(() => { + return result.then(() => { + return timeout(1).then(() => { + // timeout because promise handlers may run in a different order. We only care that these + // are fired at some point. assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); // assert.equal(1, target2.callCount); From 966bec8650854d8c2147c21886a3aa734ec9f0fb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:33:02 -0700 Subject: [PATCH 11/89] Reducing scope of try catch to just exec We want to be alerted if an exception is thrown outside of execute --- .../src/features/completions.ts | 6 +++--- .../src/features/documentHighlight.ts | 15 +++++++++------ .../src/features/refactor.ts | 9 +++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index c113f11bebc47..54e6661cac443 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -364,14 +364,14 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - let response: Proto.CompletionDetailsResponse; + let details: Proto.CompletionEntryDetails[] | undefined; try { - response = await this.client.execute('completionEntryDetails', args, token); + const response = await this.client.execute('completionEntryDetails', args, token); + details = response.body; } catch { return item; } - const details = response.body; if (!details || !details.length || !details[0]) { return item; } diff --git a/extensions/typescript-language-features/src/features/documentHighlight.ts b/extensions/typescript-language-features/src/features/documentHighlight.ts index 0741aa4628b37..dd0fb76f735db 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/features/documentHighlight.ts @@ -26,18 +26,21 @@ class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightPro } const args = typeConverters.Position.toFileLocationRequestArgs(file, position); + let items: Proto.OccurrencesResponseItem[] | undefined; try { const response = await this.client.execute('occurrences', args, token); - if (response && response.body) { - return response.body - .filter(x => !x.isInString) - .map(documentHighlightFromOccurance); - } + items = response.body; } catch { // noop } - return []; + if (!items) { + return []; + } + + return items + .filter(x => !x.isInString) + .map(documentHighlightFromOccurance); } } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index c471b10f8ff9f..fbb3a3b88ef13 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -140,17 +140,18 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { await this.formattingOptionsManager.ensureConfigurationForDocument(document, undefined); const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); - let response: Proto.GetApplicableRefactorsResponse; + let refactorings: Proto.ApplicableRefactorInfo[]; try { - response = await this.client.execute('getApplicableRefactors', args, token); - if (!response || !response.body) { + const response = await this.client.execute('getApplicableRefactors', args, token); + if (!response.body) { return undefined; } + refactorings = response.body; } catch { return undefined; } - return this.convertApplicableRefactors(response.body, document, file, rangeOrSelection); + return this.convertApplicableRefactors(refactorings, document, file, rangeOrSelection); } private convertApplicableRefactors( From 4c003dbbc1f421b6358fe08dd0056a4eaa0d52ef Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:40:53 -0700 Subject: [PATCH 12/89] Remove old navtree call This API has been replaced with navbar. The code related to navbar is not being tested and a very small number of users are using < 2.1 in their workspaces --- .../src/features/documentSymbol.ts | 47 ++++--------------- .../src/typescriptService.ts | 1 - 2 files changed, 9 insertions(+), 39 deletions(-) diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index 84d108a4d23e0..7bc47e4a3a945 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -7,10 +7,8 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; - const getSymbolKind = (kind: string): vscode.SymbolKind => { switch (kind) { case PConst.Kind.module: return vscode.SymbolKind.Module; @@ -33,7 +31,8 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider { public constructor( - private readonly client: ITypeScriptServiceClient) { } + private readonly client: ITypeScriptServiceClient + ) { } public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise { const filepath = this.client.toPath(resource.uri); @@ -45,23 +44,13 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider }; try { - if (this.client.apiVersion.gte(API.v206)) { - const response = await this.client.execute('navtree', args, token); - if (response.body) { - // The root represents the file. Ignore this when showing in the UI - const tree = response.body; - if (tree.childItems) { - const result = new Array(); - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); - return result; - } - } - } else { - const response = await this.client.execute('navbar', args, token); - if (response.body) { - const result = new Array(); - const foldingMap: ObjectMap = Object.create(null); - response.body.forEach(item => TypeScriptDocumentSymbolProvider.convertNavBar(resource.uri, 0, foldingMap, result as vscode.SymbolInformation[], item)); + const response = await this.client.execute('navtree', args, token); + if (response.body) { + // The root represents the file. Ignore this when showing in the UI + const tree = response.body; + if (tree.childItems) { + const result = new Array(); + tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); return result; } } @@ -71,24 +60,6 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } } - private static convertNavBar(resource: vscode.Uri, indent: number, foldingMap: ObjectMap, bucket: vscode.SymbolInformation[], item: Proto.NavigationBarItem, containerLabel?: string): void { - const realIndent = indent + item.indent; - const key = `${realIndent}|${item.text}`; - if (realIndent !== 0 && !foldingMap[key] && TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)) { - const result = new vscode.SymbolInformation(item.text, - getSymbolKind(item.kind), - containerLabel ? containerLabel : '', - typeConverters.Location.fromTextSpan(resource, item.spans[0])); - foldingMap[key] = result; - bucket.push(result); - } - if (item.childItems && item.childItems.length > 0) { - for (const child of item.childItems) { - TypeScriptDocumentSymbolProvider.convertNavBar(resource, realIndent + 1, foldingMap, bucket, child, item.text); - } - } - } - private static convertNavTree(resource: vscode.Uri, bucket: vscode.DocumentSymbol[], item: Proto.NavigationTree): boolean { const symbolInfo = new vscode.DocumentSymbol( item.text, diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 71c6c005d65de..110f41d26589a 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -60,7 +60,6 @@ export interface ITypeScriptServiceClient { execute(command: 'typeDefinition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken): Promise; - execute(command: 'navbar', args: Proto.FileRequestArgs, token?: CancellationToken): Promise; execute(command: 'format', args: Proto.FormatRequestArgs, token?: CancellationToken): Promise; execute(command: 'formatonkey', args: Proto.FormatOnKeyRequestArgs, token?: CancellationToken): Promise; execute(command: 'rename', args: Proto.RenameRequestArgs, token?: CancellationToken): Promise; From 6c2818d42e353cc9d65afc946dc0db065d98a4a1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:45:45 -0700 Subject: [PATCH 13/89] Clean up provideDocumentSymbols - Returned undefined instead of empty array - Only execute server call in try catch --- .../src/features/documentSymbol.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index 7bc47e4a3a945..84fc9117b9ac0 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -34,30 +34,30 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider private readonly client: ITypeScriptServiceClient ) { } - public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise { - const filepath = this.client.toPath(resource.uri); - if (!filepath) { - return []; + public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const file = this.client.toPath(resource.uri); + if (!file) { + return undefined; } - const args: Proto.FileRequestArgs = { - file: filepath - }; + + let tree: Proto.NavigationTree | undefined; try { + const args: Proto.FileRequestArgs = { file }; const response = await this.client.execute('navtree', args, token); - if (response.body) { - // The root represents the file. Ignore this when showing in the UI - const tree = response.body; - if (tree.childItems) { - const result = new Array(); - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); - return result; - } - } - return []; - } catch (e) { - return []; + tree = response.body; + } catch { + return undefined; } + + if (tree && tree.childItems) { + // The root represents the file. Ignore this when showing in the UI + const result: vscode.DocumentSymbol[] = []; + tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); + return result; + } + + return undefined; } private static convertNavTree(resource: vscode.Uri, bucket: vscode.DocumentSymbol[], item: Proto.NavigationTree): boolean { From 708b16a96ce24939791e257b36b0e90488ac786a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:50:00 -0700 Subject: [PATCH 14/89] Remove unused property --- .../src/features/formatting.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index 029db28f87055..cd6113ef1bb71 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -12,21 +12,11 @@ import FileConfigurationManager from './fileConfigurationManager'; class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { - private enabled: boolean = true; - public constructor( private readonly client: ITypeScriptServiceClient, private readonly formattingOptionsManager: FileConfigurationManager ) { } - public updateConfiguration(config: vscode.WorkspaceConfiguration): void { - this.enabled = config.get('format.enable', true); - } - - public isEnabled(): boolean { - return this.enabled; - } - private async doFormat( document: vscode.TextDocument, options: vscode.FormattingOptions, From 3331d725e54863f50760ec0385ce92257161d66f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:52:11 -0700 Subject: [PATCH 15/89] Use toFileLocationRequestArgs --- .../src/features/formatting.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index cd6113ef1bb71..e82f4ef1c5480 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -10,7 +10,6 @@ import { ConfigurationDependentRegistration } from '../utils/dependentRegistrati import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; - class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { public constructor( private readonly client: ITypeScriptServiceClient, @@ -56,17 +55,15 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise { - const filepath = this.client.toPath(document.uri); - if (!filepath) { + const file = this.client.toPath(document.uri); + if (!file) { return []; } await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); const args: Proto.FormatOnKeyRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1, + ...typeConverters.Position.toFileLocationRequestArgs(file, position), key: ch }; try { From b9bc23bb5851476bd19f1b3423cf672600e541b3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Jul 2018 17:55:00 -0700 Subject: [PATCH 16/89] Only exec server call in try catch --- .../src/features/formatting.ts | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index e82f4ef1c5480..89f68500c5b2c 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -16,36 +16,29 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit private readonly formattingOptionsManager: FileConfigurationManager ) { } - private async doFormat( + public async provideDocumentRangeFormattingEdits( document: vscode.TextDocument, + range: vscode.Range, options: vscode.FormattingOptions, - args: Proto.FormatRequestArgs, token: vscode.CancellationToken - ): Promise { + ): Promise { + const file = this.client.toPath(document.uri); + if (!file) { + return undefined; + } + await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + + let edits: Proto.CodeEdit[] | undefined; try { + const args = typeConverters.Range.toFormattingRequestArgs(file, range); const response = await this.client.execute('format', args, token); - if (response.body) { - return response.body.map(typeConverters.TextEdit.fromCodeEdit); - } + edits = response.body; } catch { // noop } - return []; - } - public async provideDocumentRangeFormattingEdits( - document: vscode.TextDocument, - range: vscode.Range, - options: vscode.FormattingOptions, - token: vscode.CancellationToken - ): Promise { - const file = this.client.toPath(document.uri); - if (!file) { - return []; - } - const args = typeConverters.Range.toFormattingRequestArgs(file, range); - return this.doFormat(document, options, args, token); + return (edits || []).map(typeConverters.TextEdit.fromCodeEdit); } public async provideOnTypeFormattingEdits( From 0fbc508f171248ff37bd4c5c9782c2135359f04d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Jul 2018 13:37:53 -0700 Subject: [PATCH 17/89] Prefix unused with _ --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 1d3e5175ff2f6..538702921260c 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -197,7 +197,7 @@ export class QuickFixAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available")); } } @@ -250,7 +250,7 @@ export class CodeActionCommand extends EditorCommand { }); } - public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) { + public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) { const args = CodeActionCommandArgs.fromUser(userArg); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), { kind: args.kind, includeSourceActions: true }, args.apply); } @@ -284,7 +284,7 @@ export class RefactorAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), { kind: CodeActionKind.Refactor }, @@ -313,7 +313,7 @@ export class SourceAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.source.noneMessage', "No source actions available"), { kind: CodeActionKind.Source, includeSourceActions: true }, @@ -340,7 +340,7 @@ export class OrganizeImportsAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), { kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, From a51874590cee301a02cbbf639475dbd5023f10e6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Jul 2018 15:42:51 -0700 Subject: [PATCH 18/89] Extract common type --- src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts b/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts index f8157b3bf0c30..8bc694f358a2d 100644 --- a/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts +++ b/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts @@ -52,19 +52,20 @@ export class ClickLinkKeyboardEvent { this.hasTriggerModifier = hasModifier(source, opts.triggerModifier); } } +export type TriggerModifier = 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; export class ClickLinkOptions { public readonly triggerKey: KeyCode; - public readonly triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + public readonly triggerModifier: TriggerModifier; public readonly triggerSideBySideKey: KeyCode; - public readonly triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + public readonly triggerSideBySideModifier: TriggerModifier; constructor( triggerKey: KeyCode, - triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey', + triggerModifier: TriggerModifier, triggerSideBySideKey: KeyCode, - triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey' + triggerSideBySideModifier: TriggerModifier ) { this.triggerKey = triggerKey; this.triggerModifier = triggerModifier; From 533c6deb7901fba48df4db8c7adfbd42a6d2fe7a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 14:28:42 -0700 Subject: [PATCH 19/89] Move cancellation files to own dir Fixes #53423 --- .../src/typescriptServiceClient.ts | 2 +- .../src/utils/electron.ts | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c5666b895dc7b..c5061316b7ace 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -974,7 +974,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } if (this.apiVersion.gte(API.v222)) { - this.cancellationPipeName = electron.getTempSock('tscancellation'); + this.cancellationPipeName = electron.getTempFile('tscancellation'); args.push('--cancellationPipeName', this.cancellationPipeName + '*'); } diff --git a/extensions/typescript-language-features/src/utils/electron.ts b/extensions/typescript-language-features/src/utils/electron.ts index 34a6534f8ec9d..46129f60f0e53 100644 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ b/extensions/typescript-language-features/src/utils/electron.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import Logger from './logger'; -import { getTempFile, makeRandomHexString } from './temp'; +import * as temp from './temp'; import path = require('path'); -import os = require('os'); +import fs = require('fs'); import net = require('net'); import cp = require('child_process'); @@ -15,13 +15,25 @@ export interface IForkOptions { execArgv?: string[]; } -export function getTempSock(prefix: string): string { - const fullName = `vscode-${prefix}-${makeRandomHexString(20)}`; - return getTempFile(fullName + '.sock'); +const getRootTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + dir = temp.getTempFile(`vscode-typescript`); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + } + return dir; + }; +})(); + +export function getTempFile(prefix: string): string { + return path.join(getRootTempDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); } function generatePipeName(): string { - return getPipeName(makeRandomHexString(40)); + return getPipeName(temp.makeRandomHexString(40)); } function getPipeName(name: string): string { @@ -31,7 +43,7 @@ function getPipeName(name: string): string { } // Mac/Unix: use socket file - return path.join(os.tmpdir(), fullName + '.sock'); + return path.join(getRootTempDir(), fullName + '.sock'); } function generatePatchedEnv( From 346b9770bb04dfaaafa135b7bfc98bb5a935d03c Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 18 Jul 2018 14:09:52 -0700 Subject: [PATCH 20/89] Block quote in comments styling, fixes https://github.com/Microsoft/vscode-pull-request-github/issues/60 --- .../comments/electron-browser/commentThreadWidget.ts | 12 +++++++++++- .../parts/comments/electron-browser/media/review.css | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index f2a7ae587b254..22237cc11b78f 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModelService } from 'vs/editor/common/services/modelService'; import { SimpleCommentEditor } from './simpleCommentEditor'; import URI from 'vs/base/common/uri'; -import { transparent, editorForeground, inputValidationErrorBorder, textLinkActiveForeground, textLinkForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { transparent, editorForeground, inputValidationErrorBorder, textLinkActiveForeground, textLinkForeground, focusBorder, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -581,6 +581,16 @@ export class ReviewZoneWidget extends ZoneWidget { content.push(`.monaco-editor .review-widget .body .review-comment a:focus { outline: 1px solid ${focusColor}; }`); } + const blockQuoteBackground = theme.getColor(textBlockQuoteBackground); + if (blockQuoteBackground) { + content.push(`.monaco-editor .review-widget .body .review-comment blockquote { background: ${blockQuoteBackground}; }`); + } + + const blockQuoteBOrder = theme.getColor(textBlockQuoteBorder); + if (blockQuoteBOrder) { + content.push(`.monaco-editor .review-widget .body .review-comment blockquote { border-color: ${blockQuoteBOrder}; }`); + } + this._styleElement.innerHTML = content.join('\n'); // Editor decorations should also be responsive to theme changes diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index 760cf0af66f92..06ae9872fa426 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -35,6 +35,13 @@ display: flex; } +.monaco-editor .review-widget .body .review-comment blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left-width: 5px; + border-left-style: solid; +} + .monaco-editor .review-widget .body .review-comment .avatar-container { margin-top: 4px !important; } From 43cdb2c69b6ef2142bd995f0cdbcec0279b4d733 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 18 Jul 2018 15:34:51 -0700 Subject: [PATCH 21/89] High contrast border in comments editor widget Fixes https://github.com/Microsoft/vscode-pull-request-github/issues/58 Fixes https://github.com/Microsoft/vscode-pull-request-github/issues/59 --- .../electron-browser/commentThreadWidget.ts | 13 ++++++++++--- .../comments/electron-browser/media/review.css | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 22237cc11b78f..41fd94bf268f8 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModelService } from 'vs/editor/common/services/modelService'; import { SimpleCommentEditor } from './simpleCommentEditor'; import URI from 'vs/base/common/uri'; -import { transparent, editorForeground, inputValidationErrorBorder, textLinkActiveForeground, textLinkForeground, focusBorder, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry'; +import { transparent, editorForeground, inputValidationErrorBorder, textLinkActiveForeground, textLinkForeground, focusBorder, textBlockQuoteBackground, textBlockQuoteBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -477,8 +477,8 @@ export class ReviewZoneWidget extends ZoneWidget { } private setCommentEditorDecorations() { - if (this._commentEditor) { - let model = this._commentEditor.getModel(); + const model = this._commentEditor && this._commentEditor.getModel(); + if (model) { let valueLength = model.getValueLength(); const hasExistingComments = this._commentThread.comments.length > 0; let placeholder = valueLength > 0 ? '' : (hasExistingComments ? 'Reply...' : 'Type a new comment'); @@ -579,6 +579,7 @@ export class ReviewZoneWidget extends ZoneWidget { const focusColor = theme.getColor(focusBorder); if (focusColor) { content.push(`.monaco-editor .review-widget .body .review-comment a:focus { outline: 1px solid ${focusColor}; }`); + content.push(`.monaco-editor .review-widget .body .comment-form .monaco-editor.focused { outline: 1px solid ${focusColor}; }`); } const blockQuoteBackground = theme.getColor(textBlockQuoteBackground); @@ -591,6 +592,12 @@ export class ReviewZoneWidget extends ZoneWidget { content.push(`.monaco-editor .review-widget .body .review-comment blockquote { border-color: ${blockQuoteBOrder}; }`); } + const hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + content.push(`.monaco-editor .review-widget .body .comment-form .review-thread-reply-button { outline-color: ${hcBorder}; }`); + content.push(`.monaco-editor .review-widget .body .comment-form .monaco-editor { outline: 1px solid ${hcBorder}; }`); + } + this._styleElement.innerHTML = content.join('\n'); // Editor decorations should also be responsive to theme changes diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index 06ae9872fa426..dd28fb338ef09 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -149,6 +149,7 @@ white-space: nowrap; border: 0px; cursor: text; + outline: 1px solid transparent; } .monaco-editor .review-widget .body .comment-form .review-thread-reply-button:focus { From 9038bc7b45faebcc63e0b4f581cb1d6b041250e5 Mon Sep 17 00:00:00 2001 From: HUA Yang Date: Thu, 19 Jul 2018 07:22:29 +0800 Subject: [PATCH 22/89] fix #53590 (#54257) --- extensions/markdown-language-features/src/slugify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index c3e167e89e07d..4bc3cee42ceff 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -23,7 +23,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { heading.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - - .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '') // Remove known puctuators + .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known puctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); From 80a472482cfb26ace52c5923a74fa4164163266f Mon Sep 17 00:00:00 2001 From: Sandy Armstrong Date: Wed, 18 Jul 2018 16:27:03 -0700 Subject: [PATCH 23/89] Treat Xamarin .workbook files as markdown (#51167) Xamarin Workbooks are interactive coding documents that are saved as straight-forward markdown files with a YAML front matter header block. Here is a sample: https://github.com/xamarin/Workbooks/blob/master/csharp/csharp6/csharp6.workbook Github has been treating them as markdown files for over a year now (https://github.com/github/linguist/pull/3500). --- extensions/markdown-basics/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index a091ad8ea291f..dd0b3f5a87d38 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -19,7 +19,8 @@ ".md", ".mdown", ".markdown", - ".markdn" + ".markdn", + ".workbook" ], "configuration": "./language-configuration.json" } From 44cd521ced3927ceef696028bd5d63ca977d9b8b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 16:32:27 -0700 Subject: [PATCH 24/89] Finalize definition link (#54424) Finalize the definition link api - Gives fields more explicit names (target and origin) - Moves api to vscode.d.ts - Makes other definition providers (such as type definition provider and implementation provider) also return definition links Fixes #54101 --- .../src/features/definitions.ts | 16 ++--- src/vs/editor/common/modes.ts | 4 +- src/vs/monaco.d.ts | 4 +- src/vs/vscode.d.ts | 41 ++++++++++++- src/vs/vscode.proposed.d.ts | 43 -------------- .../mainThreadLanguageFeatures.ts | 4 +- src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../api/node/extHostLanguageFeatures.ts | 59 +++++-------------- .../api/node/extHostTypeConverters.ts | 17 ++++++ 9 files changed, 85 insertions(+), 107 deletions(-) diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/features/definitions.ts index 9ff840cdab482..99b9c36624768 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -17,12 +17,7 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase super(client); } - public async provideDefinition() { - // Implemented by provideDefinition2 - return undefined; - } - - public async provideDefinition2( + public async provideDefinition( document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken | boolean @@ -44,10 +39,11 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined; return locations .map(location => { - const loc = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); - return { - origin: span, - ...loc, + const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); + return { + originSelectionRange: span, + targetRange: target.range, + targetUri: target.uri, }; }); } catch { diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6b8d3fb2aff1a..055f17e711450 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -565,7 +565,7 @@ export interface ImplementationProvider { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** @@ -576,7 +576,7 @@ export interface TypeDefinitionProvider { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0a9e5161b0e88..2587e66c61d9d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4933,7 +4933,7 @@ declare namespace monaco.languages { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** @@ -4944,7 +4944,7 @@ declare namespace monaco.languages { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 27a09386ff78a..943698977fbe8 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2156,6 +2156,41 @@ declare module 'vscode' { resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult; } + /** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ + export interface DefinitionLink { + /** + * Span of the symbol being defined in the source file. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + originSelectionRange?: Range; + + /** + * The resource identifier of the definition. + */ + targetUri: Uri; + + /** + * The full range of the definition. + * + * For a class definition for example, this would be the entire body of the class definition. + */ + targetRange: Range; + + /** + * The span of the symbol definition. + * + * For a class definition, this would be the class name itself in the class definition. + */ + targetSelectionRange?: Range; + } + /** * The definition of a symbol represented as one or many [locations](#Location). * For most programming languages there is only one location at which a symbol is @@ -2179,7 +2214,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -2197,7 +2232,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -2215,7 +2250,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f899c196ba947..c82b7a08c519a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1054,47 +1054,4 @@ declare module 'vscode' { export const onDidRenameFile: Event; } //#endregion - - //#region Matt: Deinition range - - /** - * Information about where a symbol is defined. - * - * Provides additional metadata over normal [location](#Location) definitions, including the range of - * the defining symbol - */ - export interface DefinitionLink { - /** - * Span of the symbol being defined in the source file. - * - * Used as the underlined span for mouse definition hover. Defaults to the word range at - * the definition position. - */ - origin?: Range; - - /** - * The resource identifier of the definition. - */ - uri: Uri; - - /** - * The full range of the definition. - * - * For a class definition for example, this would be the entire body of the class definition. - */ - range: Range; - - /** - * The span of the symbol definition. - * - * For a class definition, this would be the class name itself in the class definition. - */ - selectionRange?: Range; - } - - export interface DefinitionProvider { - provideDefinition2?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - //#endregion } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 7ecf37c7311c6..baa55c39fa667 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -162,7 +162,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.ImplementationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideImplementation: (model, position, token): Thenable => { - return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto); + return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); } }); } @@ -170,7 +170,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideTypeDefinition: (model, position, token): Thenable => { - return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto); + return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); } }); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index c65fd9cf161d1..122e6c14fb8ea 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -812,8 +812,8 @@ export interface ExtHostLanguageFeaturesShape { $provideCodeLenses(handle: number, resource: UriComponents): TPromise; $resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise; $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; - $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise; - $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; + $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise; + $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index f562cb6d6feea..8dc9a268ce5c5 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -143,6 +143,15 @@ class CodeLensAdapter { } } +function convertToDefinitionLinks(value: vscode.Definition): modes.DefinitionLink[] { + if (Array.isArray(value)) { + return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from); + } else if (value) { + return [typeConvert.DefinitionLink.from(value)]; + } + return undefined; +} + class DefinitionAdapter { constructor( @@ -153,29 +162,7 @@ class DefinitionAdapter { provideDefinition(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - - return asWinJsPromise(token => this._provider.provideDefinition2 ? this._provider.provideDefinition2(doc, pos, token) : this._provider.provideDefinition(doc, pos, token)).then((value): modes.DefinitionLink[] => { - if (Array.isArray(value)) { - return (value as (vscode.DefinitionLink | vscode.Location)[]).map(x => DefinitionAdapter.convertDefinitionLink(x)); - } else if (value) { - return [DefinitionAdapter.convertDefinitionLink(value)]; - } - return undefined; - }); - } - - private static convertDefinitionLink(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { - const definitionLink = value; - return { - origin: definitionLink.origin - ? typeConvert.Range.from(definitionLink.origin) - : undefined, - uri: value.uri, - range: typeConvert.Range.from(value.range), - selectionRange: definitionLink.selectionRange - ? typeConvert.Range.from(definitionLink.selectionRange) - : undefined, - }; + return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -186,17 +173,10 @@ class ImplementationAdapter { private readonly _provider: vscode.ImplementationProvider ) { } - provideImplementation(resource: URI, position: IPosition): TPromise { + provideImplementation(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(value => { - if (Array.isArray(value)) { - return value.map(typeConvert.location.from); - } else if (value) { - return typeConvert.location.from(value); - } - return undefined; - }); + return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -207,17 +187,10 @@ class TypeDefinitionAdapter { private readonly _provider: vscode.TypeDefinitionProvider ) { } - provideTypeDefinition(resource: URI, position: IPosition): TPromise { + provideTypeDefinition(resource: URI, position: IPosition): TPromise { const doc = this._documents.getDocumentData(resource).document; const pos = typeConvert.Position.to(position); - return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(value => { - if (Array.isArray(value)) { - return value.map(typeConvert.location.from); - } else if (value) { - return typeConvert.location.from(value); - } - return undefined; - }); + return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -999,7 +972,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise { + $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise { return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position)); } @@ -1009,7 +982,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { + $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position)); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index bf9426044115e..ae449946df0a1 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -413,6 +413,23 @@ export const location = { } }; +export namespace DefinitionLink { + export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { + const definitionLink = value; + const location = value; + return { + origin: definitionLink.originSelectionRange + ? Range.from(definitionLink.originSelectionRange) + : undefined, + uri: definitionLink.targetUri ? definitionLink.targetUri : location.uri, + range: Range.from(definitionLink.targetRange ? definitionLink.targetRange : location.range), + selectionRange: definitionLink.targetSelectionRange + ? Range.from(definitionLink.targetSelectionRange) + : undefined, + }; + } +} + export namespace Hover { export function from(hover: vscode.Hover): modes.Hover { return { From 4610214559314c5504d9942e8462f867a5552600 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 16:44:48 -0700 Subject: [PATCH 25/89] Make sure our internal DefinitionProvider has backwards compatible api --- src/vs/editor/common/modes.ts | 6 +++--- .../contrib/goToDefinition/goToDefinition.ts | 20 +++++++++---------- src/vs/monaco.d.ts | 6 +++--- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 055f17e711450..5acba54edf8ff 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -554,7 +554,7 @@ export interface DefinitionProvider { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -565,7 +565,7 @@ export interface ImplementationProvider { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -576,7 +576,7 @@ export interface TypeDefinitionProvider { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index eee248b2cb031..fcbe7ef878845 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -3,24 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - +import { flatten } from 'vs/base/common/arrays'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ITextModel } from 'vs/editor/common/model'; import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; -import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location, DefinitionLink } from 'vs/editor/common/modes'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { asWinJsPromise } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; -import { flatten } from 'vs/base/common/arrays'; +import { ITextModel } from 'vs/editor/common/model'; +import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes'; +import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; function getDefinitions( model: ITextModel, position: Position, registry: LanguageFeatureRegistry, - provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => Location | Location[] | Thenable + provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => DefinitionLink | DefinitionLink[] | Thenable ): TPromise { const provider = registry.ordered(model); @@ -45,13 +43,13 @@ export function getDefinitionsAtPosition(model: ITextModel, position: Position): }); } -export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise { +export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise { return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position, token) => { return provider.provideImplementation(model, position, token); }); } -export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { +export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position, token) => { return provider.provideTypeDefinition(model, position, token); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 2587e66c61d9d..438105b4d899d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4922,7 +4922,7 @@ declare namespace monaco.languages { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -4933,7 +4933,7 @@ declare namespace monaco.languages { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -4944,7 +4944,7 @@ declare namespace monaco.languages { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** From 0a1b3a5debe3c600110db0baac9e5b8046a66a0b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 16:53:40 -0700 Subject: [PATCH 26/89] Use DefinitionLink internally instead of location --- .../goToDefinition/goToDefinitionCommands.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 8c15d98772cdd..981b9fc99dc9c 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as nls from 'vs/nls'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; @@ -13,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { registerEditorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { Location } from 'vs/editor/common/modes'; +import { DefinitionLink } from 'vs/editor/common/modes'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDefinition'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/referencesModel'; @@ -67,7 +65,7 @@ export class DefinitionAction extends EditorAction { // * remove falsy references // * find reference at the current pos let idxOfCurrent = -1; - let result: Location[] = []; + const result: DefinitionLink[] = []; for (let i = 0; i < references.length; i++) { let reference = references[i]; if (!reference || !reference.range) { @@ -112,7 +110,7 @@ export class DefinitionAction extends EditorAction { return definitionPromise; } - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getDefinitionsAtPosition(model, position); } @@ -145,8 +143,8 @@ export class DefinitionAction extends EditorAction { } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location, sideBySide: boolean): TPromise { - let { uri, range } = reference; + private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: DefinitionLink, sideBySide: boolean): TPromise { + const { uri, range } = reference; return editorService.openCodeEditor({ resource: uri, options: { @@ -247,7 +245,7 @@ export class PeekDefinitionAction extends DefinitionAction { } export class ImplementationAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getImplementationsAtPosition(model, position); } @@ -303,7 +301,7 @@ export class PeekImplementationAction extends ImplementationAction { } export class TypeDefinitionAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getTypeDefinitionsAtPosition(model, position); } From f30234030a7a61746af08eea06bdbedba1a131c4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 17:14:06 -0700 Subject: [PATCH 27/89] Use coalesce --- src/vs/editor/contrib/goToDefinition/goToDefinition.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index fcbe7ef878845..b0474016f0aa7 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten } from 'vs/base/common/arrays'; +import { flatten, coalesce } from 'vs/base/common/arrays'; import { asWinJsPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; @@ -33,7 +33,7 @@ function getDefinitions( }); return TPromise.join(promises) .then(flatten) - .then(references => references.filter(x => !!x)); + .then(references => coalesce(references)); } From c530bbb6c77a28862d110d1fea9501eb34d148e6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 17:16:19 -0700 Subject: [PATCH 28/89] Refactoring - Use const - Remove use strict - Return early instead of nesting --- src/vs/editor/contrib/hover/getHover.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index 1c4fabf950511..46f40f0c7619a 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { coalesce } from 'vs/base/common/arrays'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; @@ -20,12 +18,14 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat const promises = supports.map((support, idx) => { return Promise.resolve(support.provideHover(model, position, token)).then((result) => { - if (result) { - let hasRange = (typeof result.range !== 'undefined'); - let hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; - if (hasRange && hasHtmlContent) { - values[idx] = result; - } + if (!result) { + return; + } + + const hasRange = (typeof result.range !== 'undefined'); + const hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; + if (hasRange && hasHtmlContent) { + values[idx] = result; } }, err => { onUnexpectedExternalError(err); From 6a9f2159e1cd1104f22e8e1ce8b5e9621e9a6e06 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 17:19:11 -0700 Subject: [PATCH 29/89] Return results directly instead of using temp array --- src/vs/editor/contrib/hover/getHover.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index 46f40f0c7619a..90e34492987a9 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -14,25 +14,23 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export function getHover(model: ITextModel, position: Position, token: CancellationToken): Promise { const supports = HoverProviderRegistry.ordered(model); - const values: Hover[] = []; - const promises = supports.map((support, idx) => { - return Promise.resolve(support.provideHover(model, position, token)).then((result) => { + const promises = supports.map(support => { + return Promise.resolve(support.provideHover(model, position, token)).then(result => { if (!result) { - return; + return undefined; } const hasRange = (typeof result.range !== 'undefined'); const hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; - if (hasRange && hasHtmlContent) { - values[idx] = result; - } + return hasRange && hasHtmlContent ? result : undefined; }, err => { onUnexpectedExternalError(err); + return undefined; }); }); - return Promise.all(promises).then(() => coalesce(values)); + return Promise.all(promises).then(values => coalesce(values)); } registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); From cec4ca0152a996a04845b8bffbce64afd2b21059 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 17:21:04 -0700 Subject: [PATCH 30/89] Extract isValid hover check --- src/vs/editor/contrib/hover/getHover.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index 90e34492987a9..577c87d814ade 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -16,14 +16,8 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat const supports = HoverProviderRegistry.ordered(model); const promises = supports.map(support => { - return Promise.resolve(support.provideHover(model, position, token)).then(result => { - if (!result) { - return undefined; - } - - const hasRange = (typeof result.range !== 'undefined'); - const hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; - return hasRange && hasHtmlContent ? result : undefined; + return Promise.resolve(support.provideHover(model, position, token)).then(hover => { + return hover && isValid(hover) ? hover : undefined; }, err => { onUnexpectedExternalError(err); return undefined; @@ -34,3 +28,9 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat } registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); + +function isValid(result: Hover) { + const hasRange = (typeof result.range !== 'undefined'); + const hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; + return hasRange && hasHtmlContent; +} From a04a714fa3a72c13c56885a0f32e12a799463963 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 19:08:07 -0700 Subject: [PATCH 31/89] Format --- extensions/make/package.json | 47 ++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/extensions/make/package.json b/extensions/make/package.json index d57f0077caada..675a93a647cf8 100644 --- a/extensions/make/package.json +++ b/extensions/make/package.json @@ -4,29 +4,44 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js fadeevab/make.tmbundle Syntaxes/Makefile.plist ./syntaxes/make.tmLanguage.json" }, "contributes": { - - "languages": [{ - "id": "makefile", - "aliases": ["Makefile", "makefile"], - "extensions": [ ".mk" ], - "filenames": [ "Makefile", "makefile", "GNUmakefile", "OCamlMakefile" ], - "firstLine": "^#!\\s*/usr/bin/make", - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "makefile", - "scopeName": "source.makefile", - "path": "./syntaxes/make.tmLanguage.json" - }], + "languages": [ + { + "id": "makefile", + "aliases": [ + "Makefile", + "makefile" + ], + "extensions": [ + ".mk" + ], + "filenames": [ + "Makefile", + "makefile", + "GNUmakefile", + "OCamlMakefile" + ], + "firstLine": "^#!\\s*/usr/bin/make", + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "makefile", + "scopeName": "source.makefile", + "path": "./syntaxes/make.tmLanguage.json" + } + ], "configurationDefaults": { "[makefile]": { "editor.insertSpaces": false } } } -} +} \ No newline at end of file From afc3d77e7cfc6534ba05c52f5e1289cbecd58b3d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 Jul 2018 19:09:46 -0700 Subject: [PATCH 32/89] Don't treat interpolated strings in make files as content strings These are more like expression instead of strings Fixes #38078 --- extensions/make/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/make/package.json b/extensions/make/package.json index 675a93a647cf8..f66762ba8001b 100644 --- a/extensions/make/package.json +++ b/extensions/make/package.json @@ -35,7 +35,10 @@ { "language": "makefile", "scopeName": "source.makefile", - "path": "./syntaxes/make.tmLanguage.json" + "path": "./syntaxes/make.tmLanguage.json", + "tokenTypes": { + "string.interpolated": "other" + } } ], "configurationDefaults": { From 0ed4d0ec02bdefb857597ad0842d54abba42a750 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 18 Jul 2018 19:42:11 -0700 Subject: [PATCH 33/89] Trimmed file search strings in the search menu As suggested by roblourens, goes through `queryBuilder`'s `query` to trim the `filePattern`. Fixes #54529. --- .../workbench/parts/search/common/queryBuilder.ts | 4 ++-- .../parts/search/test/common/queryBuilder.test.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index d0a998f91172d..af6e5cac83395 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -75,12 +75,12 @@ export class QueryBuilder { this.resolveSmartCaseToCaseSensitive(contentPattern); } - const query = { + const query: ISearchQuery = { type, folderQueries, usingSearchPaths: !!(searchPaths && searchPaths.length), extraFileResources: options.extraFileResources, - filePattern: options.filePattern, + filePattern: options.filePattern.trim(), excludePattern, includePattern, maxResults: options.maxResults, diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 4ae1b1a4fafcc..6be960c680c14 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -236,6 +236,21 @@ suite('QueryBuilder', () => { }); }); + test('file pattern trimming', () => { + const content = 'content'; + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + undefined, + { filePattern: ` ${content} ` } + ), + { + contentPattern: PATTERN_INFO, + filePattern: content, + type: QueryType.Text + }); + }); + test('exclude ./ syntax', () => { assertEqualQueries( queryBuilder.text( From d7ea94d1f6de8096e2f73592e48ec976957fe62b Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 18 Jul 2018 22:45:08 -0700 Subject: [PATCH 34/89] Allowed undefined options.filePattern Forgot you folks aren't on strict types! --- src/vs/workbench/parts/search/common/queryBuilder.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index af6e5cac83395..3ec5aa3f04ef7 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -80,7 +80,9 @@ export class QueryBuilder { folderQueries, usingSearchPaths: !!(searchPaths && searchPaths.length), extraFileResources: options.extraFileResources, - filePattern: options.filePattern.trim(), + filePattern: options.filePattern + ? options.filePattern.trim() + : options.filePattern, excludePattern, includePattern, maxResults: options.maxResults, From f9f6cece37f788c45c03b23b496819f50433d519 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 19 Jul 2018 15:26:44 +0800 Subject: [PATCH 35/89] fix microsoft/vscode-pull-request-github#43. Show comment indicator when editor has focus. --- .../electron-browser/commentsEditorContribution.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index c9613b32bad6a..1c35b032439f2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -243,6 +243,7 @@ export class ReviewController implements IEditorContribution { this._commentWidgets = []; this.localToDispose.push(this.editor.onMouseMove(e => this.onEditorMouseMove(e))); + this.localToDispose.push(this.editor.onDidBlurEditorText(() => this.onDidBlurEditorText())); this.localToDispose.push(this.editor.onDidChangeModelContent(() => { if (this._newCommentGlyph) { this.editor.removeContentWidget(this._newCommentGlyph); @@ -317,6 +318,10 @@ export class ReviewController implements IEditorContribution { return; } + if (!this.editor.hasTextFocus()) { + return; + } + const hasCommentingRanges = this._commentInfos.length && this._commentInfos.some(info => !!info.commentingRanges.length); if (hasCommentingRanges && e.target.position && e.target.position.lineNumber !== undefined) { if (this._newCommentGlyph && e.target.element.className !== 'comment-hint') { @@ -338,6 +343,12 @@ export class ReviewController implements IEditorContribution { } } + private onDidBlurEditorText(): void { + if (this._newCommentGlyph) { + this.editor.removeContentWidget(this._newCommentGlyph); + } + } + private getNewCommentAction(line: number): { replyCommand: modes.Command, ownerId: number } { for (let i = 0; i < this._commentInfos.length; i++) { const commentInfo = this._commentInfos[i]; From bcb31657f22e5ffeeea4e44c5d8813e313d4fb66 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 09:40:54 +0200 Subject: [PATCH 36/89] breadcrumbs - add 'Focus Breadcrumbs' command --- .../workbench/browser/parts/editor/breadcrumbsControl.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index f038e62afe362..05d95f480429d 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -35,6 +35,8 @@ import { BreadcrumbsFilePicker, BreadcrumbsOutlinePicker, BreadcrumbsPicker } fr import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; class Item extends BreadcrumbsItem { @@ -329,6 +331,13 @@ export class BreadcrumbsControl { //#region commands +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'breadcrumbs.focus', + title: localize('cmd.focus', "Focus Breadcrumbs") + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focus', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), From 96d06bd9e4dcc618b7d224646f0dadeebb5aecdd Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 19 Jul 2018 10:10:09 +0200 Subject: [PATCH 37/89] fixes #52601 --- src/vs/base/parts/tree/browser/treeView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index cf6dc5ab8ff3f..5d56011feacac 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -220,7 +220,7 @@ export class ViewItem implements IViewItem { this.element.removeAttribute('id'); } if (this.model.hasChildren()) { - this.element.setAttribute('aria-expanded', String(!!this.model.isExpanded())); + this.element.setAttribute('aria-expanded', String(!!this._styles['expanded'])); } else { this.element.removeAttribute('aria-expanded'); } From f8ad345f20a621ff7145d395b1b8b10e7d08af90 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 10:10:47 +0200 Subject: [PATCH 38/89] breadcrumbs - make tab select an item from the picker, also tweak arrow key behaviour --- src/vs/platform/list/browser/listService.ts | 26 ++++++++++--------- .../parts/editor/breadcrumbsControl.ts | 15 ++++++++++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index f4101fac8a2d4..9bd6cae45877d 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -654,19 +654,21 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { this.input.onDidChange(this.updateHighlights, this, this.disposables); this.disposables.push(attachInputBoxStyler(this.input, themeService)); this.disposables.push(this.input); - this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keyup', event => { + this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keydown', event => { //todo@joh make this command/context-key based - if (event.keyCode === KeyCode.DownArrow) { - this.focusNext(); - this.domFocus(); - } else if (event.keyCode === KeyCode.UpArrow) { - this.focusPrevious(); - this.domFocus(); - } else if (event.keyCode === KeyCode.Enter) { - this.setSelection(this.getSelection()); - } else if (event.keyCode === KeyCode.Escape) { - this.input.value = ''; - this.domFocus(); + switch (event.keyCode) { + case KeyCode.DownArrow: + case KeyCode.UpArrow: + this.domFocus(); + break; + case KeyCode.Enter: + case KeyCode.Tab: + this.setSelection(this.getSelection()); + break; + case KeyCode.Escape: + this.input.value = ''; + this.domFocus(); + break; } })); } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 05d95f480429d..4cb6d9a0dc075 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -37,6 +37,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; +import { WorkbenchListFocusContextKey, IListService } from 'vs/platform/list/browser/listService'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; class Item extends BreadcrumbsItem { @@ -414,5 +416,16 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ groups.activeGroup.activeControl.focus(); } }); - +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.pickFromTree', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + primary: KeyCode.Tab, + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive, WorkbenchListFocusContextKey), + handler(accessor) { + const list = accessor.get(IListService).lastFocusedList; + if (list instanceof Tree) { + list.setSelection([list.getFocus()]); + } + } +}); //#endregion From 8fce8cce268593361d4998de6272ae16e523e72e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 19 Jul 2018 10:14:32 +0200 Subject: [PATCH 39/89] fixes #52658 --- .../parts/extensions/electron-browser/extensionsViews.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 6e18d8391ed41..78b0baef7ba65 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -85,7 +85,7 @@ export class ExtensionsListView extends ViewletPanel { const delegate = new Delegate(); const renderer = this.instantiationService.createInstance(Renderer); this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { - ariaLabel: localize('extensions', "Extensions"), + ariaLabel: localize('extensions', "Extensions. Use the navigation keys to navigate extensions."), multipleSelectionSupport: false }) as WorkbenchPagedList; this.disposables.push(this.list); From 290570aaa76a4e66885a524e72074060a5d416dc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 19 Jul 2018 11:10:45 +0200 Subject: [PATCH 40/89] fixes #52658 --- src/vs/base/browser/ui/list/listWidget.ts | 3 ++- .../parts/extensions/electron-browser/extensionsViews.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index f8ffd1a0feb93..14c6ca52c3cd2 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./list'; +import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; import { range, firstIndex } from 'vs/base/common/arrays'; @@ -934,7 +935,7 @@ export class List implements ISpliceable, IDisposable { this.onSelectionChange(this._onSelectionChange, this, this.disposables); if (options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', options.ariaLabel); + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel)); } this.style(options); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 78b0baef7ba65..6e18d8391ed41 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -85,7 +85,7 @@ export class ExtensionsListView extends ViewletPanel { const delegate = new Delegate(); const renderer = this.instantiationService.createInstance(Renderer); this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { - ariaLabel: localize('extensions', "Extensions. Use the navigation keys to navigate extensions."), + ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false }) as WorkbenchPagedList; this.disposables.push(this.list); From 742ea0dfdfa02c92f7f9efa157cf860b91740c2d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 10:54:14 +0200 Subject: [PATCH 41/89] breadcrumbs - don't show folder icons, shrink items for less scrolling --- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 1 + src/vs/workbench/browser/labels.ts | 9 +++--- .../parts/editor/breadcrumbsControl.ts | 30 ++++++++----------- .../parts/editor/media/tabstitlecontrol.css | 10 ++++++- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 8d62877e57d6e..cb972251f771a 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -22,6 +22,7 @@ export interface IIconLabelCreationOptions { export interface IIconLabelValueOptions { title?: string; descriptionTitle?: string; + hideIcon?: boolean; extraClasses?: string[]; italic?: boolean; matches?: IMatch[]; diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 3f6da3e94dcd4..35768b3f5fafa 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -199,11 +199,12 @@ export class ResourceLabel extends IconLabel { iconLabelOptions.title = this.computedPathLabel; } - if (!this.computedIconClasses) { - this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); + if (this.options && !this.options.hideIcon) { + if (!this.computedIconClasses) { + this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); + } + iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); } - - iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); if (this.options && this.options.extraClasses) { iconLabelOptions.extraClasses.push(...this.options.extraClasses); } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 4cb6d9a0dc075..698b61319abbb 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -12,7 +12,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { basenameOrAuthority, isEqual } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/resources'; import 'vs/css!./media/breadcrumbscontrol'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; @@ -72,20 +72,15 @@ class Item extends BreadcrumbsItem { render(container: HTMLElement): void { if (this.element instanceof FileElement) { // file/folder - if (this.options.showFileIcons) { - let label = this._instantiationService.createInstance(FileLabel, container, {}); - label.setFile(this.element.uri, { - hidePath: true, - fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER, - fileDecorations: { colors: this.options.showDecorationColors, badges: false } - }); - this._disposables.push(label); - - } else { - let label = new IconLabel(container); - label.setValue(basenameOrAuthority(this.element.uri)); - this._disposables.push(label); - } + let label = this._instantiationService.createInstance(FileLabel, container, {}); + label.setFile(this.element.uri, { + hidePath: true, + fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER, + hideIcon: !this.element.isFile || !this.options.showFileIcons, + fileDecorations: { colors: this.options.showDecorationColors, badges: false } + }); + this._disposables.push(label); + dom.toggleClass(container, 'file', this.element.isFile); } else if (this.element instanceof OutlineGroup) { // provider @@ -100,11 +95,12 @@ class Item extends BreadcrumbsItem { let icon = document.createElement('div'); icon.className = `symbol-icon ${symbolKindToCssClass(this.element.symbol.kind)}`; container.appendChild(icon); - container.classList.add('shows-symbol-icon'); + dom.addClass(container, 'shows-symbol-icon'); } let label = new IconLabel(container); - label.setValue(this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE')); + let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'); + label.setValue(title, undefined, { title }); this._disposables.push(label); } } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 6217b034240c8..29592f29e01c0 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -268,6 +268,14 @@ } .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { - padding-right: 4px; + max-width: 260px; +} + +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { + padding-right: 8px; +} + +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) { + min-width: 33px; } From eab1a7ad1fc27ca68fbf4d6276154540bb57591c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 10:59:15 +0200 Subject: [PATCH 42/89] breadcrumbs - nicer picker creation --- .../browser/parts/editor/breadcrumbsControl.ts | 15 +++++++-------- .../browser/parts/editor/breadcrumbsPicker.ts | 7 ++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 698b61319abbb..b114d6bd8495c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -22,7 +22,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { FileKind, IFileService } from 'vs/platform/files/common/files'; -import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; @@ -31,7 +31,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; -import { BreadcrumbsFilePicker, BreadcrumbsOutlinePicker, BreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; +import { createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -272,11 +272,10 @@ export class BreadcrumbsControl { return event.node; }, render: (parent: HTMLElement) => { - let ctor: IConstructorSignature1 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; - let res = this._instantiationService.createInstance(ctor, parent); - res.layout({ width: Math.max(220, dom.getTotalWidth(event.node)), height: 330 }); - res.setInput(element); - let listener = res.onDidPickElement(data => { + let picker = createBreadcrumbsPicker(this._instantiationService, parent, element); + picker.layout({ width: Math.max(220, dom.getTotalWidth(event.node)), height: 330 }); + picker.setInput(element); + let listener = picker.onDidPickElement(data => { this._contextViewService.hideContextView(); this._widget.setFocused(undefined); this._widget.setSelection(undefined); @@ -285,7 +284,7 @@ export class BreadcrumbsControl { this._breadcrumbsPickerShowing = true; this._updateCkBreadcrumbsActive(); - return combinedDisposable([listener, res]); + return combinedDisposable([listener, picker]); }, onHide: () => { this._breadcrumbsPickerShowing = false; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 8f080b9e0e57a..48c58be1417ab 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -18,7 +18,7 @@ import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/doc import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { localize } from 'vs/nls'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { HighlightingWorkbenchTree, IHighlightingTreeConfiguration, IHighlightingRenderer } from 'vs/platform/list/browser/listService'; import { IThemeService, DARK } from 'vs/platform/theme/common/themeService'; import { FileLabel } from 'vs/workbench/browser/labels'; @@ -27,6 +27,11 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { breadcrumbsActiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry'; import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters'; +export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { + let ctor: IConstructorSignature1 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; + return instantiationService.createInstance(ctor, parent); +} + export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); From 9eedfdc2609cfa8ac1b9b1016b1de3302e7b0a78 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 11:11:12 +0200 Subject: [PATCH 43/89] fix missing icons in symbol search picker --- src/vs/editor/common/modes.ts | 2 +- src/vs/editor/contrib/documentSymbols/outlineTree.ts | 2 +- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 5acba54edf8ff..02c236a53c29c 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -646,7 +646,7 @@ export const symbolKindToCssClass = (function () { _fromMapping[SymbolKind.TypeParameter] = 'type-parameter'; return function toCssClassName(kind: SymbolKind): string { - return _fromMapping[kind] || 'property'; + return `symbol-icon ${_fromMapping[kind] || 'property'}`; }; })(); diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index e1181ea33cdca..72f66dd91e242 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -162,7 +162,7 @@ export class OutlineRenderer implements IRenderer { renderElement(tree: ITree, element: OutlineGroup | OutlineElement, templateId: string, template: OutlineTemplate): void { if (element instanceof OutlineElement) { - template.icon.className = `outline-element-icon symbol-icon ${symbolKindToCssClass(element.symbol.kind)}`; + template.icon.className = `outline-element-icon ${symbolKindToCssClass(element.symbol.kind)}`; template.label.set(element.symbol.name, element.score ? createMatches(element.score[1]) : undefined, localize('title.template', "{0} ({1})", element.symbol.name, OutlineRenderer._symbolKindNames[element.symbol.kind])); template.detail.innerText = element.symbol.detail || ''; this._renderMarkerInfo(element, template); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index b114d6bd8495c..198ba1f9d9844 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -93,7 +93,7 @@ class Item extends BreadcrumbsItem { if (this.options.showSymbolIcons) { let icon = document.createElement('div'); - icon.className = `symbol-icon ${symbolKindToCssClass(this.element.symbol.kind)}`; + icon.className = symbolKindToCssClass(this.element.symbol.kind); container.appendChild(icon); dom.addClass(container, 'shows-symbol-icon'); } From 56ba2993ec8fc3514932a2823e83b636d3af7029 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 11:31:48 +0200 Subject: [PATCH 44/89] list: do not steal focus for hidden elements --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 14c6ca52c3cd2..9b7be4b18e8e8 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -352,7 +352,7 @@ class DOMFocusController implements IDisposable { const focusedDomElement = this.view.domElement(focus[0]); const tabIndexElement = focusedDomElement.querySelector('[tabIndex]'); - if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement)) { + if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.style.visibility === 'hidden' || tabIndexElement.style.display === 'none') { return; } From d8feb77a065a7532c5d96be93578a577cbcc5891 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 11:36:56 +0200 Subject: [PATCH 45/89] list: do not steal focus for hidden elements (properly get style) --- src/vs/base/browser/ui/list/listWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9b7be4b18e8e8..8ac00184f283d 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -351,8 +351,9 @@ class DOMFocusController implements IDisposable { const focusedDomElement = this.view.domElement(focus[0]); const tabIndexElement = focusedDomElement.querySelector('[tabIndex]'); + const style = tabIndexElement && window.getComputedStyle(tabIndexElement); - if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.style.visibility === 'hidden' || tabIndexElement.style.display === 'none') { + if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || style.visibility === 'hidden' || style.display === 'none') { return; } From 3ee7bda7ca30a8e6ca6580ba459b132f944b7ff9 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 11:41:40 +0200 Subject: [PATCH 46/89] list: simplify if statement --- src/vs/base/browser/ui/list/listWidget.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8ac00184f283d..efaf1d52986f5 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -351,9 +351,13 @@ class DOMFocusController implements IDisposable { const focusedDomElement = this.view.domElement(focus[0]); const tabIndexElement = focusedDomElement.querySelector('[tabIndex]'); - const style = tabIndexElement && window.getComputedStyle(tabIndexElement); - if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || style.visibility === 'hidden' || style.display === 'none') { + if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement)) { + return; + } + + const style = window.getComputedStyle(tabIndexElement); + if (style.visibility === 'hidden' || style.display === 'none') { return; } From 3f457f95ae1dacf99779aa6b40e4003c1cfc2c66 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 12:43:44 +0200 Subject: [PATCH 47/89] breadcrumbs - don't use selection background color but an arrow --- src/vs/platform/theme/common/colorRegistry.ts | 2 +- src/vs/platform/theme/common/styler.ts | 6 +-- .../parts/editor/breadcrumbsControl.ts | 30 ++++++++--- .../browser/parts/editor/breadcrumbsPicker.ts | 51 ++++++++++++++----- .../parts/editor/media/notabstitlecontrol.css | 3 +- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 6b799c0333f42..674a52e34c766 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -226,7 +226,7 @@ export const progressBarBackground = registerColor('progressBar.background', { d export const breadcrumbsForeground = registerColor('breadcrumb.breadcrumbsForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC').transparent(.7), hc: Color.white.transparent(.7) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); export const breadcrumbsFocusForeground = registerColor('breadcrumb.breadcrumbsFocusForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.breadcrumbsActiveSelectionForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsSelectedForegound', "Color of selected breadcrumb items.")); -export const breadcrumbsActiveSelectionBackground = registerColor('breadcrumb.breadcrumbsActiveSelectionBackground', { light: '#F3F3F3', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of selected breadcrumb items.")); +export const breadcrumbsPickerBackground = registerColor('breadcrumb.breadcrumbsPickerBackground', { light: '#ECECEC', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of breadcrumb item picker.")); /** * Editor background color. diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index c8921a0a5e397..08c63b30296b3 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -6,7 +6,7 @@ 'use strict'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionBackground, breadcrumbsActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -266,20 +266,16 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides { breadcrumbsBackground?: ColorIdentifier; breadcrumbsForeground?: ColorIdentifier; - breadcrumbsHoverBackground?: ColorIdentifier; breadcrumbsHoverForeground?: ColorIdentifier; breadcrumbsFocusForeground?: ColorIdentifier; - breadcrumbsFocusAndSelectionBackground?: ColorIdentifier; breadcrumbsFocusAndSelectionForeground?: ColorIdentifier; } export const defaultBreadcrumbsStyles = { breadcrumbsBackground: editorBackground, breadcrumbsForeground: breadcrumbsForeground, - breadcrumbsHoverBackground: editorBackground, breadcrumbsHoverForeground: breadcrumbsFocusForeground, breadcrumbsFocusForeground: breadcrumbsFocusForeground, - breadcrumbsFocusAndSelectionBackground: breadcrumbsActiveSelectionBackground, breadcrumbsFocusAndSelectionForeground: breadcrumbsActiveSelectionForeground, }; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 198ba1f9d9844..7533db778bed4 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -31,7 +31,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; -import { createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; +import { createBreadcrumbsPicker, BreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -267,13 +267,10 @@ export class BreadcrumbsControl { } // show picker + let picker: BreadcrumbsPicker; this._contextViewService.showContextView({ - getAnchor() { - return event.node; - }, render: (parent: HTMLElement) => { - let picker = createBreadcrumbsPicker(this._instantiationService, parent, element); - picker.layout({ width: Math.max(220, dom.getTotalWidth(event.node)), height: 330 }); + picker = createBreadcrumbsPicker(this._instantiationService, parent, element); picker.setInput(element); let listener = picker.onDidPickElement(data => { this._contextViewService.hideContextView(); @@ -286,6 +283,27 @@ export class BreadcrumbsControl { return combinedDisposable([listener, picker]); }, + getAnchor() { + + let pickerHeight = 330; + let pickerWidth = Math.max(220, dom.getTotalWidth(event.node)); + let pickerArrowSize = 8; + let pickerArrowOffset: number; + + let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement); + let y = data.top + data.height - pickerArrowSize; + let x = data.left; + if (x + pickerWidth >= window.innerWidth) { + x = window.innerWidth - pickerWidth; + } + if (event.payload instanceof StandardMouseEvent) { + pickerArrowOffset = event.payload.posx - x - pickerArrowSize; + } else { + pickerArrowOffset = (data.left + (data.width * .3)) - x; + } + picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); + return { x, y }; + }, onHide: () => { this._breadcrumbsPickerShowing = false; this._updateCkBreadcrumbsActive(); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 48c58be1417ab..4937b896f0055 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -24,7 +24,7 @@ import { IThemeService, DARK } from 'vs/platform/theme/common/themeService'; import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { breadcrumbsActiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry'; +import { breadcrumbsPickerBackground } from 'vs/platform/theme/common/colorRegistry'; import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { @@ -36,38 +36,58 @@ export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); protected readonly _domNode: HTMLDivElement; - protected readonly _focus: dom.IFocusTracker; + protected readonly _arrow: HTMLDivElement; protected readonly _tree: HighlightingWorkbenchTree; + protected readonly _focus: dom.IFocusTracker; protected readonly _onDidPickElement = new Emitter(); readonly onDidPickElement: Event = this._onDidPickElement.event; constructor( - container: HTMLElement, + parent: HTMLElement, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IThemeService protected readonly _themeService: IThemeService, ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons'; - const theme = this._themeService.getTheme(); - const color = theme.getColor(breadcrumbsActiveSelectionBackground); - this._domNode.style.background = color.toString(); - this._domNode.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`; - container.appendChild(this._domNode); + parent.appendChild(this._domNode); this._focus = dom.trackFocus(this._domNode); this._focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); + const theme = this._themeService.getTheme(); + const color = theme.getColor(breadcrumbsPickerBackground); + + this._arrow = document.createElement('div'); + this._arrow.style.width = '0'; + this._arrow.style.borderStyle = 'solid'; + this._arrow.style.borderWidth = '8px'; + this._arrow.style.borderColor = `transparent transparent ${color.toString()}`; + this._domNode.appendChild(this._arrow); + + const container = document.createElement('div'); + container.style.background = color.toString(); + container.style.paddingTop = '2px'; + container.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`; + container.style.height = '100%'; + this._domNode.appendChild(container); + const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined }); - this._tree = this._instantiationService.createInstance(HighlightingWorkbenchTree, this._domNode, treeConifg, {}, { placeholder: localize('placeholder', "Find") }); + this._tree = this._instantiationService.createInstance( + HighlightingWorkbenchTree, + container, + treeConifg, + { useShadows: false }, + { placeholder: localize('placeholder', "Find") } + ); this._disposables.push(this._tree.onDidChangeSelection(e => { if (e.payload !== this._tree) { setTimeout(_ => this._onDidChangeSelection(e)); // need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click } })); - this._tree.domFocus(); + this._domNode.focus(); } dispose(): void { @@ -85,15 +105,18 @@ export abstract class BreadcrumbsPicker { this._tree.reveal(selection).then(() => { this._tree.setSelection([selection], this._tree); this._tree.setFocus(selection); + this._tree.domFocus(); }); } }, onUnexpectedError); } - layout(dim: dom.Dimension) { - this._domNode.style.width = `${dim.width}px`; - this._domNode.style.height = `${dim.height}px`; - this._tree.layout(dim.height, dim.width); + layout(height: number, width: number, arrowSize: number, arrowOffset: number) { + this._domNode.style.width = `${width}px`; + this._domNode.style.height = `${height}px`; + this._arrow.style.borderWidth = `${arrowSize}px`; + this._arrow.style.marginLeft = `${arrowOffset}px`; + this._tree.layout(height, width); } protected abstract _getInput(input: BreadcrumbElement): any; diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 649ceeb14c2a8..6020090acfc23 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -6,6 +6,7 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container { display: flex; justify-content: flex-start; + align-items: center; overflow: hidden; flex: auto; } @@ -33,8 +34,6 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control { flex: 1 50%; overflow: hidden; - line-height: 35px; - height: 35px; padding: 0 6px; } From 3e5f169e9714830b05c0a03c7bf1490e0e4e7996 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 15:13:00 +0200 Subject: [PATCH 48/89] open editors: show actions for focused elements --- .../parts/files/electron-browser/media/explorerviewlet.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css index eb657380a6774..c3fc2504f7076 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -59,7 +59,7 @@ } .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row:hover > .monaco-action-bar, -.explorer-viewlet .explorer-open-editors .monaco-list.focused .monaco-list-row.focused > .monaco-action-bar, +.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.focused > .monaco-action-bar, .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty > .monaco-action-bar { visibility: visible; } From 8d9db1808e31bc1021d2ae0cc41a239ae6370b31 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 18 Jul 2018 11:54:27 +0200 Subject: [PATCH 49/89] Add ability to generate standalone editor usages file --- build/monaco/api.js | 149 +++++++++++++++++++++++++++++++++-- build/monaco/api.ts | 188 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 318 insertions(+), 19 deletions(-) diff --git a/build/monaco/api.js b/build/monaco/api.js index 74e9273f35ee9..f75223f9329b8 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -134,7 +134,25 @@ function getTopLevelDeclaration(sourceFile, typeName) { function getNodeText(sourceFile, node) { return sourceFile.getFullText().substring(node.pos, node.end); } -function getMassagedTopLevelDeclarationText(sourceFile, declaration) { +function hasModifier(modifiers, kind) { + if (modifiers) { + for (var i = 0; i < modifiers.length; i++) { + var mod = modifiers[i]; + if (mod.kind === kind) { + return true; + } + } + } + return false; +} +function isStatic(member) { + return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword); +} +function isDefaultExport(declaration) { + return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) + && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)); +} +function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage) { var result = getNodeText(sourceFile, declaration); // if (result.indexOf('MonacoWorker') >= 0) { // console.log('here!'); @@ -142,6 +160,18 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) { // } if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { var interfaceDeclaration = declaration; + var staticTypeName_1 = (isDefaultExport(interfaceDeclaration) + ? importName + ".default" + : importName + "." + declaration.name.text); + var instanceTypeName_1 = staticTypeName_1; + var typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); + if (typeParametersCnt > 0) { + var arr = []; + for (var i = 0; i < typeParametersCnt; i++) { + arr.push('any'); + } + instanceTypeName_1 = instanceTypeName_1 + "<" + arr.join(',') + ">"; + } var members = interfaceDeclaration.members; members.forEach(function (member) { try { @@ -151,6 +181,15 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) { result = result.replace(memberText, ''); // console.log('AFTER: ', result); } + else { + var memberName = member.name.text; + if (isStatic(member)) { + usage.push("a = " + staticTypeName_1 + "." + memberName + ";"); + } + else { + usage.push("a = (<" + instanceTypeName_1 + ">b)." + memberName + ";"); + } + } } catch (err) { // life.. @@ -211,6 +250,16 @@ function generateDeclarationFile(out, inputFiles, recipe) { var endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; var lines = recipe.split(endl); var result = []; + var usageCounter = 0; + var usageImports = []; + var usage = []; + usage.push("var a;"); + usage.push("var b;"); + var generateUsageImport = function (moduleId) { + var importName = 'm' + (++usageCounter); + usageImports.push("import * as " + importName + " from '" + moduleId.replace(/\.d\.ts$/, '') + "';"); + return importName; + }; lines.forEach(function (line) { var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { @@ -220,6 +269,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { if (!sourceFile_1) { return; } + var importName_1 = generateUsageImport(moduleId); var replacer_1 = createReplacer(m1[2]); var typeNames = m1[3].split(/,/); typeNames.forEach(function (typeName) { @@ -232,7 +282,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { logErr('Cannot find type ' + typeName); return; } - result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration))); + result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration, importName_1, usage))); }); return; } @@ -244,6 +294,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { if (!sourceFile_2) { return; } + var importName_2 = generateUsageImport(moduleId); var replacer_2 = createReplacer(m2[2]); var typeNames = m2[3].split(/,/); var typesToExcludeMap_1 = {}; @@ -271,7 +322,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { } } } - result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration))); + result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration, importName_2, usage))); }); return; } @@ -282,9 +333,12 @@ function generateDeclarationFile(out, inputFiles, recipe) { resultTxt = resultTxt.replace(/\bEvent, kind: ts.SyntaxKind): boolean { + if (modifiers) { + for (let i = 0; i < modifiers.length; i++) { + let mod = modifiers[i]; + if (mod.kind === kind) { + return true; + } + } + } + return false; +} -function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare): string { +function isStatic(member: ts.ClassElement | ts.TypeElement): boolean { + return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword); +} + +function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean { + return ( + hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) + && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword) + ); +} + +function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[]): string { let result = getNodeText(sourceFile, declaration); // if (result.indexOf('MonacoWorker') >= 0) { // console.log('here!'); @@ -163,7 +185,23 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; - let members: ts.NodeArray = interfaceDeclaration.members; + const staticTypeName = ( + isDefaultExport(interfaceDeclaration) + ? `${importName}.default` + : `${importName}.${declaration.name.text}` + ); + + let instanceTypeName = staticTypeName; + const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); + if (typeParametersCnt > 0) { + let arr: string[] = []; + for (let i = 0; i < typeParametersCnt; i++) { + arr.push('any'); + } + instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`; + } + + const members: ts.NodeArray = interfaceDeclaration.members; members.forEach((member) => { try { let memberText = getNodeText(sourceFile, member); @@ -171,6 +209,13 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati // console.log('BEFORE: ', result); result = result.replace(memberText, ''); // console.log('AFTER: ', result); + } else { + const memberName = (member.name).text; + if (isStatic(member)) { + usage.push(`a = ${staticTypeName}.${memberName};`); + } else { + usage.push(`a = (<${instanceTypeName}>b).${memberName};`); + } } } catch (err) { // life.. @@ -237,11 +282,24 @@ function createReplacer(data: string): (str: string) => string { }; } -function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): string { +function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): [string, string] { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; let lines = recipe.split(endl); - let result = []; + let result: string[] = []; + + let usageCounter = 0; + let usageImports: string[] = []; + let usage: string[] = []; + + usage.push(`var a;`); + usage.push(`var b;`); + + const generateUsageImport = (moduleId: string) => { + let importName = 'm' + (++usageCounter); + usageImports.push(`import * as ${importName} from '${moduleId.replace(/\.d\.ts$/, '')}';`); + return importName; + }; lines.forEach(line => { @@ -254,6 +312,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri return; } + const importName = generateUsageImport(moduleId); + let replacer = createReplacer(m1[2]); let typeNames = m1[3].split(/,/); @@ -267,7 +327,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri logErr('Cannot find type ' + typeName); return; } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); }); return; } @@ -281,6 +341,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri return; } + const importName = generateUsageImport(moduleId); + let replacer = createReplacer(m2[2]); let typeNames = m2[3].split(/,/); @@ -309,7 +371,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri } } } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); }); return; } @@ -324,10 +386,13 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri resultTxt = format(resultTxt); - return resultTxt; + return [ + resultTxt, + `${usageImports.join('\n')}\n\n${usage.join('\n')}` + ]; } -export function getFilesToWatch(out: string): string[] { +function getIncludesInRecipe(): string[] { let recipe = fs.readFileSync(RECIPE_PATH).toString(); let lines = recipe.split(/\r\n|\n|\r/); let result = []; @@ -337,14 +402,14 @@ export function getFilesToWatch(out: string): string[] { let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { let moduleId = m1[1]; - result.push(moduleIdToPath(out, moduleId)); + result.push(moduleId); return; } let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { let moduleId = m2[1]; - result.push(moduleIdToPath(out, moduleId)); + result.push(moduleId); return; } }); @@ -352,8 +417,13 @@ export function getFilesToWatch(out: string): string[] { return result; } +export function getFilesToWatch(out: string): string[] { + return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); +} + export interface IMonacoDeclarationResult { content: string; + usageContent: string; filePath: string; isTheSame: boolean; } @@ -363,7 +433,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona SOURCE_FILE_MAP = {}; let recipe = fs.readFileSync(RECIPE_PATH).toString(); - let result = generateDeclarationFile(out, inputFiles, recipe); + let [result, usageContent] = generateDeclarationFile(out, inputFiles, recipe); let currentContent = fs.readFileSync(DECLARATION_PATH).toString(); log('Finished monaco.d.ts generation'); @@ -374,6 +444,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona return { content: result, + usageContent: usageContent, filePath: DECLARATION_PATH, isTheSame }; @@ -382,3 +453,98 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona export function complainErrors() { logErr('Not running monaco.d.ts generation due to compile errors'); } + + + +interface ILibMap { [libName: string]: string; } +interface IFileMap { [fileName: string]: string; } + +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { + + private readonly _libs: ILibMap; + private readonly _files: IFileMap; + private readonly _compilerOptions: ts.CompilerOptions; + + constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + + // --- language service host --------------- + + getCompilationSettings(): ts.CompilerOptions { + return this._compilerOptions; + } + getScriptFileNames(): string[] { + return ( + [] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files)) + ); + } + getScriptVersion(fileName: string): string { + return '1'; + } + getProjectVersion(): string { + return '1'; + } + getScriptSnapshot(fileName: string): ts.IScriptSnapshot { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } else { + return ts.ScriptSnapshot.fromString(''); + } + } + getScriptKind(fileName: string): ts.ScriptKind { + return ts.ScriptKind.TS; + } + getCurrentDirectory(): string { + return ''; + } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'defaultLib:es5'; + } + isDefaultLibFileName(fileName: string): boolean { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + } +} + +function execute() { + + const OUTPUT_FILES: { [file: string]: string; } = {}; + const SRC_FILES: IFileMap = {}; + const SRC_FILE_TO_EXPECTED_NAME: { [filename: string]: string; } = {}; + getIncludesInRecipe().forEach((moduleId) => { + if (/\.d\.ts$/.test(moduleId)) { + let fileName = path.join(SRC, moduleId); + OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); + return; + } + + let fileName = path.join(SRC, moduleId) + '.ts'; + SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); + SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); + }); + + const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); + + var t1 = Date.now(); + Object.keys(SRC_FILES).forEach((fileName) => { + var t = Date.now(); + const emitOutput = languageService.getEmitOutput(fileName, true); + OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; + console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`); + }); + console.log(`Generating .d.ts took ${Date.now() - t1} ms`); + + const result = run('src', OUTPUT_FILES); + + console.log(result.filePath); + fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); + fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); +} + +// execute(); From 1e82a6c1caaee7db7e74cf61d53654b556d32055 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 Jul 2018 15:18:00 +0200 Subject: [PATCH 50/89] Improve type declarations --- .../base/parts/quickopen/browser/quickOpenModel.ts | 3 ++- src/vs/editor/browser/widget/diffEditorWidget.ts | 13 ++++--------- src/vs/editor/common/services/editorSimpleWorker.ts | 4 ++-- .../standalone/browser/standaloneCodeEditor.ts | 10 +++++----- .../editor/standalone/browser/standaloneServices.ts | 5 ----- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 7482829660555..d19657a3f925f 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -501,7 +501,8 @@ export class QuickOpenModel implements IModel, IDataSource, IFilter, - IRunner + IRunner, + IAccessiblityProvider { private _entries: QuickOpenEntry[]; private _dataSource: IDataSource; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index a3f19a76245b4..dbd07dd8a74a2 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -1177,7 +1177,7 @@ interface IDataSource { getModifiedEditor(): editorBrowser.ICodeEditor; } -abstract class DiffEditorWidgetStyle extends Disposable { +abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle { _dataSource: IDataSource; _insertColor: Color; @@ -1228,6 +1228,9 @@ abstract class DiffEditorWidgetStyle extends Disposable { protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; + + public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; + public abstract layout(): number; } interface IMyViewZone extends editorBrowser.IViewZone { @@ -1529,10 +1532,6 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd this._sash.onDidReset(() => this.onSashReset()); } - public dispose(): void { - super.dispose(); - } - public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { let newDisableSash = (enableSplitViewResizing === false); if (this._disableSash !== newDisableSash) { @@ -1778,10 +1777,6 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor })); } - public dispose(): void { - super.dispose(); - } - public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { // Nothing to do.. } diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 5e5f192fb1a06..d478fef73c6b9 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; import { IInplaceReplaceSupportResult, ILink, ISuggestResult, ISuggestion, TextEdit } from 'vs/editor/common/modes'; -import { computeLinks } from 'vs/editor/common/modes/linkComputer'; +import { computeLinks, ILinkComputerTarget } from 'vs/editor/common/modes/linkComputer'; import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; @@ -50,7 +50,7 @@ export interface IRawModelData { /** * @internal */ -export interface ICommonModel { +export interface ICommonModel extends ILinkComputerTarget, IMirrorModel { uri: URI; version: number; eol: string; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 3a1fabd909300..36f324da0a7dc 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -17,7 +17,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { StandaloneKeybindingService, applyConfigurationValues } from 'vs/editor/standalone/browser/simpleServices'; -import { IEditorContextViewService } from 'vs/editor/standalone/browser/standaloneServices'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -284,7 +284,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor { - private _contextViewService: IEditorContextViewService; + private _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; private _ownsModel: boolean; @@ -311,7 +311,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon delete options.model; super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, keybindingService, themeService, notificationService); - this._contextViewService = contextViewService; + this._contextViewService = contextViewService; this._configurationService = configurationService; this._register(toDispose); @@ -359,7 +359,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { - private _contextViewService: IEditorContextViewService; + private _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; constructor( @@ -384,7 +384,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService); - this._contextViewService = contextViewService; + this._contextViewService = contextViewService; this._configurationService = configurationService; this._register(toDispose); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index e4f58e5df5816..d3275d83f30e9 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -45,11 +45,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -export interface IEditorContextViewService extends IContextViewService { - dispose(): void; - setContainer(domNode: HTMLElement): void; -} - export interface IEditorOverrideServices { [index: string]: any; } From ae266f38accd6aa9f389e5bfa361ae03ed939058 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 18 Jul 2018 14:06:32 +0200 Subject: [PATCH 51/89] Allow the Protocol to be disposed and extract buffered data --- src/vs/base/parts/ipc/node/ipc.net.ts | 60 +++++++++++++++---- .../base/parts/ipc/test/node/ipc.net.test.ts | 35 +++++++++++ 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index a28e7ab81b5f1..77926d0b8349f 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -12,6 +12,8 @@ import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } import { join } from 'path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TimeoutTimer } from 'vs/base/common/async'; export function generateRandomPipeName(): string { const randomSuffix = generateUuid(); @@ -23,17 +25,24 @@ export function generateRandomPipeName(): string { } } -export class Protocol implements IMessagePassingProtocol { +export class Protocol implements IDisposable, IMessagePassingProtocol { private static readonly _headerLen = 5; - private _onMessage = new Emitter(); + private _isDisposed: boolean; + private _chunks: Buffer[]; + + private _firstChunkTimer: TimeoutTimer; + private _socketDataListener: (data: Buffer) => void; + private _socketEndListener: () => void; + private _onMessage = new Emitter(); readonly onMessage: Event = this._onMessage.event; constructor(private _socket: Socket, firstDataChunk?: Buffer) { + this._isDisposed = false; + this._chunks = []; - let chunks: Buffer[] = []; let totalLength = 0; const state = { @@ -44,7 +53,7 @@ export class Protocol implements IMessagePassingProtocol { const acceptChunk = (data: Buffer) => { - chunks.push(data); + this._chunks.push(data); totalLength += data.length; while (totalLength > 0) { @@ -53,7 +62,7 @@ export class Protocol implements IMessagePassingProtocol { // expecting header -> read 5bytes for header // information: `bodyIsJson` and `bodyLen` if (totalLength >= Protocol._headerLen) { - const all = Buffer.concat(chunks); + const all = Buffer.concat(this._chunks); state.bodyIsJson = all.readInt8(0) === 1; state.bodyLen = all.readInt32BE(1); @@ -61,7 +70,7 @@ export class Protocol implements IMessagePassingProtocol { const rest = all.slice(Protocol._headerLen); totalLength = rest.length; - chunks = [rest]; + this._chunks = [rest]; } else { break; @@ -73,21 +82,27 @@ export class Protocol implements IMessagePassingProtocol { // the actual message or wait for more data if (totalLength >= state.bodyLen) { - const all = Buffer.concat(chunks); + const all = Buffer.concat(this._chunks); let message = all.toString('utf8', 0, state.bodyLen); if (state.bodyIsJson) { message = JSON.parse(message); } - this._onMessage.fire(message); + // ensure the public getBuffer returns a valid value if invoked from the event listeners const rest = all.slice(state.bodyLen); totalLength = rest.length; - chunks = [rest]; + this._chunks = [rest]; state.bodyIsJson = false; state.bodyLen = -1; state.readHead = true; + this._onMessage.fire(message); + + if (this._isDisposed) { + // check if an event listener lead to our disposal + break; + } } else { break; } @@ -103,14 +118,33 @@ export class Protocol implements IMessagePassingProtocol { } }; - _socket.on('data', (data: Buffer) => { + // Make sure to always handle the firstDataChunk if no more `data` event comes in + this._firstChunkTimer = new TimeoutTimer(); + this._firstChunkTimer.setIfNotSet(() => { + acceptFirstDataChunk(); + }, 0); + + this._socketDataListener = (data: Buffer) => { acceptFirstDataChunk(); acceptChunk(data); - }); + }; + _socket.on('data', this._socketDataListener); - _socket.on('end', () => { + this._socketEndListener = () => { acceptFirstDataChunk(); - }); + }; + _socket.on('end', this._socketEndListener); + } + + public dispose(): void { + this._isDisposed = true; + this._firstChunkTimer.dispose(); + this._socket.removeListener('data', this._socketDataListener); + this._socket.removeListener('end', this._socketEndListener); + } + + public getBuffer(): Buffer { + return Buffer.concat(this._chunks); } public send(message: any): void { diff --git a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts index 6b48161d02615..eb6ddffba025d 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts @@ -87,4 +87,39 @@ suite('IPC, Socket Protocol', () => { }); }); }); + + test('can devolve to a socket and evolve again without losing data', () => { + let resolve: (v: void) => void; + let result = new TPromise((_resolve, _reject) => { + resolve = _resolve; + }); + const sender = new Protocol(stream); + const receiver1 = new Protocol(stream); + + assert.equal(stream.listenerCount('data'), 2); + assert.equal(stream.listenerCount('end'), 2); + + receiver1.onMessage((msg) => { + assert.equal(msg.value, 1); + + let buffer = receiver1.getBuffer(); + receiver1.dispose(); + + assert.equal(stream.listenerCount('data'), 1); + assert.equal(stream.listenerCount('end'), 1); + + const receiver2 = new Protocol(stream, buffer); + receiver2.onMessage((msg) => { + assert.equal(msg.value, 2); + resolve(void 0); + }); + }); + + const msg1 = { value: 1 }; + const msg2 = { value: 2 }; + sender.send(msg1); + sender.send(msg2); + + return result; + }); }); From a384b259e6c34d3bcdd2d83afd47ee63777843c8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 18 Jul 2018 14:53:23 +0200 Subject: [PATCH 52/89] Allow to create a Client with a Protocol --- src/vs/base/parts/ipc/node/ipc.net.ts | 31 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 77926d0b8349f..e82503e1eb087 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -35,10 +35,14 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { private _firstChunkTimer: TimeoutTimer; private _socketDataListener: (data: Buffer) => void; private _socketEndListener: () => void; + private _socketCloseListener: () => void; private _onMessage = new Emitter(); readonly onMessage: Event = this._onMessage.event; + private _onClose = new Emitter(); + readonly onClose: Event = this._onClose.event; + constructor(private _socket: Socket, firstDataChunk?: Buffer) { this._isDisposed = false; this._chunks = []; @@ -134,6 +138,11 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { acceptFirstDataChunk(); }; _socket.on('end', this._socketEndListener); + + this._socketCloseListener = () => { + this._onClose.fire(); + }; + _socket.once('close', this._socketCloseListener); } public dispose(): void { @@ -141,6 +150,11 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { this._firstChunkTimer.dispose(); this._socket.removeListener('data', this._socketDataListener); this._socket.removeListener('end', this._socketEndListener); + this._socket.removeListener('close', this._socketCloseListener); + } + + public end(): void { + this._socket.end(); } public getBuffer(): Buffer { @@ -227,18 +241,19 @@ export class Server extends IPCServer { export class Client extends IPCClient { - private _onClose = new Emitter(); - get onClose(): Event { return this._onClose.event; } + public static fromSocket(socket: Socket, id: string): Client { + return new Client(new Protocol(socket), id); + } + + get onClose(): Event { return this.protocol.onClose; } - constructor(private socket: Socket, id: string) { - super(new Protocol(socket), id); - socket.once('close', () => this._onClose.fire()); + constructor(private protocol: Protocol, id: string) { + super(protocol, id); } dispose(): void { super.dispose(); - this.socket.end(); - this.socket = null; + this.protocol.end(); } } @@ -263,7 +278,7 @@ export function connect(hook: any, clientId: string): TPromise { return new TPromise((c, e) => { const socket = createConnection(hook, () => { socket.removeListener('error', e); - c(new Client(socket, clientId)); + c(Client.fromSocket(socket, clientId)); }); socket.once('error', e); From 48345b002d4bb491905be7777da20d759e166a38 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 14:39:08 +0200 Subject: [PATCH 53/89] breadcrumbs - show hellip when outline info is available but not intersecting with the current selection --- .../browser/parts/editor/breadcrumbsControl.ts | 8 ++++++-- .../browser/parts/editor/breadcrumbsModel.ts | 13 ++++++++----- .../browser/parts/editor/breadcrumbsPicker.ts | 6 +++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 7533db778bed4..195f7c934f140 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -82,6 +82,12 @@ class Item extends BreadcrumbsItem { this._disposables.push(label); dom.toggleClass(container, 'file', this.element.isFile); + } else if (this.element instanceof OutlineModel) { + // has outline element but not in one + let label = document.createElement('div'); + label.innerHTML = '…'; + container.appendChild(label); + } else if (this.element instanceof OutlineGroup) { // provider let label = new IconLabel(container); @@ -90,14 +96,12 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineElement) { // symbol - if (this.options.showSymbolIcons) { let icon = document.createElement('div'); icon.className = symbolKindToCssClass(this.element.symbol.kind); container.appendChild(icon); dom.addClass(container, 'shows-symbol-icon'); } - let label = new IconLabel(container); let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'); label.setValue(title, undefined, { title }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 39e2c016c23f8..f29efc47db18c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -31,7 +31,7 @@ export class FileElement { ) { } } -export type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement; +export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement; type FileInfo = { path: FileElement[], folder: IWorkspaceFolder, showFolder: boolean }; @@ -43,7 +43,7 @@ export class EditorBreadcrumbsModel { private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>; - private _outlineElements: (OutlineGroup | OutlineElement)[] = []; + private _outlineElements: (OutlineModel | OutlineGroup | OutlineElement)[] = []; private _outlineDisposables: IDisposable[] = []; private _onDidUpdate = new Emitter(); @@ -181,11 +181,14 @@ export class EditorBreadcrumbsModel { }); } - private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineGroup | OutlineElement)[] { + private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineModel | OutlineGroup | OutlineElement)[] { if (!model) { return []; } let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position); + if (!item) { + return [model]; + } let chain: (OutlineGroup | OutlineElement)[] = []; while (item) { chain.push(item); @@ -201,14 +204,14 @@ export class EditorBreadcrumbsModel { return chain.reverse(); } - private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void { + private _updateOutlineElements(elements: (OutlineModel | OutlineGroup | OutlineElement)[]): void { if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) { this._outlineElements = elements; this._onDidUpdate.fire(this); } } - private static _outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean { + private static _outlineElementEquals(a: OutlineModel | OutlineGroup | OutlineElement, b: OutlineModel | OutlineGroup | OutlineElement): boolean { if (a === b) { return true; } else if (!a || !b) { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 4937b896f0055..de9bdcd026bd0 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -107,6 +107,10 @@ export abstract class BreadcrumbsPicker { this._tree.setFocus(selection); this._tree.domFocus(); }); + } else { + this._tree.focusFirst(); + this._tree.setSelection([this._tree.getFocus()], this._tree); + this._tree.domFocus(); } }, onUnexpectedError); } @@ -278,7 +282,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any { - return input; + return input instanceof OutlineModel ? undefined : input; } protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { From d0b1708a1c68b3cc1a48475ce4fb9caa42a9d75c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 15:08:59 +0200 Subject: [PATCH 54/89] breadcrumbs - prevent scrollbar flashing --- .../base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 10 ++++++---- .../base/browser/ui/scrollbar/scrollableElement.ts | 12 +++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index e78bc4bb97a5f..0966ceca01063 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -114,13 +114,13 @@ export class BreadcrumbsWidget { } layout(dim: dom.Dimension): void { - if (!dim) { - this._scrollable.scanDomNode(); - } else { + if (dim) { this._domNode.style.width = `${dim.width}px`; this._domNode.style.height = `${dim.height}px`; - this._scrollable.scanDomNode(); } + this._scrollable.setRevealOnScroll(false); + this._scrollable.scanDomNode(); + this._scrollable.setRevealOnScroll(true); } style(style: IBreadcrumbsWidgetStyles): void { @@ -206,7 +206,9 @@ export class BreadcrumbsWidget { private _reveal(nth: number): void { const node = this._nodes[nth]; if (node) { + this._scrollable.setRevealOnScroll(false); this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + this._scrollable.setRevealOnScroll(true); } } diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 5aafe0bd8a064..3d2f82a5cc7eb 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -163,6 +163,8 @@ export abstract class AbstractScrollableElement extends Widget { private readonly _hideTimeout: TimeoutTimer; private _shouldRender: boolean; + private _revealOnScroll: boolean; + private readonly _onScroll = this._register(new Emitter()); public readonly onScroll: Event = this._onScroll.event; @@ -221,6 +223,8 @@ export abstract class AbstractScrollableElement extends Widget { this._mouseIsOver = false; this._shouldRender = true; + + this._revealOnScroll = true; } public dispose(): void { @@ -286,6 +290,10 @@ export abstract class AbstractScrollableElement extends Widget { } } + public setRevealOnScroll(value: boolean) { + this._revealOnScroll = value; + } + // -------------------- mouse wheel scrolling -------------------- private _setListeningToMouseWheel(shouldListen: boolean): void { @@ -382,7 +390,9 @@ export abstract class AbstractScrollableElement extends Widget { this._shouldRender = true; } - this._reveal(); + if (this._revealOnScroll) { + this._reveal(); + } if (!this._options.lazyRender) { this._render(); From 1a14814f05f0f7b4a2def2104ea94a3a6a5476d4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 15:17:58 +0200 Subject: [PATCH 55/89] breadcrumbs - disable min-width test --- .../workbench/browser/parts/editor/media/tabstitlecontrol.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 29592f29e01c0..74218a0d7a963 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -275,7 +275,7 @@ padding-right: 8px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) { +/* .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) { min-width: 33px; -} +} */ From d5eb9df2b4465107a54cc22806ea055f23855348 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 15:29:59 +0200 Subject: [PATCH 56/89] :lipstick: --- src/vs/vscode.d.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 943698977fbe8..1091741b1c75f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4592,22 +4592,22 @@ declare module 'vscode' { /** * The clean task group; */ - public static Clean: TaskGroup; + static Clean: TaskGroup; /** * The build task group; */ - public static Build: TaskGroup; + static Build: TaskGroup; /** * The rebuild all task group; */ - public static Rebuild: TaskGroup; + static Rebuild: TaskGroup; /** * The test all task group; */ - public static Test: TaskGroup; + static Test: TaskGroup; private constructor(id: string, label: string); } @@ -7027,13 +7027,13 @@ declare module 'vscode' { export const onDidChangeConfiguration: Event; /** - * Register a task provider. + * ~~Register a task provider.~~ + * + * @deprecated Use the corresponding function on the `tasks` namespace instead * * @param type The task kind type this provider is registered for. * @param provider A task provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - * - * @deprecated Use the corresponding function on the `tasks` namespace instead */ export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; From e90faf1fa753415e4cb2f77e31d5e3c2ea27ae60 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 15:59:51 +0200 Subject: [PATCH 57/89] breadcrumbs - add "Toggle Breadcrumbs" to "View" menu --- src/vs/code/electron-main/menus.ts | 4 +++- .../workbench/browser/parts/editor/breadcrumbs.ts | 8 +++++++- .../browser/parts/editor/breadcrumbsControl.ts | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 998479e62471e..4c5a22871d81c 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -766,6 +766,7 @@ export class CodeMenu { const toggleMinimap = this.createMenuItem(nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"), 'editor.action.toggleMinimap'); const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace'); const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter'); + const toggleBreadcrumbs = this.createMenuItem(nls.localize({ key: 'miToggleBreadcrumbs', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breadcrumbs"), 'breadcrumbs.toggle'); arrays.coalesce([ commands, @@ -788,7 +789,8 @@ export class CodeMenu { toggleWordWrap, toggleMinimap, toggleRenderWhitespace, - toggleRenderControlCharacters + toggleRenderControlCharacters, + toggleBreadcrumbs ]).forEach(item => viewMenu.append(item)); } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 136fc5bf7943b..ac7b2f27130f9 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -85,7 +85,13 @@ export abstract class BreadcrumbsConfig { return { name, - get value() { return value; }, + get value() { + return value; + }, + set value(newValue: T) { + service.updateValue(name, newValue); + value = newValue; + }, onDidChange: onDidChange.event, dispose(): void { listener.dispose(); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 195f7c934f140..28ac8c4a59d6c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -39,6 +39,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { WorkbenchListFocusContextKey, IListService } from 'vs/platform/list/browser/listService'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; class Item extends BreadcrumbsItem { @@ -356,6 +357,19 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { title: localize('cmd.focus', "Focus Breadcrumbs") } }); +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + order: 99, + command: { + id: 'breadcrumbs.toggle', + title: localize('cmd.toggle', "Toggle Breadcrumbs") + } +}); +CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => { + let config = accessor.get(IConfigurationService); + let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value; + BreadcrumbsConfig.IsEnabled.bindTo(config).value = !value; +}); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focus', From 0738ae2f7fe0a8e6e2aa50d3de6c8140d8d5fe81 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 16:04:45 +0200 Subject: [PATCH 58/89] menu: move debug menu registration to debug.contribution --- .../parts/menubar/menubar.contribution.ts | 180 ------------------ .../parts/debug/browser/debugCommands.ts | 12 +- .../parts/debug/browser/debugEditorActions.ts | 9 +- .../electron-browser/debug.contribution.ts | 177 ++++++++++++++++- src/vs/workbench/workbench.main.ts | 3 +- 5 files changed, 190 insertions(+), 191 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index d74f6c44577aa..fd88cbf3afe2d 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { isMacintosh } from 'vs/base/common/platform'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; recentMenuRegistration(); fileMenuRegistration(); @@ -16,7 +15,6 @@ viewMenuRegistration(); appearanceMenuRegistration(); layoutMenuRegistration(); goMenuRegistration(); -debugMenuRegistration(); tasksMenuRegistration(); if (isMacintosh) { @@ -1052,184 +1050,6 @@ function goMenuRegistration() { }); } -function debugMenuRegistration() { - // Start/Stop Debug - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.start', - title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging"), - precondition: ContextKeyExpr.not('inDebugMode') - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.run', - title: nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging"), - precondition: ContextKeyExpr.not('inDebugMode') - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.stop', - title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.restart', - title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 4 - }); - - // Configuration - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: 'workbench.action.debug.configure', - title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: 'debug.addConfiguration', - title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration...") - }, - order: 2 - }); - - // Step Commands - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepOver', - title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepInto', - title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepOut', - title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.continue', - title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), - precondition: ContextKeyExpr.has('inDebugMode') - }, - order: 4 - }); - - // New Breakpoints - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleBreakpoint', - title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.conditionalBreakpoint', - title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Conditional Breakpoint...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleInlineBreakpoint', - title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Inline Breakp&&oint") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'workbench.debug.viewlet.action.addFunctionBreakpointAction', - title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Function Breakpoint...") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleLogPoint', - title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Logpoint...") - }, - order: 5 - }); - - // Modify Breakpoints - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.enableAllBreakpoints', - title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.disableAllBreakpoints', - title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.removeAllBreakpoints', - title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") - }, - order: 3 - }); - -} - function tasksMenuRegistration() { // Run Tasks MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { diff --git a/src/vs/workbench/parts/debug/browser/debugCommands.ts b/src/vs/workbench/parts/debug/browser/debugCommands.ts index bf06733430f35..62d1e1ee20d28 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -25,6 +25,9 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; +export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; + export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -171,7 +174,7 @@ export function registerCommands(): void { }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'debug.addConfiguration', + id: ADD_CONFIGURATION_ID, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), when: undefined, primary: undefined, @@ -196,7 +199,6 @@ export function registerCommands(): void { } }); - const INLINE_BREAKPOINT_COMMAND_ID = 'editor.debug.action.toggleInlineBreakpoint'; const inlineBreakpointHandler = (accessor: ServicesAccessor) => { const debugService = accessor.get(IDebugService); const editorService = accessor.get(IEditorService); @@ -221,20 +223,20 @@ export function registerCommands(): void { weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), primary: KeyMod.Shift | KeyCode.F9, when: EditorContextKeys.editorTextFocus, - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, handler: inlineBreakpointHandler }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('inlineBreakpoint', "Inline Breakpoint"), category: nls.localize('debug', "Debug") } }); MenuRegistry.appendMenuItem(MenuId.EditorContext, { command: { - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint") }, when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable), diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index 4c871d8342b84..a243ca8e26d2d 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -17,10 +17,11 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView'; +export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.toggleBreakpoint', + id: TOGGLE_BREAKPOINT_ID, label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), alias: 'Debug: Toggle Breakpoint', precondition: null, @@ -49,11 +50,12 @@ class ToggleBreakpointAction extends EditorAction { } } +export const TOGGLE_CONDITIONAL_BREAKPOINT_ID = 'editor.debug.action.conditionalBreakpoint'; class ConditionalBreakpointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.conditionalBreakpoint', + id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."), alias: 'Debug: Add Conditional Breakpoint...', precondition: null @@ -70,11 +72,12 @@ class ConditionalBreakpointAction extends EditorAction { } } +export const TOGGLE_LOG_POINT_ID = 'editor.debug.action.toggleLogPoint'; class LogPointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.toggleLogPoint', + id: TOGGLE_LOG_POINT_ID, label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."), alias: 'Debug: Add Logpoint...', precondition: null diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index a63113023d12e..fc140e687b435 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -37,7 +37,7 @@ import * as service from 'vs/workbench/parts/debug/electron-browser/debugService import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; import 'vs/workbench/parts/debug/electron-browser/debugEditorContribution'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { registerCommands } from 'vs/workbench/parts/debug/browser/debugCommands'; +import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/parts/debug/browser/debugCommands'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { StatusBarColorProvider } from 'vs/workbench/parts/debug/browser/statusbarColorProvider'; import { ViewsRegistry } from 'vs/workbench/common/views'; @@ -52,6 +52,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { LoadedScriptsView } from 'vs/workbench/parts/debug/browser/loadedScriptsView'; +import { TOGGLE_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID } from 'vs/workbench/parts/debug/browser/debugEditorActions'; class OpenDebugViewletAction extends ToggleViewletAction { public static readonly ID = VIEWLET_ID; @@ -227,6 +228,180 @@ registerCommands(); const statusBar = Registry.as(StatusExtensions.Statusbar); statusBar.registerStatusbarItem(new StatusbarItemDescriptor(DebugStatus, StatusbarAlignment.LEFT, 30 /* Low Priority */)); +// Register debug menu + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: StartAction.ID, + title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RunAction.ID, + title: nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: StopAction.ID, + title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RestartAction.ID, + title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 4 +}); + +// Configuration +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ConfigureAction.ID, + title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ADD_CONFIGURATION_ID, + title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration...") + }, + order: 2 +}); + +// Step Commands +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepOverAction.ID, + title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepIntoAction.ID, + title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepOutAction.ID, + title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: ContinueAction.ID, + title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 4 +}); + +// New Breakpoints +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_BREAKPOINT_ID, + title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, + title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Conditional Breakpoint...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_INLINE_BREAKPOINT_ID, + title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Inline Breakp&&oint") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: AddFunctionBreakpointAction.ID, + title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Function Breakpoint...") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_LOG_POINT_ID, + title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Logpoint...") + }, + order: 5 +}); + +// Modify Breakpoints +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: EnableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: DisableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: RemoveAllBreakpointsAction.ID, + title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") + }, + order: 3 +}); + // Touch Bar if (isMacintosh) { diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index fa632fcf4f34b..2df703e1c1f5d 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -64,7 +64,6 @@ import 'vs/workbench/parts/scm/electron-browser/scmViewlet'; // can be packaged import 'vs/workbench/parts/debug/electron-browser/debug.contribution'; import 'vs/workbench/parts/debug/browser/debugQuickOpen'; import 'vs/workbench/parts/debug/electron-browser/repl'; -import 'vs/workbench/parts/debug/browser/debugEditorActions'; import 'vs/workbench/parts/debug/browser/debugViewlet'; // can be packaged separately import 'vs/workbench/parts/markers/electron-browser/markers.contribution'; @@ -142,4 +141,4 @@ import 'vs/workbench/parts/navigation/common/navigation.contribution'; // services import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; -import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; \ No newline at end of file +import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; From dcd5ea858f2607186b772f0ce746de584e263bff Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 Jul 2018 16:06:05 +0200 Subject: [PATCH 59/89] Fixes #54550: Define `fs` as `original-fs` only when running in Electron --- src/bootstrap-amd.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index c8e5800ab5bb6..752d996f6b97c 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -69,7 +69,10 @@ loader.config({ nodeCachedDataDir: process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] }); -loader.define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code +if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions.electron) { + // running in Electron + loader.define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code +} if (nlsConfig.pseudo) { loader(['vs/nls'], function (nlsPlugin) { From f740c599622a96ac1609ba30c6b8916e02ebf5f2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Jul 2018 16:58:42 +0200 Subject: [PATCH 60/89] breadcrumbs - fix reveal before layout (once again) --- .../parts/editor/breadcrumbsControl.ts | 2 +- .../browser/parts/editor/breadcrumbsPicker.ts | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 28ac8c4a59d6c..c8a2110bfd2de 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -276,7 +276,6 @@ export class BreadcrumbsControl { this._contextViewService.showContextView({ render: (parent: HTMLElement) => { picker = createBreadcrumbsPicker(this._instantiationService, parent, element); - picker.setInput(element); let listener = picker.onDidPickElement(data => { this._contextViewService.hideContextView(); this._widget.setFocused(undefined); @@ -307,6 +306,7 @@ export class BreadcrumbsControl { pickerArrowOffset = (data.left + (data.width * .3)) - x; } picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); + picker.setInput(element); return { x, y }; }, onHide: () => { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index de9bdcd026bd0..5a49481c2eca6 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -37,6 +37,7 @@ export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); protected readonly _domNode: HTMLDivElement; protected readonly _arrow: HTMLDivElement; + protected readonly _treeContainer: HTMLDivElement; protected readonly _tree: HighlightingWorkbenchTree; protected readonly _focus: dom.IFocusTracker; @@ -66,17 +67,16 @@ export abstract class BreadcrumbsPicker { this._arrow.style.borderColor = `transparent transparent ${color.toString()}`; this._domNode.appendChild(this._arrow); - const container = document.createElement('div'); - container.style.background = color.toString(); - container.style.paddingTop = '2px'; - container.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`; - container.style.height = '100%'; - this._domNode.appendChild(container); + this._treeContainer = document.createElement('div'); + this._treeContainer.style.background = color.toString(); + this._treeContainer.style.paddingTop = '2px'; + this._treeContainer.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`; + this._domNode.appendChild(this._treeContainer); const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined }); this._tree = this._instantiationService.createInstance( HighlightingWorkbenchTree, - container, + this._treeContainer, treeConifg, { useShadows: false }, { placeholder: localize('placeholder', "Find") } @@ -116,11 +116,14 @@ export abstract class BreadcrumbsPicker { } layout(height: number, width: number, arrowSize: number, arrowOffset: number) { - this._domNode.style.width = `${width}px`; this._domNode.style.height = `${height}px`; + this._domNode.style.width = `${width}px`; this._arrow.style.borderWidth = `${arrowSize}px`; this._arrow.style.marginLeft = `${arrowOffset}px`; - this._tree.layout(height, width); + + this._treeContainer.style.height = `${height - 2 * arrowSize}px`; + this._treeContainer.style.width = `${width}px`; + this._tree.layout(); } protected abstract _getInput(input: BreadcrumbElement): any; From e67143de42940ca249a012a6b4d1e2ecee8059df Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 17:00:55 +0200 Subject: [PATCH 61/89] menubar registration: file menu #54510 --- .../parts/editor/editor.contribution.ts | 19 ++ .../parts/menubar/menubar.contribution.ts | 258 +----------------- src/vs/workbench/electron-browser/commands.ts | 3 +- .../electron-browser/main.contribution.ts | 114 +++++++- .../extensions.contribution.ts | 15 +- .../fileActions.contribution.ts | 68 ++++- .../preferences.contribution.ts | 20 ++ .../electron-browser/configureSnippets.ts | 9 + .../electron-browser/themes.contribution.ts | 20 +- 9 files changed, 262 insertions(+), 264 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 20167466e78aa..aa087740dbfe8 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -536,3 +536,22 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorComman MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors in Group"), category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), category } }); + +// File menu +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: '1_editor', + command: { + id: ReopenClosedEditorAction.ID, + title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: 'z_clear', + command: { + id: ClearRecentFilesAction.ID, + title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") + }, + order: 1 +}); diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index fd88cbf3afe2d..6ec7221e4b052 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -7,8 +7,6 @@ import * as nls from 'vs/nls'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { isMacintosh } from 'vs/base/common/platform'; -recentMenuRegistration(); -fileMenuRegistration(); editMenuRegistration(); selectionMenuRegistration(); viewMenuRegistration(); @@ -21,171 +19,9 @@ if (isMacintosh) { windowMenuRegistration(); } -preferencesMenuRegistration(); helpMenuRegistration(); -// Menu registration - File Menu -function fileMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '1_new', - command: { - id: 'workbench.action.files.newUntitledFile', - title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '1_new', - command: { - id: 'workbench.action.newWindow', - title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.files.openFile', - title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.files.openFolder', - title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.openWorkspace', - title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), - submenu: MenuId.MenubarRecentMenu, - group: '2_open', - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '3_workspace', - command: { - id: 'workbench.action.addRootFolder', - title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '3_workspace', - command: { - id: 'workbench.action.saveWorkspaceAs', - title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.save', - title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.saveAs', - title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.saveAll', - title: nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '5_autosave', - command: { - id: 'workbench.action.toggleAutoSave', - title: nls.localize('miAutoSave', "Auto Save") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), - submenu: MenuId.MenubarPreferencesMenu, - group: '5_autosave', - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: '', - title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeActiveEditor', - title: nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeFolder', - title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeWindow', - title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") - }, - order: 4 - }); - - if (!isMacintosh) { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: 'z_Exit', - command: { - id: 'workbench.action.quit', - title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") - }, - order: 1 - }); - } -} +// Menu registration function editMenuRegistration() { MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { @@ -271,9 +107,6 @@ function editMenuRegistration() { }); - /// - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { group: '5_insert', command: { @@ -439,39 +272,6 @@ function selectionMenuRegistration() { }); } -function recentMenuRegistration() { - // Editor - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: '1_editor', - command: { - id: 'workbench.action.reopenClosedEditor', - title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor") - }, - order: 1 - }); - - // More - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: 'y_more', - command: { - id: 'workbench.action.openRecent', - title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") - }, - order: 1 - }); - - // Clear - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: 'z_clear', - command: { - id: 'workbench.action.clearRecentFiles', - title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") - }, - order: 1 - }); - -} - function viewMenuRegistration() { // Command Palette @@ -1123,62 +923,6 @@ function windowMenuRegistration() { } -function preferencesMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '1_settings', - command: { - id: 'workbench.action.openSettings2', - title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '2_keybindings', - command: { - id: 'workbench.action.openGlobalKeybindings', - title: nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '2_keybindings', - command: { - id: 'workbench.extensions.action.showRecommendedKeymapExtensions', - title: nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '3_snippets', - command: { - id: 'workbench.action.openSnippets', - title: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '4_themes', - command: { - id: 'workbench.action.selectTheme', - title: nls.localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '4_themes', - command: { - id: 'workbench.action.selectIconTheme', - title: nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme") - }, - order: 2 - }); -} - function helpMenuRegistration() { // Welcome MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 26468177c15ae..fd155abd84f88 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -32,6 +32,7 @@ function ensureDOMFocus(widget: ListWidget): void { } } +export const QUIT_ID = 'workbench.action.quit'; export function registerCommands(): void { function focusDown(accessor: ServicesAccessor, arg2?: number): void { @@ -537,7 +538,7 @@ export function registerCommands(): void { }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.quit', + id: QUIT_ID, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), handler(accessor: ServicesAccessor) { const windowsService = accessor.get(IWindowsService); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index d72216a4c8d14..a423e80844c8d 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,8 +14,8 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ShowAccessibilityOptionsAction } from 'vs/workbench/electron-browser/actions'; -import { registerCommands } from 'vs/workbench/electron-browser/commands'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ShowAccessibilityOptionsAction, OpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { registerCommands, QUIT_ID } from 'vs/workbench/electron-browser/commands'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; @@ -152,6 +152,116 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } }); +// Menu registration - file menu + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '1_new', + command: { + id: NewWindowAction.ID, + title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '2_open', + command: { + id: OpenFileAction.ID, + title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '2_open', + command: { + id: OpenFolderAction.ID, + title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '2_open', + command: { + id: OpenWorkspaceAction.ID, + title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), + submenu: MenuId.MenubarRecentMenu, + group: '2_open', + order: 4 +}); + + +// More +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: 'y_more', + command: { + id: OpenRecentAction.ID, + title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '3_workspace', + command: { + id: AddRootFolderAction.ID, + title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '3_workspace', + command: { + id: SaveWorkspaceAsAction.ID, + title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), + submenu: MenuId.MenubarPreferencesMenu, + group: '5_autosave', + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseWorkspaceAction.ID, + title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseCurrentWindowAction.ID, + title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") + }, + order: 4 +}); + +if (!isMacintosh) { + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: 'z_Exit', + command: { + id: QUIT_ID, + title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") + }, + order: 1 + }); +} + // Configuration: Workbench const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index ea4c387478535..c807845c9f173 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import * as errors from 'vs/base/common/errors'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId, PreferencesLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -236,4 +236,15 @@ CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccess if (extension.length === 1) { extensionService.open(extension[0]).done(null, errors.onUnexpectedError); } -}); \ No newline at end of file +}); + +// File menu registration + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '2_keybindings', + command: { + id: ShowRecommendedKeymapExtensionsAction.ID, + title: localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts index 9f88aa03a215f..36e761a3e9b4c 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -476,5 +476,71 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { }); // Empty Editor Group Context Menu -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.files.newUntitledFile', title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: GlobalNewUntitledFileAction.ID, title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); + +// File menu + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '1_new', + command: { + id: GlobalNewUntitledFileAction.ID, + title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SAVE_FILE_COMMAND_ID, + title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SAVE_FILE_AS_COMMAND_ID, + title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SaveAllAction.ID, + title: nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '5_autosave', + command: { + id: ToggleAutoSaveAction.ID, + title: nls.localize('miAutoSave', "Auto Save") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: REVERT_FILE_COMMAND_ID, + title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), + precondition: DirtyEditorContext + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CLOSE_EDITOR_COMMAND_ID, + title: nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index b0ea7b33c626a..b443aabf11b9a 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -493,3 +493,23 @@ const focusSettingsListCommand = new FocusSettingsListCommand({ kbOpts: { primary: KeyCode.Enter } }); KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsListCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.workbenchContrib())); + +// Preferences menu + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '1_settings', + command: { + id: OpenSettings2Action.ID, + title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '2_keybindings', + command: { + id: OpenGlobalKeybindingsAction.ID, + title: nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts index 71045971e745d..c739895403565 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts @@ -217,3 +217,12 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { category: nls.localize('preferences', "Preferences") } }); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '3_snippets', + command: { + id, + title: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 4d2291134ef95..dfa15ae450fde 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { firstIndex } from 'vs/base/common/arrays'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; @@ -228,3 +228,21 @@ const developerCategory = localize('developer', "Developer"); const generateColorThemeDescriptor = new SyncActionDescriptor(GenerateColorThemeAction, GenerateColorThemeAction.ID, GenerateColorThemeAction.LABEL); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(generateColorThemeDescriptor, 'Developer: Generate Color Theme From Current Settings', developerCategory); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '4_themes', + command: { + id: SelectColorThemeAction.ID, + title: localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '4_themes', + command: { + id: SelectIconThemeAction.ID, + title: localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme") + }, + order: 2 +}); From bd9db2df5e09d43f6b026ac63aadbaac925266e1 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 17:31:20 +0200 Subject: [PATCH 62/89] menus: move View menu contributions to appropriate owners #54510 --- .../parts/menubar/menubar.contribution.ts | 173 ++---------------- .../electron-browser/toggleMinimap.ts | 11 +- .../toggleRenderControlCharacter.ts | 11 +- .../toggleRenderWhitespace.ts | 11 +- .../electron-browser/toggleWordWrap.ts | 18 +- .../electron-browser/debug.contribution.ts | 24 ++- .../extensions.contribution.ts | 11 ++ .../electron-browser/files.contribution.ts | 12 +- .../electron-browser/markers.contribution.ts | 9 + .../electron-browser/output.contribution.ts | 9 + .../browser/quickopen.contribution.ts | 24 ++- .../scm/electron-browser/scm.contribution.ts | 13 +- .../electron-browser/search.contribution.ts | 11 ++ .../parts/terminal/common/terminalMenu.ts | 14 +- 14 files changed, 180 insertions(+), 171 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index 6ec7221e4b052..8a2c60c8ba891 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -9,7 +9,6 @@ import { isMacintosh } from 'vs/base/common/platform'; editMenuRegistration(); selectionMenuRegistration(); -viewMenuRegistration(); appearanceMenuRegistration(); layoutMenuRegistration(); goMenuRegistration(); @@ -272,163 +271,21 @@ function selectionMenuRegistration() { }); } -function viewMenuRegistration() { - - // Command Palette - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '1_open', - command: { - id: 'workbench.action.showCommands', - title: nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '1_open', - command: { - id: 'workbench.action.openView', - title: nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View...") - }, - order: 2 - }); - - // TODO: Appearance Submenu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), - submenu: MenuId.MenubarAppearanceMenu, - order: 1 - }); - - // TODO: Editor Layout Submenu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), - submenu: MenuId.MenubarLayoutMenu, - order: 2 - }); - - // Viewlets - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.explorer', - title: nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.search', - title: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.scm', - title: nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.debug', - title: nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.extensions', - title: nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions") - }, - order: 5 - }); - - // Panels - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.action.output.toggleOutput', - title: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.debug.action.toggleRepl', - title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.action.terminal.toggleTerminal', - title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.actions.view.problems', - title: nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems") - }, - order: 4 - }); - - // Toggle Editor Settings - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleWordWrap', - title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleMinimap', - title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleRenderWhitespace', - title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleRenderControlCharacters', - title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters") - }, - order: 4 - }); -} +// TODO: Appearance Submenu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), + submenu: MenuId.MenubarAppearanceMenu, + order: 1 +}); + +// TODO: Editor Layout Submenu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), + submenu: MenuId.MenubarLayoutMenu, + order: 2 +}); function appearanceMenuRegistration() { MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts index 8361e7b855bdd..36617f7b8c3d6 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleMinimapAction extends Action { public static readonly ID = 'editor.action.toggleMinimap'; @@ -32,3 +32,12 @@ export class ToggleMinimapAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMinimapAction, ToggleMinimapAction.ID, ToggleMinimapAction.LABEL), 'View: Toggle Minimap'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleMinimapAction.ID, + title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts index 7c35e71b00c4b..42c4e6bf0686f 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleRenderControlCharacterAction extends Action { @@ -33,3 +33,12 @@ export class ToggleRenderControlCharacterAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, ToggleRenderControlCharacterAction.LABEL), 'View: Toggle Control Characters'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleRenderControlCharacterAction.ID, + title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters") + }, + order: 4 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts index 856e35dce6007..d9a61a863ebd3 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleRenderWhitespaceAction extends Action { @@ -41,3 +41,12 @@ export class ToggleRenderWhitespaceAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, ToggleRenderWhitespaceAction.LABEL), 'View: Toggle Render Whitespace'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleRenderWhitespaceAction.ID, + title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace") + }, + order: 3 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts index bd18b1271c44f..882cd01e68bd4 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -130,11 +130,12 @@ function applyWordWrapState(editor: ICodeEditor, state: IWordWrapState): void { }); } +const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap'; class ToggleWordWrapAction extends EditorAction { constructor() { super({ - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, label: nls.localize('toggle.wordwrap', "View: Toggle Word Wrap"), alias: 'View: Toggle Word Wrap', precondition: null, @@ -255,7 +256,7 @@ registerEditorAction(ToggleWordWrapAction); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, title: nls.localize('unwrapMinified', "Disable wrapping for this file"), iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')) } }, @@ -269,7 +270,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, title: nls.localize('wrapMinified', "Enable wrapping for this file"), iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')) } }, @@ -281,3 +282,14 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { ContextKeyExpr.not(isWordWrapMinifiedKey) ) }); + + +// View menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: TOGGLE_WORD_WRAP_ID, + title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index fc140e687b435..a9b45955e1f74 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -30,7 +30,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager'; import { StepOverAction, ClearReplAction, FocusReplAction, StepIntoAction, StepOutAction, StartAction, RestartAction, ContinueAction, StopAction, DisconnectAction, PauseAction, AddFunctionBreakpointAction, - ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction, TerminateThreadAction + ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction, TerminateThreadAction, ToggleReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { DebugActionsWidget } from 'vs/workbench/parts/debug/browser/debugActionsWidget'; import * as service from 'vs/workbench/parts/debug/electron-browser/debugService'; @@ -228,7 +228,27 @@ registerCommands(); const statusBar = Registry.as(StatusExtensions.Statusbar); statusBar.registerStatusbarItem(new StatusbarItemDescriptor(DebugStatus, StatusbarAlignment.LEFT, 30 /* Low Priority */)); -// Register debug menu +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleReplAction.ID, + title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") + }, + order: 2 +}); + +// Debug menu MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { group: '1_debug', diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index c807845c9f173..1415dd8bcfae1 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -248,3 +248,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { }, order: 2 }); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions") + }, + order: 5 +}); diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index c152f150f4d33..9413fa3200b54 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; import * as nls from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -371,3 +371,13 @@ configurationRegistry.registerConfiguration({ }, } }); + +// View menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts index 789c79ff77b83..b9ba41a1e7aae 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts @@ -212,3 +212,12 @@ function registerAction(desc: IActionDescriptor) { }); } } + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleMarkersPanelAction.ID, + title: localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems") + }, + order: 4 +}); diff --git a/src/vs/workbench/parts/output/electron-browser/output.contribution.ts b/src/vs/workbench/parts/output/electron-browser/output.contribution.ts index 63be0b0f1c2db..4442ede409cdc 100644 --- a/src/vs/workbench/parts/output/electron-browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/electron-browser/output.contribution.ts @@ -187,3 +187,12 @@ CommandsRegistry.registerCommand(COMMAND_OPEN_LOG_VIEWER, function (accessor: Se } return null; }); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleOutputAction.ID, + title: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts index be4d637908277..5b8cb97d750d7 100644 --- a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -9,7 +9,7 @@ import * as env from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { GotoSymbolAction, GOTO_SYMBOL_PREFIX, SCOPE_PREFIX, GotoSymbolHandler } from 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler'; @@ -144,4 +144,24 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen } ] ) -); \ No newline at end of file +); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '1_open', + command: { + id: ShowAllCommandsAction.ID, + title: nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '1_open', + command: { + id: OpenViewPickerAction.ID, + title: nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View...") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts index 56943a892a055..6a37c940969b4 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts @@ -13,7 +13,7 @@ import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, To import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusUpdater, StatusBarController } from './scmActivity'; import { SCMViewlet } from 'vs/workbench/parts/scm/electron-browser/scmViewlet'; @@ -88,3 +88,14 @@ Registry.as(ConfigurationExtensions.Configuration).regis } } }); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM") + }, + order: 3 +}); diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index 6aabebef7d994..23a3349681d44 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -615,3 +615,14 @@ registerLanguageCommand('_executeWorkspaceSymbolProvider', function (accessor, a } return getWorkspaceSymbols(query); }); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEW_ID, + title: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/terminal/common/terminalMenu.ts b/src/vs/workbench/parts/terminal/common/terminalMenu.ts index ae73b0d3a7d82..52498a02f5195 100644 --- a/src/vs/workbench/parts/terminal/common/terminalMenu.ts +++ b/src/vs/workbench/parts/terminal/common/terminalMenu.ts @@ -9,6 +9,18 @@ import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminal import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export function setupTerminalMenu() { + + // View menu + + MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: TERMINAL_COMMAND_ID.TOGGLE, + title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal") + }, + order: 3 + }); + // Manage const manageGroup = '1_manage'; MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { @@ -105,4 +117,4 @@ export function setupTerminalMenu() { }, order: 4 }); -} \ No newline at end of file +} From 1141a5b8cd047fcead0643b8c46c4322d9747c5d Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 17:41:59 +0200 Subject: [PATCH 63/89] menubar registration: appearance menu to appropriate owners #54510 --- .../actions/toggleActivityBarVisibility.ts | 13 +- .../browser/actions/toggleCenteredLayout.ts | 11 +- .../browser/actions/toggleSidebarPosition.ts | 11 +- .../actions/toggleSidebarVisibility.ts | 13 +- .../actions/toggleStatusbarVisibility.ts | 13 +- .../browser/actions/toggleZenMode.ts | 13 +- .../parts/menubar/menubar.contribution.ts | 119 ------------------ .../browser/parts/panel/panelActions.ts | 11 +- .../electron-browser/main.contribution.ts | 56 +++++++++ 9 files changed, 130 insertions(+), 130 deletions(-) diff --git a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts index bb784e8947286..9f504717d7349 100644 --- a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @@ -40,4 +40,13 @@ export class ToggleActivityBarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleActivityBarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") + }, + order: 4 +}); diff --git a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts index 2ab19d4ca4ddc..b42d87db28974 100644 --- a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts +++ b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -34,3 +34,12 @@ class ToggleCenteredLayout extends Action { const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleCenteredLayout.ID, + title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") + }, + order: 3 +}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts index 3de25a53e7ea7..3c64a5a1c6dc7 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService, Position } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -41,3 +41,12 @@ export class ToggleSidebarPositionAction extends Action { const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarPositionAction.ID, + title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts index 95f4d8a21e862..e69151cac2b0e 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; @@ -35,4 +35,13 @@ export class ToggleSidebarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") + }, + order: 1 +}); diff --git a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts index 2fe83369bb881..2ec27e5f76576 100644 --- a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @@ -40,4 +40,13 @@ export class ToggleStatusbarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleStatusbarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") + }, + order: 3 +}); diff --git a/src/vs/workbench/browser/actions/toggleZenMode.ts b/src/vs/workbench/browser/actions/toggleZenMode.ts index 955a69799ceda..a13035026da87 100644 --- a/src/vs/workbench/browser/actions/toggleZenMode.ts +++ b/src/vs/workbench/browser/actions/toggleZenMode.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -33,4 +33,13 @@ class ToggleZenMode extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleZenMode.ID, + title: nls.localize('miToggleZenMode', "Toggle Zen Mode") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index 8a2c60c8ba891..f66436c9a569a 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -9,7 +9,6 @@ import { isMacintosh } from 'vs/base/common/platform'; editMenuRegistration(); selectionMenuRegistration(); -appearanceMenuRegistration(); layoutMenuRegistration(); goMenuRegistration(); tasksMenuRegistration(); @@ -271,14 +270,6 @@ function selectionMenuRegistration() { }); } -// TODO: Appearance Submenu -MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), - submenu: MenuId.MenubarAppearanceMenu, - order: 1 -}); - // TODO: Editor Layout Submenu MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '2_appearance', @@ -287,116 +278,6 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { order: 2 }); -function appearanceMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleFullScreen', - title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleZenMode', - title: nls.localize('miToggleZenMode', "Toggle Zen Mode") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleCenteredLayout', - title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleMenuBar', - title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleSidebarVisibility', - title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleSidebarPosition', - title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleStatusbarVisibility', - title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleActivityBarVisibility', - title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.togglePanel', - title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel") - }, - order: 5 - }); - - // Zoom - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomIn', - title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomOut', - title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomReset', - title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom") - }, - order: 3 - }); -} function layoutMenuRegistration() { // Split diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 654186a1c39cf..d3933cb7b9c78 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -10,7 +10,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Action } from 'vs/base/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; @@ -177,3 +177,12 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedP actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: TogglePanelAction.ID, + title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel") + }, + order: 5 +}); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index a423e80844c8d..847b5e6972fb3 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -262,6 +262,62 @@ if (!isMacintosh) { }); } +// Appereance menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), + submenu: MenuId.MenubarAppearanceMenu, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleFullScreenAction.ID, + title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleMenuBarAction.ID, + title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") + }, + order: 4 +}); + +// Zoom + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomInAction.ID, + title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomOutAction.ID, + title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomResetAction.ID, + title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom") + }, + order: 3 +}); + + // Configuration: Workbench const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); From 8e42c9741f15439bece7a68e2e5076dcc7c424f5 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 19 Jul 2018 08:46:28 -0700 Subject: [PATCH 64/89] Fix autosuggest trigger chars not working in simpleWidgets --- src/vs/editor/browser/widget/codeEditorWidget.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index fcccb8daffcf2..387e34f62d12a 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1356,22 +1356,22 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this.isSimpleWidget) { commandDelegate = { paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[]) => { - this.cursor.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); + this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); }, type: (source: string, text: string) => { - this.cursor.trigger(source, editorCommon.Handler.Type, { text }); + this.trigger(source, editorCommon.Handler.Type, { text }); }, replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this.cursor.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); + this.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); }, compositionStart: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.CompositionStart, undefined); + this.trigger(source, editorCommon.Handler.CompositionStart, undefined); }, compositionEnd: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.CompositionEnd, undefined); + this.trigger(source, editorCommon.Handler.CompositionEnd, undefined); }, cut: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.Cut, undefined); + this.trigger(source, editorCommon.Handler.Cut, undefined); } }; } else { From 94615282473aec8cfca12d6bd3a7ec3fc83bce69 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 19 Jul 2018 18:03:23 +0200 Subject: [PATCH 65/89] menubar registration: layout menu to appropriate owners #54510 --- .../browser/actions/toggleCenteredLayout.ts | 9 ++ .../browser/actions/toggleEditorLayout.ts | 13 +- .../parts/editor/editor.contribution.ts | 116 ++++++++++++++ .../parts/menubar/menubar.contribution.ts | 141 ------------------ 4 files changed, 136 insertions(+), 143 deletions(-) diff --git a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts index b42d87db28974..4be449f24acb3 100644 --- a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts +++ b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts @@ -43,3 +43,12 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { }, order: 3 }); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: 'workbench.action.editorLayoutCentered', + title: nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/toggleEditorLayout.ts b/src/vs/workbench/browser/actions/toggleEditorLayout.ts index 8f553a8fa22d7..32b9cc1c92139 100644 --- a/src/vs/workbench/browser/actions/toggleEditorLayout.ts +++ b/src/vs/workbench/browser/actions/toggleEditorLayout.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -73,4 +73,13 @@ CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', functi const registry = Registry.as(Extensions.WorkbenchActions); const group = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: 'z_flip', + command: { + id: ToggleEditorLayoutAction.ID, + title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") + }, + order: 1 +}); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index aa087740dbfe8..bd435ff853fa8 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -555,3 +555,119 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { }, order: 1 }); + +// Layout menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), + submenu: MenuId.MenubarLayoutMenu, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_UP, + title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_DOWN, + title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_LEFT, + title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_RIGHT, + title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutSingleAction.ID, + title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoColumnsAction.ID, + title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutThreeColumnsAction.ID, + title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoRowsAction.ID, + title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") + }, + order: 5 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutThreeRowsAction.ID, + title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") + }, + order: 6 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoByTwoGridAction.ID, + title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") + }, + order: 7 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoRowsRightAction.ID, + title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") + }, + order: 8 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoColumnsBottomAction.ID, + title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") + }, + order: 9 +}); diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index f66436c9a569a..3dbe2a29b129d 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -9,7 +9,6 @@ import { isMacintosh } from 'vs/base/common/platform'; editMenuRegistration(); selectionMenuRegistration(); -layoutMenuRegistration(); goMenuRegistration(); tasksMenuRegistration(); @@ -270,146 +269,6 @@ function selectionMenuRegistration() { }); } -// TODO: Editor Layout Submenu -MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), - submenu: MenuId.MenubarLayoutMenu, - order: 2 -}); - - -function layoutMenuRegistration() { - // Split - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorUp', - title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorDown', - title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorLeft', - title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorRight', - title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") - }, - order: 4 - }); - - // Layouts - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutSingle', - title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutCentered', - title: nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoColumns', - title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutThreeColumns', - title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoRows', - title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") - }, - order: 5 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutThreeRows', - title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") - }, - order: 6 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoByTwoGrid', - title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") - }, - order: 7 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoRowsRight', - title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") - }, - order: 8 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoColumnsBottom', - title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") - }, - order: 9 - }); - - // Flip - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: 'z_flip', - command: { - id: 'workbench.action.toggleEditorGroupLayout', - title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") - }, - order: 1 - }); - -} function goMenuRegistration() { // Forward/Back From 7b4576411523cb4d4c21205d0965e194e174bd00 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 19 Jul 2018 09:53:12 -0700 Subject: [PATCH 66/89] Workaround an issue where the terminal could overflow into right sidebar Fixes #54230 --- .../parts/terminal/electron-browser/media/terminal.css | 1 + .../parts/terminal/electron-browser/terminalInstance.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css index 42d4a6496dbeb..c5da665535ef6 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css +++ b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css @@ -17,6 +17,7 @@ height: 100%; width: 100%; box-sizing: border-box; + overflow: hidden; } .monaco-workbench .panel.integrated-terminal .terminal-tab { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 7d1a41951b3dc..6b98f36479739 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -665,6 +665,10 @@ export class TerminalInstance implements ITerminalInstance { const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); this.layout(new dom.Dimension(width, height)); + // HACK: Trigger another async layout to ensure xterm's CharMeasure is ready to use, + // this hack can be removed when https://github.com/xtermjs/xterm.js/issues/702 is + // supported. + setTimeout(() => this.layout(new dom.Dimension(width, height)), 0); } } } From 76ced509142ae88cc05e0c93205e29ba6303c2e9 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Jul 2018 17:00:12 -0700 Subject: [PATCH 67/89] Settings editor - remove leftover reset button --- src/vs/workbench/parts/preferences/browser/settingsTree.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index d1e85402424eb..ba7e353768585 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -627,7 +627,6 @@ export class SettingsRenderer implements IRenderer { const valueElement = DOM.append(container, $('.setting-item-value')); const controlElement = DOM.append(valueElement, $('div.setting-item-control')); - const resetButtonElement = DOM.append(valueElement, $('.reset-button-container')); const toDispose = []; const template: ISettingItemTemplate = { @@ -644,7 +643,6 @@ export class SettingsRenderer implements IRenderer { // Prevent clicks from being handled by list toDispose.push(DOM.addDisposableListener(controlElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); - toDispose.push(DOM.addDisposableListener(resetButtonElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); toDispose.push(DOM.addStandardDisposableListener(valueElement, 'keydown', (e: StandardKeyboardEvent) => { if (e.keyCode === KeyCode.Escape) { From 8b09ee78b43d930cee09fa2efd2009b9434a7cea Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Jul 2018 09:55:59 -0700 Subject: [PATCH 68/89] Bump node2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 1a2d3b971f604..670f09e16625e 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -6,7 +6,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.26.3", + "version": "1.26.4", "repo": "https://github.com/Microsoft/vscode-node-debug2" } ] From 7db5f9607511871220790e9226e6464ba32f6f53 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 19 Jul 2018 11:03:15 -0700 Subject: [PATCH 69/89] Down arrow moves focus to extensions list (#53616) * Down arrow focuses list view. * Scoping * Generalize focusFirstIfNothingFocused logic to all list views * Revert "Generalize focusFirstIfNothingFocused logic to all list views" This reverts commit 2bdfeef828c9b427c0897769ec45f315e19bbb65. * Generalize to list in debug view * Add to dispasables * Same for scmViewlet * Default 'list.inactiveFocusBackground' to null * Migrate from `onDidFocus` to `onDidChange` where possible * Remove changes for non-extensions * Undo changed to color registry * Move focus next position --- src/vs/platform/theme/common/colorRegistry.ts | 2 +- .../extensions/electron-browser/extensionsViewlet.ts | 5 +++++ .../parts/extensions/electron-browser/extensionsViews.ts | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 674a52e34c766..63787c741a809 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -197,7 +197,7 @@ export const listActiveSelectionBackground = registerColor('list.activeSelection export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#3F3F46', light: '#CCCEDB', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); -export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: '#313135', light: '#d8dae6', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: '#313135', light: '#d8dae6', hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 507bf50decdb0..4a3e0cd98288e 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -320,6 +320,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio const onKeyDownForList = onKeyDown.filter(() => this.count() > 0); onKeyDownForList.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables); + onKeyDownForList.filter(e => e.keyCode === KeyCode.DownArrow).on(this.focusListView, this, this.disposables); const onSearchInput = domEvent(this.searchBox, 'input') as EventOf; onSearchInput(e => this.triggerSearch(e.immediate), null, this.disposables); @@ -476,6 +477,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio (this.panels[0]).select(); } + private focusListView(): void { + this.panels[0].focus(); + } + private onViewletOpen(viewlet: IViewlet): void { if (!viewlet || viewlet.getId() === VIEWLET_ID) { return; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 6e18d8391ed41..02cd5b25c9fd5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -640,6 +640,14 @@ export class ExtensionsListView extends ViewletPanel { static isKeymapsRecommendedExtensionsQuery(query: string): boolean { return /@recommended:keymaps/i.test(query); } + + focus(): void { + super.focus(); + if (!(this.list.getFocus().length || this.list.getSelection().length)) { + this.list.focusNext(); + } + this.list.domFocus(); + } } export class InstalledExtensionsView extends ExtensionsListView { From a0f07e30b9f9ef7770872f317e647910cd398e02 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 19 Jul 2018 11:41:47 -0700 Subject: [PATCH 70/89] Autocomplete for extension search @-operators (#53915) * WIP * WIP again * Feature complete, refactor to Query class * Add tests * Spacing * Add tests and refactor * Maybe fix tests? Cant run locally. * Use monaco editor for completions * Remove escape handler * Update coloring * Add localizations, remove unused * Fix spacing * update serach ordering * Remove enter handling * Fix tab handling * Improve autosuggest enablment condition * Conditional styling of cursor --- src/vs/platform/theme/common/colorRegistry.ts | 2 +- .../parts/extensions/common/extensionQuery.ts | 15 ++ .../electron-browser/extensionsViewlet.ts | 168 +++++++++++++----- .../media/extensionsViewlet.css | 26 +++ 4 files changed, 166 insertions(+), 45 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 63787c741a809..5be3b6ef6a1e3 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -177,7 +177,7 @@ export const inputBackground = registerColor('input.background', { dark: '#3C3C3 export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); export const inputBorder = registerColor('input.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('inputBoxBorder', "Input box border.")); export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC', light: '#007ACC', hc: activeContrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields.")); -export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { dark: null, light: null, hc: null }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); +export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { light: transparent(foreground, 0.5), dark: transparent(foreground, 0.5), hc: transparent(foreground, 0.7) }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); export const inputValidationInfoBackground = registerColor('inputValidation.infoBackground', { dark: '#063B49', light: '#D6ECF2', hc: Color.black }, nls.localize('inputValidationInfoBackground', "Input validation background color for information severity.")); export const inputValidationInfoBorder = registerColor('inputValidation.infoBorder', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('inputValidationInfoBorder', "Input validation border color for information severity.")); diff --git a/src/vs/workbench/parts/extensions/common/extensionQuery.ts b/src/vs/workbench/parts/extensions/common/extensionQuery.ts index 8fb85a6d48e87..4a3cc885ec5b1 100644 --- a/src/vs/workbench/parts/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/parts/extensions/common/extensionQuery.ts @@ -3,12 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { flatten } from 'vs/base/common/arrays'; + export class Query { constructor(public value: string, public sortBy: string, public groupBy: string) { this.value = value.trim(); } + static autocompletions(): string[] { + const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'sort', 'category', 'tag', 'ext']; + const subcommands = { + 'sort': ['installs', 'rating', 'name'], + 'category': ['"programming languages"', 'snippets', 'linters', 'themes', 'debuggers', 'formatters', 'keymaps', '"scm providers"', 'other', '"extension packs"', '"language packs"'], + 'tag': [''], + 'ext': [''] + }; + + return flatten(commands.map(command => subcommands[command] ? subcommands[command].map(subcommand => `${command}:${subcommand}`) : [command])); + } + static parse(value: string): Query { let sortBy = ''; value = value.replace(/@sort:(\w+)(-\w*)?/g, (match, by: string, order: string) => { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 4a3e0cd98288e..0c222ed08c5dc 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -6,21 +6,21 @@ 'use strict'; import 'vs/css!./media/extensionsViewlet'; +import uri from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ThrottledDelayer, always } from 'vs/base/common/async'; import { TPromise } from 'vs/base/common/winjs.base'; import { isPromiseCanceledError, onUnexpectedError, create as createError } from 'vs/base/common/errors'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Event as EventOf, mapEvent, chain } from 'vs/base/common/event'; +import { Event as EventOf, Emitter, chain } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; -import { domEvent } from 'vs/base/browser/event'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { append, $, addStandardDisposableListener, EventType, addClass, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; +import { append, $, addClass, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -39,7 +39,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import Severity from 'vs/base/common/severity'; import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry'; +import { inputForeground, inputBackground, inputBorder, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views'; import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -58,6 +58,13 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { SingleServerExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; +import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { ITextModel } from 'vs/editor/common/model'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -252,12 +259,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private searchDelayer: ThrottledDelayer; private root: HTMLElement; - private searchBox: HTMLInputElement; + private searchBox: CodeEditorWidget; private extensionsBox: HTMLElement; private primaryActions: IAction[]; private secondaryActions: IAction[]; private groupByServerAction: IAction; private disposables: IDisposable[] = []; + private monacoStyleContainer: HTMLDivElement; + private placeholderText: HTMLDivElement; constructor( @IPartService partService: IPartService, @@ -275,7 +284,8 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IModelService private modelService: IModelService, ) { super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); @@ -299,6 +309,28 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)); } }, this, this.disposables); + + modes.SuggestRegistry.register({ scheme: 'extensions', pattern: '**/searchinput', hasAccessToAllModels: true }, { + triggerCharacters: ['@'], + provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext) => { + const sortKey = (item: string) => { + if (item.indexOf(':') === -1) { return 'a'; } + else if (/ext:/.test(item) || /tag:/.test(item)) { return 'b'; } + else if (/sort:/.test(item)) { return 'c'; } + else { return 'd'; } + }; + return { + suggestions: this.autoComplete(model.getValue(), position.column).map(item => ( + { + label: item.fullText, + insertText: item.fullText, + overwriteBefore: item.overwrite, + sortText: sortKey(item.fullText), + type: 'keyword' + })) + }; + } + }); } create(parent: HTMLElement): TPromise { @@ -306,32 +338,36 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.root = parent; const header = append(this.root, $('.header')); - - this.searchBox = append(header, $('input.search-box')); - this.searchBox.placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); - this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.FOCUS, () => addClass(this.searchBox, 'synthetic-focus'))); - this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.BLUR, () => removeClass(this.searchBox, 'synthetic-focus'))); + this.monacoStyleContainer = append(header, $('.monaco-container')); + this.searchBox = this.instantiationService.createInstance(CodeEditorWidget, this.monacoStyleContainer, SEARCH_INPUT_OPTIONS, { isSimpleWidget: true }); + this.placeholderText = append(this.monacoStyleContainer, $('.search-placeholder', null, localize('searchExtensions', "Search Extensions in Marketplace"))); this.extensionsBox = append(this.root, $('.extensions')); - const onKeyDown = chain(domEvent(this.searchBox, 'keydown')) - .map(e => new StandardKeyboardEvent(e)); - onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables); + this.searchBox.setModel(this.modelService.createModel('', null, uri.parse('extensions:searchinput'), true)); - const onKeyDownForList = onKeyDown.filter(() => this.count() > 0); - onKeyDownForList.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables); - onKeyDownForList.filter(e => e.keyCode === KeyCode.DownArrow).on(this.focusListView, this, this.disposables); + this.disposables.push(this.searchBox.onDidFocusEditorText(() => addClass(this.monacoStyleContainer, 'synthetic-focus'))); + this.disposables.push(this.searchBox.onDidBlurEditorText(() => removeClass(this.monacoStyleContainer, 'synthetic-focus'))); - const onSearchInput = domEvent(this.searchBox, 'input') as EventOf; - onSearchInput(e => this.triggerSearch(e.immediate), null, this.disposables); + const onKeyDownMonaco = chain(this.searchBox.onKeyDown); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.Enter).on(e => e.preventDefault(), this, this.disposables); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow).on(() => this.focusListView(), this, this.disposables); - this.onSearchChange = mapEvent(onSearchInput, e => e.target.value); + const searchChangeEvent = new Emitter(); + this.onSearchChange = searchChangeEvent.event; + + this.disposables.push(this.searchBox.getModel().onDidChangeContent(() => { + this.triggerSearch(); + const content = this.searchBox.getValue(); + searchChangeEvent.fire(content); + this.placeholderText.style.visibility = content ? 'hidden' : 'visible'; + })); return super.create(this.extensionsBox) .then(() => this.extensionManagementService.getInstalled(LocalExtensionType.User)) .then(installed => { if (installed.length === 0) { - this.searchBox.value = '@sort:installs'; + this.searchBox.setValue('@sort:installs'); this.searchExtensionsContextKey.set(true); } }); @@ -340,13 +376,19 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio public updateStyles(): void { super.updateStyles(); - this.searchBox.style.backgroundColor = this.getColor(inputBackground); - this.searchBox.style.color = this.getColor(inputForeground); + this.monacoStyleContainer.style.backgroundColor = this.getColor(inputBackground); + this.monacoStyleContainer.style.color = this.getColor(inputForeground); + this.placeholderText.style.color = this.getColor(inputPlaceholderForeground); const inputBorderColor = this.getColor(inputBorder); - this.searchBox.style.borderWidth = inputBorderColor ? '1px' : null; - this.searchBox.style.borderStyle = inputBorderColor ? 'solid' : null; - this.searchBox.style.borderColor = inputBorderColor; + this.monacoStyleContainer.style.borderWidth = inputBorderColor ? '1px' : null; + this.monacoStyleContainer.style.borderStyle = inputBorderColor ? 'solid' : null; + this.monacoStyleContainer.style.borderColor = inputBorderColor; + + let cursor = this.monacoStyleContainer.getElementsByClassName('cursor')[0] as HTMLDivElement; + if (cursor) { + cursor.style.backgroundColor = this.getColor(inputForeground); + } } setVisible(visible: boolean): TPromise { @@ -355,7 +397,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio if (isVisibilityChanged) { if (visible) { this.searchBox.focus(); - this.searchBox.setSelectionRange(0, this.searchBox.value.length); + this.searchBox.setSelection(new Range(1, 1, 1, this.searchBox.getValue().length + 1)); } } }); @@ -367,6 +409,9 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio layout(dimension: Dimension): void { toggleClass(this.root, 'narrow', dimension.width <= 300); + this.searchBox.layout({ height: 20, width: dimension.width - 30 }); + this.placeholderText.style.width = '' + (dimension.width - 30) + 'px'; + super.layout(new Dimension(dimension.width, dimension.height - 38)); } @@ -421,17 +466,19 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio const event = new Event('input', { bubbles: true }) as SearchInputEvent; event.immediate = true; - this.searchBox.value = value; - this.searchBox.dispatchEvent(event); + this.searchBox.setValue(value); } private triggerSearch(immediate = false): void { - this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.value ? 0 : 500) - .done(null, err => this.onError(err)); + this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.getValue() ? 0 : 500).done(null, err => this.onError(err)); + } + + private normalizedQuery(): string { + return (this.searchBox.getValue() || '').replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:'); } private doSearch(): TPromise { - const value = this.searchBox.value || ''; + const value = this.normalizedQuery(); this.searchExtensionsContextKey.set(!!value); this.searchInstalledExtensionsContextKey.set(InstalledExtensionsView.isInstalledExtensionsQuery(value)); this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value)); @@ -440,14 +487,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY); if (value) { - return this.progress(TPromise.join(this.panels.map(view => (view).show(this.searchBox.value)))); + return this.progress(TPromise.join(this.panels.map(view => (view).show(this.normalizedQuery())))); } return TPromise.as(null); } protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { const addedViews = super.onDidAddViews(added); - this.progress(TPromise.join(addedViews.map(addedView => (addedView).show(this.searchBox.value)))); + this.progress(TPromise.join(addedViews.map(addedView => (addedView).show(this.normalizedQuery())))); return addedViews; } @@ -465,20 +512,23 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel; } - private count(): number { - return this.panels.reduce((count, view) => (view).count() + count, 0); - } + private autoComplete(query: string, position: number): { fullText: string, overwrite: number }[] { + if (query.lastIndexOf('@', position - 1) !== query.lastIndexOf(' ', position - 1) + 1) { return []; } + + let wordStart = query.lastIndexOf('@', position - 1) + 1; + let alreadyTypedCount = position - wordStart - 1; - private onEscape(): void { - this.search(''); + return Query.autocompletions().map(replacement => ({ fullText: replacement, overwrite: alreadyTypedCount })); } - private onEnter(): void { - (this.panels[0]).select(); + private count(): number { + return this.panels.reduce((count, view) => (view).count() + count, 0); } private focusListView(): void { - this.panels[0].focus(); + if (this.count() > 0) { + this.panels[0].focus(); + } } private onViewletOpen(viewlet: IViewlet): void { @@ -612,4 +662,34 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { dispose(): void { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} + +let SEARCH_INPUT_OPTIONS: IEditorOptions = +{ + fontSize: 13, + lineHeight: 22, + wordWrap: 'off', + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + selectOnLineNumbers: false, + hideCursorInOverviewRuler: true, + selectionHighlight: false, + scrollbar: { + horizontal: 'hidden', + vertical: 'hidden' + }, + ariaLabel: localize('searchExtensions', "Search Extensions in Marketplace"), + cursorWidth: 1, + lineDecorationsWidth: 0, + overviewRulerBorder: false, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: 'smart', + minimap: { + enabled: false + }, + fontFamily: ' -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif' +}; diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css index a83f6a0c066d4..98391d7599edf 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -197,6 +197,32 @@ opacity: 0.9; } +.extensions-viewlet .header .monaco-container { + padding: 3px 4px 5px; +} + +.extensions-viewlet .header .monaco-container .suggest-widget { + width: 275px; +} + +.extensions-viewlet .header .monaco-container .monaco-editor-background, +.extensions-viewlet .header .monaco-container .monaco-editor, +.extensions-viewlet .header .monaco-container .mtk1 { + /* allow the embedded monaco to be styled from the outer context */ + background-color: inherit; + color: inherit; +} + +.extensions-viewlet .header .search-placeholder { + position: absolute; + z-index: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + pointer-events: none; + margin-top: 2px; +} + .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon, From 0147a1bfb1e33fb49568af80f0b7672206bdda39 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Jul 2018 15:17:43 -0700 Subject: [PATCH 71/89] Settings editor - show enumDescriptions inline when there are more than 10. This specifically targets the files.encoding setting. See #53911 --- .../parts/preferences/browser/settingsTree.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index ba7e353768585..025c9e9e97b20 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -483,6 +483,7 @@ export class SettingsRenderer implements IRenderer { private static readonly SETTING_ROW_HEIGHT = 98; private static readonly SETTING_BOOL_ROW_HEIGHT = 65; + public static readonly MAX_ENUM_DESCRIPTIONS = 10; private readonly _onDidChangeSetting: Emitter = new Emitter(); public readonly onDidChangeSetting: Event = this._onDidChangeSetting.event; @@ -846,7 +847,7 @@ export class SettingsRenderer implements IRenderer { template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - const enumDescriptionText = element.setting.enumDescriptions ? + const enumDescriptionText = element.setting.enumDescriptions && element.setting.enum && element.setting.enum.length < SettingsRenderer.MAX_ENUM_DESCRIPTIONS ? '\n' + element.setting.enumDescriptions .map((desc, i) => ` - \`${element.setting.enum[i]}\`: ${desc}`) .join('\n') : @@ -911,7 +912,7 @@ export class SettingsRenderer implements IRenderer { } private renderEnum(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingEnumItemTemplate, onChange: (value: string) => void): void { - const displayOptions = dataElement.setting.enum.map(escapeInvisibleChars); + const displayOptions = getDisplayEnumOptions(dataElement.setting); template.selectBox.setOptions(displayOptions); const label = dataElement.displayCategory + ' ' + dataElement.displayLabel; @@ -955,6 +956,20 @@ export class SettingsRenderer implements IRenderer { } } +function getDisplayEnumOptions(setting: ISetting): string[] { + if (setting.enum.length > SettingsRenderer.MAX_ENUM_DESCRIPTIONS && setting.enumDescriptions) { + return setting.enum + .map(escapeInvisibleChars) + .map((value, i) => { + return setting.enumDescriptions[i] ? + `${value}: ${setting.enumDescriptions[i]}` : + value; + }); + } + + return setting.enum.map(escapeInvisibleChars); +} + function escapeInvisibleChars(enumValue: string): string { return enumValue && enumValue .replace(/\n/g, '\\n') From 26e5a55cd8452477bfb7a472ea9475761600adf0 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 19 Jul 2018 16:16:06 -0700 Subject: [PATCH 72/89] Sbatten/menu font size (#54695) * increase menu font size * updating padding to keep menus compact --- src/vs/base/browser/ui/menu/menu.css | 16 ++++++++-------- .../browser/parts/menubar/media/menubarpart.css | 1 - .../workbench/electron-browser/media/shell.css | 8 ++++++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index 80e83e1c4a078..76f2599c526cf 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -40,15 +40,18 @@ flex: 1 1 auto; display: -ms-flexbox; display: flex; + height: 2.6em; + align-items: center; } .monaco-menu .monaco-action-bar.vertical .action-label { -ms-flex: 1 1 auto; flex: 1 1 auto; text-decoration: none; - padding: 0.8em 1em; - line-height: 1.1em; + padding: 0 1em; background: none; + font-size: inherit; + line-height: 1; } .monaco-menu .monaco-action-bar.vertical .keybinding, @@ -56,15 +59,12 @@ display: inline-block; -ms-flex: 2 1 auto; flex: 2 1 auto; - padding: 0.8em 1em; - line-height: 1.1em; - font-size: 12px; + padding: 0 1em; text-align: right; + font-size: inherit; + line-height: 1; } -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - padding: 0.8em .5em; -} .monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, .monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { diff --git a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css index 5c2bd38576fbe..5b52a860a939f 100644 --- a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css +++ b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css @@ -6,7 +6,6 @@ .monaco-workbench > .part.menubar { display: flex; position: absolute; - font-size: 12px; box-sizing: border-box; padding-left: 35px; padding-right: 138px; diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index 1f6a5fbdb7587..4911010c77c34 100644 --- a/src/vs/workbench/electron-browser/media/shell.css +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -69,9 +69,13 @@ padding: .5em 0; } +.monaco-shell .monaco-menu .monaco-action-bar.vertical .action-menu-item { + height: 1.8em; +} + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), .monaco-shell .monaco-menu .monaco-action-bar.vertical .keybinding { - padding: 0.5em 2em; + padding: 0 1.5em; } .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label.separator { @@ -80,7 +84,7 @@ } .monaco-shell .monaco-menu .monaco-action-bar.vertical .submenu-indicator { - padding: 0.5em 1em; + padding: 0 1em; } .monaco-shell .monaco-menu .action-item { From 80b08b4c7f50738fac66168354b653fb57d6be06 Mon Sep 17 00:00:00 2001 From: Erich Gamma Date: Fri, 20 Jul 2018 00:05:50 +0200 Subject: [PATCH 73/89] Add code lenses to run/debug a script --- extensions/npm/package.json | 11 ----- extensions/npm/package.nls.json | 3 +- extensions/npm/src/lenses.ts | 73 +++++++++++++++++++++++++++++++ extensions/npm/src/main.ts | 44 +++++++------------ extensions/npm/src/npmView.ts | 52 +++------------------- extensions/npm/src/tasks.ts | 77 +++++++++++++++++++++++++-------- 6 files changed, 156 insertions(+), 104 deletions(-) create mode 100644 extensions/npm/src/lenses.ts diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 7856f52a62cd8..8f1b193197bfc 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -59,10 +59,6 @@ "dark": "resources/dark/continue.svg" } }, - { - "command": "npm.runScriptFromSource", - "title": "%command.runScriptFromSource%" - }, { "command": "npm.debugScript", "title": "%command.debug%", @@ -122,13 +118,6 @@ "group": "navigation" } ], - "editor/context": [ - { - "command": "npm.runScriptFromSource", - "when": "resourceFilename == 'package.json'", - "group": "navigation@+1" - } - ], "view/item/context": [ { "command": "npm.openScript", diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 70e8880002c4f..92665d5f65a56 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -15,6 +15,5 @@ "command.run": "Run", "command.debug": "Debug", "command.openScript": "Open", - "command.runInstall": "Run Install", - "command.runScriptFromSource": "Run Script" + "command.runInstall": "Run Install" } diff --git a/extensions/npm/src/lenses.ts b/extensions/npm/src/lenses.ts new file mode 100644 index 0000000000000..6276cc1e10e9e --- /dev/null +++ b/extensions/npm/src/lenses.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { + ExtensionContext, CodeLensProvider, TextDocument, commands, ProviderResult, CodeLens, CancellationToken, + workspace, tasks, Range, Command +} from 'vscode'; +import { + createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript +} from './tasks'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export class NpmLenseProvider implements CodeLensProvider { + private extensionContext: ExtensionContext; + + constructor(context: ExtensionContext) { + const subscriptions = context.subscriptions; + this.extensionContext = context; + context.subscriptions.push(commands.registerCommand('npm.runScriptFromLense', this.runScriptFromLense, this)); + context.subscriptions.push(commands.registerCommand('npm.debugScriptFromLense', this.debugScriptFromLense, this)); + } + + public provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult { + let result = findAllScriptRanges(document.getText()); + let lenses: CodeLens[] = []; + + result.forEach((value, key) => { + let start = document.positionAt(value[0]); + let end = document.positionAt(value[0] + value[1]); + let lens: CodeLens; + let command: Command = { + command: 'npm.runScriptFromLense', + title: localize('run', "Run"), + arguments: [document, key] + }; + lens = new CodeLens(new Range(start, end), command); + lenses.push(lens); + let debugArgs = extractDebugArgFromScript(value[2]); + if (debugArgs) { + command = { + command: 'npm.debugScriptFromLense', + title: localize('debug', "Debug"), + arguments: [document, key, debugArgs[0], debugArgs[1]] + }; + lens = new CodeLens(new Range(start, end), command); + lenses.push(lens); + } + }); + return lenses; + } + + public runScriptFromLense(document: TextDocument, script: string) { + let uri = document.uri; + let folder = workspace.getWorkspaceFolder(uri); + if (folder) { + let task = createTask(script, `run ${script}`, folder, uri); + tasks.executeTask(task); + } + } + + public debugScriptFromLense(document: TextDocument, script: string, protocol: string, port: number) { + let uri = document.uri; + let folder = workspace.getWorkspaceFolder(uri); + if (folder) { + startDebugging(script, protocol, port, folder); + } + } +} diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 66c6cad1c2866..4cd70568192d1 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -9,17 +9,17 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { provideNpmScripts, invalidateScriptsCache, findScriptAtPosition, createTask } from './tasks'; +import { provideNpmScripts, invalidateScriptsCache } from './tasks'; -import * as nls from 'vscode-nls'; +import { NpmLenseProvider } from './lenses'; let taskProvider: vscode.Disposable | undefined; -const localize = nls.loadMessageBundle(); - export async function activate(context: vscode.ExtensionContext): Promise { taskProvider = registerTaskProvider(context); const treeDataProvider = registerExplorer(context); + registerLenseProvider(context); + configureHttpRequest(); vscode.workspace.onDidChangeConfiguration((e) => { configureHttpRequest(); @@ -36,7 +36,6 @@ export async function activate(context: vscode.ExtensionContext): Promise } }); context.subscriptions.push(addJSONProviders(httpRequest.xhr)); - context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromSource', runScriptFromSource)); } function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined { @@ -70,34 +69,23 @@ function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataP return undefined; } +function registerLenseProvider(context: vscode.ExtensionContext) { + if (vscode.workspace.workspaceFolders) { + let npmSelector: vscode.DocumentSelector = { + language: 'json', + scheme: 'file', + pattern: '**/package.json' + }; + let provider = new NpmLenseProvider(context); + context.subscriptions.push(vscode.languages.registerCodeLensProvider(npmSelector, provider)); + } +} + function configureHttpRequest() { const httpSettings = vscode.workspace.getConfiguration('http'); httpRequest.configure(httpSettings.get('proxy', ''), httpSettings.get('proxyStrictSSL', true)); } -async function runScriptFromSource() { - let editor = vscode.window.activeTextEditor; - if (!editor) { - return; - } - let document = editor.document; - let contents = document.getText(); - let selection = editor.selection; - let offset = document.offsetAt(selection.anchor); - let script = findScriptAtPosition(contents, offset); - if (script) { - let uri = document.uri; - let folder = vscode.workspace.getWorkspaceFolder(uri); - if (folder) { - let task = createTask(script, `run ${script}`, folder, uri); - vscode.tasks.executeTask(task); - } - } else { - let message = localize('noScriptFound', 'Could not find a script at the selection.'); - vscode.window.showErrorMessage(message); - } -} - export function deactivate(): void { if (taskProvider) { taskProvider.dispose(); diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 2bb290876a891..f737df23e1b56 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -6,14 +6,14 @@ import * as path from 'path'; import { - DebugConfiguration, Event, EventEmitter, ExtensionContext, Task, + Event, EventEmitter, ExtensionContext, Task, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, - WorkspaceFolder, commands, debug, window, workspace, tasks, Selection, TaskGroup + WorkspaceFolder, commands, window, workspace, tasks, Selection, TaskGroup } from 'vscode'; import { visit, JSONVisitor } from 'jsonc-parser'; import { NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, - isWorkspaceFolder, getPackageManager, getTaskName, createTask + isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging } from './tasks'; import * as nls from 'vscode-nls'; @@ -162,25 +162,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private extractDebugArg(scripts: any, task: Task): [string, number] | undefined { - let script: string = scripts[task.name]; - - // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, - // --inspect=1234, --inspect-brk, --inspect-brk=1234, - // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 - let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); - - if (match) { - if (match[6]) { - return [match[1], parseInt(match[6])]; - } - if (match[1] === 'inspect') { - return [match[1], 9229]; - } - if (match[1] === 'debug') { - return [match[1], 5858]; - } - } - return undefined; + return extractDebugArgFromScript(scripts[task.name]); } private async debugScript(script: NpmScript) { @@ -193,7 +175,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { return; } - let debugArg = await this.extractDebugArg(scripts, task); + let debugArg = this.extractDebugArg(scripts, task); if (!debugArg) { let message = localize('noDebugOptions', 'Could not launch "{0}" for debugging because the scripts lacks a node debug option, e.g. "--inspect-brk".', task.name); let learnMore = localize('learnMore', 'Learn More'); @@ -204,29 +186,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } return; } - - let protocol = 'inspector'; - if (debugArg[0] === 'debug') { - protocol = 'legacy'; - } - - let packageManager = getPackageManager(script.getFolder()); - const config: DebugConfiguration = { - type: 'node', - request: 'launch', - name: `Debug ${task.name}`, - runtimeExecutable: packageManager, - runtimeArgs: [ - 'run-script', - task.name, - ], - port: debugArg[1], - protocol: protocol - }; - - if (isWorkspaceFolder(task.scope)) { - debug.startDebugging(task.scope, config); - } + startDebugging(task.name, debugArg[0], debugArg[1], script.getFolder()); } private scriptNotValid(task: Task) { diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 55d81c70500d7..6798a8cb9e98e 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace } from 'vscode'; +import { + TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, + DebugConfiguration, debug +} from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as minimatch from 'minimatch'; @@ -162,7 +165,7 @@ function isExcluded(folder: WorkspaceFolder, packageJsonUri: Uri) { } function isDebugScript(script: string): boolean { - let match = script.match(/--(inspect|debug)(-brk)?(=(\d*))?/); + let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); return match !== null; } @@ -269,6 +272,52 @@ async function readFile(file: string): Promise { }); } +export function extractDebugArgFromScript(scriptValue: string): [string, number] | undefined { + // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, + // --inspect=1234, --inspect-brk, --inspect-brk=1234, + // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 + let match = scriptValue.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); + + if (match) { + if (match[6]) { + return [match[1], parseInt(match[6])]; + } + if (match[1] === 'inspect') { + return [match[1], 9229]; + } + if (match[1] === 'debug') { + return [match[1], 5858]; + } + } + return undefined; +} + +export function startDebugging(scriptName: string, protocol: string, port: number, folder: WorkspaceFolder) { + let p = 'inspector'; + if (protocol === 'debug') { + p = 'legacy'; + } + + let packageManager = getPackageManager(folder); + const config: DebugConfiguration = { + type: 'node', + request: 'launch', + name: `Debug ${scriptName}`, + runtimeExecutable: packageManager, + runtimeArgs: [ + 'run-script', + scriptName, + ], + port: port, + protocol: p + }; + + if (folder) { + debug.startDebugging(folder, config); + } +} + + export type StringMap = { [s: string]: string; }; async function findAllScripts(buffer: string): Promise { @@ -304,45 +353,39 @@ async function findAllScripts(buffer: string): Promise { return scripts; } -export function findScriptAtPosition(buffer: string, offset: number): string | undefined { +export function findAllScriptRanges(buffer: string): Map { + var scripts: Map = new Map(); let script: string | undefined = undefined; let inScripts = false; - let scriptStart: number | undefined; let visitor: JSONVisitor = { onError(_error: ParseErrorCode, _offset: number, _length: number) { - // TODO: inform user about the parse error }, onObjectEnd() { if (inScripts) { inScripts = false; - scriptStart = undefined; } }, - onLiteralValue(value: any, nodeOffset: number, nodeLength: number) { - if (inScripts && scriptStart) { - if (offset >= scriptStart && offset < nodeOffset + nodeLength) { - // found the script - inScripts = false; - } else { - script = undefined; - } + onLiteralValue(value: any, offset: number, length: number) { + if (script) { + scripts.set(script, [offset, length, value]); + script = undefined; } }, - onObjectProperty(property: string, nodeOffset: number, nodeLength: number) { + onObjectProperty(property: string, offset: number, length: number) { if (property === 'scripts') { inScripts = true; } else if (inScripts) { - scriptStart = nodeOffset; script = property; } } }; visit(buffer, visitor); - return script; + return scripts; } + export async function getScripts(packageJsonUri: Uri): Promise { if (packageJsonUri.scheme !== 'file') { From 8d964cbd594cb8a7b34f818ec974130d697b2e85 Mon Sep 17 00:00:00 2001 From: Erich Gamma Date: Fri, 20 Jul 2018 09:43:25 +0200 Subject: [PATCH 74/89] Added setting to control visibility of code lens --- extensions/npm/README.md | 6 ++++-- extensions/npm/package.json | 10 +++++---- extensions/npm/src/lenses.ts | 39 ++++++++++++++++++++++++------------ extensions/npm/src/main.ts | 38 ++++++++++++++++------------------- extensions/npm/src/tasks.ts | 19 ++++++++++++++++-- 5 files changed, 70 insertions(+), 42 deletions(-) diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 007cb59abe6c4..c3dd943720362 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -15,11 +15,11 @@ For more information about auto detection of Tasks, see the [documentation](http ### Script Explorer -The Npm Script Explorer shows the npm scripts found in your workspace. The explorer view is enabled by the setting `npm.enableScriptExplorer`. +The Npm Script Explorer shows the npm scripts found in your workspace. The explorer view is enabled by the setting `npm.enableScriptExplorer`. A script can be opened, run, or debug from the explorer. ### Run Scripts from the Editor -The extension provides commands to run the script containing the selection. +The extension provides code lense actions to run or debug a script from the editor. ## Settings @@ -29,3 +29,5 @@ The extension provides commands to run the script containing the selection. - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. +- `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script. + diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 8f1b193197bfc..7f4b794ee0c57 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -94,10 +94,6 @@ "command": "npm.runScript", "when": "false" }, - { - "command": "npm.runScriptFromSource", - "when": "false" - }, { "command": "npm.debugScript", "when": "false" @@ -182,6 +178,12 @@ "scope": "resource", "description": "%config.npm.runSilent%" }, + "npm.scriptCodeLens.enable": { + "type": "boolean", + "default": true, + "scope": "resource", + "description": "%config.scriptCodeLens.enable%" + }, "npm.packageManager": { "scope": "resource", "type": "string", diff --git a/extensions/npm/src/lenses.ts b/extensions/npm/src/lenses.ts index 6276cc1e10e9e..6394fbad5b309 100644 --- a/extensions/npm/src/lenses.ts +++ b/extensions/npm/src/lenses.ts @@ -6,7 +6,7 @@ import { ExtensionContext, CodeLensProvider, TextDocument, commands, ProviderResult, CodeLens, CancellationToken, - workspace, tasks, Range, Command + workspace, tasks, Range, Command, Event, EventEmitter } from 'vscode'; import { createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript @@ -15,46 +15,59 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -export class NpmLenseProvider implements CodeLensProvider { +export class NpmLensProvider implements CodeLensProvider { private extensionContext: ExtensionContext; + private _onDidChangeCodeLenses: EventEmitter = new EventEmitter(); + readonly onDidChangeCodeLenses: Event = this._onDidChangeCodeLenses.event; constructor(context: ExtensionContext) { - const subscriptions = context.subscriptions; this.extensionContext = context; - context.subscriptions.push(commands.registerCommand('npm.runScriptFromLense', this.runScriptFromLense, this)); - context.subscriptions.push(commands.registerCommand('npm.debugScriptFromLense', this.debugScriptFromLense, this)); + context.subscriptions.push(commands.registerCommand('npm.runScriptFromLens', this.runScriptFromLens, this)); + context.subscriptions.push(commands.registerCommand('npm.debugScriptFromLens', this.debugScriptFromLens, this)); } - public provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult { + public provideCodeLenses(document: TextDocument, _token: CancellationToken): ProviderResult { let result = findAllScriptRanges(document.getText()); + let folder = workspace.getWorkspaceFolder(document.uri); let lenses: CodeLens[] = []; + + if (folder && !workspace.getConfiguration('npm', folder.uri).get('scriptCodeLens.enable', 'true')) { + return lenses; + } + result.forEach((value, key) => { let start = document.positionAt(value[0]); let end = document.positionAt(value[0] + value[1]); - let lens: CodeLens; + let range = new Range(start, end); + let command: Command = { - command: 'npm.runScriptFromLense', + command: 'npm.runScriptFromLens', title: localize('run', "Run"), arguments: [document, key] }; - lens = new CodeLens(new Range(start, end), command); + let lens: CodeLens = new CodeLens(range, command); lenses.push(lens); + let debugArgs = extractDebugArgFromScript(value[2]); if (debugArgs) { command = { - command: 'npm.debugScriptFromLense', + command: 'npm.debugScriptFromLens', title: localize('debug', "Debug"), arguments: [document, key, debugArgs[0], debugArgs[1]] }; - lens = new CodeLens(new Range(start, end), command); + lens = new CodeLens(range, command); lenses.push(lens); } }); return lenses; } - public runScriptFromLense(document: TextDocument, script: string) { + public refresh() { + this._onDidChangeCodeLenses.fire(); + } + + public runScriptFromLens(document: TextDocument, script: string) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { @@ -63,7 +76,7 @@ export class NpmLenseProvider implements CodeLensProvider { } } - public debugScriptFromLense(document: TextDocument, script: string, protocol: string, port: number) { + public debugScriptFromLens(document: TextDocument, script: string, protocol: string, port: number) { let uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); if (folder) { diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 4cd70568192d1..f2c80f5f47600 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -9,16 +9,13 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { provideNpmScripts, invalidateScriptsCache } from './tasks'; - -import { NpmLenseProvider } from './lenses'; - -let taskProvider: vscode.Disposable | undefined; +import { invalidateScriptsCache, NpmTaskProvider } from './tasks'; +import { NpmLensProvider } from './lenses'; export async function activate(context: vscode.ExtensionContext): Promise { - taskProvider = registerTaskProvider(context); + const taskProvider = registerTaskProvider(context); const treeDataProvider = registerExplorer(context); - registerLenseProvider(context); + const lensProvider = registerLensProvider(context); configureHttpRequest(); vscode.workspace.onDidChangeConfiguration((e) => { @@ -34,6 +31,11 @@ export async function activate(context: vscode.ExtensionContext): Promise treeDataProvider.refresh(); } } + if (e.affectsConfiguration('npm.scriptCodeLens.enable')) { + if (lensProvider) { + lensProvider.refresh(); + } + } }); context.subscriptions.push(addJSONProviders(httpRequest.xhr)); } @@ -46,15 +48,10 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab watcher.onDidCreate((_e) => invalidateScriptsCache()); context.subscriptions.push(watcher); - let provider: vscode.TaskProvider = { - provideTasks: async () => { - return provideNpmScripts(); - }, - resolveTask(_task: vscode.Task): vscode.Task | undefined { - return undefined; - } - }; - return vscode.workspace.registerTaskProvider('npm', provider); + let provider: vscode.TaskProvider = new NpmTaskProvider(context); + let disposable = vscode.workspace.registerTaskProvider('npm', provider); + context.subscriptions.push(disposable); + return disposable; } return undefined; } @@ -69,16 +66,18 @@ function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataP return undefined; } -function registerLenseProvider(context: vscode.ExtensionContext) { +function registerLensProvider(context: vscode.ExtensionContext): NpmLensProvider | undefined { if (vscode.workspace.workspaceFolders) { let npmSelector: vscode.DocumentSelector = { language: 'json', scheme: 'file', pattern: '**/package.json' }; - let provider = new NpmLenseProvider(context); + let provider = new NpmLensProvider(context); context.subscriptions.push(vscode.languages.registerCodeLensProvider(npmSelector, provider)); + return provider; } + return undefined; } function configureHttpRequest() { @@ -87,7 +86,4 @@ function configureHttpRequest() { } export function deactivate(): void { - if (taskProvider) { - taskProvider.dispose(); - } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 6798a8cb9e98e..5d19a4c4c0055 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -6,7 +6,7 @@ import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug + DebugConfiguration, debug, TaskProvider, ExtensionContext } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; @@ -25,6 +25,22 @@ type AutoDetect = 'on' | 'off'; let cachedTasks: Task[] | undefined = undefined; +export class NpmTaskProvider implements TaskProvider { + private extensionContext: ExtensionContext; + + constructor(context: ExtensionContext) { + this.extensionContext = context; + } + + public provideTasks() { + return provideNpmScripts(); + } + + public resolveTask(_task: Task): Task | undefined { + return undefined; + } +} + export function invalidateScriptsCache() { cachedTasks = undefined; } @@ -327,7 +343,6 @@ async function findAllScripts(buffer: string): Promise { let visitor: JSONVisitor = { onError(_error: ParseErrorCode, _offset: number, _length: number) { - // TODO: inform user about the parse error }, onObjectEnd() { if (inScripts) { From 0ef2155f02aa4bc77e97841fd63427819308ced7 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 20 Jul 2018 10:22:06 +0200 Subject: [PATCH 75/89] fixes #54011 --- .../parts/debug/electron-browser/watchExpressionsView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 99931a7b9598b..3ff99dc07652c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -309,7 +309,7 @@ class WatchExpressionsRenderer implements IRenderer { }); data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value; - if (watchExpression.value) { + if (typeof watchExpression.value === 'string') { data.name.textContent += ':'; } } From f07a19895b50dd49f7250528eec80713a099cdd8 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 20 Jul 2018 11:08:38 +0200 Subject: [PATCH 76/89] debug: simplify context keys computation fixes #54378 --- src/vs/workbench/parts/debug/common/debug.ts | 6 +++--- .../workbench/parts/debug/electron-browser/debugService.ts | 7 ------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 6d05e411b14d4..7b6338bcad8e8 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -36,9 +36,9 @@ export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView'; export const REPL_ID = 'workbench.panel.repl'; export const DEBUG_SERVICE_ID = 'debugService'; export const CONTEXT_DEBUG_TYPE = new RawContextKey('debugType', undefined); -export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', undefined); -export const CONTEXT_IN_DEBUG_MODE = new RawContextKey('inDebugMode', false); -export const CONTEXT_NOT_IN_DEBUG_MODE: ContextKeyExpr = CONTEXT_IN_DEBUG_MODE.toNegated(); +export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', 'inactive'); +export const CONTEXT_NOT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.isEqualTo('inactive'); +export const CONTEXT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.notEqualsTo('inactive'); export const CONTEXT_IN_DEBUG_REPL = new RawContextKey('inDebugRepl', false); export const CONTEXT_NOT_IN_DEBUG_REPL: ContextKeyExpr = CONTEXT_IN_DEBUG_REPL.toNegated(); export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey('breakpointWidgetVisible', false); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index bc11ac3f751e9..637e1aed9df21 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -76,7 +76,6 @@ export class DebugService implements debug.IDebugService { private configurationManager: ConfigurationManager; private toDispose: lifecycle.IDisposable[]; private toDisposeOnSessionEnd: Map; - private inDebugMode: IContextKey; private debugType: IContextKey; private debugState: IContextKey; private breakpointsToSendOnResourceSaved: Set; @@ -120,7 +119,6 @@ export class DebugService implements debug.IDebugService { this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.toDispose.push(this.configurationManager); - this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService); @@ -909,7 +907,6 @@ export class DebugService implements debug.IDebugService { const resolved = configuration.resolved; resolved.__sessionId = sessionId; - this.inDebugMode.set(true); const dbg = this.configurationManager.getDebugger(resolved.type); return this.initializeRawSession(root, configuration, sessionId).then(session => { @@ -985,9 +982,6 @@ export class DebugService implements debug.IDebugService { if (this.model.getReplElements().length > 0) { this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); } - if (this.model.getSessions().length === 0) { - this.inDebugMode.reset(); - } this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []); return undefined; @@ -1209,7 +1203,6 @@ export class DebugService implements debug.IDebugService { this.updateStateAndEmit(raw.getId(), debug.State.Inactive); if (this.model.getSessions().length === 0) { - this.inDebugMode.reset(); this.debugType.reset(); this.viewModel.setMultiSessionView(false); From 03103a4f668b0c36420e07a92c378b5e451a6f03 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 Jul 2018 17:43:17 +0200 Subject: [PATCH 77/89] Add extract-editor-src with treeshaking task --- .gitignore | 1 + build/gulpfile.editor.js | 29 +- build/lib/standalone.js | 86 ++++- build/lib/standalone.ts | 91 ++++- build/lib/treeshaking.js | 676 ++++++++++++++++++++++++++++++++ build/lib/treeshaking.ts | 806 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 1680 insertions(+), 9 deletions(-) create mode 100644 build/lib/treeshaking.js create mode 100644 build/lib/treeshaking.ts diff --git a/.gitignore b/.gitignore index 0b257508fe925..5c902cc558683 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ node_modules/ out/ out-build/ out-editor/ +out-editor-src/ out-editor-esm/ out-editor-min/ out-monaco-editor-core/ diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index abfeb7e4a4be3..a6883d2cfe318 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -75,6 +75,33 @@ function editorLoaderConfig() { const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); +gulp.task('clean-editor-src', util.rimraf('out-editor-src')); +gulp.task('extract-editor-src', ['clean-editor-src'], function() { + standalone.extractEditor({ + sourcesRoot: path.join(root, 'src'), + entryPoints: [ + 'vs/editor/editor.main', + 'vs/editor/editor.worker', + // 'user', + // 'user2', + ], + libs: [ + `lib.d.ts`, + `lib.es2015.collection.d.ts` + ], + redirects: { + 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', + }, + compilerOptions: { + module: 2, // ModuleKind.AMD + // moduleResolution: 'classic' + }, + shakeLevel: 1, // 1-InnerFile, 2-ClassMembers + importIgnorePattern: /^vs\/css!/, + destRoot: path.join(root, 'out-editor-src') + }); +}); + gulp.task('clean-optimized-editor', util.rimraf('out-editor')); gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], common.optimizeTask({ entryPoints: editorEntryPoints, @@ -229,7 +256,7 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify }); gulp.task('analyze-editor-distro', function () { - // @ts-ignore + // @ts-ignore var bundleInfo = require('../out-editor/bundleInfo.json'); var graph = bundleInfo.graph; var bundles = bundleInfo.bundles; diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 12511b01d36b4..06b57482ca9bc 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -7,9 +7,87 @@ Object.defineProperty(exports, "__esModule", { value: true }); var ts = require("typescript"); var fs = require("fs"); var path = require("path"); +var tss = require("./treeshaking"); var REPO_ROOT = path.join(__dirname, '../../'); var SRC_DIR = path.join(REPO_ROOT, 'src'); var OUT_EDITOR = path.join(REPO_ROOT, 'out-editor'); +var dirCache = {}; +function writeFile(filePath, contents) { + function ensureDirs(dirPath) { + if (dirCache[dirPath]) { + return; + } + dirCache[dirPath] = true; + ensureDirs(path.dirname(dirPath)); + if (fs.existsSync(dirPath)) { + return; + } + fs.mkdirSync(dirPath); + } + ensureDirs(path.dirname(filePath)); + fs.writeFileSync(filePath, contents); +} +function extractEditor(options) { + var result = tss.shake(options); + for (var fileName in result) { + if (result.hasOwnProperty(fileName)) { + writeFile(path.join(options.destRoot, fileName), result[fileName]); + } + } + var copied = {}; + var copyFile = function (fileName) { + if (copied[fileName]) { + return; + } + copied[fileName] = true; + var srcPath = path.join(options.sourcesRoot, fileName); + var dstPath = path.join(options.destRoot, fileName); + fs.writeFileSync(dstPath, fs.readFileSync(srcPath)); + }; + var writeOutputFile = function (fileName, contents) { + writeFile(path.join(options.destRoot, fileName), contents); + }; + for (var fileName in result) { + if (result.hasOwnProperty(fileName)) { + var fileContents = result[fileName]; + var info = ts.preProcessFile(fileContents); + for (var i = info.importedFiles.length - 1; i >= 0; i--) { + var importedFileName = info.importedFiles[i].fileName; + var importedFilePath = void 0; + if (/^vs\/css!/.test(importedFileName)) { + importedFilePath = importedFileName.substr('vs/css!'.length) + '.css'; + } + else { + importedFilePath = importedFileName; + } + if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { + importedFilePath = path.join(path.dirname(fileName), importedFilePath); + } + if (/\.css$/.test(importedFilePath)) { + transportCSS(importedFilePath, copyFile, writeOutputFile); + } + else { + if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) { + copyFile(importedFilePath + '.js'); + } + } + } + } + } + [ + 'tsconfig.json', + 'vs/css.build.js', + 'vs/css.d.ts', + 'vs/css.js', + 'vs/loader.js', + 'vs/monaco.d.ts', + 'vs/nls.build.js', + 'vs/nls.d.ts', + 'vs/nls.js', + 'vs/nls.mock.ts', + ].forEach(copyFile); +} +exports.extractEditor = extractEditor; function createESMSourcesAndResources(options) { var OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); var OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); @@ -94,7 +172,7 @@ function createESMSourcesAndResources(options) { options.entryPoints.forEach(function (entryPoint) { return enqueue(entryPoint); }); while (queue.length > 0) { var module_1 = queue.shift(); - if (transportCSS(options, module_1, enqueue, write)) { + if (transportCSS(module_1, enqueue, write)) { continue; } if (transportResource(options, module_1, enqueue, write)) { @@ -171,7 +249,7 @@ function createESMSourcesAndResources(options) { fs.writeFileSync(path.join(OUT_FOLDER, 'vs/monaco.d.ts'), monacodts); } exports.createESMSourcesAndResources = createESMSourcesAndResources; -function transportCSS(options, module, enqueue, write) { +function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; } @@ -179,10 +257,10 @@ function transportCSS(options, module, enqueue, write) { var fileContents = fs.readFileSync(filename).toString(); var inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 var inlineResourcesLimit = 300000; //3000; // see https://github.com/Microsoft/monaco-editor/issues/336 - var newContents = _rewriteOrInlineUrls(filename, fileContents, inlineResources === 'base64', inlineResourcesLimit); + var newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64', inlineResourcesLimit); write(module, newContents); return true; - function _rewriteOrInlineUrls(originalFileFSPath, contents, forceBase64, inlineByteLimit) { + function _rewriteOrInlineUrls(contents, forceBase64, inlineByteLimit) { return _replaceURL(contents, function (url) { var imagePath = path.join(path.dirname(module), url); var fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index a402cf6840564..9378dd3e7d953 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -6,11 +6,94 @@ import * as ts from 'typescript'; import * as fs from 'fs'; import * as path from 'path'; +import * as tss from './treeshaking'; const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); const OUT_EDITOR = path.join(REPO_ROOT, 'out-editor'); +let dirCache: { [dir: string]: boolean; } = {}; + +function writeFile(filePath: string, contents: string): void { + function ensureDirs(dirPath: string): void { + if (dirCache[dirPath]) { + return; + } + dirCache[dirPath] = true; + + ensureDirs(path.dirname(dirPath)); + if (fs.existsSync(dirPath)) { + return; + } + fs.mkdirSync(dirPath); + } + ensureDirs(path.dirname(filePath)); + fs.writeFileSync(filePath, contents); +} + +export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { + let result = tss.shake(options); + for (let fileName in result) { + if (result.hasOwnProperty(fileName)) { + writeFile(path.join(options.destRoot, fileName), result[fileName]); + } + } + let copied: { [fileName:string]: boolean; } = {}; + const copyFile = (fileName: string) => { + if (copied[fileName]) { + return; + } + copied[fileName] = true; + const srcPath = path.join(options.sourcesRoot, fileName); + const dstPath = path.join(options.destRoot, fileName); + fs.writeFileSync(dstPath, fs.readFileSync(srcPath)); + }; + const writeOutputFile = (fileName: string, contents: string) => { + writeFile(path.join(options.destRoot, fileName), contents); + }; + for (let fileName in result) { + if (result.hasOwnProperty(fileName)) { + const fileContents = result[fileName]; + const info = ts.preProcessFile(fileContents); + + for (let i = info.importedFiles.length - 1; i >= 0; i--) { + const importedFileName = info.importedFiles[i].fileName; + + let importedFilePath: string; + if (/^vs\/css!/.test(importedFileName)) { + importedFilePath = importedFileName.substr('vs/css!'.length) + '.css'; + } else { + importedFilePath = importedFileName; + } + if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { + importedFilePath = path.join(path.dirname(fileName), importedFilePath); + } + + if (/\.css$/.test(importedFilePath)) { + transportCSS(importedFilePath, copyFile, writeOutputFile); + } else { + if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) { + copyFile(importedFilePath + '.js'); + } + } + } + } + } + + [ + 'tsconfig.json', + 'vs/css.build.js', + 'vs/css.d.ts', + 'vs/css.js', + 'vs/loader.js', + 'vs/monaco.d.ts', + 'vs/nls.build.js', + 'vs/nls.d.ts', + 'vs/nls.js', + 'vs/nls.mock.ts', + ].forEach(copyFile); +} + export interface IOptions { entryPoints: string[]; outFolder: string; @@ -111,7 +194,7 @@ export function createESMSourcesAndResources(options: IOptions): void { while (queue.length > 0) { const module = queue.shift(); - if (transportCSS(options, module, enqueue, write)) { + if (transportCSS(module, enqueue, write)) { continue; } if (transportResource(options, module, enqueue, write)) { @@ -198,7 +281,7 @@ export function createESMSourcesAndResources(options: IOptions): void { } -function transportCSS(options: IOptions, module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { +function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { if (!/\.css/.test(module)) { return false; @@ -209,11 +292,11 @@ function transportCSS(options: IOptions, module: string, enqueue: (module: strin const inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 const inlineResourcesLimit = 300000;//3000; // see https://github.com/Microsoft/monaco-editor/issues/336 - const newContents = _rewriteOrInlineUrls(filename, fileContents, inlineResources === 'base64', inlineResourcesLimit); + const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64', inlineResourcesLimit); write(module, newContents); return true; - function _rewriteOrInlineUrls(originalFileFSPath: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + function _rewriteOrInlineUrls(contents: string, forceBase64: boolean, inlineByteLimit: number): string { return _replaceURL(contents, (url) => { let imagePath = path.join(path.dirname(module), url); let fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js new file mode 100644 index 0000000000000..05b08276344a8 --- /dev/null +++ b/build/lib/treeshaking.js @@ -0,0 +1,676 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var fs = require("fs"); +var path = require("path"); +var ts = require("typescript"); +var TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); +var ShakeLevel; +(function (ShakeLevel) { + ShakeLevel[ShakeLevel["Files"] = 0] = "Files"; + ShakeLevel[ShakeLevel["InnerFile"] = 1] = "InnerFile"; + ShakeLevel[ShakeLevel["ClassMembers"] = 2] = "ClassMembers"; +})(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); +function shake(options) { + var languageService = createTypeScriptLanguageService(options); + markNodes(languageService, options.shakeLevel, options.entryPoints.map(function (moduleId) { return moduleId + '.ts'; }), options.importIgnorePattern); + return generateResult(languageService, options.shakeLevel); +} +exports.shake = shake; +//#region Discovery, LanguageService & Setup +function createTypeScriptLanguageService(options) { + // Discover referenced files + var FILES = discoverAndReadFiles(options); + // Resolve libs + var RESOLVED_LIBS = {}; + options.libs.forEach(function (filename) { + var filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); + RESOLVED_LIBS["defaultLib:" + filename] = fs.readFileSync(filepath).toString(); + }); + var host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, options.compilerOptions); + return ts.createLanguageService(host); +} +/** + * Read imports and follow them until all files have been handled + */ +function discoverAndReadFiles(options) { + var FILES = {}; + var in_queue = Object.create(null); + var queue = []; + var enqueue = function (moduleId) { + if (in_queue[moduleId]) { + return; + } + in_queue[moduleId] = true; + queue.push(moduleId); + }; + options.entryPoints.forEach(function (entryPoint) { return enqueue(entryPoint); }); + while (queue.length > 0) { + var moduleId = queue.shift(); + var dts_filename = path.join(options.sourcesRoot, moduleId + '.d.ts'); + if (fs.existsSync(dts_filename)) { + var dts_filecontents = fs.readFileSync(dts_filename).toString(); + FILES[moduleId + '.d.ts'] = dts_filecontents; + continue; + } + var ts_filename = void 0; + if (options.redirects[moduleId]) { + ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); + } + else { + ts_filename = path.join(options.sourcesRoot, moduleId + '.ts'); + } + var ts_filecontents = fs.readFileSync(ts_filename).toString(); + var info = ts.preProcessFile(ts_filecontents); + for (var i = info.importedFiles.length - 1; i >= 0; i--) { + var importedFileName = info.importedFiles[i].fileName; + if (options.importIgnorePattern.test(importedFileName)) { + // Ignore vs/css! imports + continue; + } + var importedModuleId = importedFileName; + if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { + importedModuleId = path.join(path.dirname(moduleId), importedModuleId); + } + enqueue(importedModuleId); + } + FILES[moduleId + '.ts'] = ts_filecontents; + } + return FILES; +} +/** + * A TypeScript language service host + */ +var TypeScriptLanguageServiceHost = /** @class */ (function () { + function TypeScriptLanguageServiceHost(libs, files, compilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + // --- language service host --------------- + TypeScriptLanguageServiceHost.prototype.getCompilationSettings = function () { + return this._compilerOptions; + }; + TypeScriptLanguageServiceHost.prototype.getScriptFileNames = function () { + return ([] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files))); + }; + TypeScriptLanguageServiceHost.prototype.getScriptVersion = function (fileName) { + return '1'; + }; + TypeScriptLanguageServiceHost.prototype.getProjectVersion = function () { + return '1'; + }; + TypeScriptLanguageServiceHost.prototype.getScriptSnapshot = function (fileName) { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } + else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } + else { + return ts.ScriptSnapshot.fromString(''); + } + }; + TypeScriptLanguageServiceHost.prototype.getScriptKind = function (fileName) { + return ts.ScriptKind.TS; + }; + TypeScriptLanguageServiceHost.prototype.getCurrentDirectory = function () { + return ''; + }; + TypeScriptLanguageServiceHost.prototype.getDefaultLibFileName = function (options) { + return 'defaultLib:lib.d.ts'; + }; + TypeScriptLanguageServiceHost.prototype.isDefaultLibFileName = function (fileName) { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + }; + return TypeScriptLanguageServiceHost; +}()); +//#endregion +//#region Tree Shaking +var NodeColor; +(function (NodeColor) { + NodeColor[NodeColor["White"] = 0] = "White"; + NodeColor[NodeColor["Gray"] = 1] = "Gray"; + NodeColor[NodeColor["Black"] = 2] = "Black"; +})(NodeColor || (NodeColor = {})); +function getColor(node) { + return node.$$$color || 0 /* White */; +} +function setColor(node, color) { + node.$$$color = color; +} +function nodeOrParentIsBlack(node) { + while (node) { + var color = getColor(node); + if (color === 2 /* Black */) { + return true; + } + node = node.parent; + } + return false; +} +function nodeOrChildIsBlack(node) { + if (getColor(node) === 2 /* Black */) { + return true; + } + for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) { + var child = _a[_i]; + if (nodeOrChildIsBlack(child)) { + return true; + } + } + return false; +} +function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePattern) { + var program = languageService.getProgram(); + if (shakeLevel === 0 /* Files */) { + // Mark all source files Black + program.getSourceFiles().forEach(function (sourceFile) { + setColor(sourceFile, 2 /* Black */); + }); + return; + } + var black_queue = []; + var gray_queue = []; + var sourceFilesLoaded = {}; + function enqueueTopLevelModuleStatements(sourceFile) { + sourceFile.forEachChild(function (node) { + if (ts.isImportDeclaration(node)) { + if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, 2 /* Black */); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + if (ts.isExportDeclaration(node)) { + if (ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, 2 /* Black */); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + if (ts.isExpressionStatement(node) + || ts.isIfStatement(node) + || ts.isIterationStatement(node, true) + || ts.isExportAssignment(node)) { + enqueue_black(node); + } + if (ts.isImportEqualsDeclaration(node)) { + if (/export/.test(node.getFullText(sourceFile))) { + // e.g. "export import Severity = BaseSeverity;" + enqueue_black(node); + } + } + }); + } + function enqueue_gray(node) { + if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* Gray */) { + return; + } + setColor(node, 1 /* Gray */); + gray_queue.push(node); + } + function enqueue_black(node) { + var previousColor = getColor(node); + if (previousColor === 2 /* Black */) { + return; + } + if (previousColor === 1 /* Gray */) { + // remove from gray queue + gray_queue.splice(gray_queue.indexOf(node), 1); + setColor(node, 0 /* White */); + // add to black queue + enqueue_black(node); + // // move from one queue to the other + // black_queue.push(node); + // setColor(node, NodeColor.Black); + return; + } + if (nodeOrParentIsBlack(node)) { + return; + } + var fileName = node.getSourceFile().fileName; + if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) { + setColor(node, 2 /* Black */); + return; + } + var sourceFile = node.getSourceFile(); + if (!sourceFilesLoaded[sourceFile.fileName]) { + sourceFilesLoaded[sourceFile.fileName] = true; + enqueueTopLevelModuleStatements(sourceFile); + } + if (ts.isSourceFile(node)) { + return; + } + setColor(node, 2 /* Black */); + black_queue.push(node); + if (shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + var references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); + if (references) { + for (var i = 0, len = references.length; i < len; i++) { + var reference = references[i]; + var referenceSourceFile = program.getSourceFile(reference.fileName); + var referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false); + if (ts.isMethodDeclaration(referenceNode.parent) + || ts.isPropertyDeclaration(referenceNode.parent) + || ts.isGetAccessor(referenceNode.parent) + || ts.isSetAccessor(referenceNode.parent)) { + enqueue_gray(referenceNode.parent); + } + } + } + } + } + function enqueueFile(filename) { + var sourceFile = program.getSourceFile(filename); + if (!sourceFile) { + console.warn("Cannot find source file " + filename); + return; + } + enqueue_black(sourceFile); + } + function enqueueImport(node, importText) { + if (importIgnorePattern.test(importText)) { + // this import should be ignored + return; + } + var nodeSourceFile = node.getSourceFile(); + var fullPath; + if (/(^\.\/)|(^\.\.\/)/.test(importText)) { + fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + } + else { + fullPath = importText + '.ts'; + } + enqueueFile(fullPath); + } + entryPointFiles.forEach(function (filename) { return enqueueFile(filename); }); + var step = 0; + var checker = program.getTypeChecker(); + var _loop_1 = function () { + ++step; + var node = void 0; + if (step % 100 === 0) { + console.log(step + "/" + (step + black_queue.length + gray_queue.length) + " (" + black_queue.length + ", " + gray_queue.length + ")"); + } + if (black_queue.length === 0) { + for (var i = 0; i < gray_queue.length; i++) { + var node_1 = gray_queue[i]; + var nodeParent = node_1.parent; + if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { + gray_queue.splice(i, 1); + black_queue.push(node_1); + setColor(node_1, 2 /* Black */); + i--; + } + } + } + if (black_queue.length > 0) { + node = black_queue.shift(); + } + else { + return "break"; + } + var nodeSourceFile = node.getSourceFile(); + var loop = function (node) { + var _a = getRealNodeSymbol(checker, node), symbol = _a[0], symbolImportNode = _a[1]; + if (symbolImportNode) { + setColor(symbolImportNode, 2 /* Black */); + } + if (symbol && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (var i = 0, len = symbol.declarations.length; i < len; i++) { + var declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; + } + if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + enqueue_black(declaration.name); + for (var j = 0; j < declaration.members.length; j++) { + var member = declaration.members[j]; + var memberName = member.name ? member.name.getText() : null; + if (ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose' // TODO: keeping all `dispose` methods + ) { + enqueue_black(member); + } + } + // queue the heritage clauses + if (declaration.heritageClauses) { + for (var _i = 0, _b = declaration.heritageClauses; _i < _b.length; _i++) { + var heritageClause = _b[_i]; + enqueue_black(heritageClause); + } + } + } + else { + enqueue_black(declaration); + } + } + } + node.forEachChild(loop); + }; + node.forEachChild(loop); + }; + while (black_queue.length > 0 || gray_queue.length > 0) { + var state_1 = _loop_1(); + if (state_1 === "break") + break; + } +} +function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) { + for (var i = 0, len = symbol.declarations.length; i < len; i++) { + var declaration = symbol.declarations[i]; + var declarationSourceFile = declaration.getSourceFile(); + if (nodeSourceFile === declarationSourceFile) { + if (declaration.pos <= node.pos && node.end <= declaration.end) { + return true; + } + } + } + return false; +} +function generateResult(languageService, shakeLevel) { + var program = languageService.getProgram(); + var result = {}; + var writeFile = function (filePath, contents) { + result[filePath] = contents; + }; + program.getSourceFiles().forEach(function (sourceFile) { + var fileName = sourceFile.fileName; + if (/^defaultLib:/.test(fileName)) { + return; + } + var destination = fileName; + if (/\.d\.ts$/.test(fileName)) { + if (nodeOrChildIsBlack(sourceFile)) { + writeFile(destination, sourceFile.text); + } + return; + } + var text = sourceFile.text; + var result = ''; + function keep(node) { + result += text.substring(node.pos, node.end); + } + function write(data) { + result += data; + } + function writeMarkedNodes(node) { + if (getColor(node) === 2 /* Black */) { + return keep(node); + } + // Always keep certain top-level statements + if (ts.isSourceFile(node.parent)) { + if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use strict') { + return keep(node); + } + if (ts.isVariableStatement(node) && nodeOrChildIsBlack(node)) { + return keep(node); + } + } + // Keep the entire import in import * as X cases + if (ts.isImportDeclaration(node)) { + if (node.importClause && node.importClause.namedBindings) { + if (ts.isNamespaceImport(node.importClause.namedBindings)) { + if (getColor(node.importClause.namedBindings) === 2 /* Black */) { + return keep(node); + } + } + else { + var survivingImports = []; + for (var i = 0; i < node.importClause.namedBindings.elements.length; i++) { + var importNode = node.importClause.namedBindings.elements[i]; + if (getColor(importNode) === 2 /* Black */) { + survivingImports.push(importNode.getFullText(sourceFile)); + } + } + var leadingTriviaWidth = node.getLeadingTriviaWidth(); + var leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); + if (survivingImports.length > 0) { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return write(leadingTrivia + "import " + node.importClause.name.text + ", {" + survivingImports.join(',') + " } from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + return write(leadingTrivia + "import {" + survivingImports.join(',') + " } from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + else { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return write(leadingTrivia + "import " + node.importClause.name.text + " from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + } + } + } + else { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return keep(node); + } + } + } + if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { + var toWrite = node.getFullText(); + for (var i = node.members.length - 1; i >= 0; i--) { + var member = node.members[i]; + if (getColor(member) === 2 /* Black */) { + // keep method + continue; + } + if (/^_(.*)Brand$/.test(member.name.getText())) { + // TODO: keep all members ending with `Brand`... + continue; + } + var pos = member.pos - node.pos; + var end = member.end - node.pos; + toWrite = toWrite.substring(0, pos) + toWrite.substring(end); + } + return write(toWrite); + } + if (ts.isFunctionDeclaration(node)) { + // Do not go inside functions if they haven't been marked + return; + } + node.forEachChild(writeMarkedNodes); + } + if (getColor(sourceFile) !== 2 /* Black */) { + if (!nodeOrChildIsBlack(sourceFile)) { + // none of the elements are reachable => don't write this file at all! + return; + } + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); + } + else { + result = text; + } + writeFile(destination, result); + }); + return result; +} +//#endregion +//#region Utils +/** + * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) + */ +function getRealNodeSymbol(checker, node) { + /** + * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 } + */ + /* @internal */ + function getContainingObjectLiteralElement(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + if (node.parent.kind === ts.SyntaxKind.ComputedPropertyName) { + return ts.isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined; + } + // falls through + case ts.SyntaxKind.Identifier: + return ts.isObjectLiteralElement(node.parent) && + (node.parent.parent.kind === ts.SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === ts.SyntaxKind.JsxAttributes) && + node.parent.name === node ? node.parent : undefined; + } + return undefined; + } + function getPropertySymbolsFromType(type, propName) { + function getTextOfPropertyName(name) { + function isStringOrNumericLiteral(node) { + var kind = node.kind; + return kind === ts.SyntaxKind.StringLiteral + || kind === ts.SyntaxKind.NumericLiteral; + } + switch (name.kind) { + case ts.SyntaxKind.Identifier: + return name.text; + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + return name.text; + case ts.SyntaxKind.ComputedPropertyName: + return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined; + } + } + var name = getTextOfPropertyName(propName); + if (name && type) { + var result = []; + var symbol_1 = type.getProperty(name); + if (type.flags & ts.TypeFlags.Union) { + for (var _i = 0, _a = type.types; _i < _a.length; _i++) { + var t = _a[_i]; + var symbol_2 = t.getProperty(name); + if (symbol_2) { + result.push(symbol_2); + } + } + return result; + } + if (symbol_1) { + result.push(symbol_1); + return result; + } + } + return undefined; + } + function getPropertySymbolsFromContextualType(typeChecker, node) { + var objectLiteral = node.parent; + var contextualType = typeChecker.getContextualType(objectLiteral); + return getPropertySymbolsFromType(contextualType, node.name); + } + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from an import. + // + function shouldSkipAlias(node, declaration) { + if (node.kind !== ts.SyntaxKind.Identifier) { + return false; + } + if (node.parent === declaration) { + return true; + } + switch (declaration.kind) { + case ts.SyntaxKind.ImportClause: + case ts.SyntaxKind.ImportEqualsDeclaration: + return true; + case ts.SyntaxKind.ImportSpecifier: + return declaration.parent.kind === ts.SyntaxKind.NamedImports; + default: + return false; + } + } + if (!ts.isShorthandPropertyAssignment(node)) { + if (node.getChildCount() !== 0) { + return [null, null]; + } + } + var symbol = checker.getSymbolAtLocation(node); + var importNode = null; + if (symbol && symbol.flags & ts.SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + var aliased = checker.getAliasedSymbol(symbol); + if (aliased.declarations) { + // We should mark the import as visited + importNode = symbol.declarations[0]; + symbol = aliased; + } + } + if (symbol) { + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { + symbol = checker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + } + // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the + // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern + // and return the property declaration for the referenced property. + // For example: + // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" + // + // function bar(onfulfilled: (value: T) => void) { //....} + // interface Test { + // pr/*destination*/op1: number + // } + // bar(({pr/*goto*/op1})=>{}); + if (ts.isPropertyName(node) && ts.isBindingElement(node.parent) && ts.isObjectBindingPattern(node.parent.parent) && + (node === (node.parent.propertyName || node.parent.name))) { + var type = checker.getTypeAtLocation(node.parent.parent); + if (type) { + var propSymbols = getPropertySymbolsFromType(type, node); + if (propSymbols) { + symbol = propSymbols[0]; + } + } + } + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. + // For example + // interface Props{ + // /*first*/prop1: number + // prop2: boolean + // } + // function Foo(arg: Props) {} + // Foo( { pr/*1*/op1: 10, prop2: false }) + var element = getContainingObjectLiteralElement(node); + if (element && checker.getContextualType(element.parent)) { + var propertySymbols = getPropertySymbolsFromContextualType(checker, element); + if (propertySymbols) { + symbol = propertySymbols[0]; + } + } + } + if (symbol && symbol.declarations) { + return [symbol, importNode]; + } + return [null, null]; +} +/** Get the token whose text contains the position */ +function getTokenAtPosition(sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) { + var current = sourceFile; + outer: while (true) { + // find the child that contains 'position' + for (var _i = 0, _a = current.getChildren(); _i < _a.length; _i++) { + var child = _a[_i]; + var start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true); + if (start > position) { + // If this child begins after position, then all subsequent children will as well. + break; + } + var end = child.getEnd(); + if (position < end || (position === end && (child.kind === ts.SyntaxKind.EndOfFileToken || includeEndPosition))) { + current = child; + continue outer; + } + } + return current; + } +} diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts new file mode 100644 index 0000000000000..ef82fa2ff2340 --- /dev/null +++ b/build/lib/treeshaking.ts @@ -0,0 +1,806 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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 ts from 'typescript'; + +const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); + +export const enum ShakeLevel { + Files = 0, + InnerFile = 1, + ClassMembers = 2 +} + +export interface ITreeShakingOptions { + /** + * The full path to the root where sources are. + */ + sourcesRoot: string; + /** + * Module ids. + * e.g. `vs/editor/editor.main` or `index` + */ + entryPoints: string[]; + /** + * TypeScript libs. + * e.g. `lib.d.ts`, `lib.es2015.collection.d.ts` + */ + libs: string[]; + /** + * TypeScript compiler options. + */ + compilerOptions: ts.CompilerOptions; + /** + * The shake level to perform. + */ + shakeLevel: ShakeLevel; + /** + * regex pattern to ignore certain imports e.g. `vs/css!` imports + */ + importIgnorePattern: RegExp; + + redirects: { [module: string]: string; }; +} + +export interface ITreeShakingResult { + [file: string]: string; +} + +export function shake(options: ITreeShakingOptions): ITreeShakingResult { + const languageService = createTypeScriptLanguageService(options); + + markNodes(languageService, options.shakeLevel, options.entryPoints.map(moduleId => moduleId + '.ts'), options.importIgnorePattern); + + return generateResult(languageService, options.shakeLevel); +} + +//#region Discovery, LanguageService & Setup +function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.LanguageService { + // Discover referenced files + const FILES = discoverAndReadFiles(options); + + // Resolve libs + const RESOLVED_LIBS: ILibMap = {}; + options.libs.forEach((filename) => { + const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); + RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); + }); + + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, options.compilerOptions); + return ts.createLanguageService(host); +} + +/** + * Read imports and follow them until all files have been handled + */ +function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap { + const FILES: IFileMap = {}; + + const in_queue: { [module: string]: boolean; } = Object.create(null); + const queue: string[] = []; + + const enqueue = (moduleId: string) => { + if (in_queue[moduleId]) { + return; + } + in_queue[moduleId] = true; + queue.push(moduleId); + }; + + options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); + + while (queue.length > 0) { + const moduleId = queue.shift(); + const dts_filename = path.join(options.sourcesRoot, moduleId + '.d.ts'); + if (fs.existsSync(dts_filename)) { + const dts_filecontents = fs.readFileSync(dts_filename).toString(); + FILES[moduleId + '.d.ts'] = dts_filecontents; + continue; + } + + let ts_filename: string; + if (options.redirects[moduleId]) { + ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); + } else { + ts_filename = path.join(options.sourcesRoot, moduleId + '.ts'); + } + const ts_filecontents = fs.readFileSync(ts_filename).toString(); + const info = ts.preProcessFile(ts_filecontents); + for (let i = info.importedFiles.length - 1; i >= 0; i--) { + const importedFileName = info.importedFiles[i].fileName; + + if (options.importIgnorePattern.test(importedFileName)) { + // Ignore vs/css! imports + continue; + } + + let importedModuleId = importedFileName; + if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { + importedModuleId = path.join(path.dirname(moduleId), importedModuleId); + } + enqueue(importedModuleId); + } + + FILES[moduleId + '.ts'] = ts_filecontents; + } + + return FILES; +} + +interface ILibMap { [libName: string]: string; } +interface IFileMap { [fileName: string]: string; } + +/** + * A TypeScript language service host + */ +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { + + private readonly _libs: ILibMap; + private readonly _files: IFileMap; + private readonly _compilerOptions: ts.CompilerOptions; + + constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + + // --- language service host --------------- + + getCompilationSettings(): ts.CompilerOptions { + return this._compilerOptions; + } + getScriptFileNames(): string[] { + return ( + [] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files)) + ); + } + getScriptVersion(fileName: string): string { + return '1'; + } + getProjectVersion(): string { + return '1'; + } + getScriptSnapshot(fileName: string): ts.IScriptSnapshot { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } else { + return ts.ScriptSnapshot.fromString(''); + } + } + getScriptKind(fileName: string): ts.ScriptKind { + return ts.ScriptKind.TS; + } + getCurrentDirectory(): string { + return ''; + } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'defaultLib:lib.d.ts'; + } + isDefaultLibFileName(fileName: string): boolean { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + } +} +//#endregion + +//#region Tree Shaking + +const enum NodeColor { + White = 0, + Gray = 1, + Black = 2 +} + +function getColor(node: ts.Node): NodeColor { + return (node).$$$color || NodeColor.White; +} +function setColor(node: ts.Node, color: NodeColor): void { + (node).$$$color = color; +} +function nodeOrParentIsBlack(node: ts.Node): boolean { + while (node) { + const color = getColor(node); + if (color === NodeColor.Black) { + return true; + } + node = node.parent; + } + return false; +} +function nodeOrChildIsBlack(node: ts.Node): boolean { + if (getColor(node) === NodeColor.Black) { + return true; + } + for (const child of node.getChildren()) { + if (nodeOrChildIsBlack(child)) { + return true; + } + } + return false; +} + +function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, entryPointFiles: string[], importIgnorePattern: RegExp) { + const program = languageService.getProgram(); + + if (shakeLevel === ShakeLevel.Files) { + // Mark all source files Black + program.getSourceFiles().forEach((sourceFile) => { + setColor(sourceFile, NodeColor.Black); + }); + return; + } + + const black_queue: ts.Node[] = []; + const gray_queue: ts.Node[] = []; + const sourceFilesLoaded: { [fileName: string]: boolean } = {}; + + function enqueueTopLevelModuleStatements(sourceFile: ts.SourceFile): void { + + sourceFile.forEachChild((node: ts.Node) => { + + if (ts.isImportDeclaration(node)) { + if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, NodeColor.Black); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + + if (ts.isExportDeclaration(node)) { + if (ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, NodeColor.Black); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + + if ( + ts.isExpressionStatement(node) + || ts.isIfStatement(node) + || ts.isIterationStatement(node, true) + || ts.isExportAssignment(node) + ) { + enqueue_black(node); + } + + if (ts.isImportEqualsDeclaration(node)) { + if (/export/.test(node.getFullText(sourceFile))) { + // e.g. "export import Severity = BaseSeverity;" + enqueue_black(node); + } + } + + }); + } + + function enqueue_gray(node: ts.Node): void { + if (nodeOrParentIsBlack(node) || getColor(node) === NodeColor.Gray) { + return; + } + setColor(node, NodeColor.Gray); + gray_queue.push(node); + } + + function enqueue_black(node: ts.Node): void { + const previousColor = getColor(node); + + if (previousColor === NodeColor.Black) { + return; + } + + if (previousColor === NodeColor.Gray) { + // remove from gray queue + gray_queue.splice(gray_queue.indexOf(node), 1); + setColor(node, NodeColor.White); + + // add to black queue + enqueue_black(node); + + // // move from one queue to the other + // black_queue.push(node); + // setColor(node, NodeColor.Black); + return; + } + + if (nodeOrParentIsBlack(node)) { + return; + } + + const fileName = node.getSourceFile().fileName; + if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) { + setColor(node, NodeColor.Black); + return; + } + + const sourceFile = node.getSourceFile(); + if (!sourceFilesLoaded[sourceFile.fileName]) { + sourceFilesLoaded[sourceFile.fileName] = true; + enqueueTopLevelModuleStatements(sourceFile); + } + + if (ts.isSourceFile(node)) { + return; + } + + setColor(node, NodeColor.Black); + black_queue.push(node); + + if (shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); + if (references) { + for (let i = 0, len = references.length; i < len; i++) { + const reference = references[i]; + const referenceSourceFile = program.getSourceFile(reference.fileName); + const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false); + if ( + ts.isMethodDeclaration(referenceNode.parent) + || ts.isPropertyDeclaration(referenceNode.parent) + || ts.isGetAccessor(referenceNode.parent) + || ts.isSetAccessor(referenceNode.parent) + ) { + enqueue_gray(referenceNode.parent); + } + } + } + } + } + + function enqueueFile(filename: string): void { + const sourceFile = program.getSourceFile(filename); + if (!sourceFile) { + console.warn(`Cannot find source file ${filename}`); + return; + } + enqueue_black(sourceFile); + } + + function enqueueImport(node: ts.Node, importText: string): void { + if (importIgnorePattern.test(importText)) { + // this import should be ignored + return; + } + + const nodeSourceFile = node.getSourceFile(); + let fullPath: string; + if (/(^\.\/)|(^\.\.\/)/.test(importText)) { + fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + } else { + fullPath = importText + '.ts'; + } + enqueueFile(fullPath); + } + + entryPointFiles.forEach((filename) => enqueueFile(filename)); + + let step = 0; + + const checker = program.getTypeChecker(); + while (black_queue.length > 0 || gray_queue.length > 0) { + ++step; + let node: ts.Node; + + if (step % 100 === 0) { + console.log(`${step}/${step+black_queue.length+gray_queue.length} (${black_queue.length}, ${gray_queue.length})`); + } + + if (black_queue.length === 0) { + for (let i = 0; i < gray_queue.length; i++) { + const node = gray_queue[i]; + const nodeParent = node.parent; + if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { + gray_queue.splice(i, 1); + black_queue.push(node); + setColor(node, NodeColor.Black); + i--; + } + } + } + + if (black_queue.length > 0) { + node = black_queue.shift(); + } else { + // only gray nodes remaining... + break; + } + const nodeSourceFile = node.getSourceFile(); + + const loop = (node: ts.Node) => { + const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node); + if (symbolImportNode) { + setColor(symbolImportNode, NodeColor.Black); + } + + if (symbol && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; + } + + if (shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + enqueue_black(declaration.name); + + for (let j = 0; j < declaration.members.length; j++) { + const member = declaration.members[j]; + const memberName = member.name ? member.name.getText() : null; + if ( + ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose'// TODO: keeping all `dispose` methods + ) { + enqueue_black(member); + } + } + + // queue the heritage clauses + if (declaration.heritageClauses) { + for (let heritageClause of declaration.heritageClauses) { + enqueue_black(heritageClause); + } + } + } else { + enqueue_black(declaration); + } + } + } + node.forEachChild(loop); + }; + node.forEachChild(loop); + } +} + +function nodeIsInItsOwnDeclaration(nodeSourceFile: ts.SourceFile, node: ts.Node, symbol: ts.Symbol): boolean { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + const declarationSourceFile = declaration.getSourceFile(); + + if (nodeSourceFile === declarationSourceFile) { + if (declaration.pos <= node.pos && node.end <= declaration.end) { + return true; + } + } + } + + return false; +} + +function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult { + const program = languageService.getProgram(); + + let result: ITreeShakingResult = {}; + const writeFile = (filePath: string, contents: string): void => { + result[filePath] = contents; + }; + + program.getSourceFiles().forEach((sourceFile) => { + const fileName = sourceFile.fileName; + if (/^defaultLib:/.test(fileName)) { + return; + } + const destination = fileName; + if (/\.d\.ts$/.test(fileName)) { + if (nodeOrChildIsBlack(sourceFile)) { + writeFile(destination, sourceFile.text); + } + return; + } + + let text = sourceFile.text; + let result = ''; + + function keep(node: ts.Node): void { + result += text.substring(node.pos, node.end); + } + function write(data: string): void { + result += data; + } + + function writeMarkedNodes(node: ts.Node): void { + if (getColor(node) === NodeColor.Black) { + return keep(node); + } + + // Always keep certain top-level statements + if (ts.isSourceFile(node.parent)) { + if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use strict') { + return keep(node); + } + + if (ts.isVariableStatement(node) && nodeOrChildIsBlack(node)) { + return keep(node); + } + } + + // Keep the entire import in import * as X cases + if (ts.isImportDeclaration(node)) { + if (node.importClause && node.importClause.namedBindings) { + if (ts.isNamespaceImport(node.importClause.namedBindings)) { + if (getColor(node.importClause.namedBindings) === NodeColor.Black) { + return keep(node); + } + } else { + let survivingImports: string[] = []; + for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { + const importNode = node.importClause.namedBindings.elements[i]; + if (getColor(importNode) === NodeColor.Black) { + survivingImports.push(importNode.getFullText(sourceFile)); + } + } + const leadingTriviaWidth = node.getLeadingTriviaWidth(); + const leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); + if (survivingImports.length > 0) { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return write(`${leadingTrivia}import ${node.importClause.name.text}, {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); + } + return write(`${leadingTrivia}import {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); + } else { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return write(`${leadingTrivia}import ${node.importClause.name.text} from${node.moduleSpecifier.getFullText(sourceFile)};`); + } + } + } + } else { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return keep(node); + } + } + } + + if (shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { + let toWrite = node.getFullText(); + for (let i = node.members.length - 1; i >= 0; i--) { + const member = node.members[i]; + if (getColor(member) === NodeColor.Black) { + // keep method + continue; + } + if (/^_(.*)Brand$/.test(member.name.getText())) { + // TODO: keep all members ending with `Brand`... + continue; + } + + let pos = member.pos - node.pos; + let end = member.end - node.pos; + toWrite = toWrite.substring(0, pos) + toWrite.substring(end); + } + return write(toWrite); + } + + if (ts.isFunctionDeclaration(node)) { + // Do not go inside functions if they haven't been marked + return; + } + + node.forEachChild(writeMarkedNodes); + } + + if (getColor(sourceFile) !== NodeColor.Black) { + if (!nodeOrChildIsBlack(sourceFile)) { + // none of the elements are reachable => don't write this file at all! + return; + } + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); + } else { + result = text; + } + + writeFile(destination, result); + }); + + return result; +} + +//#endregion + +//#region Utils + +/** + * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) + */ +function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol, ts.Declaration] { + /** + * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 } + */ + /* @internal */ + function getContainingObjectLiteralElement(node: ts.Node): ts.ObjectLiteralElement | undefined { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + if (node.parent.kind === ts.SyntaxKind.ComputedPropertyName) { + return ts.isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined; + } + // falls through + case ts.SyntaxKind.Identifier: + return ts.isObjectLiteralElement(node.parent) && + (node.parent.parent.kind === ts.SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === ts.SyntaxKind.JsxAttributes) && + node.parent.name === node ? node.parent : undefined; + } + return undefined; + } + + function getPropertySymbolsFromType(type: ts.Type, propName: ts.PropertyName) { + function getTextOfPropertyName(name: ts.PropertyName): string { + + function isStringOrNumericLiteral(node: ts.Node): node is ts.StringLiteral | ts.NumericLiteral { + const kind = node.kind; + return kind === ts.SyntaxKind.StringLiteral + || kind === ts.SyntaxKind.NumericLiteral; + } + + switch (name.kind) { + case ts.SyntaxKind.Identifier: + return name.text; + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + return name.text; + case ts.SyntaxKind.ComputedPropertyName: + return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined!; + } + } + + const name = getTextOfPropertyName(propName); + if (name && type) { + const result: ts.Symbol[] = []; + const symbol = type.getProperty(name); + if (type.flags & ts.TypeFlags.Union) { + for (const t of (type).types) { + const symbol = t.getProperty(name); + if (symbol) { + result.push(symbol); + } + } + return result; + } + + if (symbol) { + result.push(symbol); + return result; + } + } + return undefined; + } + + function getPropertySymbolsFromContextualType(typeChecker: ts.TypeChecker, node: ts.ObjectLiteralElement): ts.Symbol[] { + const objectLiteral = node.parent; + const contextualType = typeChecker.getContextualType(objectLiteral)!; + return getPropertySymbolsFromType(contextualType, node.name!)!; + } + + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from an import. + // + function shouldSkipAlias(node: ts.Node, declaration: ts.Node): boolean { + if (node.kind !== ts.SyntaxKind.Identifier) { + return false; + } + if (node.parent === declaration) { + return true; + } + switch (declaration.kind) { + case ts.SyntaxKind.ImportClause: + case ts.SyntaxKind.ImportEqualsDeclaration: + return true; + case ts.SyntaxKind.ImportSpecifier: + return declaration.parent.kind === ts.SyntaxKind.NamedImports; + default: + return false; + } + } + + if (!ts.isShorthandPropertyAssignment(node)) { + if (node.getChildCount() !== 0) { + return [null, null]; + } + } + + let symbol = checker.getSymbolAtLocation(node); + let importNode: ts.Declaration = null; + if (symbol && symbol.flags & ts.SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + const aliased = checker.getAliasedSymbol(symbol); + if (aliased.declarations) { + // We should mark the import as visited + importNode = symbol.declarations[0]; + symbol = aliased; + } + } + + if (symbol) { + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { + symbol = checker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + } + + // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the + // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern + // and return the property declaration for the referenced property. + // For example: + // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" + // + // function bar(onfulfilled: (value: T) => void) { //....} + // interface Test { + // pr/*destination*/op1: number + // } + // bar(({pr/*goto*/op1})=>{}); + if (ts.isPropertyName(node) && ts.isBindingElement(node.parent) && ts.isObjectBindingPattern(node.parent.parent) && + (node === (node.parent.propertyName || node.parent.name))) { + const type = checker.getTypeAtLocation(node.parent.parent); + if (type) { + const propSymbols = getPropertySymbolsFromType(type, node); + if (propSymbols) { + symbol = propSymbols[0]; + } + } + } + + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. + // For example + // interface Props{ + // /*first*/prop1: number + // prop2: boolean + // } + // function Foo(arg: Props) {} + // Foo( { pr/*1*/op1: 10, prop2: false }) + const element = getContainingObjectLiteralElement(node); + if (element && checker.getContextualType(element.parent as ts.Expression)) { + const propertySymbols = getPropertySymbolsFromContextualType(checker, element); + if (propertySymbols) { + symbol = propertySymbols[0]; + } + } + } + + if (symbol && symbol.declarations) { + return [symbol, importNode]; + } + + return [null, null]; +} + +/** Get the token whose text contains the position */ +function getTokenAtPosition(sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node { + let current: ts.Node = sourceFile; + outer: while (true) { + // find the child that contains 'position' + for (const child of current.getChildren()) { + const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true); + if (start > position) { + // If this child begins after position, then all subsequent children will as well. + break; + } + + const end = child.getEnd(); + if (position < end || (position === end && (child.kind === ts.SyntaxKind.EndOfFileToken || includeEndPosition))) { + current = child; + continue outer; + } + } + + return current; + } +} + +//#endregion From d8e13dc71746a33cd826fead7d97468a12b8b87b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 Jul 2018 21:05:38 +0200 Subject: [PATCH 78/89] Add a compile-editor-build task --- .gitignore | 1 + build/gulpfile.editor.js | 5 +++++ build/lib/compilation.js | 40 +++++++++++++++++++++----------------- build/lib/compilation.ts | 42 ++++++++++++++++++++++------------------ build/lib/standalone.js | 10 ++++++++-- build/lib/standalone.ts | 13 ++++++++++--- gulpfile.js | 4 ++-- 7 files changed, 71 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 5c902cc558683..08adb4af663a6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ out/ out-build/ out-editor/ out-editor-src/ +out-editor-build/ out-editor-esm/ out-editor-min/ out-monaco-editor-core/ diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index a6883d2cfe318..5e43659fcaaf4 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -12,6 +12,7 @@ const File = require('vinyl'); const i18n = require('./lib/i18n'); const standalone = require('./lib/standalone'); const cp = require('child_process'); +const compilation = require('./lib/compilation'); var root = path.dirname(__dirname); var sha1 = util.getVersion(root); @@ -115,6 +116,10 @@ gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], languages: languages })); +// Full compile, including nls and inline sources in sourcemaps, for build +gulp.task('clean-editor-build', util.rimraf('out-editor-build')); +gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); + gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 998ebb4f37933..ad73e2dcf7309 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,18 +18,21 @@ var _ = require("underscore"); var monacodts = require("../monaco/api"); var fs = require("fs"); var reporter = reporter_1.createReporter(); -var rootDir = path.join(__dirname, '../../src'); -var options = require('../../src/tsconfig.json').compilerOptions; -options.verbose = false; -options.sourceMap = true; -if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry - options.sourceMap = false; +function getTypeScriptCompilerOptions(src) { + var rootDir = path.join(__dirname, "../../" + src); + var options = require("../../" + src + "/tsconfig.json").compilerOptions; + options.verbose = false; + options.sourceMap = true; + if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry + options.sourceMap = false; + } + options.rootDir = rootDir; + options.sourceRoot = util.toFileUri(rootDir); + options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; + return options; } -options.rootDir = rootDir; -options.sourceRoot = util.toFileUri(rootDir); -options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; -function createCompile(build, emitError) { - var opts = _.clone(options); +function createCompile(src, build, emitError) { + var opts = _.clone(getTypeScriptCompilerOptions(src)); opts.inlineSources = !!build; opts.noFilesystemLookup = true; var ts = tsb.create(opts, null, null, function (err) { return reporter(err.toString()); }); @@ -51,31 +54,31 @@ function createCompile(build, emitError) { .pipe(sourcemaps.write('.', { addComment: false, includeContent: !!build, - sourceRoot: options.sourceRoot + sourceRoot: opts.sourceRoot })) .pipe(tsFilter.restore) .pipe(reporter.end(emitError)); return es.duplex(input, output); }; } -function compileTask(out, build) { +function compileTask(src, out, build) { return function () { - var compile = createCompile(build, true); - var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); + var compile = createCompile(src, build, true); + var srcPipe = es.merge(gulp.src(src + "/**", { base: "" + src }), gulp.src('node_modules/typescript/lib/lib.d.ts')); // Do not write .d.ts files to disk, as they are not needed there. var dtsFilter = util.filter(function (data) { return !/\.d\.ts$/.test(data.path); }); - return src + return srcPipe .pipe(compile()) .pipe(dtsFilter) .pipe(gulp.dest(out)) .pipe(dtsFilter.restore) - .pipe(monacodtsTask(out, false)); + .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } exports.compileTask = compileTask; function watchTask(out, build) { return function () { - var compile = createCompile(build); + var compile = createCompile('src', build); var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); var watchSrc = watch('src/**', { base: 'src' }); // Do not write .d.ts files to disk, as they are not needed there. @@ -122,6 +125,7 @@ function monacodtsTask(out, isWatch) { fs.writeFileSync(result.filePath, result.content); } else { + fs.writeFileSync(result.filePath, result.content); resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); } } diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index cedcb4155b678..33d8c11169093 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -21,19 +21,22 @@ import * as fs from 'fs'; const reporter = createReporter(); -const rootDir = path.join(__dirname, '../../src'); -const options = require('../../src/tsconfig.json').compilerOptions; -options.verbose = false; -options.sourceMap = true; -if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry - options.sourceMap = false; +function getTypeScriptCompilerOptions(src: string) { + const rootDir = path.join(__dirname, `../../${src}`); + const options = require(`../../${src}/tsconfig.json`).compilerOptions; + options.verbose = false; + options.sourceMap = true; + if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry + options.sourceMap = false; + } + options.rootDir = rootDir; + options.sourceRoot = util.toFileUri(rootDir); + options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; + return options; } -options.rootDir = rootDir; -options.sourceRoot = util.toFileUri(rootDir); -options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; -function createCompile(build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { - const opts = _.clone(options); +function createCompile(src: string, build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { + const opts = _.clone(getTypeScriptCompilerOptions(src)); opts.inlineSources = !!build; opts.noFilesystemLookup = true; @@ -59,7 +62,7 @@ function createCompile(build: boolean, emitError?: boolean): (token?: util.ICanc .pipe(sourcemaps.write('.', { addComment: false, includeContent: !!build, - sourceRoot: options.sourceRoot + sourceRoot: opts.sourceRoot })) .pipe(tsFilter.restore) .pipe(reporter.end(emitError)); @@ -68,32 +71,32 @@ function createCompile(build: boolean, emitError?: boolean): (token?: util.ICanc }; } -export function compileTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { +export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile(build, true); + const compile = createCompile(src, build, true); - const src = es.merge( - gulp.src('src/**', { base: 'src' }), + const srcPipe = es.merge( + gulp.src(`${src}/**`, { base: `${src}` }), gulp.src('node_modules/typescript/lib/lib.d.ts'), ); // Do not write .d.ts files to disk, as they are not needed there. const dtsFilter = util.filter(data => !/\.d\.ts$/.test(data.path)); - return src + return srcPipe .pipe(compile()) .pipe(dtsFilter) .pipe(gulp.dest(out)) .pipe(dtsFilter.restore) - .pipe(monacodtsTask(out, false)); + .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile(build); + const compile = createCompile('src', build); const src = es.merge( gulp.src('src/**', { base: 'src' }), @@ -150,6 +153,7 @@ function monacodtsTask(out: string, isWatch: boolean): NodeJS.ReadWriteStream { if (isWatch) { fs.writeFileSync(result.filePath, result.content); } else { + fs.writeFileSync(result.filePath, result.content); resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); } } diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 06b57482ca9bc..885d3e789f5ba 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -42,7 +42,7 @@ function extractEditor(options) { copied[fileName] = true; var srcPath = path.join(options.sourcesRoot, fileName); var dstPath = path.join(options.destRoot, fileName); - fs.writeFileSync(dstPath, fs.readFileSync(srcPath)); + writeFile(dstPath, fs.readFileSync(srcPath)); }; var writeOutputFile = function (fileName, contents) { writeFile(path.join(options.destRoot, fileName), contents); @@ -74,8 +74,10 @@ function extractEditor(options) { } } } + var tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + tsConfig.compilerOptions.noUnusedLocals = false; + writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'tsconfig.json', 'vs/css.build.js', 'vs/css.d.ts', 'vs/css.js', @@ -85,6 +87,10 @@ function extractEditor(options) { 'vs/nls.d.ts', 'vs/nls.js', 'vs/nls.mock.ts', + 'typings/lib.ie11_safe_es6.d.ts', + 'typings/thenable.d.ts', + 'typings/es6-promise.d.ts', + 'typings/require.d.ts', ].forEach(copyFile); } exports.extractEditor = extractEditor; diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 9378dd3e7d953..7931737fadbb2 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -14,7 +14,7 @@ const OUT_EDITOR = path.join(REPO_ROOT, 'out-editor'); let dirCache: { [dir: string]: boolean; } = {}; -function writeFile(filePath: string, contents: string): void { +function writeFile(filePath: string, contents: Buffer | string): void { function ensureDirs(dirPath: string): void { if (dirCache[dirPath]) { return; @@ -46,7 +46,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str copied[fileName] = true; const srcPath = path.join(options.sourcesRoot, fileName); const dstPath = path.join(options.destRoot, fileName); - fs.writeFileSync(dstPath, fs.readFileSync(srcPath)); + writeFile(dstPath, fs.readFileSync(srcPath)); }; const writeOutputFile = (fileName: string, contents: string) => { writeFile(path.join(options.destRoot, fileName), contents); @@ -80,8 +80,11 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str } } + const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + tsConfig.compilerOptions.noUnusedLocals = false; + writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + [ - 'tsconfig.json', 'vs/css.build.js', 'vs/css.d.ts', 'vs/css.js', @@ -91,6 +94,10 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str 'vs/nls.d.ts', 'vs/nls.js', 'vs/nls.mock.ts', + 'typings/lib.ie11_safe_es6.d.ts', + 'typings/thenable.d.ts', + 'typings/es6-promise.d.ts', + 'typings/require.d.ts', ].forEach(copyFile); } diff --git a/gulpfile.js b/gulpfile.js index db6d924ae734a..ebdca25bcb4a0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -15,12 +15,12 @@ const compilation = require('./build/lib/compilation'); // Fast compile for development time gulp.task('clean-client', util.rimraf('out')); -gulp.task('compile-client', ['clean-client'], compilation.compileTask('out', false)); +gulp.task('compile-client', ['clean-client'], compilation.compileTask('src', 'out', false)); gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)); // Full compile, including nls and inline sources in sourcemaps, for build gulp.task('clean-client-build', util.rimraf('out-build')); -gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('out-build', true)); +gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('src', 'out-build', true)); gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('out-build', true)); // Default From 5a52c24f110de090402f4590d7775a4bef1a1f3d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 20 Jul 2018 10:38:56 +0200 Subject: [PATCH 79/89] Add an alternative optimize-editor task --- build/gulpfile.editor.js | 20 ++++++++++++++++++++ build/gulpfile.vscode.js | 1 + build/lib/optimize.js | 29 +++++++++++++++-------------- build/lib/optimize.ts | 33 +++++++++++++++++++-------------- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 5e43659fcaaf4..8dbe685609a0a 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -83,6 +83,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function() { entryPoints: [ 'vs/editor/editor.main', 'vs/editor/editor.worker', + 'vs/base/worker/workerMain', // 'user', // 'user2', ], @@ -105,6 +106,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function() { gulp.task('clean-optimized-editor', util.rimraf('out-editor')); gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], common.optimizeTask({ + src: 'out-build', entryPoints: editorEntryPoints, otherSources: editorOtherSources, resources: editorResources, @@ -120,6 +122,24 @@ gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], gulp.task('clean-editor-build', util.rimraf('out-editor-build')); gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); +gulp.task('optimize-editor2', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ + src: 'out-editor-build', + entryPoints: editorEntryPoints, + otherSources: editorOtherSources, + resources: editorResources, + loaderConfig: { + paths: { + 'vs': 'out-editor-build/vs', + 'vscode': 'empty:' + } + }, + bundleLoader: false, + header: BUNDLED_FILE_HEADER, + bundleInfo: true, + out: 'out-editor', + languages: languages +})); + gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 71c9530041be5..090db00ffebba 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -95,6 +95,7 @@ const BUNDLED_FILE_HEADER = [ gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ + src: 'out-build', entryPoints: vscodeEntryPoints, otherSources: [], resources: vscodeResources, diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 3599086a4b43b..4fafbd800a98a 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -37,19 +37,19 @@ function loaderConfig(emptyPaths) { } exports.loaderConfig = loaderConfig; var IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(bundledFileHeader, bundleLoader) { +function loader(src, bundledFileHeader, bundleLoader) { var sources = [ - 'out-build/vs/loader.js' + src + "/vs/loader.js" ]; if (bundleLoader) { sources = sources.concat([ - 'out-build/vs/css.js', - 'out-build/vs/nls.js' + src + "/vs/css.js", + src + "/vs/nls.js" ]); } var isFirst = true; return (gulp - .src(sources, { base: 'out-build' }) + .src(sources, { base: "" + src }) .pipe(es.through(function (data) { if (isFirst) { isFirst = false; @@ -71,7 +71,7 @@ function loader(bundledFileHeader, bundleLoader) { return f; }))); } -function toConcatStream(bundledFileHeader, sources, dest) { +function toConcatStream(src, bundledFileHeader, sources, dest) { var useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); // If a bundle ends up including in any of the sources our copyright, then // insert a fake source at the beginning of each bundle with our copyright @@ -91,7 +91,7 @@ function toConcatStream(bundledFileHeader, sources, dest) { } var treatedSources = sources.map(function (source) { var root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - var base = source.path ? root + '/out-build' : ''; + var base = source.path ? root + ("/" + src) : ''; return new VinylFile({ path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', base: base, @@ -102,12 +102,13 @@ function toConcatStream(bundledFileHeader, sources, dest) { .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) .pipe(concat(dest)); } -function toBundleStream(bundledFileHeader, bundles) { +function toBundleStream(src, bundledFileHeader, bundles) { return es.merge(bundles.map(function (bundle) { - return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); })); } function optimizeTask(opts) { + var src = opts.src; var entryPoints = opts.entryPoints; var otherSources = opts.otherSources; var resources = opts.resources; @@ -123,7 +124,7 @@ function optimizeTask(opts) { if (err) { return bundlesStream.emit('error', JSON.stringify(err)); } - toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources var filteredResources = resources.slice(); result.cssInlinedResources.forEach(function (resource) { @@ -132,7 +133,7 @@ function optimizeTask(opts) { } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + gulp.src(filteredResources, { base: "" + src }).pipe(resourcesStream); var bundleInfoArray = []; if (opts.bundleInfo) { bundleInfoArray.push(new VinylFile({ @@ -145,9 +146,9 @@ function optimizeTask(opts) { }); var otherSourcesStream = es.through(); var otherSourcesStreamArr = []; - gulp.src(otherSources, { base: 'out-build' }) + gulp.src(otherSources, { base: "" + src }) .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); }, function () { if (!otherSourcesStreamArr.length) { setTimeout(function () { otherSourcesStream.emit('end'); }, 0); @@ -156,7 +157,7 @@ function optimizeTask(opts) { es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); } })); - var result = es.merge(loader(bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); + var result = es.merge(loader(src, bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); return result .pipe(sourcemaps.write('./', { sourceRoot: null, diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 86e8db56a7d6d..78328e3ce2cdb 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -50,21 +50,21 @@ declare class FileSourceMap extends VinylFile { public sourceMap: sm.RawSourceMap; } -function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { +function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { let sources = [ - 'out-build/vs/loader.js' + `${src}/vs/loader.js` ]; if (bundleLoader) { sources = sources.concat([ - 'out-build/vs/css.js', - 'out-build/vs/nls.js' + `${src}/vs/css.js`, + `${src}/vs/nls.js` ]); } let isFirst = true; return ( gulp - .src(sources, { base: 'out-build' }) + .src(sources, { base: `${src}` }) .pipe(es.through(function (data) { if (isFirst) { isFirst = false; @@ -87,7 +87,7 @@ function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWr ); } -function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { +function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); // If a bundle ends up including in any of the sources our copyright, then @@ -110,7 +110,7 @@ function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest const treatedSources = sources.map(function (source) { const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + '/out-build' : ''; + const base = source.path ? root + `/${src}` : ''; return new VinylFile({ path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', @@ -124,13 +124,17 @@ function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest .pipe(concat(dest)); } -function toBundleStream(bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { +function toBundleStream(src:string, bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { return es.merge(bundles.map(function (bundle) { - return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); })); } export interface IOptimizeTaskOpts { + /** + * The folder to read files from. + */ + src: string; /** * (for AMD files, will get bundled and get Copyright treatment) */ @@ -167,6 +171,7 @@ export interface IOptimizeTaskOpts { } export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { + const src = opts.src; const entryPoints = opts.entryPoints; const otherSources = opts.otherSources; const resources = opts.resources; @@ -183,7 +188,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr bundle.bundle(entryPoints, loaderConfig, function (err, result) { if (err) { return bundlesStream.emit('error', JSON.stringify(err)); } - toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources const filteredResources = resources.slice(); @@ -193,7 +198,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + gulp.src(filteredResources, { base: `${src}` }).pipe(resourcesStream); const bundleInfoArray: VinylFile[] = []; if (opts.bundleInfo) { @@ -209,9 +214,9 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr const otherSourcesStream = es.through(); const otherSourcesStreamArr: NodeJS.ReadWriteStream[] = []; - gulp.src(otherSources, { base: 'out-build' }) + gulp.src(otherSources, { base: `${src}` }) .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); }, function () { if (!otherSourcesStreamArr.length) { setTimeout(function () { otherSourcesStream.emit('end'); }, 0); @@ -221,7 +226,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr })); const result = es.merge( - loader(bundledFileHeader, bundleLoader), + loader(src, bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, From 09ec2eb1e4fa805d18fb3c860a5e5b0c7390f8b0 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 20 Jul 2018 11:25:46 +0200 Subject: [PATCH 80/89] Use the new optimize-editor task --- build/gulpfile.editor.js | 48 +++++-------------- build/lib/treeshaking.js | 20 +++++--- build/lib/treeshaking.ts | 25 +++++++--- build/monaco/api.js | 12 ++--- build/monaco/api.ts | 14 +++--- build/monaco/monaco.usage.recipe | 82 ++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 63 deletions(-) create mode 100644 build/monaco/monaco.usage.recipe diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 8dbe685609a0a..562ee1dd7540e 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -13,6 +13,8 @@ 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 fs = require('fs'); var root = path.dirname(__dirname); var sha1 = util.getVersion(root); @@ -59,33 +61,23 @@ var BUNDLED_FILE_HEADER = [ '' ].join('\n'); -function editorLoaderConfig() { - var result = common.loaderConfig(); - - // never ship octicons in editor - result.paths['vs/base/browser/ui/octiconLabel/octiconLabel'] = 'out-build/vs/base/browser/ui/octiconLabel/octiconLabel.mock'; - - // force css inlining to use base64 -- see https://github.com/Microsoft/monaco-editor/issues/148 - result['vs/css'] = { - inlineResources: 'base64', - inlineResourcesLimit: 3000 // see https://github.com/Microsoft/monaco-editor/issues/336 - }; - - return result; -} - const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); gulp.task('clean-editor-src', util.rimraf('out-editor-src')); -gulp.task('extract-editor-src', ['clean-editor-src'], function() { +gulp.task('extract-editor-src', ['clean-editor-src'], function () { + console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); + const apiusages = monacoapi.execute().usageContent; + const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); standalone.extractEditor({ sourcesRoot: path.join(root, 'src'), entryPoints: [ 'vs/editor/editor.main', 'vs/editor/editor.worker', 'vs/base/worker/workerMain', - // 'user', - // 'user2', + ], + inlineEntryPoints: [ + apiusages, + extrausages ], libs: [ `lib.d.ts`, @@ -96,33 +88,19 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function() { }, compilerOptions: { module: 2, // ModuleKind.AMD - // moduleResolution: 'classic' }, - shakeLevel: 1, // 1-InnerFile, 2-ClassMembers + shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers importIgnorePattern: /^vs\/css!/, destRoot: path.join(root, 'out-editor-src') }); }); -gulp.task('clean-optimized-editor', util.rimraf('out-editor')); -gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], common.optimizeTask({ - src: 'out-build', - entryPoints: editorEntryPoints, - otherSources: editorOtherSources, - resources: editorResources, - loaderConfig: editorLoaderConfig(), - bundleLoader: false, - header: BUNDLED_FILE_HEADER, - bundleInfo: true, - out: 'out-editor', - languages: languages -})); - // Full compile, including nls and inline sources in sourcemaps, for build gulp.task('clean-editor-build', util.rimraf('out-editor-build')); gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); -gulp.task('optimize-editor2', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ +gulp.task('clean-optimized-editor', util.rimraf('out-editor')); +gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ src: 'out-editor-build', entryPoints: editorEntryPoints, otherSources: editorOtherSources, diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 05b08276344a8..c77fa12891254 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -16,7 +16,7 @@ var ShakeLevel; })(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); function shake(options) { var languageService = createTypeScriptLanguageService(options); - markNodes(languageService, options.shakeLevel, options.entryPoints.map(function (moduleId) { return moduleId + '.ts'; }), options.importIgnorePattern); + markNodes(languageService, options); return generateResult(languageService, options.shakeLevel); } exports.shake = shake; @@ -24,6 +24,10 @@ exports.shake = shake; function createTypeScriptLanguageService(options) { // Discover referenced files var FILES = discoverAndReadFiles(options); + // Add fake usage files + options.inlineEntryPoints.forEach(function (inlineEntryPoint, index) { + FILES["inlineEntryPoint:" + index + ".ts"] = inlineEntryPoint; + }); // Resolve libs var RESOLVED_LIBS = {}; options.libs.forEach(function (filename) { @@ -166,9 +170,9 @@ function nodeOrChildIsBlack(node) { } return false; } -function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePattern) { +function markNodes(languageService, options) { var program = languageService.getProgram(); - if (shakeLevel === 0 /* Files */) { + if (options.shakeLevel === 0 /* Files */) { // Mark all source files Black program.getSourceFiles().forEach(function (sourceFile) { setColor(sourceFile, 2 /* Black */); @@ -249,7 +253,7 @@ function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePat } setColor(node, 2 /* Black */); black_queue.push(node); - if (shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { var references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); if (references) { for (var i = 0, len = references.length; i < len; i++) { @@ -275,7 +279,7 @@ function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePat enqueue_black(sourceFile); } function enqueueImport(node, importText) { - if (importIgnorePattern.test(importText)) { + if (options.importIgnorePattern.test(importText)) { // this import should be ignored return; } @@ -289,7 +293,9 @@ function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePat } enqueueFile(fullPath); } - entryPointFiles.forEach(function (filename) { return enqueueFile(filename); }); + options.entryPoints.forEach(function (moduleId) { return enqueueFile(moduleId + '.ts'); }); + // Add fake usage files + options.inlineEntryPoints.forEach(function (_, index) { return enqueueFile("inlineEntryPoint:" + index + ".ts"); }); var step = 0; var checker = program.getTypeChecker(); var _loop_1 = function () { @@ -330,7 +336,7 @@ function markNodes(languageService, shakeLevel, entryPointFiles, importIgnorePat // (they can be the declaration of a module import) continue; } - if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { enqueue_black(declaration.name); for (var j = 0; j < declaration.members.length; j++) { var member = declaration.members[j]; diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index ef82fa2ff2340..0527fe3ebce89 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -27,6 +27,10 @@ export interface ITreeShakingOptions { * e.g. `vs/editor/editor.main` or `index` */ entryPoints: string[]; + /** + * Inline usages. + */ + inlineEntryPoints: string[]; /** * TypeScript libs. * e.g. `lib.d.ts`, `lib.es2015.collection.d.ts` @@ -55,7 +59,7 @@ export interface ITreeShakingResult { export function shake(options: ITreeShakingOptions): ITreeShakingResult { const languageService = createTypeScriptLanguageService(options); - markNodes(languageService, options.shakeLevel, options.entryPoints.map(moduleId => moduleId + '.ts'), options.importIgnorePattern); + markNodes(languageService, options); return generateResult(languageService, options.shakeLevel); } @@ -65,6 +69,11 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu // Discover referenced files const FILES = discoverAndReadFiles(options); + // Add fake usage files + options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { + FILES[`inlineEntryPoint:${index}.ts`] = inlineEntryPoint; + }); + // Resolve libs const RESOLVED_LIBS: ILibMap = {}; options.libs.forEach((filename) => { @@ -229,10 +238,10 @@ function nodeOrChildIsBlack(node: ts.Node): boolean { return false; } -function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, entryPointFiles: string[], importIgnorePattern: RegExp) { +function markNodes(languageService: ts.LanguageService, options: ITreeShakingOptions) { const program = languageService.getProgram(); - if (shakeLevel === ShakeLevel.Files) { + if (options.shakeLevel === ShakeLevel.Files) { // Mark all source files Black program.getSourceFiles().forEach((sourceFile) => { setColor(sourceFile, NodeColor.Black); @@ -335,7 +344,7 @@ function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, setColor(node, NodeColor.Black); black_queue.push(node); - if (shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); if (references) { for (let i = 0, len = references.length; i < len; i++) { @@ -365,7 +374,7 @@ function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, } function enqueueImport(node: ts.Node, importText: string): void { - if (importIgnorePattern.test(importText)) { + if (options.importIgnorePattern.test(importText)) { // this import should be ignored return; } @@ -380,7 +389,9 @@ function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, enqueueFile(fullPath); } - entryPointFiles.forEach((filename) => enqueueFile(filename)); + options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); + // Add fake usage files + options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint:${index}.ts`)); let step = 0; @@ -429,7 +440,7 @@ function markNodes(languageService: ts.LanguageService, shakeLevel: ShakeLevel, continue; } - if (shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { enqueue_black(declaration.name); for (let j = 0; j < declaration.members.length; j++) { diff --git a/build/monaco/api.js b/build/monaco/api.js index f75223f9329b8..ae4a0d266164a 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -450,12 +450,12 @@ function execute() { var t = Date.now(); var emitOutput = languageService.getEmitOutput(fileName, true); OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - console.log("Generating .d.ts for " + fileName + " took " + (Date.now() - t) + " ms"); + // console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`); }); console.log("Generating .d.ts took " + (Date.now() - t1) + " ms"); - var result = run('src', OUTPUT_FILES); - console.log(result.filePath); - fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); - fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); + // console.log(result.filePath); + // fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); + // fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); + return run('src', OUTPUT_FILES); } -// execute(); +exports.execute = execute; diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 2638534d40d0c..64ca2243b5887 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -512,7 +512,7 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { } } -function execute() { +export function execute(): IMonacoDeclarationResult { const OUTPUT_FILES: { [file: string]: string; } = {}; const SRC_FILES: IFileMap = {}; @@ -536,15 +536,13 @@ function execute() { var t = Date.now(); const emitOutput = languageService.getEmitOutput(fileName, true); OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`); + // console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`); }); console.log(`Generating .d.ts took ${Date.now() - t1} ms`); - const result = run('src', OUTPUT_FILES); + // console.log(result.filePath); + // fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); + // fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); - console.log(result.filePath); - fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); - fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); + return run('src', OUTPUT_FILES); } - -// execute(); diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe new file mode 100644 index 0000000000000..05377a19ba09f --- /dev/null +++ b/build/monaco/monaco.usage.recipe @@ -0,0 +1,82 @@ + +// This file is adding references to various symbols which should not be removed via tree shaking + +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { SimpleWorkerClient, create as create1 } from 'vs/base/common/worker/simpleWorker'; +import { create as create2 } from 'vs/editor/common/services/editorSimpleWorker'; +import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; +import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from 'vs/platform/instantiation/common/descriptors'; +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; +import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; +import * as editorAPI from 'vs/editor/editor.api'; + +(function () { + var a: any; + var b: any; + a = (b).layout; // IContextViewProvider + a = (b).getWorkspaceFolder; // IWorkspaceFolderProvider + a = (b).getWorkspace; // IWorkspaceFolderProvider + a = (b).style; // IThemable + a = (b).style; // IThemable + a = (b).userHome; // IUserHomeProvider + a = (b).previous; // IDiffNavigator + a = (>b).type; + a = (b).start; + a = (b).end; + a = (>b).getProxyObject; // IWorkerClient + a = create1; + a = create2; + + // promise polyfill + a = PolyfillPromise.all; + a = PolyfillPromise.race; + a = PolyfillPromise.resolve; + a = PolyfillPromise.reject; + a = (b).then; + a = (b).catch; + + // injection madness + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + + // exported API + a = editorAPI.CancellationTokenSource; + a = editorAPI.Emitter; + a = editorAPI.KeyCode; + a = editorAPI.KeyMod; + a = editorAPI.Position; + a = editorAPI.Range; + a = editorAPI.Selection; + a = editorAPI.SelectionDirection; + a = editorAPI.Severity; + a = editorAPI.MarkerSeverity; + a = editorAPI.MarkerTag; + a = editorAPI.Promise; + a = editorAPI.Uri; + a = editorAPI.Token; + a = editorAPI.editor; + a = editorAPI.languages; +})(); From 1f90bac9e08f2c1c62d0084fcd31a650323673da Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 20 Jul 2018 11:31:10 +0200 Subject: [PATCH 81/89] debug: remove not in debug repl context key --- src/vs/workbench/parts/debug/browser/debugCommands.ts | 4 ++-- .../workbench/parts/debug/browser/debugEditorActions.ts | 8 ++++---- src/vs/workbench/parts/debug/common/debug.ts | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugCommands.ts b/src/vs/workbench/parts/debug/browser/debugCommands.ts index 62d1e1ee20d28..bc62db248c546 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -11,7 +11,7 @@ import * as errors from 'vs/base/common/errors'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel'; import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -239,7 +239,7 @@ export function registerCommands(): void { id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint") }, - when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable), + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, EditorContextKeys.editorTextFocus), group: 'debug', order: 1 }); diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index a243ca8e26d2d..a8e56c2bc4c73 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -10,7 +10,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -101,7 +101,7 @@ class RunToCursorAction extends EditorAction { id: 'editor.debug.action.runToCursor', label: nls.localize('runToCursor', "Run to Cursor"), alias: 'Debug: Run to Cursor', - precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 2 @@ -144,7 +144,7 @@ class SelectionToReplAction extends EditorAction { id: 'editor.debug.action.selectionToRepl', label: nls.localize('debugEvaluate', "Debug: Evaluate"), alias: 'Debug: Evaluate', - precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 0 @@ -170,7 +170,7 @@ class SelectionToWatchExpressionsAction extends EditorAction { id: 'editor.debug.action.selectionToWatch', label: nls.localize('debugAddToWatch', "Debug: Add to Watch"), alias: 'Debug: Add to Watch', - precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 1 diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 7b6338bcad8e8..8db643264cde8 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -17,7 +17,7 @@ import { Position } from 'vs/editor/common/core/position'; import { ISuggestion } from 'vs/editor/common/modes'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -40,7 +40,6 @@ export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', 'inac export const CONTEXT_NOT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.isEqualTo('inactive'); export const CONTEXT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.notEqualsTo('inactive'); export const CONTEXT_IN_DEBUG_REPL = new RawContextKey('inDebugRepl', false); -export const CONTEXT_NOT_IN_DEBUG_REPL: ContextKeyExpr = CONTEXT_IN_DEBUG_REPL.toNegated(); export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey('breakpointWidgetVisible', false); export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey('inBreakpointWidget', false); export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey('breakpointsFocused', true); From e7058655de73b5a19a5b4286079ce68cb7c13523 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 Jul 2018 15:02:16 +0200 Subject: [PATCH 82/89] Fix #54721 --- .../issue/issueReporterMain.ts | 2 +- .../environment/common/environment.ts | 5 ++- src/vs/platform/environment/node/argv.ts | 2 + .../environment/node/environmentService.ts | 16 ++++++- .../common/extensionEnablementService.ts | 44 ++++++++++++++----- .../common/extensionManagement.ts | 4 +- .../common/extensionEnablementService.test.ts | 32 ++++++++++++-- src/vs/platform/windows/common/windows.ts | 4 +- src/vs/platform/windows/common/windowsIpc.ts | 4 +- .../windows/electron-browser/windowService.ts | 2 +- .../windows/electron-main/windowsService.ts | 4 +- .../electron-browser/mainThreadWorkspace.ts | 15 ++++++- src/vs/workbench/api/node/extHost.protocol.ts | 1 - .../electron-browser/extensionHost.ts | 1 - .../electron-browser/extensionService.ts | 12 ++--- 15 files changed, 113 insertions(+), 35 deletions(-) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index cc305121e2cb4..a642157cdcafc 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -86,7 +86,7 @@ export class IssueReporter extends Disposable { vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, os: `${os.type()} ${os.arch()} ${os.release()}` }, - extensionsDisabled: this.environmentService.disableExtensions, + extensionsDisabled: !!this.environmentService.disableExtensions, }); this.previewButton = new Button(document.getElementById('issue-reporter')); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 99792a19f0034..f592536793e82 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -29,7 +29,6 @@ export interface ParsedArgs { verbose?: boolean; log?: string; logExtensionHostCommunication?: boolean; - 'disable-extensions'?: boolean; 'extensions-dir'?: string; extensionDevelopmentPath?: string; extensionTestsPath?: string; @@ -38,6 +37,8 @@ export interface ParsedArgs { debugId?: string; debugSearch?: string; debugBrkSearch?: string; + 'disable-extensions'?: boolean; + 'disable-extension'?: string | string[]; 'list-extensions'?: boolean; 'show-versions'?: boolean; 'install-extension'?: string | string[]; @@ -100,7 +101,7 @@ export interface IEnvironmentService { workspacesHome: string; isExtensionDevelopment: boolean; - disableExtensions: boolean; + disableExtensions: boolean | string[]; extensionsPath: string; extensionDevelopmentPath: string; extensionTestsPath: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3a8b2891705f1..efbfbe36d4e2c 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -21,6 +21,7 @@ const options: minimist.Opts = { 'extensionDevelopmentPath', 'extensionTestsPath', 'install-extension', + 'disable-extension', 'uninstall-extension', 'debugId', 'debugPluginHost', @@ -170,6 +171,7 @@ const troubleshootingHelp: { [name: string]: string; } = { '-p, --performance': localize('performance', "Start with the 'Developer: Startup Performance' command enabled."), '--prof-startup': localize('prof-startup', "Run CPU profiler during startup"), '--disable-extensions': localize('disableExtensions', "Disable all installed extensions."), + '--disable-extension ': localize('disableExtension', "Disable an extension."), '--inspect-extensions': localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI."), '--inspect-brk-extensions': localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI."), '--disable-gpu': localize('disableGPU', "Disable GPU hardware acceleration."), diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 06e2171dfb51b..9ef3481c43d6a 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -153,7 +153,21 @@ export class EnvironmentService implements IEnvironmentService { @memoize get extensionTestsPath(): string { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } - get disableExtensions(): boolean { return this._args['disable-extensions']; } + get disableExtensions(): boolean | string[] { + if (this._args['disable-extensions']) { + return true; + } + const disableExtensions: string | string[] = this._args['disable-extension']; + if (disableExtensions) { + if (typeof disableExtensions === 'string') { + return [disableExtensions]; + } + if (Array.isArray(disableExtensions) && disableExtensions.length > 0) { + return disableExtensions; + } + } + return false; + } get skipGettingStarted(): boolean { return this._args['skip-getting-started']; } diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 28c6ce6805f06..f56f3aabbbf2f 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -29,7 +29,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { @IStorageService private storageService: IStorageService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IEnvironmentService private environmentService: IEnvironmentService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService + @IExtensionManagementService private extensionManagementService: IExtensionManagementService ) { extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this, this.disposables); } @@ -38,7 +38,11 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; } - getDisabledExtensions(): TPromise { + get allUserExtensionsDisabled(): boolean { + return this.environmentService.disableExtensions === true; + } + + async getDisabledExtensions(): Promise { let result = this._getDisabledExtensions(StorageScope.GLOBAL); @@ -54,14 +58,25 @@ export class ExtensionEnablementService implements IExtensionEnablementService { } } - return TPromise.as(result); + if (this.environmentService.disableExtensions) { + const allInstalledExtensions = await this.extensionManagementService.getInstalled(); + for (const installedExtension of allInstalledExtensions) { + if (this._isExtensionDisabledInEnvironment(installedExtension)) { + if (!result.some(r => areSameExtensions(r, installedExtension.galleryIdentifier))) { + result.push(installedExtension.galleryIdentifier); + } + } + } + } + + return result; } getEnablementState(extension: ILocalExtension): EnablementState { - if (this.environmentService.disableExtensions && extension.type === LocalExtensionType.User) { + if (this._isExtensionDisabledInEnvironment(extension)) { return EnablementState.Disabled; } - const identifier = this._getIdentifier(extension); + const identifier = extension.galleryIdentifier; if (this.hasWorkspace) { if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) { return EnablementState.WorkspaceEnabled; @@ -95,7 +110,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { if (!this.canChangeEnablement(arg)) { return TPromise.wrap(false); } - identifier = this._getIdentifier(arg); + identifier = arg.galleryIdentifier; } const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled; @@ -134,6 +149,17 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return enablementState === EnablementState.WorkspaceEnabled || enablementState === EnablementState.Enabled; } + private _isExtensionDisabledInEnvironment(extension: ILocalExtension): boolean { + if (this.allUserExtensionsDisabled) { + return extension.type === LocalExtensionType.User; + } + const disabledExtensions = this.environmentService.disableExtensions; + if (Array.isArray(disabledExtensions)) { + return disabledExtensions.some(id => areSameExtensions({ id }, extension.galleryIdentifier)); + } + return false; + } + private _getEnablementState(identifier: IExtensionIdentifier): EnablementState { if (this.hasWorkspace) { if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) { @@ -150,10 +176,6 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return EnablementState.Enabled; } - private _getIdentifier(extension: ILocalExtension): IExtensionIdentifier { - return { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid }; - } - private _enableExtension(identifier: IExtensionIdentifier): void { this._removeFromDisabledExtensions(identifier, StorageScope.WORKSPACE); this._removeFromEnabledExtensions(identifier, StorageScope.WORKSPACE); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index eaa3ec6ed6f55..e30d7e8787e73 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -343,6 +343,8 @@ export const IExtensionEnablementService = createDecorator; + getDisabledExtensions(): Promise; /** * Returns the enablement state for the given extension diff --git a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts index 717a17dd3b0e7..78759e1933292 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts @@ -35,10 +35,11 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { constructor(instantiationService: TestInstantiationService) { super(storageService(instantiationService), instantiationService.get(IWorkspaceContextService), instantiationService.get(IEnvironmentService) || instantiationService.stub(IEnvironmentService, {} as IEnvironmentService), - instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: new Emitter() })); + instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, + { onDidUninstallExtension: new Emitter().event } as IExtensionManagementService)); } - public reset(): TPromise { + public reset(): Promise { return this.getDisabledExtensions().then(extensions => extensions.forEach(d => this.setEnablement(aLocalExtension(d.id), EnablementState.Enabled))); } } @@ -52,7 +53,7 @@ suite('ExtensionEnablementService Test', () => { setup(() => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event }); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([]) } as IExtensionManagementService); testObject = new TestExtensionEnablementService(instantiationService); }); @@ -331,6 +332,12 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false); }); + test('test canChangeEnablement return false when the extension is disabled in environment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false); + }); + test('test canChangeEnablement return true for system extensions when extensions are disabled in environment', () => { instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService); testObject = new TestExtensionEnablementService(instantiationService); @@ -338,6 +345,25 @@ suite('ExtensionEnablementService Test', () => { extension.type = LocalExtensionType.System; assert.equal(testObject.canChangeEnablement(extension), true); }); + + test('test canChangeEnablement return false for system extensions when extension is disabled in environment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + testObject = new TestExtensionEnablementService(instantiationService); + const extension = aLocalExtension('pub.a'); + extension.type = LocalExtensionType.System; + assert.equal(testObject.canChangeEnablement(extension), true); + }); + + test('test getDisabledExtensions include extensions disabled in enviroment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService); + testObject = new TestExtensionEnablementService(instantiationService); + return testObject.getDisabledExtensions() + .then(actual => { + assert.equal(actual.length, 1); + assert.equal(actual[0].id, 'pub.a'); + }); + }); }); function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 6964c04bf3e47..1700e900b6984 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -156,7 +156,7 @@ export interface IWindowsService { toggleSharedProcess(): TPromise; // Global methods - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise; + openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; openNewWindow(): TPromise; showWindow(windowId: number): TPromise; getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]>; @@ -209,7 +209,7 @@ export interface IWindowService { getRecentlyOpened(): TPromise; focusWindow(): TPromise; closeWindow(): TPromise; - openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise; + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; isFocused(): TPromise; setDocumentEdited(flag: boolean): TPromise; isMaximized(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 3fde497f0177e..b60c6ecf2798e 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -59,7 +59,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'onWindowTitleDoubleClick', arg: number): TPromise; call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise; call(command: 'quit'): TPromise; - call(command: 'openWindow', arg: [number, string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }]): TPromise; + call(command: 'openWindow', arg: [number, string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }]): TPromise; call(command: 'openNewWindow'): TPromise; call(command: 'showWindow', arg: number): TPromise; call(command: 'getWindows'): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]>; @@ -344,7 +344,7 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('toggleSharedProcess'); } - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { return this.channel.call('openWindow', [windowId, paths, options]); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 837aee8581d15..e9ba9f2d0c308 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -93,7 +93,7 @@ export class WindowService implements IWindowService { return this.windowsService.saveAndEnterWorkspace(this.windowId, path); } - openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise { + openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { return this.windowsService.openWindow(this.windowId, paths, options); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index c9e45bedb107e..c47648628ea0b 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -392,7 +392,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { this.logService.trace('windowsService#openWindow'); if (!paths || !paths.length) { return TPromise.as(null); @@ -401,7 +401,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable this.windowsMainService.open({ context: OpenContext.API, contextWindowId: windowId, - cli: this.environmentService.args, + cli: options && options.args ? { ...this.environmentService.args, ...options.args } : this.environmentService.args, pathsToOpen: paths, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow, diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 59c54e164db9a..6e5d3309ac4aa 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -20,6 +20,9 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IWindowService } from 'vs/platform/windows/common/windows'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -213,8 +216,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { } } -CommandsRegistry.registerCommand('_workbench.enterWorkspace', function (accessor: ServicesAccessor, workspace: URI) { +CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) { const workspaceEditingService = accessor.get(IWorkspaceEditingService); + const extensionService = accessor.get(IExtensionService); + const windowService = accessor.get(IWindowService); + + if (disableExtensions && disableExtensions.length) { + const runningExtensions = await extensionService.getExtensions(); + // If requested extension to disable is running, then reload window with given workspace + if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => areSameExtensions({ id }, { id: runningExtension.id })))) { + return windowService.openWindow([workspace.fsPath], { args: { _: [], 'disable-extension': disableExtensions } }); + } + } return workspaceEditingService.enterWorkspace(workspace.fsPath); }); \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 122e6c14fb8ea..44947586fd539 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -46,7 +46,6 @@ export interface IEnvironment { isExtensionDevelopmentDebug: boolean; appRoot: string; appSettingsHome: string; - disableExtensions: boolean; extensionDevelopmentPath: string; extensionTestsPath: string; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index c7250d5cc51dd..574136bbcf573 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -369,7 +369,6 @@ export class ExtensionHostProcessWorker { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot, appSettingsHome: this._environmentService.appSettingsHome, - disableExtensions: this._environmentService.disableExtensions, extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, extensionTestsPath: this._environmentService.extensionTestsPath }, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index c2f0c78bfe2c2..45c442c325bb9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -281,8 +281,8 @@ export class ExtensionService extends Disposable implements IExtensionService { this.startDelayed(lifecycleService); - if (this._environmentService.disableExtensions) { - this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All extensions are temporarily disabled. Reload the window to return to the previous state."), [{ + if (this._extensionEnablementService.allUserExtensionsDisabled) { + this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ label: nls.localize('Reload', "Reload"), run: () => { this._windowService.reloadWindow(); @@ -513,7 +513,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(severity, this._isDev ? messageWithSource(source, message) : message); }); - return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, log) + return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log) .then(({ system, user, development }) => { let result: { [extensionId: string]: IExtensionDescription; } = {}; system.forEach((systemExtension) => { @@ -536,7 +536,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }); } - private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): TPromise { + private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { return this._extensionEnablementService.getDisabledExtensions() .then(disabledExtensions => { @@ -759,7 +759,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return result; } - private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { + private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, extensionEnablementService: IExtensionEnablementService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { const translationConfig: TPromise = platform.translationsConfigFile ? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => { @@ -831,7 +831,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } const userExtensions = ( - environmentService.disableExtensions || !environmentService.extensionsPath + extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath ? TPromise.as([]) : this._scanExtensionsWithCache( windowService, From ed2bbf3ac0060a1a1fea0438191397409aa81ea3 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Fri, 20 Jul 2018 15:47:54 +0200 Subject: [PATCH 83/89] Task part of #54510 --- .../parts/menubar/menubar.contribution.ts | 70 ---------------- src/vs/workbench/parts/tasks/common/tasks.ts | 3 + .../electron-browser/task.contribution.ts | 84 ++++++++++++++++++- 3 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts index 3dbe2a29b129d..6c2e0b4adfc20 100644 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts @@ -10,7 +10,6 @@ import { isMacintosh } from 'vs/base/common/platform'; editMenuRegistration(); selectionMenuRegistration(); goMenuRegistration(); -tasksMenuRegistration(); if (isMacintosh) { windowMenuRegistration(); @@ -447,75 +446,6 @@ function goMenuRegistration() { }); } -function tasksMenuRegistration() { - // Run Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', - command: { - id: 'workbench.action.tasks.runTask', - title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', - command: { - id: 'workbench.action.tasks.build', - title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") - }, - order: 2 - }); - - // Manage Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.showTasks', - title: nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.restartTask', - title: nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.terminate', - title: nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task...") - }, - order: 3 - }); - - // Configure Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', - command: { - id: 'workbench.action.tasks.configureTaskRunner', - title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', - command: { - id: 'workbench.action.tasks.configureDefaultBuildTask', - title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") - }, - order: 2 - }); - -} - function windowMenuRegistration() { } diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 7d92c1af7da1e..9a771032c99ce 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -12,6 +12,9 @@ import { UriComponents } from 'vs/base/common/uri'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ProblemMatcher } from 'vs/workbench/parts/tasks/common/problemMatcher'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false); export enum ShellQuoting { /** diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 1f2742364268b..c8f2b40f41636 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -31,7 +31,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -75,7 +75,7 @@ import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, T import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, - TaskSorter, TaskIdentifier, KeyedTaskIdentifier + TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; @@ -459,6 +459,8 @@ class TaskService implements ITaskService { private _taskSystemListener: IDisposable; private _recentlyUsedTasks: LinkedMap; + private _taskRunningState: IContextKey; + private _outputChannel: IOutputChannel; private readonly _onDidStateChange: Emitter; @@ -482,7 +484,9 @@ class TaskService implements ITaskService { @IOpenerService private openerService: IOpenerService, @IWindowService private readonly _windowService: IWindowService, @IDialogService private dialogService: IDialogService, - @INotificationService private notificationService: INotificationService + @INotificationService private notificationService: INotificationService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { this._configHasErrors = false; this._workspaceTasksPromise = undefined; @@ -521,6 +525,7 @@ class TaskService implements ITaskService { this.updateSetup(folderSetup); this.updateWorkspaceTasks(); }); + this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); this._onDidStateChange = new Emitter(); this.registerCommands(); @@ -1292,6 +1297,9 @@ class TaskService implements ITaskService { this._taskSystem = system; } this._taskSystemListener = this._taskSystem.onDidStateChange((event) => { + if (this._taskSystem) { + this._taskRunningState.set(this._taskSystem.isActiveSync()); + } this._onDidStateChange.fire(event); }); return this._taskSystem; @@ -2420,6 +2428,75 @@ class TaskService implements ITaskService { } } +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '1_run', + command: { + id: 'workbench.action.tasks.runTask', + title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '1_run', + command: { + id: 'workbench.action.tasks.build', + title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") + }, + order: 2 +}); + +// Manage Tasks +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.showTasks', + title: nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.restartTask', + title: nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.terminate', + title: nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task...") + }, + order: 3 +}); + +// Configure Tasks +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '3_configure', + command: { + id: 'workbench.action.tasks.configureTaskRunner', + title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '3_configure', + command: { + id: 'workbench.action.tasks.configureDefaultBuildTask', + title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") + }, + order: 2 +}); + + MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); @@ -2488,6 +2565,7 @@ let schema: IJSONSchema = { import schemaVersion1 from './jsonSchema_v1'; import schemaVersion2 from './jsonSchema_v2'; import { TaskDefinitionRegistry } from 'vs/workbench/parts/tasks/common/taskDefinitionRegistry'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; schema.definitions = { ...schemaVersion1.definitions, ...schemaVersion2.definitions, From 99ba40165b424a6a7b22b41f5aaa54f2a88065ec Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 Jul 2018 15:50:12 +0200 Subject: [PATCH 84/89] Fix tests --- .../test/common/extensionEnablementService.test.ts | 3 ++- .../test/electron-browser/extensionsActions.test.ts | 7 ++++--- .../electron-browser/extensionsWorkbenchService.test.ts | 7 ++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts index 78759e1933292..655d4b05b5027 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts @@ -39,7 +39,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { { onDidUninstallExtension: new Emitter().event } as IExtensionManagementService)); } - public reset(): Promise { + public async reset(): Promise { return this.getDisabledExtensions().then(extensions => extensions.forEach(d => this.setEnablement(aLocalExtension(d.id), EnablementState.Enabled))); } } @@ -370,6 +370,7 @@ function aLocalExtension(id: string, contributes?: IExtensionContributions): ILo const [publisher, name] = id.split('.'); return Object.create({ identifier: { id }, + galleryIdentifier: { id, uuid: void 0 }, manifest: { name, publisher, diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 926f041931e7f..1c34aeeffa7d2 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -16,7 +16,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; @@ -79,12 +79,12 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(IURLService, URLService); }); - setup(() => { + setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stub(IExtensionService, { getExtensions: () => TPromise.wrap([]) }); - (instantiationService.get(IExtensionEnablementService)).reset(); + await (instantiationService.get(IExtensionEnablementService)).reset(); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); }); @@ -1207,6 +1207,7 @@ suite('ExtensionsActions Test', () => { assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: void 0 }; return localExtension; } diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index f3f0e333ec457..704d31d312aa7 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -17,7 +17,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; @@ -82,13 +82,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IDialogService, { show: () => TPromise.as(0) }); }); - setup(() => { + setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stub(IDialogService, { show: () => TPromise.as(0) }); instantiationService.stubPromise(INotificationService, 'prompt', 0); - (instantiationService.get(IExtensionEnablementService)).reset(); + await (instantiationService.get(IExtensionEnablementService)).reset(); }); teardown(() => { @@ -1238,6 +1238,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: void 0 }; return localExtension; } From 5b8a81742a562811acd2e0603eced8fafdb8e5d7 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 20 Jul 2018 16:13:09 +0200 Subject: [PATCH 85/89] markers: display paths relative to the workspace root --- .../parts/markers/electron-browser/markersTreeViewer.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 0436d143f3b3c..4b8f13c73de75 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -98,7 +98,8 @@ export class Renderer implements IRenderer { constructor( @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, - @IEnvironmentService private environmentService: IEnvironmentService + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { } @@ -203,7 +204,7 @@ export class Renderer implements IRenderer { if (templateData.resourceLabel instanceof FileLabel) { templateData.resourceLabel.setFile(element.uri, { matches: element.uriMatches }); } else { - templateData.resourceLabel.setLabel({ name: element.name, description: getPathLabel(element.uri, this.environmentService), resource: element.uri }, { matches: element.uriMatches }); + templateData.resourceLabel.setLabel({ name: element.name, description: getPathLabel(element.uri, this.environmentService, this.contextService), resource: element.uri }, { matches: element.uriMatches }); } (templateData).count.setCount(element.filteredCount); } @@ -230,7 +231,7 @@ export class Renderer implements IRenderer { private renderRelatedInfoElement(tree: ITree, element: RelatedInformation, templateData: IRelatedInformationTemplateData) { templateData.resourceLabel.set(paths.basename(element.raw.resource.fsPath), element.uriMatches); - templateData.resourceLabel.element.title = getPathLabel(element.raw.resource, this.environmentService); + templateData.resourceLabel.element.title = getPathLabel(element.raw.resource, this.environmentService, this.contextService); templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.raw.startLineNumber, element.raw.startColumn); templateData.description.set(element.raw.message, element.messageMatches); templateData.description.element.title = element.raw.message; From dab93d371a90d87a7aabf887b71aaaf8b028582a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 Jul 2018 17:56:15 +0200 Subject: [PATCH 86/89] #54483 Forward compatibility --- .../electron-main/historyMainService.ts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 9b7309f7c55ba..dfb4c357ae84a 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -16,11 +16,13 @@ import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; import { IPath } from 'vs/platform/windows/common/windows'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceSavedEvent } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceSavedEvent, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isEqual } from 'vs/base/common/paths'; import { RunOnceScheduler } from 'vs/base/common/async'; +import URI from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; export class HistoryMainService implements IHistoryMainService { @@ -179,7 +181,7 @@ export class HistoryMainService implements IHistoryMainService { let files: string[]; // Get from storage - const storedRecents = this.stateService.getItem(HistoryMainService.recentlyOpenedStorageKey); + const storedRecents = this.getRecentlyOpenedFromStorage(); if (storedRecents) { workspaces = storedRecents.workspaces || []; files = storedRecents.files || []; @@ -216,6 +218,24 @@ export class HistoryMainService implements IHistoryMainService { return workspaceOrFile.id; } + private getRecentlyOpenedFromStorage(): IRecentlyOpened { + const storedRecents: IRecentlyOpened = this.stateService.getItem(HistoryMainService.recentlyOpenedStorageKey) || { workspaces: [], files: [] }; + const result: IRecentlyOpened = { workspaces: [], files: storedRecents.files }; + for (const workspace of storedRecents.workspaces) { + if (typeof workspace === 'string') { + result.workspaces.push(workspace); + } else if (isWorkspaceIdentifier(workspace)) { + result.workspaces.push(workspace); + } else { + const uri = URI.revive(workspace); + if (uri.scheme === Schemas.file) { + result.workspaces.push(uri.fsPath); + } + } + } + return result; + } + private saveRecentlyOpened(recent: IRecentlyOpened): void { this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, recent); } From 7b715524f770e1c611745dc1e700c14822b0db41 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 Jul 2018 18:05:10 +0200 Subject: [PATCH 87/89] #54483 Forward compatibility --- src/vs/code/electron-main/windows.ts | 35 ++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1303639321131..a116107708204 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -34,7 +34,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; import { normalizeNFC } from 'vs/base/common/normalization'; -import URI from 'vs/base/common/uri'; +import URI, { UriComponents } from 'vs/base/common/uri'; import { Queue } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; @@ -54,6 +54,11 @@ interface IWindowState { uiState: ISingleWindowState; } +interface IBackwardCompatibleWindowState extends IWindowState { + folderUri?: UriComponents; +} + + interface IWindowsState { lastActiveWindow?: IWindowState; lastPluginDevelopmentHostWindow?: IWindowState; @@ -144,7 +149,7 @@ export class WindowsManager implements IWindowsMainService { @IWorkspacesMainService private workspacesMainService: IWorkspacesMainService, @IInstantiationService private instantiationService: IInstantiationService ) { - this.windowsState = this.stateService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + this.windowsState = this.getWindowsState(); if (!Array.isArray(this.windowsState.openedWindows)) { this.windowsState.openedWindows = []; } @@ -153,6 +158,32 @@ export class WindowsManager implements IWindowsMainService { this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this); } + private getWindowsState(): IWindowsState { + const windowsState = this.stateService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + if (windowsState.lastActiveWindow) { + windowsState.lastActiveWindow = this.revive(windowsState.lastActiveWindow); + } + if (windowsState.lastPluginDevelopmentHostWindow) { + windowsState.lastPluginDevelopmentHostWindow = this.revive(windowsState.lastPluginDevelopmentHostWindow); + } + if (windowsState.openedWindows) { + windowsState.openedWindows = arrays.coalesce(windowsState.openedWindows.map(windowState => this.revive(windowState))); + } + return windowsState; + } + + private revive(windowState: IWindowState): IWindowState { + if ((windowState).folderUri) { + const uri = URI.revive((windowState).folderUri); + if (uri.scheme === Schemas.file) { + windowState.folderPath = uri.fsPath; + } else { + return null; + } + } + return windowState; + } + ready(initialUserEnv: IProcessEnvironment): void { this.initialUserEnv = initialUserEnv; From 569c21de607f3165a2cedb94298a717d5475e1a7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Jul 2018 16:22:19 -0700 Subject: [PATCH 88/89] Settings editor - don't write empty enumDescriptions --- src/vs/workbench/parts/preferences/browser/settingsTree.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 025c9e9e97b20..bb396a8d92bb7 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -849,7 +849,8 @@ export class SettingsRenderer implements IRenderer { const enumDescriptionText = element.setting.enumDescriptions && element.setting.enum && element.setting.enum.length < SettingsRenderer.MAX_ENUM_DESCRIPTIONS ? '\n' + element.setting.enumDescriptions - .map((desc, i) => ` - \`${element.setting.enum[i]}\`: ${desc}`) + .map((desc, i) => desc && ` - \`${element.setting.enum[i]}\`: ${desc}`) + .filter(desc => !!desc) .join('\n') : ''; const descriptionText = element.description + enumDescriptionText; From 1fad9cb846df9416ed9238c998c21ddc200a331b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Jul 2018 16:22:57 -0700 Subject: [PATCH 89/89] Settings editor - clean some setting descriptions, #54690 --- .../common/config/commonEditorConfig.ts | 27 +++++++++++-------- .../electron-browser/files.contribution.ts | 10 +++---- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 556665f4a6e21..1069bff17dd49 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -361,17 +361,17 @@ const editorConfiguration: IConfigurationNode = { 'editor.find.seedSearchStringFromSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection, - 'description': nls.localize('find.seedSearchStringFromSelection', "Controls if we seed the search string in Find Widget from editor selection") + 'description': nls.localize('find.seedSearchStringFromSelection', "Controls if we seed the search string in Find Widget from editor selection.") }, 'editor.find.autoFindInSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection, - 'description': nls.localize('find.autoFindInSelection', "Controls if Find in Selection flag is turned on when multiple characters or lines of text are selected in the editor") + 'description': nls.localize('find.autoFindInSelection', "Controls if the Find in Selection flag is turned on when multiple characters or lines of text are selected in the editor.") }, 'editor.find.globalFindClipboard': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.globalFindClipboard, - 'description': nls.localize('find.globalFindClipboard', "Controls if the Find Widget should read or modify the shared find clipboard on macOS"), + 'description': nls.localize('find.globalFindClipboard', "Controls if the Find Widget should read or modify the shared find clipboard on macOS."), 'included': platform.isMacintosh }, 'editor.wordWrap': { @@ -401,7 +401,7 @@ const editorConfiguration: IConfigurationNode = { '- \'off\', \'on\', \'wordWrapColumn\' and \'bounded\' refer to values the setting can take and should not be localized.', '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' ] - }, "Controls how lines should wrap. Can be:\n - 'off' (disable wrapping),\n - 'on' (viewport wrapping),\n - 'wordWrapColumn' (wrap at `editor.wordWrapColumn`) or\n - 'bounded' (wrap at minimum of viewport and `editor.wordWrapColumn`).") + }, "Controls how lines should wrap.") }, 'editor.wordWrapColumn': { 'type': 'integer', @@ -413,7 +413,7 @@ const editorConfiguration: IConfigurationNode = { '- `editor.wordWrap` refers to a different setting and should not be localized.', '- \'wordWrapColumn\' and \'bounded\' refer to values the different setting can take and should not be localized.' ] - }, "Controls the wrapping column of the editor when `editor.wordWrap` is 'wordWrapColumn' or 'bounded'.") + }, "Controls the wrapping column of the editor when [`editor.wordWrap`](#editor.wordWrap) is `wordWrapColumn` or `bounded`.") }, 'editor.wrappingIndent': { 'type': 'string', @@ -440,7 +440,7 @@ const editorConfiguration: IConfigurationNode = { '- `ctrlCmd` refers to a value the setting can take and should not be localized.', '- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.' ] - }, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier)") + }, "The modifier to be used to add multiple cursors with the mouse. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier).") }, 'editor.multiCursorMergeOverlapping': { 'type': 'boolean', @@ -495,7 +495,7 @@ const editorConfiguration: IConfigurationNode = { 'editor.formatOnType': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.formatOnType, - 'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing") + 'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing.") }, 'editor.formatOnPaste': { 'type': 'boolean', @@ -613,17 +613,17 @@ const editorConfiguration: IConfigurationNode = { 'type': 'string', 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], 'default': editorOptions.cursorStyleToString(EDITOR_DEFAULTS.viewInfo.cursorStyle), - 'description': nls.localize('cursorStyle', "Controls the cursor style, accepted values are 'block', 'block-outline', 'line', 'line-thin', 'underline' and 'underline-thin'") + 'description': nls.localize('cursorStyle', "Controls the cursor style.") }, 'editor.cursorWidth': { 'type': 'integer', 'default': EDITOR_DEFAULTS.viewInfo.cursorWidth, - 'description': nls.localize('cursorWidth', "Controls the width of the cursor when editor.cursorStyle is set to 'line'") + 'description': nls.localize('cursorWidth', "Controls the width of the cursor when [`editor.cursorStyle`](#editor.cursorStyle) is set to `line`.") }, 'editor.fontLigatures': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.fontLigatures, - 'description': nls.localize('fontLigatures', "Enables font ligatures") + 'description': nls.localize('fontLigatures', "Enables font ligatures.") }, 'editor.hideCursorInOverviewRuler': { 'type': 'boolean', @@ -633,8 +633,13 @@ const editorConfiguration: IConfigurationNode = { 'editor.renderWhitespace': { 'type': 'string', 'enum': ['none', 'boundary', 'all'], + 'enumDescriptions': [ + '', + nls.localize('renderWhiteSpace.boundary', "Render whitespace characters except for single spaces between words."), + '' + ], default: EDITOR_DEFAULTS.viewInfo.renderWhitespace, - description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, possibilities are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.") + description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.") }, 'editor.renderControlCharacters': { 'type': 'boolean', diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index 9413fa3200b54..6d90810ce94a4 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -192,7 +192,7 @@ configurationRegistry.registerConfiguration({ }, 'files.associations': { 'type': 'object', - 'description': nls.localize('associations', "Configure file associations to languages (e.g. \"*.extension\": \"html\"). These have precedence over the default associations of the languages installed."), + 'description': nls.localize('associations', "Configure file associations to languages (e.g. `\"*.extension\": \"html\"`). These have precedence over the default associations of the languages installed."), }, 'files.encoding': { 'type': 'string', @@ -246,17 +246,17 @@ configurationRegistry.registerConfiguration({ 'enum': [AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE], 'enumDescriptions': [ nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.off' }, "A dirty file is never automatically saved."), - nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.afterDelay' }, "A dirty file is automatically saved after the configured 'files.autoSaveDelay'."), + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.afterDelay' }, "A dirty file is automatically saved after the configured [`files.autoSaveDelay`](#files.autoSaveDelay)."), nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onFocusChange' }, "A dirty file is automatically saved when the editor loses focus."), nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.") ], 'default': AutoSaveConfiguration.OFF, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [`files.autoSaveDelay`](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save).", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) }, 'files.autoSaveDelay': { 'type': 'number', 'default': 1000, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when [`files.autoSave`](#files.autoSave) is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY) + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when [`files.autoSave`](#files.autoSave) is set to `{0}`.", AutoSaveConfiguration.AFTER_DELAY) }, 'files.watcherExclude': { 'type': 'object', @@ -308,7 +308,7 @@ configurationRegistry.registerConfiguration({ 'editor.formatOnSaveTimeout': { 'type': 'number', 'default': 750, - 'description': nls.localize('formatOnSaveTimeout', "Format on save timeout. Specifies a time limit in milliseconds for formatOnSave-commands. Commands taking longer than the specified timeout will be cancelled."), + 'description': nls.localize('formatOnSaveTimeout', "Format on save timeout. Specifies a time limit in milliseconds for `formatOnSave`-commands. Commands taking longer than the specified timeout will be cancelled."), 'overridable': true, 'scope': ConfigurationScope.RESOURCE }