From 8c8f30fa963c453d9b31a1b5d5fe879de5aa8954 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 14 Feb 2018 17:25:08 -0800 Subject: [PATCH 01/53] Add action to reload the window with disableExtensions set, #43156 --- .../issue/issueReporterMain.ts | 3 +-- .../issue/media/issueReporter.css | 5 ++++- src/vs/code/electron-main/window.ts | 15 +++++++++++---- src/vs/code/electron-main/windows.ts | 4 ++-- src/vs/platform/windows/common/windows.ts | 2 ++ src/vs/platform/windows/common/windowsIpc.ts | 6 ++++++ .../windows/electron-browser/windowService.ts | 4 ++++ .../platform/windows/electron-main/windows.ts | 2 +- .../windows/electron-main/windowsService.ts | 10 ++++++++++ src/vs/workbench/electron-browser/actions.ts | 18 ++++++++++++++++++ src/vs/workbench/electron-browser/workbench.ts | 4 ++-- src/vs/workbench/test/workbenchTestServices.ts | 8 ++++++++ 12 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index d4508cc68b8..9932881cf06 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -349,8 +349,7 @@ export class IssueReporter extends Disposable { const disableExtensions = document.getElementById('disableExtensions'); disableExtensions.addEventListener('click', () => { - ipcRenderer.send('workbenchCommand', 'workbench.extensions.action.disableAll'); - ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindow'); + ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindowWithExtensionsDisabled'); }); disableExtensions.addEventListener('keydown', (e) => { diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-browser/issue/media/issueReporter.css index 8f1de9a6a4d..13006e1e10c 100644 --- a/src/vs/code/electron-browser/issue/media/issueReporter.css +++ b/src/vs/code/electron-browser/issue/media/issueReporter.css @@ -263,9 +263,12 @@ input:disabled { margin-top: .5em; } +.workbenchCommand { + cursor: pointer; +} + .workbenchCommand:disabled { color: #868e96; - cursor: pointer; } .block-extensions .block-info { diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index dbe69eb291e..83598a3b6a8 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -482,7 +482,7 @@ export class CodeWindow implements ICodeWindow { }); } - public load(config: IWindowConfiguration, isReload?: boolean): void { + public load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work @@ -499,6 +499,13 @@ export class CodeWindow implements ICodeWindow { this._readyState = ReadyState.NAVIGATING; } + // Copy the config so that the change to disable-extensions is not preserved + // on another reload. + const configuration = objects.mixin({}, config); + if (disableExtensions) { + configuration['disable-extensions'] = true; + } + // Clear Document Edited if needed if (isMacintosh && this._win.isDocumentEdited()) { if (!isReload || !this.backupMainService.isHotExitEnabled()) { @@ -517,7 +524,7 @@ export class CodeWindow implements ICodeWindow { // Load URL mark('main:loadWindow'); - this._win.loadURL(this.getUrl(config)); + this._win.loadURL(this.getUrl(configuration)); // Make window visible if it did not open in N seconds because this indicates an error // Only do this when running out of sources and not when running tests @@ -532,7 +539,7 @@ export class CodeWindow implements ICodeWindow { } } - public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { + public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs, disableExtensions?: boolean): void { // If config is not provided, copy our current one if (!configuration) { @@ -558,7 +565,7 @@ export class CodeWindow implements ICodeWindow { configuration.isInitialStartup = false; // since this is a reload // Load config - this.load(configuration, true); + this.load(configuration, true, disableExtensions); } private getUrl(windowConfiguration: IWindowConfiguration): string { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index ce7112ed726..4eb0ab9c85c 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1266,12 +1266,12 @@ export class WindowsManager implements IWindowsMainService { return state; } - public reload(win: CodeWindow, cli?: ParsedArgs): void { + public reload(win: CodeWindow, cli?: ParsedArgs, disableExtensions?: boolean): void { // Only reload when the window has not vetoed this this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => { if (!veto) { - win.reload(void 0, cli); + win.reload(void 0, cli, disableExtensions); // Emit this._onWindowReload.fire(win.id); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 227a9a81106..fe1b297c5fa 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -109,6 +109,7 @@ export interface IWindowsService { showOpenDialog(windowId: number, options: OpenDialogOptions): TPromise; reloadWindow(windowId: number): TPromise; + reloadWindowExtensionsDisabled(windowId: number): TPromise; openDevTools(windowId: number): TPromise; toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; @@ -184,6 +185,7 @@ export interface IWindowService { pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; reloadWindow(): TPromise; + reloadWindowExtensionsDisabled(): TPromise; openDevTools(): TPromise; toggleDevTools(): TPromise; closeWorkspace(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 6a899f79556..7f4c21287b2 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -26,6 +26,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): TPromise; call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): TPromise; call(command: 'reloadWindow', arg: number): TPromise; + call(command: 'reloadWindowExtensionsDisabled', arg: number): TPromise; call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[], string]): TPromise; @@ -91,6 +92,7 @@ export class WindowsChannel implements IWindowsChannel { case 'showMessageBox': return this.service.showMessageBox(arg[0], arg[1]); case 'showSaveDialog': return this.service.showSaveDialog(arg[0], arg[1]); case 'showOpenDialog': return this.service.showOpenDialog(arg[0], arg[1]); + case 'reloadWindowExtensionsDisabled': return this.service.reloadWindowExtensionsDisabled(arg); case 'reloadWindow': return this.service.reloadWindow(arg); case 'openDevTools': return this.service.openDevTools(arg); case 'toggleDevTools': return this.service.toggleDevTools(arg); @@ -196,6 +198,10 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('reloadWindow', windowId); } + reloadWindowExtensionsDisabled(windowId: number): TPromise { + return this.channel.call('reloadWindowExtensionsDisabled', windowId); + } + openDevTools(windowId: number): TPromise { return this.channel.call('openDevTools', windowId); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 8548cf17b9f..56f566d1e9f 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -64,6 +64,10 @@ export class WindowService implements IWindowService { return this.windowsService.reloadWindow(this.windowId); } + reloadWindowExtensionsDisabled(): TPromise { + return this.windowsService.reloadWindowExtensionsDisabled(this.windowId); + } + openDevTools(): TPromise { return this.windowsService.openDevTools(this.windowId); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index c105c89f2d2..837eee39463 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -59,7 +59,7 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; - reload(win: ICodeWindow, cli?: ParsedArgs): void; + reload(win: ICodeWindow, cli?: ParsedArgs, disableExtensions?: boolean): void; createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise; saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise; closeWorkspace(win: ICodeWindow): void; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 553b78f3dfc..96da008f7b6 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -98,6 +98,16 @@ export class WindowsService implements IWindowsService, IDisposable { return this.windowsMainService.showOpenDialog(options, codeWindow); } + reloadWindowExtensionsDisabled(windowId: number): TPromise { + const codeWindow = this.windowsMainService.getWindowById(windowId); + + if (codeWindow) { + this.windowsMainService.reload(codeWindow, undefined, true); + } + + return TPromise.as(null); + } + reloadWindow(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index f04c6bdbf0e..b9bdfa6ecaf 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -556,6 +556,24 @@ export class ReloadWindowAction extends Action { } } +export class ReloadWindowWithExtensionsDisabledAction extends Action { + + static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; + static LABEL = nls.localize('reloadWindowWithExntesionsDisabled', "Reload Window With Extensions Disabled"); + + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService + ) { + super(id, label); + } + + run(): TPromise { + return this.windowService.reloadWindowExtensionsDisabled().then(() => true); + } +} + export abstract class BaseSwitchWindow extends Action { private closeWindowAction: CloseWindowAction; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 3666039dbf0..31ad4f8aa2e 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -86,7 +86,7 @@ import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar } from 'vs/workbench/electron-browser/actions'; +import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; @@ -405,7 +405,7 @@ export class Workbench implements IPartService { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_R } : void 0), 'Reload Window'); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } } : void 0), 'Developer: Toggle Developer Tools', localize('developer', "Developer")); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File")); - + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Reload Window Without Extensions'); // Actions for macOS native tabs management (only when enabled) const windowConfig = this.configurationService.getValue(); if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0910cc68b03..95114e28e67 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -923,6 +923,10 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } + reloadWindowExtensionsDisabled(): TPromise { + return TPromise.as(void 0); + } + openDevTools(): TPromise { return TPromise.as(void 0); } @@ -1057,6 +1061,10 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } + reloadWindowExtensionsDisabled(): TPromise { + return TPromise.as(void 0); + } + openDevTools(windowId: number): TPromise { return TPromise.as(void 0); } From 95b5c94581a0166b1451afd4381bc716f220edb0 Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Mon, 19 Feb 2018 15:22:42 -0500 Subject: [PATCH 02/53] added collapse all in debug console --- .../parts/debug/browser/debugActions.ts | 34 +- .../debug/browser/media/collapse-all.svg | 1 + .../debug/browser/media/collapse-inverse.svg | 1 + .../parts/debug/browser/media/repl.css | 9 + .../parts/debug/electron-browser/repl.ts | 437 +++++++++--------- 5 files changed, 255 insertions(+), 227 deletions(-) create mode 100644 src/vs/workbench/parts/debug/browser/media/collapse-all.svg create mode 100644 src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 92812bd6b07..278aabbf82e 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -1,7 +1,7 @@ /*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; @@ -13,7 +13,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState } - from 'vs/workbench/parts/debug/common/debug'; +from 'vs/workbench/parts/debug/common/debug'; import { Variable, Expression, Thread, Breakpoint, Process } from 'vs/workbench/parts/debug/common/debugModel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -21,6 +21,8 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { CollapseAction } from 'vs/workbench/browser/viewlet'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; export abstract class AbstractDebugAction extends Action { @@ -394,7 +396,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { public run(breakpoint: IBreakpoint): TPromise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) - : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); + : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); } } @@ -521,7 +523,7 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const model = this.debugService.getModel(); return super.isEnabled(state) && (state === State.Running || state === State.Stopped) && - (model.getFunctionBreakpoints().length + model.getBreakpoints().length + model.getExceptionBreakpoints().length > 0); + (model.getFunctionBreakpoints().length + model.getBreakpoints().length + model.getExceptionBreakpoints().length > 0); } } @@ -541,7 +543,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { return !this.debugService.getViewModel().getSelectedFunctionBreakpoint() - && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); + && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); } } @@ -708,6 +710,20 @@ export class ClearReplAction extends AbstractDebugAction { } } +export class CollapseAllReplAction extends Action { + static readonly ID = 'workbench.debug.panel.action.collapseReplAction'; + static LABEL = nls.localize('collapseRepl', "Collaps All"); + + constructor(id: string, label: string, private tree: ITree){ + super(id, label, 'debug-action collapse-repl collapse-all'); + } + public run(): TPromise{ + let collapseAction = new CollapseAction(this.tree, false, 'explore-action collapse-all'); + collapseAction.run(); + return null; + } +} + export class ToggleReplAction extends TogglePanelAction { static readonly ID = 'workbench.debug.action.toggleRepl'; static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); @@ -811,7 +827,7 @@ export class StepBackAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const process = this.debugService.getViewModel().focusedProcess; return super.isEnabled(state) && state === State.Stopped && - process && process.session.capabilities.supportsStepBack; + process && process.session.capabilities.supportsStepBack; } } @@ -834,6 +850,6 @@ export class ReverseContinueAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const process = this.debugService.getViewModel().focusedProcess; return super.isEnabled(state) && state === State.Stopped && - process && process.session.capabilities.supportsStepBack; + process && process.session.capabilities.supportsStepBack; } } diff --git a/src/vs/workbench/parts/debug/browser/media/collapse-all.svg b/src/vs/workbench/parts/debug/browser/media/collapse-all.svg new file mode 100644 index 00000000000..7d11a30f6e0 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/collapse-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg b/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg new file mode 100644 index 00000000000..46a65fff71a --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css index 045daba87c5..5612d6299c4 100644 --- a/src/vs/workbench/parts/debug/browser/media/repl.css +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -135,6 +135,15 @@ background: url('clear-repl-inverse.svg') center center no-repeat; } +.monaco-workbench .debug-action.collapse-repl { + background: url('collapse-all.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .debug-action.collapse-repl, +.hc-black .monaco-workbench .debug-action.collapse-repl { + background: url('collapse-inverse.svg') center center no-repeat; +} + /* Output coloring */ .vs .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .warn { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 82e3c72b1d7..8758e7ce37b 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -1,7 +1,7 @@ /*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ import 'vs/css!vs/workbench/parts/debug/browser/media/repl'; import * as nls from 'vs/nls'; @@ -33,7 +33,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer'; import { ReplInputEditor } from 'vs/workbench/parts/debug/electron-browser/replEditor'; import * as debug from 'vs/workbench/parts/debug/common/debug'; -import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { ClearReplAction, CollapseAllReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { ReplHistory } from 'vs/workbench/parts/debug/common/replHistory'; import { Panel } from 'vs/workbench/browser/panel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -176,252 +176,253 @@ export class Repl extends Panel implements IPrivateReplService { const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, scopedContextKeyService], [IPrivateReplService, this])); - this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions()); + this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions()); - modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { - triggerCharacters: ['.'], - provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { - const word = this.replInput.getModel().getWordAtPosition(position); - const overwriteBefore = word ? word.word.length : 0; - const text = this.replInput.getModel().getLineContent(position.lineNumber); - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; - const focusedProcess = this.debugService.getViewModel().focusedProcess; - const completions = focusedProcess ? focusedProcess.completions(frameId, text, position, overwriteBefore) : TPromise.as([]); - return wireCancellationToken(token, completions.then(suggestions => ({ - suggestions - }))); - } - }); + modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { + triggerCharacters: ['.'], + provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { + const word = this.replInput.getModel().getWordAtPosition(position); + const overwriteBefore = word ? word.word.length : 0; + const text = this.replInput.getModel().getLineContent(position.lineNumber); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; + const focusedProcess = this.debugService.getViewModel().focusedProcess; + const completions = focusedProcess ? focusedProcess.completions(frameId, text, position, overwriteBefore) : TPromise.as([]); + return wireCancellationToken(token, completions.then(suggestions => ({ + suggestions + }))); + } + }); - this.toUnbind.push(this.replInput.onDidScrollChange(e => { - if (!e.scrollHeightChanged) { - return; + this.toUnbind.push(this.replInput.onDidScrollChange(e => { + if (!e.scrollHeightChanged) { + return; + } + this.replInputHeight = Math.max(Repl.REPL_INPUT_INITIAL_HEIGHT, Math.min(Repl.REPL_INPUT_MAX_HEIGHT, e.scrollHeight, this.dimension.height)); + this.layout(this.dimension); + })); + this.toUnbind.push(this.replInput.onDidChangeCursorPosition(e => { + onFirstReplLine.set(e.position.lineNumber === 1); + onLastReplLine.set(e.position.lineNumber === this.replInput.getModel().getLineCount()); + })); + + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); + } + + public navigateHistory(previous: boolean): void { + const historyInput = previous ? Repl.HISTORY.previous() : Repl.HISTORY.next(); + if (historyInput) { + Repl.HISTORY.remember(this.replInput.getValue(), previous); + this.replInput.setValue(historyInput); + // always leave cursor at the end. + this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); } - this.replInputHeight = Math.max(Repl.REPL_INPUT_INITIAL_HEIGHT, Math.min(Repl.REPL_INPUT_MAX_HEIGHT, e.scrollHeight, this.dimension.height)); + } + + public acceptReplInput(): void { + this.debugService.addReplExpression(this.replInput.getValue()); + Repl.HISTORY.evaluated(this.replInput.getValue()); + this.replInput.setValue(''); + // Trigger a layout to shrink a potential multi line input + this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; this.layout(this.dimension); - })); - this.toUnbind.push(this.replInput.onDidChangeCursorPosition(e => { - onFirstReplLine.set(e.position.lineNumber === 1); - onLastReplLine.set(e.position.lineNumber === this.replInput.getModel().getLineCount()); - })); - - this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); - this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); - } - - public navigateHistory(previous: boolean): void { - const historyInput = previous ? Repl.HISTORY.previous() : Repl.HISTORY.next(); - if (historyInput) { - Repl.HISTORY.remember(this.replInput.getValue(), previous); - this.replInput.setValue(historyInput); - // always leave cursor at the end. - this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); } - } - public acceptReplInput(): void { - this.debugService.addReplExpression(this.replInput.getValue()); - Repl.HISTORY.evaluated(this.replInput.getValue()); - this.replInput.setValue(''); - // Trigger a layout to shrink a potential multi line input - this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; - this.layout(this.dimension); - } - - public getVisibleContent(): string { - let text = ''; - const navigator = this.tree.getNavigator(); - // skip first navigator element - the root node - while (navigator.next()) { - if (text) { - text += `\n`; + public getVisibleContent(): string { + let text = ''; + const navigator = this.tree.getNavigator(); + // skip first navigator element - the root node + while (navigator.next()) { + if (text) { + text += `\n`; + } + text += navigator.current().toString(); } - text += navigator.current().toString(); + + return text; } - return text; - } + public layout(dimension: Dimension): void { + this.dimension = dimension; + if (this.tree) { + this.renderer.setWidth(dimension.width - 25, this.characterWidth); + const treeHeight = dimension.height - this.replInputHeight; + this.treeContainer.style.height = `${treeHeight}px`; + this.tree.layout(treeHeight); + } + this.replInputContainer.style.height = `${this.replInputHeight}px`; - public layout(dimension: Dimension): void { - this.dimension = dimension; - if (this.tree) { - this.renderer.setWidth(dimension.width - 25, this.characterWidth); - const treeHeight = dimension.height - this.replInputHeight; - this.treeContainer.style.height = `${treeHeight}px`; - this.tree.layout(treeHeight); + this.replInput.layout({ width: dimension.width - 20, height: this.replInputHeight }); } - this.replInputContainer.style.height = `${this.replInputHeight}px`; - this.replInput.layout({ width: dimension.width - 20, height: this.replInputHeight }); - } + @memoize + private get characterWidth(): number { + const characterWidthSurveyor = dom.append(this.container, $('.surveyor')); + characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL; + for (let i = 0; i < 10; i++) { + characterWidthSurveyor.textContent += characterWidthSurveyor.textContent; + } + characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px'; - @memoize - private get characterWidth(): number { - const characterWidthSurveyor = dom.append(this.container, $('.surveyor')); - characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL; - for (let i = 0; i < 10; i++) { - characterWidthSurveyor.textContent += characterWidthSurveyor.textContent; + return characterWidthSurveyor.clientWidth / characterWidthSurveyor.textContent.length; } - characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px'; - return characterWidthSurveyor.clientWidth / characterWidthSurveyor.textContent.length; + public focus(): void { + this.replInput.focus(); + } + + public getActions(): IAction[] { + if (!this.actions) { + this.actions = [ + this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), + this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, this.tree) + ]; + + this.actions.forEach(a => { + this.toUnbind.push(a); + }); + } + + return this.actions; + } + + public shutdown(): void { + const replHistory = Repl.HISTORY.save(); + if (replHistory.length) { + this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); + } else { + this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + } + } + + private getReplInputOptions(): IEditorOptions { + return { + wordWrap: 'on', + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + selectOnLineNumbers: false, + selectionHighlight: false, + scrollbar: { + horizontal: 'hidden' + }, + lineDecorationsWidth: 0, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: 'smart', + minimap: { + enabled: false + } + }; + } + + public dispose(): void { + this.replInput.dispose(); + super.dispose(); + } } - public focus(): void { - this.replInput.focus(); - } + class ReplHistoryPreviousAction extends EditorAction { - public getActions(): IAction[] { - if (!this.actions) { - this.actions = [ - this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL) - ]; - - this.actions.forEach(a => { - this.toUnbind.push(a); + constructor() { + super({ + id: 'repl.action.historyPrevious', + label: nls.localize('actions.repl.historyPrevious', "History Previous"), + alias: 'History Previous', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE), + primary: KeyCode.UpArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } }); } - return this.actions; - } - - public shutdown(): void { - const replHistory = Repl.HISTORY.save(); - if (replHistory.length) { - this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); - } else { - this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(true); } } - private getReplInputOptions(): IEditorOptions { - return { - wordWrap: 'on', - overviewRulerLanes: 0, - glyphMargin: false, - lineNumbers: 'off', - folding: false, - selectOnLineNumbers: false, - selectionHighlight: false, - scrollbar: { - horizontal: 'hidden' - }, - lineDecorationsWidth: 0, - scrollBeyondLastLine: false, - renderLineHighlight: 'none', - fixedOverflowWidgets: true, - acceptSuggestionOnEnter: 'smart', - minimap: { - enabled: false - } - }; + class ReplHistoryNextAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.historyNext', + label: nls.localize('actions.repl.historyNext', "History Next"), + alias: 'History Next', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE), + primary: KeyCode.DownArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(false); + } } - public dispose(): void { - this.replInput.dispose(); - super.dispose(); - } -} + class AcceptReplInputAction extends EditorAction { -class ReplHistoryPreviousAction extends EditorAction { + constructor() { + super({ + id: 'repl.action.acceptInput', + label: nls.localize({ key: 'actions.repl.acceptInput', comment: ['Apply input from the debug console input box'] }, "REPL Accept Input"), + alias: 'REPL Accept Input', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Enter + } + }); + } - constructor() { - super({ - id: 'repl.action.historyPrevious', - label: nls.localize('actions.repl.historyPrevious', "History Previous"), - alias: 'History Previous', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE), - primary: KeyCode.UpArrow, - weight: 50 - }, - menuOpts: { - group: 'debug' - } - }); + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + SuggestController.get(editor).acceptSelectedSuggestion(); + accessor.get(IPrivateReplService).acceptReplInput(); + } } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - accessor.get(IPrivateReplService).navigateHistory(true); - } -} + export class ReplCopyAllAction extends EditorAction { -class ReplHistoryNextAction extends EditorAction { + constructor() { + super({ + id: 'repl.action.copyAll', + label: nls.localize('actions.repl.copyAll', "Debug: Console Copy All"), + alias: 'Debug Console Copy All', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + }); + } - constructor() { - super({ - id: 'repl.action.historyNext', - label: nls.localize('actions.repl.historyNext', "History Next"), - alias: 'History Next', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE), - primary: KeyCode.DownArrow, - weight: 50 - }, - menuOpts: { - group: 'debug' - } - }); + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); + } } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - accessor.get(IPrivateReplService).navigateHistory(false); - } -} + registerEditorAction(ReplHistoryPreviousAction); + registerEditorAction(ReplHistoryNextAction); + registerEditorAction(AcceptReplInputAction); + registerEditorAction(ReplCopyAllAction); -class AcceptReplInputAction extends EditorAction { - - constructor() { - super({ - id: 'repl.action.acceptInput', - label: nls.localize({ key: 'actions.repl.acceptInput', comment: ['Apply input from the debug console input box'] }, "REPL Accept Input"), - alias: 'REPL Accept Input', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: EditorContextKeys.textFocus, - primary: KeyCode.Enter - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - SuggestController.get(editor).acceptSelectedSuggestion(); - accessor.get(IPrivateReplService).acceptReplInput(); - } -} - -export class ReplCopyAllAction extends EditorAction { - - constructor() { - super({ - id: 'repl.action.copyAll', - label: nls.localize('actions.repl.copyAll', "Debug: Console Copy All"), - alias: 'Debug Console Copy All', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); - } -} - -registerEditorAction(ReplHistoryPreviousAction); -registerEditorAction(ReplHistoryNextAction); -registerEditorAction(AcceptReplInputAction); -registerEditorAction(ReplCopyAllAction); - -const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); -registerEditorCommand(new SuggestCommand({ - id: 'repl.action.acceptSuggestion', - precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), - handler: x => x.acceptSelectedSuggestion(), - kbOpts: { - weight: 50, - kbExpr: EditorContextKeys.textFocus, - primary: KeyCode.RightArrow - } -})); + const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); + registerEditorCommand(new SuggestCommand({ + id: 'repl.action.acceptSuggestion', + precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: 50, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.RightArrow + } + })); From 4a668da5fb38f0b7bef28752f100f08901438dcb Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Tue, 20 Feb 2018 09:09:30 -0500 Subject: [PATCH 03/53] fixing indentation --- .../parts/debug/browser/debugActions.ts | 16 +- .../parts/debug/electron-browser/repl.ts | 454 +++++++++--------- 2 files changed, 235 insertions(+), 235 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 278aabbf82e..af94579aba6 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -1,7 +1,7 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; @@ -13,7 +13,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState } -from 'vs/workbench/parts/debug/common/debug'; + from 'vs/workbench/parts/debug/common/debug'; import { Variable, Expression, Thread, Breakpoint, Process } from 'vs/workbench/parts/debug/common/debugModel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -396,7 +396,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { public run(breakpoint: IBreakpoint): TPromise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) - : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); + : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); } } @@ -827,7 +827,7 @@ export class StepBackAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const process = this.debugService.getViewModel().focusedProcess; return super.isEnabled(state) && state === State.Stopped && - process && process.session.capabilities.supportsStepBack; + process && process.session.capabilities.supportsStepBack; } } @@ -850,6 +850,6 @@ export class ReverseContinueAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const process = this.debugService.getViewModel().focusedProcess; return super.isEnabled(state) && state === State.Stopped && - process && process.session.capabilities.supportsStepBack; + process && process.session.capabilities.supportsStepBack; } } diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 8758e7ce37b..bc3abff1e64 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -1,7 +1,7 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import 'vs/css!vs/workbench/parts/debug/browser/media/repl'; import * as nls from 'vs/nls'; @@ -176,253 +176,253 @@ export class Repl extends Panel implements IPrivateReplService { const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, scopedContextKeyService], [IPrivateReplService, this])); - this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions()); + this.replInput = scopedInstantiationService.createInstance(ReplInputEditor, this.replInputContainer, this.getReplInputOptions()); - modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { - triggerCharacters: ['.'], - provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { - const word = this.replInput.getModel().getWordAtPosition(position); - const overwriteBefore = word ? word.word.length : 0; - const text = this.replInput.getModel().getLineContent(position.lineNumber); - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; - const focusedProcess = this.debugService.getViewModel().focusedProcess; - const completions = focusedProcess ? focusedProcess.completions(frameId, text, position, overwriteBefore) : TPromise.as([]); - return wireCancellationToken(token, completions.then(suggestions => ({ - suggestions - }))); - } - }); - - this.toUnbind.push(this.replInput.onDidScrollChange(e => { - if (!e.scrollHeightChanged) { - return; - } - this.replInputHeight = Math.max(Repl.REPL_INPUT_INITIAL_HEIGHT, Math.min(Repl.REPL_INPUT_MAX_HEIGHT, e.scrollHeight, this.dimension.height)); - this.layout(this.dimension); - })); - this.toUnbind.push(this.replInput.onDidChangeCursorPosition(e => { - onFirstReplLine.set(e.position.lineNumber === 1); - onLastReplLine.set(e.position.lineNumber === this.replInput.getModel().getLineCount()); - })); - - this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); - this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); - } - - public navigateHistory(previous: boolean): void { - const historyInput = previous ? Repl.HISTORY.previous() : Repl.HISTORY.next(); - if (historyInput) { - Repl.HISTORY.remember(this.replInput.getValue(), previous); - this.replInput.setValue(historyInput); - // always leave cursor at the end. - this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); + modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { + triggerCharacters: ['.'], + provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { + const word = this.replInput.getModel().getWordAtPosition(position); + const overwriteBefore = word ? word.word.length : 0; + const text = this.replInput.getModel().getLineContent(position.lineNumber); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; + const focusedProcess = this.debugService.getViewModel().focusedProcess; + const completions = focusedProcess ? focusedProcess.completions(frameId, text, position, overwriteBefore) : TPromise.as([]); + return wireCancellationToken(token, completions.then(suggestions => ({ + suggestions + }))); } - } + }); - public acceptReplInput(): void { - this.debugService.addReplExpression(this.replInput.getValue()); - Repl.HISTORY.evaluated(this.replInput.getValue()); - this.replInput.setValue(''); - // Trigger a layout to shrink a potential multi line input - this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; + this.toUnbind.push(this.replInput.onDidScrollChange(e => { + if (!e.scrollHeightChanged) { + return; + } + this.replInputHeight = Math.max(Repl.REPL_INPUT_INITIAL_HEIGHT, Math.min(Repl.REPL_INPUT_MAX_HEIGHT, e.scrollHeight, this.dimension.height)); this.layout(this.dimension); - } + })); + this.toUnbind.push(this.replInput.onDidChangeCursorPosition(e => { + onFirstReplLine.set(e.position.lineNumber === 1); + onLastReplLine.set(e.position.lineNumber === this.replInput.getModel().getLineCount()); + })); - public getVisibleContent(): string { - let text = ''; - const navigator = this.tree.getNavigator(); - // skip first navigator element - the root node - while (navigator.next()) { - if (text) { - text += `\n`; - } - text += navigator.current().toString(); - } + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); + this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); + } - return text; - } - - public layout(dimension: Dimension): void { - this.dimension = dimension; - if (this.tree) { - this.renderer.setWidth(dimension.width - 25, this.characterWidth); - const treeHeight = dimension.height - this.replInputHeight; - this.treeContainer.style.height = `${treeHeight}px`; - this.tree.layout(treeHeight); - } - this.replInputContainer.style.height = `${this.replInputHeight}px`; - - this.replInput.layout({ width: dimension.width - 20, height: this.replInputHeight }); - } - - @memoize - private get characterWidth(): number { - const characterWidthSurveyor = dom.append(this.container, $('.surveyor')); - characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL; - for (let i = 0; i < 10; i++) { - characterWidthSurveyor.textContent += characterWidthSurveyor.textContent; - } - characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px'; - - return characterWidthSurveyor.clientWidth / characterWidthSurveyor.textContent.length; - } - - public focus(): void { - this.replInput.focus(); - } - - public getActions(): IAction[] { - if (!this.actions) { - this.actions = [ - this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), - this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, this.tree) - ]; - - this.actions.forEach(a => { - this.toUnbind.push(a); - }); - } - - return this.actions; - } - - public shutdown(): void { - const replHistory = Repl.HISTORY.save(); - if (replHistory.length) { - this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); - } else { - this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); - } - } - - private getReplInputOptions(): IEditorOptions { - return { - wordWrap: 'on', - overviewRulerLanes: 0, - glyphMargin: false, - lineNumbers: 'off', - folding: false, - selectOnLineNumbers: false, - selectionHighlight: false, - scrollbar: { - horizontal: 'hidden' - }, - lineDecorationsWidth: 0, - scrollBeyondLastLine: false, - renderLineHighlight: 'none', - fixedOverflowWidgets: true, - acceptSuggestionOnEnter: 'smart', - minimap: { - enabled: false - } - }; - } - - public dispose(): void { - this.replInput.dispose(); - super.dispose(); + public navigateHistory(previous: boolean): void { + const historyInput = previous ? Repl.HISTORY.previous() : Repl.HISTORY.next(); + if (historyInput) { + Repl.HISTORY.remember(this.replInput.getValue(), previous); + this.replInput.setValue(historyInput); + // always leave cursor at the end. + this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); } } - class ReplHistoryPreviousAction extends EditorAction { + public acceptReplInput(): void { + this.debugService.addReplExpression(this.replInput.getValue()); + Repl.HISTORY.evaluated(this.replInput.getValue()); + this.replInput.setValue(''); + // Trigger a layout to shrink a potential multi line input + this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; + this.layout(this.dimension); + } - constructor() { - super({ - id: 'repl.action.historyPrevious', - label: nls.localize('actions.repl.historyPrevious', "History Previous"), - alias: 'History Previous', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE), - primary: KeyCode.UpArrow, - weight: 50 - }, - menuOpts: { - group: 'debug' - } + public getVisibleContent(): string { + let text = ''; + const navigator = this.tree.getNavigator(); + // skip first navigator element - the root node + while (navigator.next()) { + if (text) { + text += `\n`; + } + text += navigator.current().toString(); + } + + return text; + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + if (this.tree) { + this.renderer.setWidth(dimension.width - 25, this.characterWidth); + const treeHeight = dimension.height - this.replInputHeight; + this.treeContainer.style.height = `${treeHeight}px`; + this.tree.layout(treeHeight); + } + this.replInputContainer.style.height = `${this.replInputHeight}px`; + + this.replInput.layout({ width: dimension.width - 20, height: this.replInputHeight }); + } + + @memoize + private get characterWidth(): number { + const characterWidthSurveyor = dom.append(this.container, $('.surveyor')); + characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL; + for (let i = 0; i < 10; i++) { + characterWidthSurveyor.textContent += characterWidthSurveyor.textContent; + } + characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px'; + + return characterWidthSurveyor.clientWidth / characterWidthSurveyor.textContent.length; + } + + public focus(): void { + this.replInput.focus(); + } + + public getActions(): IAction[] { + if (!this.actions) { + this.actions = [ + this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), + this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, this.tree) + ]; + + this.actions.forEach(a => { + this.toUnbind.push(a); }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - accessor.get(IPrivateReplService).navigateHistory(true); + return this.actions; + } + + public shutdown(): void { + const replHistory = Repl.HISTORY.save(); + if (replHistory.length) { + this.storageService.store(HISTORY_STORAGE_KEY, JSON.stringify(replHistory), StorageScope.WORKSPACE); + } else { + this.storageService.remove(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); } } - class ReplHistoryNextAction extends EditorAction { - - constructor() { - super({ - id: 'repl.action.historyNext', - label: nls.localize('actions.repl.historyNext', "History Next"), - alias: 'History Next', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE), - primary: KeyCode.DownArrow, - weight: 50 - }, - menuOpts: { - group: 'debug' - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - accessor.get(IPrivateReplService).navigateHistory(false); - } + private getReplInputOptions(): IEditorOptions { + return { + wordWrap: 'on', + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + selectOnLineNumbers: false, + selectionHighlight: false, + scrollbar: { + horizontal: 'hidden' + }, + lineDecorationsWidth: 0, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: 'smart', + minimap: { + enabled: false + } + }; } - class AcceptReplInputAction extends EditorAction { + public dispose(): void { + this.replInput.dispose(); + super.dispose(); + } +} - constructor() { - super({ - id: 'repl.action.acceptInput', - label: nls.localize({ key: 'actions.repl.acceptInput', comment: ['Apply input from the debug console input box'] }, "REPL Accept Input"), - alias: 'REPL Accept Input', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - kbOpts: { - kbExpr: EditorContextKeys.textFocus, - primary: KeyCode.Enter - } - }); - } +class ReplHistoryPreviousAction extends EditorAction { - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - SuggestController.get(editor).acceptSelectedSuggestion(); - accessor.get(IPrivateReplService).acceptReplInput(); - } + constructor() { + super({ + id: 'repl.action.historyPrevious', + label: nls.localize('actions.repl.historyPrevious', "History Previous"), + alias: 'History Previous', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_FIRST_DEBUG_REPL_LINE), + primary: KeyCode.UpArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } + }); } - export class ReplCopyAllAction extends EditorAction { + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(true); + } +} - constructor() { - super({ - id: 'repl.action.copyAll', - label: nls.localize('actions.repl.copyAll', "Debug: Console Copy All"), - alias: 'Debug Console Copy All', - precondition: debug.CONTEXT_IN_DEBUG_REPL, - }); - } +class ReplHistoryNextAction extends EditorAction { - public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { - clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); - } + constructor() { + super({ + id: 'repl.action.historyNext', + label: nls.localize('actions.repl.historyNext', "History Next"), + alias: 'History Next', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.textFocus, debug.CONTEXT_ON_LAST_DEBUG_REPL_LINE), + primary: KeyCode.DownArrow, + weight: 50 + }, + menuOpts: { + group: 'debug' + } + }); } - registerEditorAction(ReplHistoryPreviousAction); - registerEditorAction(ReplHistoryNextAction); - registerEditorAction(AcceptReplInputAction); - registerEditorAction(ReplCopyAllAction); + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + accessor.get(IPrivateReplService).navigateHistory(false); + } +} - const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); - registerEditorCommand(new SuggestCommand({ - id: 'repl.action.acceptSuggestion', - precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), - handler: x => x.acceptSelectedSuggestion(), - kbOpts: { - weight: 50, - kbExpr: EditorContextKeys.textFocus, - primary: KeyCode.RightArrow - } - })); +class AcceptReplInputAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.acceptInput', + label: nls.localize({ key: 'actions.repl.acceptInput', comment: ['Apply input from the debug console input box'] }, "REPL Accept Input"), + alias: 'REPL Accept Input', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.Enter + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + SuggestController.get(editor).acceptSelectedSuggestion(); + accessor.get(IPrivateReplService).acceptReplInput(); + } +} + +export class ReplCopyAllAction extends EditorAction { + + constructor() { + super({ + id: 'repl.action.copyAll', + label: nls.localize('actions.repl.copyAll', "Debug: Console Copy All"), + alias: 'Debug Console Copy All', + precondition: debug.CONTEXT_IN_DEBUG_REPL, + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); + } +} + +registerEditorAction(ReplHistoryPreviousAction); +registerEditorAction(ReplHistoryNextAction); +registerEditorAction(AcceptReplInputAction); +registerEditorAction(ReplCopyAllAction); + +const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); +registerEditorCommand(new SuggestCommand({ + id: 'repl.action.acceptSuggestion', + precondition: ContextKeyExpr.and(debug.CONTEXT_IN_DEBUG_REPL, SuggestContext.Visible), + handler: x => x.acceptSelectedSuggestion(), + kbOpts: { + weight: 50, + kbExpr: EditorContextKeys.textFocus, + primary: KeyCode.RightArrow + } +})); From 82f70f4892346ac98ad7ad1a8c411935eae1639f Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Tue, 20 Feb 2018 09:12:07 -0500 Subject: [PATCH 04/53] fixing indentation --- src/vs/workbench/parts/debug/browser/debugActions.ts | 6 +++--- src/vs/workbench/parts/debug/electron-browser/repl.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index af94579aba6..45e2289a086 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -1,4 +1,4 @@ - /*--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ @@ -523,7 +523,7 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { const model = this.debugService.getModel(); return super.isEnabled(state) && (state === State.Running || state === State.Stopped) && - (model.getFunctionBreakpoints().length + model.getBreakpoints().length + model.getExceptionBreakpoints().length > 0); + (model.getFunctionBreakpoints().length + model.getBreakpoints().length + model.getExceptionBreakpoints().length > 0); } } @@ -543,7 +543,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { protected isEnabled(state: State): boolean { return !this.debugService.getViewModel().getSelectedFunctionBreakpoint() - && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); + && this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index bc3abff1e64..a5acdcfc52b 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -1,4 +1,4 @@ - /*--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ From 7ee39771b9ddbc03580716ee0952326a7c8064cf Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Tue, 20 Feb 2018 11:13:20 -0500 Subject: [PATCH 05/53] added collapse all to context menu --- src/vs/workbench/parts/debug/browser/debugActions.ts | 2 +- .../workbench/parts/debug/browser/media/collapse-all.svg | 1 - .../parts/debug/browser/media/collapse-inverse.svg | 1 - src/vs/workbench/parts/debug/browser/media/repl.css | 9 --------- src/vs/workbench/parts/debug/electron-browser/repl.ts | 5 ++--- .../workbench/parts/debug/electron-browser/replViewer.ts | 3 ++- 6 files changed, 5 insertions(+), 16 deletions(-) delete mode 100644 src/vs/workbench/parts/debug/browser/media/collapse-all.svg delete mode 100644 src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 45e2289a086..0c33bc6a575 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -712,7 +712,7 @@ export class ClearReplAction extends AbstractDebugAction { export class CollapseAllReplAction extends Action { static readonly ID = 'workbench.debug.panel.action.collapseReplAction'; - static LABEL = nls.localize('collapseRepl', "Collaps All"); + static LABEL = nls.localize('collapseRepl', "Collapse All"); constructor(id: string, label: string, private tree: ITree){ super(id, label, 'debug-action collapse-repl collapse-all'); diff --git a/src/vs/workbench/parts/debug/browser/media/collapse-all.svg b/src/vs/workbench/parts/debug/browser/media/collapse-all.svg deleted file mode 100644 index 7d11a30f6e0..00000000000 --- a/src/vs/workbench/parts/debug/browser/media/collapse-all.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg b/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg deleted file mode 100644 index 46a65fff71a..00000000000 --- a/src/vs/workbench/parts/debug/browser/media/collapse-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css index 5612d6299c4..045daba87c5 100644 --- a/src/vs/workbench/parts/debug/browser/media/repl.css +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -135,15 +135,6 @@ background: url('clear-repl-inverse.svg') center center no-repeat; } -.monaco-workbench .debug-action.collapse-repl { - background: url('collapse-all.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .debug-action.collapse-repl, -.hc-black .monaco-workbench .debug-action.collapse-repl { - background: url('collapse-inverse.svg') center center no-repeat; -} - /* Output coloring */ .vs .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression > .warn { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index a5acdcfc52b..82e3c72b1d7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -33,7 +33,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer'; import { ReplInputEditor } from 'vs/workbench/parts/debug/electron-browser/replEditor'; import * as debug from 'vs/workbench/parts/debug/common/debug'; -import { ClearReplAction, CollapseAllReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { ReplHistory } from 'vs/workbench/parts/debug/common/replHistory'; import { Panel } from 'vs/workbench/browser/panel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -275,8 +275,7 @@ export class Repl extends Panel implements IPrivateReplService { public getActions(): IAction[] { if (!this.actions) { this.actions = [ - this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), - this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, this.tree) + this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL) ]; this.actions.forEach(a => { diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index a46709ce358..3488f8214e3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -18,7 +18,7 @@ import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; import { Model, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView'; -import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { ClearReplAction, CollapseAllReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -438,6 +438,7 @@ export class ReplExpressionsActionProvider implements IActionProvider { actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL)); + actions.push(this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, tree)); return TPromise.as(actions); } From 5cdacb2ee54383b6c1ee25232aef426e5aaf025d Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Tue, 20 Feb 2018 15:38:24 -0500 Subject: [PATCH 06/53] removed extra class --- .../parts/debug/browser/debugActions.ts | 16 ---------------- .../parts/debug/electron-browser/replViewer.ts | 5 +++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 0c33bc6a575..92812bd6b07 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -21,8 +21,6 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { CollapseAction } from 'vs/workbench/browser/viewlet'; -import { ITree } from 'vs/base/parts/tree/browser/tree'; export abstract class AbstractDebugAction extends Action { @@ -710,20 +708,6 @@ export class ClearReplAction extends AbstractDebugAction { } } -export class CollapseAllReplAction extends Action { - static readonly ID = 'workbench.debug.panel.action.collapseReplAction'; - static LABEL = nls.localize('collapseRepl', "Collapse All"); - - constructor(id: string, label: string, private tree: ITree){ - super(id, label, 'debug-action collapse-repl collapse-all'); - } - public run(): TPromise{ - let collapseAction = new CollapseAction(this.tree, false, 'explore-action collapse-all'); - collapseAction.run(); - return null; - } -} - export class ToggleReplAction extends TogglePanelAction { static readonly ID = 'workbench.debug.action.toggleRepl'; static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console'); diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index 3488f8214e3..7ba0adfe195 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -18,11 +18,12 @@ import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; import { Model, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView'; -import { ClearReplAction, CollapseAllReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; +import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; +import { CollapseAction } from 'vs/workbench/browser/viewlet'; const $ = dom.$; @@ -437,8 +438,8 @@ export class ReplExpressionsActionProvider implements IActionProvider { const actions: IAction[] = []; actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); + actions.push(new CollapseAction(tree, true, 'explore-action collapse-all')); actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL)); - actions.push(this.instantiationService.createInstance(CollapseAllReplAction, CollapseAllReplAction.ID, CollapseAllReplAction.LABEL, tree)); return TPromise.as(actions); } From 8786016090d47fa1a0dd62a85954602c2671c3f0 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Wed, 21 Feb 2018 08:31:48 -0800 Subject: [PATCH 07/53] reclassify machineid --- src/vs/platform/telemetry/node/commonProperties.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index ba097b617ff..75450151be7 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -11,7 +11,7 @@ import { readFile } from 'vs/base/node/pfs'; export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> { const result: { [name: string]: string; } = Object.create(null); - // __GDPR__COMMON__ "common.machineId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" } + // __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" } result['common.machineId'] = machineId; // __GDPR__COMMON__ "sessionID" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['sessionID'] = uuid.generateUuid() + Date.now(); From d7f719b1ddaeb5cf1d8e2839a2a8688da9c86de6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Feb 2018 17:32:03 +0100 Subject: [PATCH 08/53] Fix #35006 --- .../node/extensionLifecycle.ts | 123 ++++++++++++++++++ .../node/extensionManagementService.ts | 50 +++++-- .../extensions/common/extensionsRegistry.ts | 4 + 3 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 src/vs/platform/extensionManagement/node/extensionLifecycle.ts diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts new file mode 100644 index 00000000000..cbeef87b2bc --- /dev/null +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Disposable } from 'vs/base/common/lifecycle'; +import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ILogService } from 'vs/platform/log/common/log'; +import { fork, ChildProcess } from 'child_process'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { join } from 'vs/base/common/paths'; +import { Limiter } from 'vs/base/common/async'; +import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event'; + +export class ExtensionsLifecycle extends Disposable { + + private processesLimiter: Limiter = new Limiter(5); // Run max 5 processes in parallel + + constructor( + @ILogService private logService: ILogService + ) { + super(); + } + + uninstall(extension: ILocalExtension): TPromise { + const uninstallScript = this.parseUninstallScript(extension); + if (uninstallScript) { + this.logService.info(extension.identifier.id, 'Running Uninstall hook'); + return this.processesLimiter.queue(() => + this.runUninstallHook(uninstallScript.uninstallHook, uninstallScript.args, extension) + .then(() => this.logService.info(extension.identifier.id, 'Finished running uninstall hook'), err => this.logService.error(extension.identifier.id, `Failed to run uninstall hook: ${err}`))); + } + return TPromise.as(null); + } + + private parseUninstallScript(extension: ILocalExtension): { uninstallHook: string, args: string[] } { + if (extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') { + const uninstallScript = (extension.manifest['scripts']['vscode:uninstall']).split(' '); + if (uninstallScript.length < 2 || uninstallScript[0] !== 'node' || !uninstallScript[1]) { + this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script'); + return null; + } + return { uninstallHook: join(extension.path, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; + } + return null; + } + + private runUninstallHook(lifecycleHook: string, args: string[], extension: ILocalExtension): TPromise { + return new TPromise((c, e) => { + + const extensionLifecycleProcess = this.start(lifecycleHook, args, extension); + let timeoutHandler; + + const onexit = (error?: string) => { + clearTimeout(timeoutHandler); + timeoutHandler = null; + if (error) { + e(error); + } else { + c(null); + } + }; + + // on error + extensionLifecycleProcess.on('error', (err) => { + if (timeoutHandler) { + onexit(toErrorMessage(err) || 'Unknown'); + } + }); + + // on exit + extensionLifecycleProcess.on('exit', (code: number, signal: string) => { + if (timeoutHandler) { + onexit(code ? `Process exited with code ${code}` : void 0); + } + }); + + // timeout: kill process after waiting for 5s + timeoutHandler = setTimeout(() => { + timeoutHandler = null; + extensionLifecycleProcess.kill(); + e('timed out'); + }, 5000); + }); + } + + private start(uninstallHook: string, args: string[], extension: ILocalExtension): ChildProcess { + const opts = { + silent: true, + execArgv: undefined + }; + const extensionUninstallProcess = fork(uninstallHook, ['--type=extensionUninstall', ...args], opts); + + // Catch all output coming from the process + type Output = { data: string, format: string[] }; + extensionUninstallProcess.stdout.setEncoding('utf8'); + extensionUninstallProcess.stderr.setEncoding('utf8'); + const onStdout = fromNodeEventEmitter(extensionUninstallProcess.stdout, 'data'); + const onStderr = fromNodeEventEmitter(extensionUninstallProcess.stderr, 'data'); + const onOutput = anyEvent( + mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), + mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) + ); + + // Debounce all output, so we can render it in the Chrome console as a group + const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { + return r + ? { data: r.data + o.data, format: [...r.format, ...o.format] } + : { data: o.data, format: o.format }; + }, 100); + + // Print out extension host output + onDebouncedOutput(data => { + console.group(extension.identifier.id); + console.log(data.data, ...data.format); + console.groupEnd(); + }); + + return extensionUninstallProcess; + } +} diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 718e8fd1b3d..a102d12a254 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -35,6 +35,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache'; import { IChoiceService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; +import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; @@ -110,6 +111,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private lastReportTimestamp = 0; private readonly installingExtensions: Map> = new Map>(); private readonly manifestCache: ExtensionsManifestCache; + private readonly extensionLifecycle: ExtensionsLifecycle; private readonly _onInstallExtension = new Emitter(); readonly onInstallExtension: Event = this._onInstallExtension.event; @@ -135,6 +137,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.uninstalledFileLimiter = new Limiter(1); this._register(toDisposable(() => this.installingExtensions.clear())); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); + this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService)); } install(zipPath: string): TPromise { @@ -708,24 +711,43 @@ export class ExtensionManagementService extends Disposable implements IExtension } removeDeprecatedExtensions(): TPromise { + return this.removeUninstalledExtensions() + .then(() => this.removeOutdatedExtensions()); + } + + private removeUninstalledExtensions(): TPromise { return this.getUninstalledExtensions() .then(uninstalled => this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions .then(extensions => { - const toRemove: { path: string, id: string }[] = []; - // Uninstalled extensions - toRemove.push(...extensions.filter(e => uninstalled[e.identifier.id]).map(e => ({ path: e.path, id: e.identifier.id }))); - - // Outdated extensions - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid })); - toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1))) - .map(e => ({ path: e.path, id: e.identifier.id }))); - - return TPromise.join(distinct(toRemove, e => e.path).map(({ path, id }) => { - return pfs.rimraf(path) - .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[id])); - })); + const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[e.identifier.id]); + return TPromise.join(toRemove.map(e => this.removeUninstalledExtension(e))); }) - ); + ).then(() => null); + } + + private removeOutdatedExtensions(): TPromise { + return this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions + .then(extensions => { + const toRemove: ILocalExtension[] = []; + + // Outdated extensions + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid })); + toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); + + return TPromise.join(toRemove.map(extension => this.removeExtension(extension))); + }).then(() => null); + } + + private removeUninstalledExtension(extension: ILocalExtension): TPromise { + return this.extensionLifecycle.uninstall(extension) + .then(() => this.removeExtension(extension)) + .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[extension.identifier.id])) + .then(() => null); + } + + private removeExtension(extension: ILocalExtension): TPromise { + this.logService.trace(extension.identifier.id, 'Deleting from disk'); + return pfs.rimraf(extension.path).then(() => this.logService.info(extension.identifier.id, 'Deleted from disk')); } private isUninstalled(id: string): TPromise { diff --git a/src/vs/platform/extensions/common/extensionsRegistry.ts b/src/vs/platform/extensions/common/extensionsRegistry.ts index d5cf06b32c3..c230839ad34 100644 --- a/src/vs/platform/extensions/common/extensionsRegistry.ts +++ b/src/vs/platform/extensions/common/extensionsRegistry.ts @@ -264,6 +264,10 @@ const schema: IJSONSchema = { 'vscode:prepublish': { description: nls.localize('vscode.extension.scripts.prepublish', 'Script executed before the package is published as a VS Code extension.'), type: 'string' + }, + 'vscode:uninstall': { + description: nls.localize('vscode.extension.scripts.uninstall', 'Script executed after the extension is uninstalled from VS Code. Only Node script is supported.'), + type: 'string' } } }, From 729a6d43297152ec28cceafbb64f1c4a4b20a584 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Feb 2018 17:47:28 +0100 Subject: [PATCH 09/53] fixes #44001 --- .../electron-browser/views/explorerViewer.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index e4d632d5b27..4ea35fb2089 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -799,11 +799,15 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // In-Explorer DND else { + const sources: FileStat[] = data.getData(); if (target instanceof Model) { + if (sources.length === 1 && sources[0].isRoot) { + return DRAG_OVER_ACCEPT_BUBBLE_DOWN(false); + } + return DRAG_OVER_REJECT; } - const sources: FileStat[] = data.getData(); if (!Array.isArray(sources)) { return DRAG_OVER_REJECT; } @@ -864,9 +868,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // In-Explorer DND (Move/Copy file) else { - if (target instanceof FileStat) { - promise = this.handleExplorerDrop(tree, data, target, originalEvent); - } + promise = this.handleExplorerDrop(tree, data, target, originalEvent); } promise.done(null, errors.onUnexpectedError); @@ -915,7 +917,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }); } - private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: FileStat, originalEvent: DragMouseEvent): TPromise { + private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: FileStat | Model, originalEvent: DragMouseEvent): TPromise { const sources: FileStat[] = distinctParents(data.getData(), s => s.resource); const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); @@ -955,19 +957,20 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }); } - private doHandleExplorerDrop(tree: ITree, source: FileStat, target: FileStat, isCopy: boolean): TPromise { + private doHandleExplorerDrop(tree: ITree, source: FileStat, target: FileStat | Model, isCopy: boolean): TPromise { return tree.expand(target).then(() => { if (source.isRoot) { const folders = this.contextService.getWorkspace().folders; let sourceIndex: number; let targetIndex: number; const workspaceCreationData: IWorkspaceFolderCreationData[] = []; + const targetUri = target instanceof FileStat ? target.resource : folders[folders.length - 1].uri; for (let index = 0; index < folders.length; index++) { if (folders[index].uri.toString() === source.resource.toString()) { sourceIndex = index; } - if (folders[index].uri.toString() === target.resource.toString()) { + if (folders[index].uri.toString() === targetUri.toString()) { targetIndex = index; } workspaceCreationData.push({ @@ -1004,6 +1007,9 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d))); }; + if (!(target instanceof FileStat)) { + return TPromise.as(void 0); + } // 1. check for dirty files that are being moved and backup to new target const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, source.resource, !isLinux /* ignorecase */)); From 20b3bf2df4ff87abcd803c4fb51f00a70255822f Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 21 Feb 2018 17:48:36 +0100 Subject: [PATCH 10/53] attempt to fix #43683 --- .../parts/debug/electron-browser/terminalSupport.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts index 061852aad56..914a20f5984 100644 --- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -56,12 +56,17 @@ export class TerminalSupport { const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']); if (result.stdout) { const pids = result.stdout.toString().split('\r\n'); - return pids.some(p => parseInt(p) === t.processId); + if (!pids.some(p => parseInt(p) === t.processId)) { + return false; + } } } else { - const result = cp.spawnSync('/usr/bin/pgrep', ['-P', String(t.processId)]); + const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(t.processId)]); if (result.stdout) { - return result.stdout.toString().trim().length > 0; + const r = result.stdout.toString().trim(); + if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683 + return false; + } } } } From dcc72d16999a8acd979cb52f02709d970656921c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Feb 2018 17:54:56 +0100 Subject: [PATCH 11/53] more logging --- .../node/extensionManagementService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index a102d12a254..7f82e757218 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -734,19 +734,19 @@ export class ExtensionManagementService extends Disposable implements IExtension const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid })); toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); - return TPromise.join(toRemove.map(extension => this.removeExtension(extension))); + return TPromise.join(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); }).then(() => null); } private removeUninstalledExtension(extension: ILocalExtension): TPromise { return this.extensionLifecycle.uninstall(extension) - .then(() => this.removeExtension(extension)) + .then(() => this.removeExtension(extension, 'uninstalled')) .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[extension.identifier.id])) .then(() => null); } - private removeExtension(extension: ILocalExtension): TPromise { - this.logService.trace(extension.identifier.id, 'Deleting from disk'); + private removeExtension(extension: ILocalExtension, type: string): TPromise { + this.logService.trace(extension.identifier.id, 'Deleting from disk', type); return pfs.rimraf(extension.path).then(() => this.logService.info(extension.identifier.id, 'Deleted from disk')); } From 83dd7fcc28a5e0603a26bd3ab5bf6aad2382d4da Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Feb 2018 18:04:37 +0100 Subject: [PATCH 12/53] Fix #44104 --- .../parts/extensions/browser/extensionsActions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 9f84e91c9af..ccfdb915b63 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -1601,7 +1601,7 @@ export class DisableAllAction extends Action { } run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled))); + return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled))); } dispose(): void { @@ -1633,7 +1633,7 @@ export class DisableAllWorkpsaceAction extends Action { } run(): TPromise { - return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled))); + return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled))); } dispose(): void { @@ -1645,7 +1645,7 @@ export class DisableAllWorkpsaceAction extends Action { export class EnableAllAction extends Action { static readonly ID = 'workbench.extensions.action.enableAll'; - static LABEL = localize('enableAll', "Enable All Installed Extensions"); + static LABEL = localize('enableAll', "Enable All Extensions"); private disposables: IDisposable[] = []; @@ -1676,7 +1676,7 @@ export class EnableAllAction extends Action { export class EnableAllWorkpsaceAction extends Action { static readonly ID = 'workbench.extensions.action.enableAllWorkspace'; - static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace"); + static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace"); private disposables: IDisposable[] = []; From 4f724cbff83c0d9ee18e26fc671104280dad3662 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Feb 2018 18:30:32 +0100 Subject: [PATCH 13/53] notifications - proper center rooting --- src/vs/base/browser/ui/list/listView.ts | 2 ++ src/vs/platform/statusbar/common/statusbar.ts | 5 ++++ .../media/notificationsCenter.css | 5 ++-- .../media/notificationsToasts.css | 5 ++-- .../notifications/notificationsCenter.ts | 6 ++--- .../notifications/notificationsStatus.ts | 3 ++- .../notifications/notificationsToasts.ts | 2 +- .../parts/statusbar/media/statusbarpart.css | 16 ++++++++++++- .../browser/parts/statusbar/statusbarPart.ts | 23 +++++++++++++++---- .../debug/browser/statusbarColorProvider.ts | 15 ++++++++++-- 10 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index c0aadf8e400..79dbdfe746c 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -294,12 +294,14 @@ export class ListView implements ISpliceable, IDisposable { item.row.domNode.style.top = `${this.elementTop(index)}px`; item.row.domNode.style.height = `${item.size}px`; item.row.domNode.setAttribute('data-index', `${index}`); + item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); renderer.renderElement(item.element, index, item.row.templateData); } private updateItemInDOM(item: IItem, index: number): void { item.row.domNode.style.top = `${this.elementTop(index)}px`; item.row.domNode.setAttribute('data-index', `${index}`); + item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); } private removeItemFromDOM(item: IItem): void { diff --git a/src/vs/platform/statusbar/common/statusbar.ts b/src/vs/platform/statusbar/common/statusbar.ts index a7287a62e9f..fffa1184e33 100644 --- a/src/vs/platform/statusbar/common/statusbar.ts +++ b/src/vs/platform/statusbar/common/statusbar.ts @@ -51,6 +51,11 @@ export interface IStatusbarEntry { * An optional extension ID if this entry is provided from an extension. */ extensionId?: string; + + /** + * Wether to show a beak above the status bar entry. + */ + showBeak?: boolean; } export interface IStatusbarService { diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsCenter.css b/src/vs/workbench/browser/parts/notifications/media/notificationsCenter.css index 066804bea77..c8356cea896 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsCenter.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsCenter.css @@ -7,14 +7,13 @@ position: absolute; z-index: 1000; right: 8px; - bottom: 30px; /* above status bar */ + bottom: 31px; display: none; overflow: hidden; } .monaco-workbench.nostatusbar > .notifications-center { - right: 12px; - bottom: 12px; + bottom: 8px; } .monaco-workbench > .notifications-center.visible { diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css index 80cb61d0eb4..bf75bd6e55b 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsToasts.css @@ -7,14 +7,13 @@ position: absolute; z-index: 1000; right: 3px; - bottom: 28px; /* above status bar */ + bottom: 26px; display: none; overflow: hidden; } .monaco-workbench.nostatusbar > .notifications-toasts { - right: 12px; - bottom: 12px; + bottom: 3px; } .monaco-workbench > .notifications-toasts.visible { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 428f1158a71..dcbbaa57562 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -23,7 +23,7 @@ import { localize } from 'vs/nls'; export class NotificationsCenter extends Themable { - private static MAX_DIMENSIONS = new Dimension(600, 400); + private static MAX_DIMENSIONS = new Dimension(450, 400); private notificationsCenterContainer: HTMLElement; private notificationsList: NotificationsList; @@ -166,7 +166,7 @@ export class NotificationsCenter extends Themable { protected updateStyles(): void { if (this.notificationsCenterContainer) { const widgetShadowColor = this.getColor(widgetShadow); - this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null; + this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null; } } @@ -219,6 +219,6 @@ export class NotificationsCenter extends Themable { registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const notificationBorderColor = theme.getColor(NOTIFICATIONS_BORDER); if (notificationBorderColor) { - collector.addRule(`.monaco-workbench > .notifications-center .notifications-list-container .notification-list-item { border-bottom: 1px solid ${notificationBorderColor}; }`); + collector.addRule(`.monaco-workbench > .notifications-center .notifications-list-container .monaco-list-row[data-last-element="false"] > .notification-list-item { border-bottom: 1px solid ${notificationBorderColor}; }`); } }); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index c9215fa253c..30a1a50dffa 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -72,7 +72,8 @@ export class NotificationsStatus { this.statusItem = this.statusbarService.addEntry({ text: this.counter === 0 ? '$(megaphone)' : `$(megaphone) ${this.counter}`, command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER_COMMAND_ID : this.model.notifications.length > 0 ? SHOW_NOTIFICATIONS_CENTER_COMMAND_ID : void 0, - tooltip: this.getTooltip() + tooltip: this.getTooltip(), + showBeak: this.isNotificationsCenterVisible }, StatusbarAlignment.RIGHT, -1000 /* towards the far end of the right hand side */); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index dfa261b4128..0f4f959b177 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -299,7 +299,7 @@ export class NotificationsToasts extends Themable { protected updateStyles(): void { this.mapNotificationToToast.forEach(toast => { const widgetShadowColor = this.getColor(widgetShadow); - toast.container.style.boxShadow = widgetShadowColor ? `0 2px 8px ${widgetShadowColor}` : null; + toast.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null; }); } diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index 77ace7d3011..886fc554729 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -9,7 +9,6 @@ height: 22px; font-size: 12px; padding: 0 10px; - overflow: hidden; } .monaco-workbench > .part.statusbar > .statusbar-item { @@ -19,6 +18,21 @@ vertical-align: top; } +.monaco-workbench > .part.statusbar > .statusbar-item.has-beak { + position: relative; +} + +.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { + content: ''; + position: absolute; + left: 11px; + top: -5px; + border-bottom-width: 5px; + border-bottom-style: solid; + border-left: 5px solid transparent; + border-right: 5px solid transparent; +} + .monaco-workbench > .part.statusbar > .statusbar-item.left > :first-child { margin-right: 5px; } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 60f6efe34df..8c875405a93 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -29,7 +29,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; -import { addClass, EventHelper } from 'vs/base/browser/dom'; +import { addClass, EventHelper, createStyleSheet } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; export class StatusbarPart extends Part implements IStatusbarService { @@ -42,6 +42,8 @@ export class StatusbarPart extends Part implements IStatusbarService { private statusItemsContainer: Builder; private statusMsgDispose: IDisposable; + private styleElement: HTMLStyleElement; + constructor( id: string, @IInstantiationService private instantiationService: IInstantiationService, @@ -60,7 +62,7 @@ export class StatusbarPart extends Part implements IStatusbarService { public addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority: number = 0): IDisposable { // Render entry in status bar - const el = this.doCreateStatusItem(alignment, priority); + const el = this.doCreateStatusItem(alignment, priority, entry.showBeak ? 'has-beak' : void 0); const item = this.instantiationService.createInstance(StatusBarEntryItem, entry); const toDispose = item.render(el); @@ -140,18 +142,31 @@ export class StatusbarPart extends Part implements IStatusbarService { const container = this.getContainer(); + // Background colors + const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND); + container.style('background-color', backgroundColor); container.style('color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); - container.style('background-color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND)); + // Border color const borderColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BORDER : STATUS_BAR_NO_FOLDER_BORDER) || this.getColor(contrastBorder); container.style('border-top-width', borderColor ? '1px' : null); container.style('border-top-style', borderColor ? 'solid' : null); container.style('border-top-color', borderColor); + + // Notification Beak + if (!this.styleElement) { + this.styleElement = createStyleSheet(container.getHTMLElement()); + } + + this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; } - private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0): HTMLElement { + private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0, extraClass?: string): HTMLElement { const el = document.createElement('div'); addClass(el, 'statusbar-item'); + if (extraClass) { + addClass(el, extraClass); + } if (alignment === StatusbarAlignment.RIGHT) { addClass(el, 'right'); diff --git a/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts index 579b84ebb18..4afe110097b 100644 --- a/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/parts/debug/browser/statusbarColorProvider.ts @@ -11,7 +11,7 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme'; -import { addClass, removeClass } from 'vs/base/browser/dom'; +import { addClass, removeClass, createStyleSheet } from 'vs/base/browser/dom'; // colors for theming @@ -34,6 +34,7 @@ export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBor }, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window")); export class StatusBarColorProvider extends Themable implements IWorkbenchContribution { + private styleElement: HTMLStyleElement; constructor( @IThemeService themeService: IThemeService, @@ -61,13 +62,23 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri removeClass(container, 'debugging'); } - container.style.backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND)); + // Container Colors + const backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND)); + container.style.backgroundColor = backgroundColor; container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND)); + // Border Color const borderColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_DEBUGGING_BORDER, STATUS_BAR_BORDER)) || this.getColor(contrastBorder); container.style.borderTopWidth = borderColor ? '1px' : null; container.style.borderTopStyle = borderColor ? 'solid' : null; container.style.borderTopColor = borderColor; + + // Notification Beak + if (!this.styleElement) { + this.styleElement = createStyleSheet(container); + } + + this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; } private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { From 6d5c53808d4f9967e446ec4f4b857ef394829850 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Feb 2018 10:32:38 -0800 Subject: [PATCH 14/53] Fix terminal local links not opening line/col Fixes #44145 --- .../electron-browser/terminalLinkHandler.ts | 49 +++++++------------ 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index 87351d5db48..a0d6296a026 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -14,7 +14,7 @@ import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/termi import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; -import { IEditorService } from 'vs/platform/editor/common/editor'; +import { IEditorService, ITextEditorSelection } from 'vs/platform/editor/common/editor'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -144,17 +144,16 @@ export class TerminalLinkHandler { return void 0; } - let normalizedPath = path.normalize(path.resolve(resolvedLink)); + const normalizedPath = path.normalize(path.resolve(resolvedLink)); const normalizedUrl = this.extractLinkUrl(normalizedPath); + const resource = Uri.file(normalizedUrl); + const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); + const selection: ITextEditorSelection = { + startLineNumber: lineColumnInfo.lineNumber, + startColumn: lineColumnInfo.columnNumber + }; - normalizedPath = this._formatLocalLinkPath(normalizedPath); - - let resource = Uri.file(normalizedUrl); - resource = resource.with({ - fragment: Uri.parse(normalizedPath).fragment - }); - - return this._editorService.openEditor({ resource, options: { pinned: true } }); + return this._editorService.openEditor({ resource, options: { pinned: true, selection } }); }); } @@ -240,23 +239,6 @@ export class TerminalLinkHandler { }); } - /** - * Appends line number and column number to link if they exists. - * @param link link to format, will become link#line_num,col_num. - */ - private _formatLocalLinkPath(link: string): string { - const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); - if (lineColumnInfo.lineNumber) { - link += `#${lineColumnInfo.lineNumber}`; - - if (lineColumnInfo.columnNumber) { - link += `,${lineColumnInfo.columnNumber}`; - } - } - - return link; - } - /** * Returns line and column number of URl if that is present. * @@ -264,18 +246,21 @@ export class TerminalLinkHandler { */ public extractLineColumnInfo(link: string): LineColumnInfo { const matches: string[] = this._localLinkRegex.exec(link); - const lineColumnInfo: LineColumnInfo = {}; + const lineColumnInfo: LineColumnInfo = { + lineNumber: 1, + columnNumber: 1 + }; const lineAndColumnMatchIndex = this._platform === platform.Platform.Windows ? winLineAndColumnMatchIndex : unixLineAndColumnMatchIndex; for (let i = 0; i < lineAndColumnClause.length; i++) { const lineMatchIndex = lineAndColumnMatchIndex + (lineAndColumnClauseGroupCount * i); const rowNumber = matches[lineMatchIndex]; if (rowNumber) { - lineColumnInfo['lineNumber'] = rowNumber; + lineColumnInfo['lineNumber'] = parseInt(rowNumber, 10); // Check if column number exists const columnNumber = matches[lineMatchIndex + 2]; if (columnNumber) { - lineColumnInfo['columnNumber'] = columnNumber; + lineColumnInfo['columnNumber'] = parseInt(columnNumber, 10); } break; } @@ -299,6 +284,6 @@ export class TerminalLinkHandler { } export interface LineColumnInfo { - lineNumber?: string; - columnNumber?: string; + lineNumber: number; + columnNumber: number; } \ No newline at end of file From 8b1b7735495d2a2c4802a59faf8b87c643916583 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Feb 2018 11:31:41 -0800 Subject: [PATCH 15/53] Add null check for terminal focus when showing panel Fixes #43558 --- .../parts/terminal/common/terminalService.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 6f97e2f97fa..6212923c472 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -267,7 +267,12 @@ export abstract class TerminalService implements ITerminalService { if (focus) { // Do the focus call asynchronously as going through the // command palette will force editor focus - setTimeout(() => this.getActiveInstance().focus(true), 0); + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focus(true); + } + }, 0); } complete(void 0); }); @@ -275,7 +280,12 @@ export abstract class TerminalService implements ITerminalService { if (focus) { // Do the focus call asynchronously as going through the // command palette will force editor focus - setTimeout(() => this.getActiveInstance().focus(true), 0); + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focus(true); + } + }, 0); } complete(void 0); } From 3dee7019edfbb3b80782d3d3f058cb249bd14e8a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 14:48:13 -0800 Subject: [PATCH 16/53] Add offical webview toggle dev tools action Part of #44068 --- .../parts/html/browser/html.contribution.ts | 17 ------------- .../html/browser/webview.contribution.ts | 9 ++++++- .../parts/html/browser/webviewCommands.ts | 25 +++++++++++++++++++ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index 6316766624e..1569008fb38 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -16,7 +16,6 @@ import { HtmlPreviewPart } from 'vs/workbench/parts/html/browser/htmlPreviewPart import { Registry } from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; @@ -93,19 +92,3 @@ CommandsRegistry.registerCommand('_workbench.htmlPreview.postMessage', function } return activePreviews.length > 0; }); - -CommandsRegistry.registerCommand('_webview.openDevTools', function () { - const elements = document.querySelectorAll('webview.ready'); - for (let i = 0; i < elements.length; i++) { - try { - (elements.item(i) as Electron.WebviewTag).openDevTools(); - } catch (e) { - console.error(e); - } - } -}); - -MenuRegistry.addCommand({ - id: '_webview.openDevTools', - title: localize('devtools.webview', "Developer: Webview Tools") -}); diff --git a/src/vs/workbench/parts/html/browser/webview.contribution.ts b/src/vs/workbench/parts/html/browser/webview.contribution.ts index a1a41b33605..6b1bb9e11a0 100644 --- a/src/vs/workbench/parts/html/browser/webview.contribution.ts +++ b/src/vs/workbench/parts/html/browser/webview.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ContextKeyExpr, } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -11,7 +12,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './webviewEditor'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand } from './webviewCommands'; +import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction } from './webviewCommands'; const category = 'Webview'; @@ -52,3 +53,9 @@ const hideCommand = new HideWebViewEditorFindCommand({ } }); KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), + 'Webview Tools', + nls.localize('developer', "Developer")); \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webviewCommands.ts b/src/vs/workbench/parts/html/browser/webviewCommands.ts index 01c0ce9a775..ac8aa27b197 100644 --- a/src/vs/workbench/parts/html/browser/webviewCommands.ts +++ b/src/vs/workbench/parts/html/browser/webviewCommands.ts @@ -82,3 +82,28 @@ export class ShowWebViewEditorFindTermCommand extends Command { return null; } } + + +export class OpenWebviewDeveloperToolsAction extends Action { + static readonly ID = 'workbench.action.webview.openDeveloperTools'; + static LABEL = nls.localize('openToolsLabel', "Webview Tools"); + + public constructor( + id: string, + label: string + ) { + super(id, label); + } + + public run(): TPromise { + const elements = document.querySelectorAll('webview.ready'); + for (let i = 0; i < elements.length; i++) { + try { + (elements.item(i) as Electron.WebviewTag).openDevTools(); + } catch (e) { + console.error(e); + } + } + return null; + } +} \ No newline at end of file From e05609d5ec95f804daf0e403647b98483abc4fb5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 15:28:21 -0800 Subject: [PATCH 17/53] Fix markdown style load error --- extensions/markdown/media/loading.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/markdown/media/loading.js b/extensions/markdown/media/loading.js index 720ff7fb204..cfa4c0adbde 100644 --- a/extensions/markdown/media/loading.js +++ b/extensions/markdown/media/loading.js @@ -27,10 +27,9 @@ if (!unloadedStyles.length) { return; } - const args = [unloadedStyles]; window.parent.postMessage({ - command: 'did-click-link', - data: `command:_markdown.onPreviewStyleLoadError?${encodeURIComponent(JSON.stringify(args))}` + command: '_markdown.onPreviewStyleLoadError', + args: [unloadedStyles] }, '*'); }); }()); \ No newline at end of file From 58a169f3f5ab5f285977bdd26976d1242dd1604c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 15:35:53 -0800 Subject: [PATCH 18/53] Added reload webviews command Fixes #44068 --- .../html/browser/webview.contribution.ts | 11 ++++++-- .../workbench/parts/html/browser/webview.ts | 7 ++++- .../parts/html/browser/webviewCommands.ts | 27 +++++++++++++++++++ .../parts/html/browser/webviewEditor.ts | 6 +++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/webview.contribution.ts b/src/vs/workbench/parts/html/browser/webview.contribution.ts index 6b1bb9e11a0..79ce5b3c4bf 100644 --- a/src/vs/workbench/parts/html/browser/webview.contribution.ts +++ b/src/vs/workbench/parts/html/browser/webview.contribution.ts @@ -12,10 +12,12 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './webviewEditor'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction } from './webviewCommands'; +import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction } from './webviewCommands'; const category = 'Webview'; +const webviewDeveloperCategory = nls.localize('developer', "Developer"); + const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindWidgetAction.ID, ShowWebViewEditorFindWidgetAction.LABEL, { @@ -58,4 +60,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKey actionRegistry.registerWorkbenchAction( new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), 'Webview Tools', - nls.localize('developer', "Developer")); \ No newline at end of file + webviewDeveloperCategory); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), + 'Reload Webview', + webviewDeveloperCategory); \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 65c4376fc92..1852afc6cb7 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -33,6 +33,7 @@ export class Webview { private _webviewFindWidget: WebviewFindWidget; private _findStarted: boolean = false; + private _contents: string = ''; constructor( private readonly parent: HTMLElement, @@ -84,7 +85,6 @@ export class Webview { const contents = this._webview.getWebContents(); this.registerFileProtocols(contents); - })); } @@ -245,6 +245,7 @@ export class Webview { } set contents(value: string) { + this._contents = value; this._send('content', { contents: value, options: this._options @@ -406,6 +407,10 @@ export class Webview { public showPreviousFindTerm() { this._webviewFindWidget.showPreviousFindTerm(); } + + public reload() { + this.contents = this._contents; + } } diff --git a/src/vs/workbench/parts/html/browser/webviewCommands.ts b/src/vs/workbench/parts/html/browser/webviewCommands.ts index ac8aa27b197..6bd277359e6 100644 --- a/src/vs/workbench/parts/html/browser/webviewCommands.ts +++ b/src/vs/workbench/parts/html/browser/webviewCommands.ts @@ -106,4 +106,31 @@ export class OpenWebviewDeveloperToolsAction extends Action { } return null; } +} + + +export class ReloadWebviewAction extends Action { + static readonly ID = 'workbench.action.webview.reloadWebviewAction'; + static LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); + + public constructor( + id: string, + label: string, + @IWorkbenchEditorService private readonly workbenchEditorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + for (const webview of this.getVisibleWebviews()) { + webview.reload(); + } + return null; + } + + private getVisibleWebviews() { + return this.workbenchEditorService.getVisibleEditors() + .filter(c => c && (c as any).isWebviewEditor) + .map(e => e as WebviewEditor); + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts index 9021b5f7725..87e7fa6a3c4 100644 --- a/src/vs/workbench/parts/html/browser/webviewEditor.ts +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -84,6 +84,12 @@ export abstract class WebviewEditor extends BaseWebviewEditor { return true; } + public reload() { + if (this._webview) { + this._webview.reload(); + } + } + protected abstract createEditor(parent: Builder): void; } From 5c0aa64dcce770f497eb1ebf7b606a8fde55efd4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 15:39:41 -0800 Subject: [PATCH 19/53] Remove unused events --- src/vs/workbench/parts/html/browser/webview.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 1852afc6cb7..9080b542333 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -167,16 +167,11 @@ export class Webview { if (this._contextKey) { this._contextKey.set(true); } - this._onFocus.fire(); }), addDisposableListener(this._webview, 'blur', () => { if (this._contextKey) { this._contextKey.reset(); } - this._onBlur.fire(); - }), - addDisposableListener(this._webview, 'found-in-page', (event) => { - this._onFoundInPageResults.fire(event.result); }) ); @@ -218,18 +213,9 @@ export class Webview { private readonly _onDidScroll = new Emitter<{ scrollYPercentage: number }>(); public readonly onDidScroll: Event<{ scrollYPercentage: number }> = this._onDidScroll.event; - private readonly _onFoundInPageResults = new Emitter(); - public readonly onFindResults: Event = this._onFoundInPageResults.event; - private readonly _onMessage = new Emitter(); public readonly onMessage: Event = this._onMessage.event; - private readonly _onFocus = new Emitter(); - public readonly onFocus: Event = this._onFocus.event; - - private readonly _onBlur = new Emitter(); - public readonly onBlur: Event = this._onBlur.event; - private _send(channel: string, ...args: any[]): void { this._ready .then(() => this._webview.send(channel, ...args)) From 732a30a655fef6e0302eb2a87d49dd913de632ac Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 15:58:37 -0800 Subject: [PATCH 20/53] Mark injected services readonly --- .../extensions/browser/extensionEditor.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index ced754665f7..0e421122a64 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -183,19 +183,19 @@ export class ExtensionEditor extends BaseEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, - @IInstantiationService private instantiationService: IInstantiationService, - @IViewletService private viewletService: IViewletService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IViewletService private readonly viewletService: IViewletService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IThemeService protected themeService: IThemeService, - @IKeybindingService private keybindingService: IKeybindingService, - @INotificationService private notificationService: INotificationService, - @IOpenerService private openerService: IOpenerService, - @IPartService private partService: IPartService, - @IContextViewService private contextViewService: IContextViewService, - @IContextKeyService private contextKeyService: IContextKeyService, - @IExtensionTipsService private extensionTipsService: IExtensionTipsService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @INotificationService private readonly notificationService: INotificationService, + @IOpenerService private readonly openerService: IOpenerService, + @IPartService private readonly partService: IPartService, + @IContextViewService private readonly contextViewService: IContextViewService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { super(ExtensionEditor.ID, telemetryService, themeService); From 7cbbf47f9519d2d0af3d396dd577aa65340f0aea Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 16:01:17 -0800 Subject: [PATCH 21/53] Mark injected services readonly --- .../update/electron-browser/releaseNotesEditor.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 52abc7a5262..cfe22f0a444 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -57,13 +57,13 @@ export class ReleaseNotesEditor extends WebviewEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, - @IThemeService protected themeService: IThemeService, @IStorageService storageService: IStorageService, @IContextKeyService contextKeyService: IContextKeyService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IOpenerService private openerService: IOpenerService, - @IModeService private modeService: IModeService, - @IPartService private partService: IPartService, + @IThemeService protected readonly themeService: IThemeService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IOpenerService private readonly openerService: IOpenerService, + @IModeService private readonly modeService: IModeService, + @IPartService private readonly partService: IPartService, @IContextViewService private readonly _contextViewService: IContextViewService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService ) { From 5eeed6d275b76c31dddf506fac672320c04a83b6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 16:05:12 -0800 Subject: [PATCH 22/53] Move layout and focus ot base class --- .../api/electron-browser/mainThreadWebview.ts | 8 +------- .../parts/html/browser/htmlPreviewPart.ts | 7 +------ .../workbench/parts/html/browser/webviewEditor.ts | 13 +++++++++++++ .../update/electron-browser/releaseNotesEditor.ts | 14 -------------- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index cf42eee83d2..fca82bf3247 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -151,14 +151,8 @@ class WebviewEditor extends BaseWebviewEditor { public layout(dimension: Dimension): void { if (this._webview) { this.doUpdateContainer(); - this._webview.layout(); - } - } - - public focus() { - if (this._webview) { - this._webview.focus(); } + super.layout(dimension); } public dispose(): void { diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index 60894b14c39..3d47a3c1522 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -157,13 +157,8 @@ export class HtmlPreviewPart extends WebviewEditor { const { width, height } = dimension; this.content.style.width = `${width}px`; this.content.style.height = `${height}px`; - if (this._webview) { - this._webview.layout(); - } - } - public focus(): void { - this.webview.focus(); + super.layout(dimension); } public clearInput(): void { diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts index 87e7fa6a3c4..4d268f9baea 100644 --- a/src/vs/workbench/parts/html/browser/webviewEditor.ts +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -12,6 +12,7 @@ import { IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/cont import { Webview } from './webview'; import { Builder } from 'vs/base/browser/builder'; +import { Dimension } from 'vs/workbench/services/part/common/partService'; /** A context key that is set when a webview editor has focus. */ export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS = new RawContextKey('webviewEditorFocus', false); @@ -90,6 +91,18 @@ export abstract class WebviewEditor extends BaseWebviewEditor { } } + public layout(dimension: Dimension): void { + if (this._webview) { + this._webview.layout(); + } + } + + public focus(): void { + if (this._webview) { + this._webview.focus(); + } + } + protected abstract createEditor(parent: Builder): void; } diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index cfe22f0a444..378c2402de3 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -139,20 +139,6 @@ export class ReleaseNotesEditor extends WebviewEditor { this.contentDisposables.push(toDisposable(() => this._webview = null)); } - layout(): void { - if (this._webview) { - this._webview.layout(); - } - } - - focus(): void { - if (!this._webview) { - return; - } - - this._webview.focus(); - } - dispose(): void { this.contentDisposables = dispose(this.contentDisposables); super.dispose(); From 2d8e9f2d8f2c96cd288eb1dbf4122faa50328ffe Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 16:11:34 -0800 Subject: [PATCH 23/53] Extract render functions --- .../electron-browser/releaseNotesEditor.ts | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 378c2402de3..69a5fe23c39 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -87,25 +87,7 @@ export class ReleaseNotesEditor extends WebviewEditor { await super.setInput(input, options); - const result: TPromise[] = []; - const renderer = new marked.Renderer(); - renderer.code = (code, lang) => { - const modeId = this.modeService.getModeIdForLanguageName(lang); - result.push(this.modeService.getOrCreateMode(modeId)); - return ''; - }; - - marked(text, { renderer }); - await TPromise.join(result); - - renderer.code = (code, lang) => { - const modeId = this.modeService.getModeIdForLanguageName(lang); - return `${tokenizeToString(code, modeId)}`; - }; - - const colorMap = TokenizationRegistry.getColorMap(); - const css = generateTokensCSSForColorMap(colorMap); - const body = renderBody(marked(text, { renderer }), css); + const body = await this.renderBody(text); this._webview = new Webview( this.content, this.partService.getContainer(Parts.EDITOR_PART), @@ -167,4 +149,35 @@ export class ReleaseNotesEditor extends WebviewEditor { } super.shutdown(); } + + private async renderBody(text: string) { + const colorMap = TokenizationRegistry.getColorMap(); + const css = generateTokensCSSForColorMap(colorMap); + const body = renderBody(await this.renderContent(text), css); + return body; + } + + private async renderContent(text: string): TPromise { + const renderer = await this.getRenderer(text); + return marked(text, { renderer }); + } + + private async getRenderer(text: string) { + const result: TPromise[] = []; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + result.push(this.modeService.getOrCreateMode(modeId)); + return ''; + }; + + marked(text, { renderer }); + await TPromise.join(result); + + renderer.code = (code, lang) => { + const modeId = this.modeService.getModeIdForLanguageName(lang); + return `${tokenizeToString(code, modeId)}`; + }; + return renderer; + } } From cc3f1aadce0b44cca0d14c6a7238aab6e2453e95 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 16:17:54 -0800 Subject: [PATCH 24/53] Call webview style directly instead of using onThemeChanged --- src/vs/workbench/parts/html/browser/htmlPreviewPart.ts | 2 +- .../parts/update/electron-browser/releaseNotesEditor.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index 3d47a3c1522..fc69a1d23d8 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -107,7 +107,7 @@ export class HtmlPreviewPart extends WebviewEditor { const resourceUri = this.input.getResource(); this.webview.baseUrl = resourceUri.toString(true); } - this.onThemeChange(this.themeService.getTheme()); + this._webview.style(this.themeService.getTheme()); this._webviewDisposables = [ this._webview, this._webview.onDidClickLink(uri => this.openerService.open(uri)), diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 69a5fe23c39..5a76d993bac 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -105,7 +105,8 @@ export class ReleaseNotesEditor extends WebviewEditor { this._webview.initialScrollProgress = state.scrollYPercentage; } } - this.onThemeChange(this.themeService.getTheme()); + this._webview.style(this.themeService.getTheme()); + this._webview.contents = body; this._webview.onDidClickLink(link => { @@ -116,7 +117,6 @@ export class ReleaseNotesEditor extends WebviewEditor { this._webview.onDidScroll(event => { this.scrollYPercentage = event.scrollYPercentage; }, null, this.contentDisposables); - this.themeService.onThemeChange(this.onThemeChange, this, this.contentDisposables); this.contentDisposables.push(this._webview); this.contentDisposables.push(toDisposable(() => this._webview = null)); } From c7da9389d5975d74f73c7268018a3aef61d45ed7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Feb 2018 16:25:56 -0800 Subject: [PATCH 25/53] Move theme change logic into webview itself --- .../api/electron-browser/mainThreadWebview.ts | 10 +--------- .../parts/extensions/browser/extensionEditor.ts | 5 +---- src/vs/workbench/parts/html/browser/htmlPreviewPart.ts | 2 +- src/vs/workbench/parts/html/browser/webview.ts | 8 ++++++-- src/vs/workbench/parts/html/browser/webviewEditor.ts | 7 ------- .../update/electron-browser/releaseNotesEditor.ts | 2 +- 6 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index fca82bf3247..7dc37dd9fa1 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -204,7 +204,6 @@ class WebviewEditor extends BaseWebviewEditor { enableWrappedPostMessage: true }; this.webview.contents = input.html; - this.webview.style(this.themeService.getTheme()); } private get webview(): Webview { @@ -214,6 +213,7 @@ class WebviewEditor extends BaseWebviewEditor { this._webview = new Webview( this.webviewContent, this._partService.getContainer(Parts.EDITOR_PART), + this.themeService, this._environmentService, this._contextService, this._contextViewService, @@ -223,17 +223,9 @@ class WebviewEditor extends BaseWebviewEditor { enableWrappedPostMessage: true }, false); - this.webview.style(this.themeService.getTheme()); this._webview.onDidClickLink(this.onDidClickLink, this, this._contentDisposables); - - this.themeService.onThemeChange(theme => { - if (this._webview) { - this._webview.style(theme); - } - }, null, this._contentDisposables); - this._webview.onMessage(message => { if (this.input) { (this.input as WebviewInput).events.onMessage(message); diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index 0e421122a64..706eb304bef 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -421,11 +421,9 @@ export class ExtensionEditor extends BaseEditor { .then(body => { const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {}; - this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions, false); + this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions, false); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); this.contentDisposables.push(toDisposable(removeLayoutParticipant)); - - this.activeWebview.style(this.themeService.getTheme()); this.activeWebview.contents = body; this.activeWebview.onDidClickLink(link => { @@ -434,7 +432,6 @@ export class ExtensionEditor extends BaseEditor { this.openerService.open(link); } }, null, this.contentDisposables); - this.themeService.onThemeChange(theme => this.activeWebview.style(theme), null, this.contentDisposables); this.contentDisposables.push(this.activeWebview); }) .then(null, () => { diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index fc69a1d23d8..bc9904993a5 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -91,6 +91,7 @@ export class HtmlPreviewPart extends WebviewEditor { this._webview = new Webview( this.content, this.partService.getContainer(Parts.EDITOR_PART), + this.themeService, this._environmentService, this._contextService, this._contextViewService, @@ -107,7 +108,6 @@ export class HtmlPreviewPart extends WebviewEditor { const resourceUri = this.input.getResource(); this.webview.baseUrl = resourceUri.toString(true); } - this._webview.style(this.themeService.getTheme()); this._webviewDisposables = [ this._webview, this._webview.onDidClickLink(uri => this.openerService.open(uri)), diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 9080b542333..0b406b797ed 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -10,7 +10,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; import { addDisposableListener, addClass } from 'vs/base/browser/dom'; import { editorBackground, editorForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; +import { ITheme, LIGHT, DARK, IThemeService } from 'vs/platform/theme/common/themeService'; import { WebviewFindWidget } from './webviewFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -38,6 +38,7 @@ export class Webview { constructor( private readonly parent: HTMLElement, private readonly _styleElement: Element, + private readonly _themeService: IThemeService, private readonly _environmentService: IEnvironmentService, private readonly _contextService: IWorkspaceContextService, private readonly _contextViewService: IContextViewService, @@ -178,6 +179,9 @@ export class Webview { this._webviewFindWidget = new WebviewFindWidget(this._contextViewService, this); this._disposables.push(this._webviewFindWidget); + this.style(this._themeService.getTheme()); + this._themeService.onThemeChange(this.style, this, this._disposables); + if (parent) { parent.appendChild(this._webviewFindWidget.getDomNode()); parent.appendChild(this._webview); @@ -257,7 +261,7 @@ export class Webview { }); } - style(theme: ITheme): void { + private style(theme: ITheme): void { const { fontFamily, fontWeight, fontSize } = window.getComputedStyle(this._styleElement); // TODO@theme avoid styleElement const styles = { diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts index 4d268f9baea..c3f870f510c 100644 --- a/src/vs/workbench/parts/html/browser/webviewEditor.ts +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -74,13 +74,6 @@ export abstract class WebviewEditor extends BaseWebviewEditor { } } - public updateStyles() { - super.updateStyles(); - if (this._webview) { - this._webview.style(this.themeService.getTheme()); - } - } - public get isWebviewEditor() { return true; } diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 5a76d993bac..a4b84a8f94e 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -91,6 +91,7 @@ export class ReleaseNotesEditor extends WebviewEditor { this._webview = new Webview( this.content, this.partService.getContainer(Parts.EDITOR_PART), + this.themeService, this.environmentService, this._contextService, this._contextViewService, @@ -105,7 +106,6 @@ export class ReleaseNotesEditor extends WebviewEditor { this._webview.initialScrollProgress = state.scrollYPercentage; } } - this._webview.style(this.themeService.getTheme()); this._webview.contents = body; From e00f1ae737700c297679e587fa4d7756a54223c4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Feb 2018 11:41:28 -0800 Subject: [PATCH 26/53] Explicitly mark public webview methods --- src/vs/workbench/parts/html/browser/webview.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 0b406b797ed..b5a3fa71a3d 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -226,15 +226,15 @@ export class Webview { .catch(err => console.error(err)); } - set initialScrollProgress(value: number) { + public set initialScrollProgress(value: number) { this._send('initial-scroll-position', value); } - set options(value: WebviewOptions) { + public set options(value: WebviewOptions) { this._options = value; } - set contents(value: string) { + public set contents(value: string) { this._contents = value; this._send('content', { contents: value, @@ -242,11 +242,11 @@ export class Webview { }); } - set baseUrl(value: string) { + public set baseUrl(value: string) { this._send('baseUrl', value); } - focus(): void { + public focus(): void { this._webview.focus(); this._send('focus'); } From 0d350b140a6ed664952a9f5650093ee8fd1369c4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Feb 2018 11:58:16 -0800 Subject: [PATCH 27/53] Open website download page on Linux instead of downloading tarball Fixes #42311 --- .../platform/update/electron-main/updateService.linux.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index cb36c658585..7b0ece17fe3 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -5,6 +5,7 @@ 'use strict'; +import product from 'vs/platform/node/product'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -76,7 +77,13 @@ export class LinuxUpdateService extends AbstractUpdateService { } protected doDownloadUpdate(state: AvailableForDownload): TPromise { - shell.openExternal(state.update.url); + // Use the download URL if available as we don't currently detect the package type that was + // installed and the website download page is more useful than the tarball generally. + if (product.downloadUrl && product.downloadUrl.length > 0) { + shell.openExternal(product.downloadUrl); + } else { + shell.openExternal(state.update.url); + } this.setState(State.Idle); return TPromise.as(null); From d2d8e45c80e320a35432d6df0e929af7bda9e001 Mon Sep 17 00:00:00 2001 From: Erich Gamma Date: Wed, 21 Feb 2018 22:10:36 +0100 Subject: [PATCH 28/53] Remove tslint disabling --- extensions/npm/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 17d2c47b1c9..dd154d49e0d 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -//tslint:disable 'use strict'; import * as path from 'path'; From 966aeb0c7eb04e136646889888ad5fb5241bf2aa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Feb 2018 12:03:02 -0800 Subject: [PATCH 29/53] Move useSameOriginForRoot into options --- .../workbench/api/electron-browser/mainThreadWebview.ts | 6 +++--- .../workbench/parts/extensions/browser/extensionEditor.ts | 2 +- src/vs/workbench/parts/html/browser/htmlPreviewPart.ts | 6 ++++-- src/vs/workbench/parts/html/browser/webview.ts | 8 ++++---- .../parts/update/electron-browser/releaseNotesEditor.ts | 3 +-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 7dc37dd9fa1..57d40fb3cb2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -220,9 +220,9 @@ class WebviewEditor extends BaseWebviewEditor { this.contextKey, this.findInputFocusContextKey, { - enableWrappedPostMessage: true - }, - false); + enableWrappedPostMessage: true, + useSameOriginForRoot: false + }); this._webview.onDidClickLink(this.onDidClickLink, this, this._contentDisposables); diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index 706eb304bef..e6d5499598a 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -421,7 +421,7 @@ export class ExtensionEditor extends BaseEditor { .then(body => { const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {}; - this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions, false); + this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); this.contentDisposables.push(toDisposable(removeLayoutParticipant)); this.activeWebview.contents = body; diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index bc9904993a5..39542505062 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -97,8 +97,10 @@ export class HtmlPreviewPart extends WebviewEditor { this._contextViewService, this.contextKey, this.findInputFocusContextKey, - webviewOptions, - true); + { + ...webviewOptions, + useSameOriginForRoot: true + }); if (this.input && this.input instanceof HtmlInput) { const state = this.loadViewState(this.input.getResource()); diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index b5a3fa71a3d..1775cad5e66 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -24,6 +24,7 @@ export interface WebviewOptions { readonly allowSvgs?: boolean; readonly svgWhiteList?: string[]; readonly enableWrappedPostMessage?: boolean; + readonly useSameOriginForRoot?: boolean; } export class Webview { @@ -44,8 +45,7 @@ export class Webview { private readonly _contextViewService: IContextViewService, private readonly _contextKey: IContextKey, private readonly _findInputContextKey: IContextKey, - private _options: WebviewOptions, - useSameOriginForRoot: boolean + private _options: WebviewOptions ) { this._webview = document.createElement('webview'); this._webview.setAttribute('partition', this._options.allowSvgs ? 'webview' : `webview${Date.now()}`); @@ -62,7 +62,7 @@ export class Webview { this._webview.style.outline = '0'; this._webview.preload = require.toUrl('./webview-pre.js'); - this._webview.src = useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; + this._webview.src = this._options.useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; this._ready = new Promise(resolve => { const subscription = addDisposableListener(this._webview, 'ipc-message', (event) => { @@ -76,7 +76,7 @@ export class Webview { }); }); - if (!useSameOriginForRoot) { + if (!this._options.useSameOriginForRoot) { let loaded = false; this._disposables.push(addDisposableListener(this._webview, 'did-start-loading', () => { if (loaded) { diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index a4b84a8f94e..0c81a3376dd 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -97,8 +97,7 @@ export class ReleaseNotesEditor extends WebviewEditor { this._contextViewService, this.contextKey, this.findInputFocusContextKey, - {}, - false); + {}); if (this.input && this.input instanceof ReleaseNotesInput) { const state = this.loadViewState(this.input.version); From bcbd3395bd2dcaf56aafda9bbf171813c17c098e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Feb 2018 13:36:10 -0800 Subject: [PATCH 30/53] Remove old way of changing an html preview's input options The `options` argument of `_workbench.previewHtml` was never properly documented and never offically supported. --- .../parts/html/browser/html.contribution.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index 1569008fb38..16303e15d9e 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -38,17 +38,11 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI // --- Register Commands -const defaultPreviewHtmlOptions: HtmlInputOptions = { - allowScripts: true, - allowSvgs: true -}; - CommandsRegistry.registerCommand('_workbench.previewHtml', function ( accessor: ServicesAccessor, resource: URI | string, position?: EditorPosition, - label?: string, - options?: HtmlInputOptions + label?: string ) { const uri = resource instanceof URI ? resource : URI.parse(resource); label = label || uri.fsPath; @@ -65,9 +59,13 @@ CommandsRegistry.registerCommand('_workbench.previewHtml', function ( } } - const inputOptions = (Object as any).assign({}, options || defaultPreviewHtmlOptions); const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - inputOptions.svgWhiteList = extensionsWorkbenchService.allowedBadgeProviders; + + const inputOptions: HtmlInputOptions = { + allowScripts: true, + allowSvgs: true, + svgWhiteList: extensionsWorkbenchService.allowedBadgeProviders + }; // Otherwise, create new input and open it if (!input) { From 2d1f6d422b7a08ec391fcedb615006f70a7e7ea1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Feb 2018 14:04:35 -0800 Subject: [PATCH 31/53] Add localResourceRoots to webview options Fixes #44039 Add a new option that lets extensions override which root folders a webview can load local resources from. Defaults to allowing any resource in the workspace --- .../src/features/previewContentProvider.ts | 20 ++++++++++++++++++- src/vs/vscode.proposed.d.ts | 11 +++++++++- .../api/electron-browser/mainThreadWebview.ts | 5 +++-- .../extensions/browser/extensionEditor.ts | 6 ++---- .../parts/html/browser/html.contribution.ts | 1 + .../parts/html/browser/htmlPreviewPart.ts | 5 +---- .../workbench/parts/html/browser/webview.ts | 16 +++++++-------- .../electron-browser/releaseNotesEditor.ts | 5 +---- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/extensions/markdown/src/features/previewContentProvider.ts b/extensions/markdown/src/features/previewContentProvider.ts index a0356d51ce4..a942b4e16e6 100644 --- a/extensions/markdown/src/features/previewContentProvider.ts +++ b/extensions/markdown/src/features/previewContentProvider.ts @@ -354,7 +354,10 @@ export class MarkdownPreviewWebviewManager { const view = vscode.window.createWebview( localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)), viewColumn, - { enableScripts: true }); + { + enableScripts: true, + localResourceRoots: this.getLocalResourceRoots(resource) + }); this.contentProvider.provideTextDocumentContent(resource, this.previewConfigurations).then(x => view.html = x); @@ -373,4 +376,19 @@ export class MarkdownPreviewWebviewManager { this.webviews.set(resource.fsPath, view); return view; } + + private getLocalResourceRoots( + resource: vscode.Uri + ): vscode.Uri[] { + const folder = vscode.workspace.getWorkspaceFolder(resource); + if (folder) { + return [folder.uri]; + } + + if (!resource.scheme || resource.scheme === 'file') { + return [vscode.Uri.parse(path.dirname(resource.fsPath))]; + } + + return []; + } } \ No newline at end of file diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index dc2a142150c..a3be8f61888 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -396,7 +396,7 @@ declare module 'vscode' { */ export interface WebviewOptions { /** - * Should scripts be enabled in the webview contetn? + * Should scripts be enabled in the webview content? * * Defaults to false (scripts-disabled). */ @@ -423,6 +423,15 @@ declare module 'vscode' { * webview content cannot be quickly saved and restored. */ readonly keepAlive?: boolean; + + /** + * Root paths from which the webview can load local (filesystem) resources using the `vscode-workspace-resource:` scheme. + * + * Default to the root folders of the current workspace. + * + * Pass in an empty array to disallow access to any local resources. + */ + readonly localResourceRoots?: Uri[]; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 57d40fb3cb2..6cbfd960fae 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -201,7 +201,9 @@ class WebviewEditor extends BaseWebviewEditor { this.webview.options = { allowScripts: input.options.enableScripts, - enableWrappedPostMessage: true + enableWrappedPostMessage: true, + useSameOriginForRoot: false, + localResourceRoots: (input && input.options.localResourceRoots) || this._contextService.getWorkspace().folders.map(x => x.uri) }; this.webview.contents = input.html; } @@ -215,7 +217,6 @@ class WebviewEditor extends BaseWebviewEditor { this._partService.getContainer(Parts.EDITOR_PART), this.themeService, this._environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey, diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index e6d5499598a..12432f28ceb 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -52,7 +52,6 @@ import { Color } from 'vs/base/common/color'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { assign } from 'vs/base/common/objects'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { INotificationService } from 'vs/platform/notification/common/notification'; /** A context key that is set when an extension editor webview has focus. */ @@ -194,8 +193,7 @@ export class ExtensionEditor extends BaseEditor { @IContextViewService private readonly contextViewService: IContextViewService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IEnvironmentService private readonly environmentService: IEnvironmentService ) { super(ExtensionEditor.ID, telemetryService, themeService); @@ -421,7 +419,7 @@ export class ExtensionEditor extends BaseEditor { .then(body => { const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {}; - this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); + this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); this.contentDisposables.push(toDisposable(removeLayoutParticipant)); this.activeWebview.contents = body; diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index 16303e15d9e..4aa5a6481f5 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -30,6 +30,7 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI } // --- Register Editor + (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( HtmlPreviewPart, HtmlPreviewPart.ID, diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index 39542505062..5e6aa8ac6a4 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -26,7 +26,6 @@ import { Webview, WebviewOptions } from './webview'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { WebviewEditor } from './webviewEditor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; /** @@ -55,8 +54,7 @@ export class HtmlPreviewPart extends WebviewEditor { @IOpenerService private readonly openerService: IOpenerService, @IPartService private readonly partService: IPartService, @IContextViewService private readonly _contextViewService: IContextViewService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IEnvironmentService private readonly _environmentService: IEnvironmentService ) { super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, contextKeyService); } @@ -93,7 +91,6 @@ export class HtmlPreviewPart extends WebviewEditor { this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this._environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey, diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 1775cad5e66..00669ce6dad 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -17,7 +17,6 @@ import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { normalize, nativeSep } from 'vs/base/common/paths'; import { startsWith } from 'vs/base/common/strings'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export interface WebviewOptions { readonly allowScripts?: boolean; @@ -25,6 +24,7 @@ export interface WebviewOptions { readonly svgWhiteList?: string[]; readonly enableWrappedPostMessage?: boolean; readonly useSameOriginForRoot?: boolean; + readonly localResourceRoots?: URI[]; } export class Webview { @@ -41,7 +41,6 @@ export class Webview { private readonly _styleElement: Element, private readonly _themeService: IThemeService, private readonly _environmentService: IEnvironmentService, - private readonly _contextService: IWorkspaceContextService, private readonly _contextViewService: IContextViewService, private readonly _contextKey: IContextKey, private readonly _findInputContextKey: IContextKey, @@ -321,16 +320,16 @@ export class Webview { return; } - registerFileProtocol(contents, 'vscode-core-resource', [ + registerFileProtocol(contents, 'vscode-core-resource', () => [ this._environmentService.appRoot ]); - registerFileProtocol(contents, 'vscode-extension-resource', [ + registerFileProtocol(contents, 'vscode-extension-resource', () => [ this._environmentService.extensionsPath, this._environmentService.appRoot, this._environmentService.extensionDevelopmentPath ]); - registerFileProtocol(contents, 'vscode-workspace-resource', - this._contextService.getWorkspace().folders.map(folder => folder.uri.fsPath) + registerFileProtocol(contents, 'vscode-workspace-resource', () => + this._options.localResourceRoots.map(uri => uri.fsPath) ); } @@ -352,7 +351,6 @@ export class Webview { this._findStarted = true; this._webview.findInPage(value, findOptions); - return; } /** @@ -425,11 +423,11 @@ namespace ApiThemeClassName { function registerFileProtocol( contents: Electron.WebContents, protocol: string, - roots: string[] + getRoots: () => string[] ) { contents.session.protocol.registerFileProtocol(protocol, (request, callback: any) => { const requestPath = URI.parse(request.url).fsPath; - for (const root of roots) { + for (const root of getRoots()) { const normalizedPath = normalize(requestPath, true); if (startsWith(normalizedPath, root + nativeSep)) { callback({ path: normalizedPath }); diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 0c81a3376dd..04a9a71e292 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -28,7 +28,6 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { onUnexpectedError } from 'vs/base/common/errors'; import { addGAParameters } from 'vs/platform/telemetry/node/telemetryNodeUtils'; import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; function renderBody( body: string, @@ -64,8 +63,7 @@ export class ReleaseNotesEditor extends WebviewEditor { @IOpenerService private readonly openerService: IOpenerService, @IModeService private readonly modeService: IModeService, @IPartService private readonly partService: IPartService, - @IContextViewService private readonly _contextViewService: IContextViewService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IContextViewService private readonly _contextViewService: IContextViewService ) { super(ReleaseNotesEditor.ID, telemetryService, themeService, storageService, contextKeyService); } @@ -93,7 +91,6 @@ export class ReleaseNotesEditor extends WebviewEditor { this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey, From eb3d86721eb9d4b2099a3c2ce88d072add729b50 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Feb 2018 14:16:27 -0800 Subject: [PATCH 32/53] Support additional term alt sequences Fixes #42629 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b366709dbfe..8fd963690f4 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "vscode-debugprotocol": "1.25.0", "vscode-ripgrep": "^0.7.1-patch.1.5", "vscode-textmate": "^3.2.0", - "vscode-xterm": "3.2.0-beta3", + "vscode-xterm": "3.2.0-beta4", "yauzl": "2.8.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 42b109ec6dd..07d221d90f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6003,9 +6003,9 @@ vscode-textmate@^3.2.0: fast-plist "^0.1.2" oniguruma "^6.0.1" -vscode-xterm@3.2.0-beta3: - version "3.2.0-beta3" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.2.0-beta3.tgz#f82a54dd56f33b8875b68651cdcdd950317c8fb4" +vscode-xterm@3.2.0-beta4: + version "3.2.0-beta4" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.2.0-beta4.tgz#fdcabafe1b6f89e5490cc816dcf29f0383147d48" vso-node-api@^6.1.2-preview: version "6.1.2-preview" From cb9f5e6cca4a81eedcba6a5462ceb0aaa4aa9465 Mon Sep 17 00:00:00 2001 From: Svitlana Galianova Date: Wed, 21 Feb 2018 18:26:46 -0500 Subject: [PATCH 33/53] added separator --- src/vs/workbench/parts/debug/electron-browser/replViewer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index 7ba0adfe195..f3562497d05 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -9,7 +9,7 @@ import { IAction } from 'vs/base/common/actions'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as errors from 'vs/base/common/errors'; import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import * as dom from 'vs/base/browser/dom'; import severity from 'vs/base/common/severity'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -439,6 +439,7 @@ export class ReplExpressionsActionProvider implements IActionProvider { actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); actions.push(new CollapseAction(tree, true, 'explore-action collapse-all')); + actions.push(new Separator()); actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL)); return TPromise.as(actions); From d367f2f2ae044d377e369e6c67feb245d88b8eb5 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 21 Feb 2018 15:37:53 -0800 Subject: [PATCH 34/53] Pass disable extensions as part of CLI configuration --- src/vs/code/electron-main/window.ts | 19 ++++++++----------- src/vs/code/electron-main/windows.ts | 4 ++-- src/vs/platform/windows/common/windows.ts | 6 ++---- src/vs/platform/windows/common/windowsIpc.ts | 15 +++++---------- .../windows/electron-browser/windowService.ts | 9 +++------ .../windows/electron-main/windowsService.ts | 16 +++------------- src/vs/workbench/electron-browser/actions.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 4 ---- 8 files changed, 24 insertions(+), 51 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 5c01d3f1f8a..6e1dbe926b8 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -482,7 +482,7 @@ export class CodeWindow implements ICodeWindow { }); } - public load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { + public load(config: IWindowConfiguration, isReload?: boolean): void { // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work @@ -499,13 +499,6 @@ export class CodeWindow implements ICodeWindow { this._readyState = ReadyState.NAVIGATING; } - // Copy the config so that the change to disable-extensions is not preserved - // on another reload. - const configuration = objects.mixin({}, config); - if (disableExtensions) { - configuration['disable-extensions'] = true; - } - // Clear Document Edited if needed if (isMacintosh && this._win.isDocumentEdited()) { if (!isReload || !this.backupMainService.isHotExitEnabled()) { @@ -524,7 +517,7 @@ export class CodeWindow implements ICodeWindow { // Load URL mark('main:loadWindow'); - this._win.loadURL(this.getUrl(configuration)); + this._win.loadURL(this.getUrl(config)); // Make window visible if it did not open in N seconds because this indicates an error // Only do this when running out of sources and not when running tests @@ -539,7 +532,7 @@ export class CodeWindow implements ICodeWindow { } } - public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs, disableExtensions?: boolean): void { + public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { // If config is not provided, copy our current one if (!configuration) { @@ -562,10 +555,14 @@ export class CodeWindow implements ICodeWindow { configuration['extensions-dir'] = cli['extensions-dir']; } + if (cli) { + configuration['disable-extensions'] = cli['disable-extensions']; + } + configuration.isInitialStartup = false; // since this is a reload // Load config - this.load(configuration, true, disableExtensions); + this.load(configuration, true); } private getUrl(windowConfiguration: IWindowConfiguration): string { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 4eb0ab9c85c..ce7112ed726 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1266,12 +1266,12 @@ export class WindowsManager implements IWindowsMainService { return state; } - public reload(win: CodeWindow, cli?: ParsedArgs, disableExtensions?: boolean): void { + public reload(win: CodeWindow, cli?: ParsedArgs): void { // Only reload when the window has not vetoed this this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => { if (!veto) { - win.reload(void 0, cli, disableExtensions); + win.reload(void 0, cli); // Emit this._onWindowReload.fire(win.id); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index fe1b297c5fa..03c83a946b4 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -108,8 +108,7 @@ export interface IWindowsService { showSaveDialog(windowId: number, options: SaveDialogOptions): TPromise; showOpenDialog(windowId: number, options: OpenDialogOptions): TPromise; - reloadWindow(windowId: number): TPromise; - reloadWindowExtensionsDisabled(windowId: number): TPromise; + reloadWindow(windowId: number, args?: ParsedArgs): TPromise; openDevTools(windowId: number): TPromise; toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; @@ -184,8 +183,7 @@ export interface IWindowService { pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; - reloadWindow(): TPromise; - reloadWindowExtensionsDisabled(): TPromise; + reloadWindow(args?: ParsedArgs): TPromise; openDevTools(): TPromise; toggleDevTools(): TPromise; closeWorkspace(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 7f4c21287b2..aae46542f41 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -13,6 +13,7 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolde import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ICommandAction } from 'vs/platform/actions/common/actions'; import URI from 'vs/base/common/uri'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; export interface IWindowsChannel extends IChannel { call(command: 'event:onWindowOpen'): TPromise; @@ -25,8 +26,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'showMessageBox', arg: [number, MessageBoxOptions]): TPromise; call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): TPromise; call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): TPromise; - call(command: 'reloadWindow', arg: number): TPromise; - call(command: 'reloadWindowExtensionsDisabled', arg: number): TPromise; + call(command: 'reloadWindow', arg: [number, ParsedArgs]): TPromise; call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[], string]): TPromise; @@ -92,8 +92,7 @@ export class WindowsChannel implements IWindowsChannel { case 'showMessageBox': return this.service.showMessageBox(arg[0], arg[1]); case 'showSaveDialog': return this.service.showSaveDialog(arg[0], arg[1]); case 'showOpenDialog': return this.service.showOpenDialog(arg[0], arg[1]); - case 'reloadWindowExtensionsDisabled': return this.service.reloadWindowExtensionsDisabled(arg); - case 'reloadWindow': return this.service.reloadWindow(arg); + case 'reloadWindow': return this.service.reloadWindow(arg[0], arg[1]); case 'openDevTools': return this.service.openDevTools(arg); case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); @@ -194,12 +193,8 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('showOpenDialog', [windowId, options]); } - reloadWindow(windowId: number): TPromise { - return this.channel.call('reloadWindow', windowId); - } - - reloadWindowExtensionsDisabled(windowId: number): TPromise { - return this.channel.call('reloadWindowExtensionsDisabled', windowId); + reloadWindow(windowId: number, args?: ParsedArgs): TPromise { + return this.channel.call('reloadWindow', [windowId, args]); } openDevTools(windowId: number): TPromise { diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 56f566d1e9f..7e442728b50 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -11,6 +11,7 @@ import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorksp import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ICommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; export class WindowService implements IWindowService { @@ -60,12 +61,8 @@ export class WindowService implements IWindowService { return this.windowsService.pickWorkspaceAndOpen(options); } - reloadWindow(): TPromise { - return this.windowsService.reloadWindow(this.windowId); - } - - reloadWindowExtensionsDisabled(): TPromise { - return this.windowsService.reloadWindowExtensionsDisabled(this.windowId); + reloadWindow(args?: ParsedArgs): TPromise { + return this.windowsService.reloadWindow(this.windowId, args); } openDevTools(): TPromise { diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index f38a94025ea..4afa98698bf 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -12,7 +12,7 @@ import { assign } from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import product from 'vs/platform/node/product'; import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { shell, crashReporter, app, Menu, clipboard } from 'electron'; import Event, { chain, fromNodeEventEmitter } from 'vs/base/common/event'; import { IURLService } from 'vs/platform/url/common/url'; @@ -107,22 +107,12 @@ export class WindowsService implements IWindowsService, IDisposable { return this.windowsMainService.showOpenDialog(options, codeWindow); } - reloadWindowExtensionsDisabled(windowId: number): TPromise { - const codeWindow = this.windowsMainService.getWindowById(windowId); - - if (codeWindow) { - this.windowsMainService.reload(codeWindow, undefined, true); - } - - return TPromise.as(null); - } - - reloadWindow(windowId: number): TPromise { + reloadWindow(windowId: number, args: ParsedArgs): TPromise { this.logService.trace('windowsService#reloadWindow', windowId); const codeWindow = this.windowsMainService.getWindowById(windowId); if (codeWindow) { - this.windowsMainService.reload(codeWindow); + this.windowsMainService.reload(codeWindow, args); } return TPromise.as(null); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 4708802a2d3..38ba0708cfc 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -569,7 +569,7 @@ export class ReloadWindowWithExtensionsDisabledAction extends Action { } run(): TPromise { - return this.windowService.reloadWindowExtensionsDisabled().then(() => true); + return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true); } } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 57194cc7b7d..30d8c9b3760 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -927,10 +927,6 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } - reloadWindowExtensionsDisabled(): TPromise { - return TPromise.as(void 0); - } - openDevTools(): TPromise { return TPromise.as(void 0); } From 257e865be71cd7052fd7cffa7b6dd7a0427f1f5f Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 21 Feb 2018 15:40:26 -0800 Subject: [PATCH 35/53] Remove unneeded disableExtensions arg --- src/vs/platform/windows/electron-main/windows.ts | 2 +- src/vs/workbench/test/workbenchTestServices.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 837eee39463..c105c89f2d2 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -59,7 +59,7 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; - reload(win: ICodeWindow, cli?: ParsedArgs, disableExtensions?: boolean): void; + reload(win: ICodeWindow, cli?: ParsedArgs): void; createAndEnterWorkspace(win: ICodeWindow, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise; saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise; closeWorkspace(win: ICodeWindow): void; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 30d8c9b3760..a6f8b912eb6 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -1061,10 +1061,6 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - reloadWindowExtensionsDisabled(): TPromise { - return TPromise.as(void 0); - } - openDevTools(windowId: number): TPromise { return TPromise.as(void 0); } From 6935d1cf548a541de856238f0becc3c7105d9e2c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Feb 2018 07:26:31 +0100 Subject: [PATCH 36/53] fix #43750 --- .../services/themes/common/workbenchThemeService.ts | 3 +++ .../electron-browser/workbenchThemeService.ts | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index f8f97e35a74..c812adc567b 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -17,7 +17,10 @@ export const VS_LIGHT_THEME = 'vs'; export const VS_DARK_THEME = 'vs-dark'; export const VS_HC_THEME = 'hc-black'; +export const HC_THEME_ID = 'Default High Contrast'; + export const COLOR_THEME_SETTING = 'workbench.colorTheme'; +export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 6aff720a1c3..a001da55c57 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -8,7 +8,7 @@ import { TPromise, Promise } from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, CUSTOM_EDITOR_SCOPE_COLORS_SETTING } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, CUSTOM_EDITOR_SCOPE_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -31,6 +31,7 @@ import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadc import { ColorThemeStore } from 'vs/workbench/services/themes/electron-browser/colorThemeStore'; import { FileIconThemeStore } from 'vs/workbench/services/themes/electron-browser/fileIconThemeStore'; import { FileIconThemeData } from 'vs/workbench/services/themes/electron-browser/fileIconThemeData'; +import { IWindowService } from 'vs/platform/windows/common/windows'; // implementation @@ -97,6 +98,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IBroadcastService private broadcastService: IBroadcastService, @IConfigurationService private configurationService: IConfigurationService, @ITelemetryService private telemetryService: ITelemetryService, + @IWindowService private windowService: IWindowService, @IInstantiationService private instantiationService: IInstantiationService) { this.container = container; @@ -192,8 +194,15 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private initialize(): TPromise<[IColorTheme, IFileIconTheme]> { + let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); + + let colorThemeSetting: string; + if (this.windowService.getConfiguration().highContrast && detectHCThemeSetting) { + colorThemeSetting = HC_THEME_ID; + } else { + colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + } - let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING) || ''; return Promise.join([ From a9dc3120865fcc4a7b3bc3dc3704fe8836cb23e8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Feb 2018 07:38:55 +0100 Subject: [PATCH 37/53] notifications - disable scrollbar in toasts --- .../browser/parts/notifications/notificationsCenter.ts | 5 ++++- .../browser/parts/notifications/notificationsList.ts | 10 ++-------- .../browser/parts/notifications/notificationsToasts.ts | 6 +++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index dcbbaa57562..7a5d2319390 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -75,7 +75,10 @@ export class NotificationsCenter extends Themable { this.notificationsCenterContainer = document.createElement('div'); addClass(this.notificationsCenterContainer, 'notifications-center'); - this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, { ariaLabel: localize('notificationsList', "Notifications List") }); + this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, { + ariaLabel: localize('notificationsList', "Notifications List"), + useShadows: false + }); this.container.appendChild(this.notificationsCenterContainer); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index fe162dd8c57..d835d7ef0b3 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -18,10 +18,6 @@ import { NotificationsListDelegate, NotificationRenderer } from 'vs/workbench/br import { NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { NotificationFocusedContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; -export interface INotificationsListOptions { - ariaLabel: string; -} - export class NotificationsList extends Themable { private listContainer: HTMLElement; private list: WorkbenchList; @@ -30,7 +26,7 @@ export class NotificationsList extends Themable { constructor( private container: HTMLElement, - private options: INotificationsListOptions, + private options: IListOptions, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService themeService: IThemeService ) { @@ -77,9 +73,7 @@ export class NotificationsList extends Themable { this.listContainer, new NotificationsListDelegate(this.listContainer), [renderer], - { - ariaLabel: this.options.ariaLabel - } as IListOptions + this.options ); this.toUnbind.push(this.list); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 0f4f959b177..62d94a47f55 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -22,6 +22,7 @@ import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/no import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; import { Severity } from 'vs/platform/notification/common/notification'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; interface INotificationToast { list: NotificationsList; @@ -106,7 +107,10 @@ export class NotificationsToasts extends Themable { itemDisposeables.push(toDisposable(() => this.notificationsToastsContainer.removeChild(notificationToastContainer))); // Create toast with item and show - const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToastContainer, { ariaLabel: localize('notificationsToast', "Notification Toast") }); + const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToastContainer, { + ariaLabel: localize('notificationsToast', "Notification Toast"), + verticalScrollMode: ScrollbarVisibility.Hidden + }); itemDisposeables.push(notificationList); this.mapNotificationToToast.set(item, { list: notificationList, container: notificationToastContainer, disposeables: itemDisposeables }); From 5258c6e0d49cabd926bd1a387f13e40d6c7018cc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Feb 2018 08:22:03 +0100 Subject: [PATCH 38/53] notifications - fix status message --- .../browser/parts/notifications/notificationsStatus.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 30a1a50dffa..258ccc96e82 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -82,14 +82,14 @@ export class NotificationsStatus { return localize('hideNotifications', "Hide Notifications"); } - if (this.counter === 0) { - return localize('noNotifications', "No New Notifications"); - } - if (this.model.notifications.length === 0) { return localize('zeroNotifications', "No Notifications"); } + if (this.counter === 0) { + return localize('noNotifications', "No New Notifications"); + } + if (this.counter === 1) { return localize('oneNotification', "1 new notification"); } From 1e6d18050ee22c5781615a1f655c068a160702d3 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 21 Feb 2018 21:45:16 +0100 Subject: [PATCH 39/53] Delayer: support Task that return a promise --- src/vs/base/common/async.ts | 4 ++-- src/vs/workbench/electron-browser/workbench.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 34247aa7796..64e2f36c829 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -180,7 +180,7 @@ export class Delayer { private timeout: number; private completionPromise: Promise; private onSuccess: ValueCallback; - private task: ITask; + private task: ITask>; constructor(public defaultDelay: number) { this.timeout = null; @@ -189,7 +189,7 @@ export class Delayer { this.task = null; } - trigger(task: ITask, delay: number = this.defaultDelay): TPromise { + trigger(task: ITask>, delay: number = this.defaultDelay): TPromise { this.task = task; this.cancelTimeout(); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 0d1cc59de2b..67bcc00e7b6 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -207,7 +207,7 @@ export class Workbench implements IPartService { private sideBarPosition: Position; private panelPosition: Position; private panelHidden: boolean; - private editorBackgroundDelayer: Delayer; + private editorBackgroundDelayer: Delayer; private closeEmptyWindowScheduler: RunOnceScheduler; private editorsVisibleContext: IContextKey; private inZenMode: IContextKey; From 1062ac792b792a078d7ebb669a56eba4ca943ba5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 22 Feb 2018 09:28:05 +0100 Subject: [PATCH 40/53] fixes #44181 --- src/vs/base/browser/ui/list/listView.ts | 5 +++++ .../extensions/electron-browser/media/extensionsViewlet.css | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 79dbdfe746c..886f8d3ca4c 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -120,6 +120,11 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement.onScroll(this.onScroll, this, this.disposables); domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables); + // Prevent the monaco-scrollable-element from scrolling + // https://github.com/Microsoft/vscode/issues/44181 + domEvent(this.scrollableElement.getDomNode(), 'scroll') + (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); + const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); onDragOver(this.onDragOver, this, this.disposables); 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 7927dffe0ab..e34f01a8025 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -27,10 +27,6 @@ height: calc(100% - 38px); } -.extensions-viewlet > .extensions .extensions-list > .monaco-list { - height: auto; -} - .extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item > .octicon { font-size: 12px; line-height: 1; From 03f92dde0a8c17737f5d68f94e822c838e8c7002 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Feb 2018 10:19:23 +0100 Subject: [PATCH 41/53] Fix #44187 --- .../configuration-editing/src/extension.ts | 28 +------------------ .../configuration-editing/tsconfig.json | 5 ++-- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index 2266b8005af..01badfd0768 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -7,7 +7,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import * as vscode from 'vscode'; -import { getLocation, visit, parse, ParseError, ParseErrorCode } from 'jsonc-parser'; +import { getLocation, visit, parse, ParseErrorCode } from 'jsonc-parser'; import * as path from 'path'; import { SettingsDocument } from './settingsDocumentHelper'; @@ -70,8 +70,6 @@ function autoFixSettingsJSON(willSaveEvent: vscode.TextDocumentWillSaveEvent): v onError(error: ParseErrorCode, offset: number, length: number): void { if (error === ParseErrorCode.CommaExpected && lastEndOfSomething > -1) { - const errorPosition = document.positionAt(offset); - const fixPosition = document.positionAt(lastEndOfSomething); edit.insert(document.uri, fixPosition, ','); } @@ -106,30 +104,6 @@ function registerSettingsCompletions(): vscode.Disposable { }); } -function provideContributedLocalesProposals(range: vscode.Range): vscode.ProviderResult { - const contributedLocales: string[] = []; - for (const extension of vscode.extensions.all) { - if (extension.packageJSON && extension.packageJSON['contributes'] && extension.packageJSON['contributes']['localizations'] && extension.packageJSON['contributes']['localizations'].length) { - const localizations: { languageId: string }[] = extension.packageJSON['contributes']['localizations']; - for (const localization of localizations) { - if (contributedLocales.indexOf(localization.languageId) === -1) { - contributedLocales.push(localization.languageId); - } - } - } - } - return contributedLocales.map(locale => { - const text = `"${locale}"`; - const item = new vscode.CompletionItem(text); - item.kind = vscode.CompletionItemKind.Value; - item.insertText = text; - item.range = range; - item.filterText = text; - return item; - }); -} - - interface IExtensionsContent { recommendations: string[]; } diff --git a/extensions/configuration-editing/tsconfig.json b/extensions/configuration-editing/tsconfig.json index 23392904dee..84b75434d20 100644 --- a/extensions/configuration-editing/tsconfig.json +++ b/extensions/configuration-editing/tsconfig.json @@ -6,9 +6,10 @@ "lib": [ "es2015" ], - "strict": true + "strict": true, + "noUnusedLocals": true }, "include": [ "src/**/*" ] -} +} \ No newline at end of file From ca58c21bfc2f8e1a1f6f09667402d58d3dafe7d0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 22 Feb 2018 10:22:10 +0100 Subject: [PATCH 42/53] fixes #44188 --- extensions/git/src/api.ts | 2 +- extensions/git/src/commands.ts | 13 +++---------- extensions/git/src/decorationProvider.ts | 4 ++-- extensions/git/src/decorators.ts | 2 +- extensions/git/src/git.ts | 2 -- extensions/git/src/main.ts | 2 +- extensions/git/src/model.ts | 4 ++-- extensions/git/src/repository.ts | 10 ++++------ extensions/git/tsconfig.json | 3 ++- 9 files changed, 16 insertions(+), 26 deletions(-) diff --git a/extensions/git/src/api.ts b/extensions/git/src/api.ts index 4e569a8e884..8d94502e041 100644 --- a/extensions/git/src/api.ts +++ b/extensions/git/src/api.ts @@ -6,7 +6,7 @@ 'use strict'; import { Model } from './model'; -import { SourceControlInputBox, Uri } from 'vscode'; +import { Uri } from 'vscode'; export interface InputBox { value: string; diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 776e6c8ac1b..8ce710d56b4 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -7,10 +7,10 @@ import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor, CancellationTokenSource, StatusBarAlignment } from 'vscode'; import { Ref, RefType, Git, GitErrorCodes, Branch } from './git'; -import { Repository, Resource, Status, CommitOptions, ResourceGroupType, RepositoryState } from './repository'; +import { Repository, Resource, Status, CommitOptions, ResourceGroupType } from './repository'; import { Model } from './model'; import { toGitUri, fromGitUri } from './uri'; -import { grep, eventToPromise, isDescendant } from './util'; +import { grep, isDescendant } from './util'; import { applyLineChanges, intersectDiffWithRange, toLineRanges, invertLineChange, getModifiedRange } from './staging'; import * as path from 'path'; import { lstat, Stats } from 'fs'; @@ -237,7 +237,7 @@ export class CommandCenter { } const { size, object } = await repository.lstree(gitRef, uri.fsPath); - const { mimetype, encoding } = await repository.detectObjectType(object); + const { mimetype } = await repository.detectObjectType(object); if (mimetype === 'text/plain') { return toGitUri(uri, ref); @@ -1051,13 +1051,6 @@ export class CommandCenter { value = (await repository.getCommit(repository.HEAD.commit)).message; } - const getPreviousCommitMessage = async () => { - //Only return the previous commit message if it's an amend commit and the repo already has a commit - if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { - return (await repository.getCommit('HEAD')).message; - } - }; - return await window.showInputBox({ value, placeHolder: localize('commit message', "Commit message"), diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index a25f6052d69..48594918f5d 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -10,8 +10,8 @@ import * as path from 'path'; import { Repository, GitResourceGroup, Status } from './repository'; import { Model } from './model'; import { debounce } from './decorators'; -import { filterEvent, dispose, anyEvent, mapEvent, fireEvent } from './util'; -import { Submodule, GitErrorCodes } from './git'; +import { filterEvent, dispose, anyEvent, fireEvent } from './util'; +import { GitErrorCodes } from './git'; type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void }; diff --git a/extensions/git/src/decorators.ts b/extensions/git/src/decorators.ts index 83ee04b17ad..abd0265ef9b 100644 --- a/extensions/git/src/decorators.ts +++ b/extensions/git/src/decorators.ts @@ -78,7 +78,7 @@ function _throttle(fn: Function, key: string): Function { export const throttle = decorate(_throttle); -function _sequentialize(fn: Function, key: string): Function { +function _sequentialize(fn: Function, key: string): Function { const currentKey = `__$sequence$${key}`; return function (this: any, ...args: any[]) { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 7074ab22b90..d2f0ee6f914 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -362,7 +362,6 @@ function getGitErrorCode(stderr: string): string | undefined { export class Git { private gitPath: string; - private version: string; private env: any; private _onOutput = new EventEmitter(); @@ -370,7 +369,6 @@ export class Git { constructor(options: IGitOptions) { this.gitPath = options.gitPath; - this.version = options.version; this.env = options.env || {}; } diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index a003fe9bcfb..e7e0c7e8768 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -14,7 +14,7 @@ import { CommandCenter } from './commands'; import { GitContentProvider } from './contentProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; -import { toDisposable, filterEvent, mapEvent, eventToPromise } from './util'; +import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import { API, createApi } from './api'; diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 235b07787ba..3c0e8d6b59d 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -5,10 +5,10 @@ 'use strict'; -import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, ConfigurationChangeEvent } from 'vscode'; +import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento } from 'vscode'; import { Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; -import { dispose, anyEvent, filterEvent, IDisposable, isDescendant, find, firstIndex } from './util'; +import { dispose, anyEvent, filterEvent, isDescendant, firstIndex } from './util'; import { Git, GitErrorCodes } from './git'; import * as path from 'path'; import * as fs from 'fs'; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 3824f010265..38c57678e43 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -398,7 +398,7 @@ class ProgressManager { private disposable: IDisposable = EmptyDisposable; - constructor(private repository: Repository) { + constructor(repository: Repository) { const start = onceEvent(filterEvent(repository.onDidChangeOperations, () => repository.operations.shouldShowProgress())); const end = onceEvent(filterEvent(debounceEvent(repository.onDidChangeOperations, 300), () => !repository.operations.shouldShowProgress())); @@ -790,8 +790,8 @@ export class Repository implements Disposable { async buffer(ref: string, filePath: string): Promise { return await this.run(Operation.Show, async () => { const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/'); - const configFiles = workspace.getConfiguration('files', Uri.file(filePath)); - const encoding = configFiles.get('encoding'); + // const configFiles = workspace.getConfiguration('files', Uri.file(filePath)); + // const encoding = configFiles.get('encoding'); // TODO@joao: REsource config api return await this.repository.buffer(`${ref}:${relativePath}`); @@ -1009,10 +1009,8 @@ export class Repository implements Disposable { case 'UU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons)); } - let isModifiedInIndex = false; - switch (raw.x) { - case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); isModifiedInIndex = true; break; + case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break; case 'A': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break; case 'D': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break; case 'R': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break; diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index ea7679c9ac0..a7eca805f97 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -10,7 +10,8 @@ "./node_modules/@types" ], "strict": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "noUnusedLocals": true }, "include": [ "src/**/*" From 04750498693d138952be1c526a92923596372782 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Feb 2018 10:58:32 +0100 Subject: [PATCH 43/53] Fix #43271 --- extensions/log/package.json | 5 ----- src/vs/workbench/parts/output/common/output.ts | 16 +++++++++++++--- .../parts/output/common/outputLinkProvider.ts | 4 ++-- .../electron-browser/output.contribution.ts | 10 +++++++++- .../output/electron-browser/outputServices.ts | 9 +++++---- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/extensions/log/package.json b/extensions/log/package.json index ab8b0da1c9d..b0c0122fd39 100644 --- a/extensions/log/package.json +++ b/extensions/log/package.json @@ -24,11 +24,6 @@ } ], "grammars": [ - { - "language": "Log", - "scopeName": "text.log", - "path": "./syntaxes/log.tmLanguage.json" - }, { "language": "log", "scopeName": "text.log", diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 16786934caa..a81b9c68c42 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -22,14 +22,24 @@ export const OUTPUT_MIME = 'text/x-code-output'; export const OUTPUT_SCHEME = 'output'; /** - * Output resource scheme. + * Id used by the output editor. + */ +export const OUTPUT_MODE_ID = 'Log'; + +/** + * Mime type used by the log output editor. + */ +export const LOG_MIME = 'text/x-code-log-output'; + +/** + * Log resource scheme. */ export const LOG_SCHEME = 'log'; /** - * Id used by the output editor. + * Id used by the log output editor. */ -export const OUTPUT_MODE_ID = 'Log'; +export const LOG_MODE_ID = 'log'; /** * Output panel id diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts index 8f2b9dba601..8014d8bd950 100644 --- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -11,7 +11,7 @@ import { RunOnceScheduler, wireCancellationToken } from 'vs/base/common/async'; import { IModelService } from 'vs/editor/common/services/modelService'; import { LinkProviderRegistry, ILink } from 'vs/editor/common/modes'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { OUTPUT_MODE_ID } from 'vs/workbench/parts/output/common/output'; +import { OUTPUT_MODE_ID, LOG_MODE_ID } from 'vs/workbench/parts/output/common/output'; import { MonacoWebWorker, createWebWorker } from 'vs/editor/common/services/webWorker'; import { ICreateData, OutputLinkComputer } from 'vs/workbench/parts/output/common/outputLinkComputer'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -44,7 +44,7 @@ export class OutputLinkProvider { const folders = this.contextService.getWorkspace().folders; if (folders.length > 0) { if (!this.linkProviderRegistration) { - this.linkProviderRegistration = LinkProviderRegistry.register({ language: OUTPUT_MODE_ID, scheme: '*' }, { + this.linkProviderRegistration = LinkProviderRegistry.register([{ language: OUTPUT_MODE_ID, scheme: '*' }, { language: LOG_MODE_ID, scheme: '*' }], { provideLinks: (model, token): Thenable => { return wireCancellationToken(token, this.provideLinks(model.uri)); } 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 e989b92d769..3f4518913ff 100644 --- a/src/vs/workbench/parts/output/electron-browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/electron-browser/output.contribution.ts @@ -13,7 +13,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { OutputService, LogContentProvider } from 'vs/workbench/parts/output/electron-browser/outputServices'; import { ToggleOutputAction, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions'; -import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output'; +import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, COMMAND_OPEN_LOG_VIEWER, LOG_MODE_ID, LOG_MIME } from 'vs/workbench/parts/output/common/output'; import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -39,6 +39,14 @@ ModesRegistry.registerLanguage({ mimetypes: [OUTPUT_MIME] }); +// Register Log Output Mode +ModesRegistry.registerLanguage({ + id: LOG_MODE_ID, + extensions: [], + aliases: [null], + mimetypes: [LOG_MIME] +}); + // Register Output Panel Registry.as(Extensions.Panels).registerPanel(new PanelDescriptor( OutputPanel, diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index 2b642f95a58..546e3855152 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, MAX_OUTPUT_LENGTH, LOG_SCHEME } from 'vs/workbench/parts/output/common/output'; +import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, MAX_OUTPUT_LENGTH, LOG_SCHEME, LOG_MIME } from 'vs/workbench/parts/output/common/output'; import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -134,6 +134,7 @@ abstract class AbstractFileOutputChannel extends Disposable { constructor( protected readonly outputChannelIdentifier: IOutputChannelIdentifier, private readonly modelUri: URI, + private mimeType: string, protected fileService: IFileService, protected modelService: IModelService, protected modeService: IModeService, @@ -166,7 +167,7 @@ abstract class AbstractFileOutputChannel extends Disposable { if (this.model) { this.model.setValue(content); } else { - this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), this.modelUri); + this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(this.mimeType), this.modelUri); this.onModelCreated(this.model); const disposables: IDisposable[] = []; disposables.push(this.model.onWillDispose(() => { @@ -217,7 +218,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out @IModeService modeService: IModeService, @ILogService logService: ILogService ) { - super({ ...outputChannelIdentifier, file: URI.file(paths.join(outputDir, `${outputChannelIdentifier.id}.log`)) }, modelUri, fileService, modelService, modeService); + super({ ...outputChannelIdentifier, file: URI.file(paths.join(outputDir, `${outputChannelIdentifier.id}.log`)) }, modelUri, OUTPUT_MIME, fileService, modelService, modeService); // Use one rotating file to check for main file reset this.outputWriter = new RotatingLogger(this.id, this.file.fsPath, 1024 * 1024 * 30, 1); @@ -363,7 +364,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann @IModeService modeService: IModeService, @ILogService logService: ILogService, ) { - super(outputChannelIdentifier, modelUri, fileService, modelService, modeService); + super(outputChannelIdentifier, modelUri, LOG_MIME, fileService, modelService, modeService); this.fileHandler = this._register(new OutputFileListener(this.file)); this._register(this.fileHandler.onDidContentChange(() => this.onDidContentChange())); From d6dab13eb5e84b2dbe3fda545edffaf56ebee4ec Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 22 Feb 2018 11:12:44 +0100 Subject: [PATCH 44/53] Symlinks are not the same on win, and we can not create them programitically without adming privileges --- src/vs/base/test/node/extfs/extfs.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts index 17d020743e5..bb01309a025 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -16,7 +16,7 @@ import strings = require('vs/base/common/strings'); import extfs = require('vs/base/node/extfs'); import { onError } from 'vs/base/test/common/utils'; import { Readable } from 'stream'; -import { isLinux } from 'vs/base/common/platform'; +import { isLinux, isWindows } from 'vs/base/common/platform'; const ignore = () => { }; @@ -75,6 +75,11 @@ suite('Extfs', () => { }); test('stat link', function (done: () => void) { + if (isWindows) { + // Symlinks are not the same on win, and we can not create them programitically without admin privileges + return done(); + } + const id1 = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id1); const directory = path.join(parentDir, 'extfs', id1); From 7648968f84b0915d6b268615be603b526a91b946 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Feb 2018 12:25:20 +0100 Subject: [PATCH 45/53] Installer toast notification doesn't show full message text (fixes #44151) --- .../notifications/media/notificationsList.css | 10 ++++++++++ .../notifications/notificationsViewer.ts | 20 ++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index fd017ca60e6..451a93476c1 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -14,6 +14,16 @@ box-sizing: border-box; } +.monaco-workbench .notifications-list-container .notification-offset-helper { + opacity: 0; + margin-top: 10px; + margin-bottom: 10px; + margin-left: 35px; /* 5px padding and 30px severity icon */ + margin-right: 77px; /* 5px padding and (3 * 24px) toolbar */ + position: absolute; + line-height: 22px; +} + /** Notification: Main Row */ .monaco-workbench .notifications-list-container .notification-list-item > .notification-list-item-main-row { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 4bdc59a5789..adf4bbe9941 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -42,11 +42,7 @@ export class NotificationsListDelegate implements IDelegate Date: Thu, 22 Feb 2018 12:31:39 +0100 Subject: [PATCH 46/53] fix #44140 --- .../browser/parts/notifications/notificationsViewer.ts | 6 ++++++ src/vs/workbench/common/notifications.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index adf4bbe9941..3fae826811e 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -71,6 +71,12 @@ export class NotificationsListDelegate implements IDelegate; expand(): void; - collapse(): void; + collapse(skipEvents?: boolean): void; toggle(): void; hasProgress(): boolean; @@ -494,13 +494,16 @@ export class NotificationViewItem implements INotificationViewItem { this._onDidExpansionChange.fire(); } - public collapse(): void { + public collapse(skipEvents?: boolean): void { if (!this._expanded || !this.canCollapse) { return; } this._expanded = false; - this._onDidExpansionChange.fire(); + + if (!skipEvents) { + this._onDidExpansionChange.fire(); + } } public toggle(): void { From c2a227b77e50a625c4b8f407a18c48deae51883e Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Thu, 22 Feb 2018 12:34:40 +0100 Subject: [PATCH 47/53] Locking version for language client and server --- extensions/css/package.json | 2 +- extensions/css/server/package.json | 2 +- extensions/css/server/yarn.lock | 2 +- extensions/css/yarn.lock | 2 +- extensions/html/package.json | 2 +- extensions/html/server/package.json | 2 +- extensions/html/server/yarn.lock | 2 +- extensions/html/yarn.lock | 2 +- extensions/json/package.json | 2 +- extensions/json/server/package.json | 2 +- extensions/json/server/yarn.lock | 2 +- extensions/json/yarn.lock | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/css/package.json b/extensions/css/package.json index c5022b59db6..3374eea3078 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -715,7 +715,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^4.0.0-next.9", + "vscode-languageclient": "4.0.0-next.9", "vscode-nls": "^3.2.1" }, "devDependencies": { diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index eaf6a20397b..3d2778ecfae 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -10,7 +10,7 @@ "dependencies": { "vscode-css-languageservice": "^3.0.6", "vscode-emmet-helper": "^1.1.34", - "vscode-languageserver": "^4.0.0-next.4" + "vscode-languageserver": "4.0.0-next.4" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/css/server/yarn.lock b/extensions/css/server/yarn.lock index 3deaf65197a..c886d30c17f 100644 --- a/extensions/css/server/yarn.lock +++ b/extensions/css/server/yarn.lock @@ -48,7 +48,7 @@ vscode-languageserver-types@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3" -vscode-languageserver@^4.0.0-next.4: +vscode-languageserver@4.0.0-next.4: version "4.0.0-next.4" resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.4.tgz#162440b15bedaab07e1676f046e4d9b8578b3d92" dependencies: diff --git a/extensions/css/yarn.lock b/extensions/css/yarn.lock index 7ab2a57b25f..5f63bd90373 100644 --- a/extensions/css/yarn.lock +++ b/extensions/css/yarn.lock @@ -10,7 +10,7 @@ vscode-jsonrpc@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe" -vscode-languageclient@^4.0.0-next.9: +vscode-languageclient@4.0.0-next.9: version "4.0.0-next.9" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a" dependencies: diff --git a/extensions/html/package.json b/extensions/html/package.json index 7c97ed5ac7c..d7a3611d25e 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -229,7 +229,7 @@ }, "dependencies": { "vscode-extension-telemetry": "0.0.13", - "vscode-languageclient": "^4.0.0-next.9", + "vscode-languageclient": "4.0.0-next.9", "vscode-nls": "^3.2.1" }, "devDependencies": { diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index 9237e4b8716..4253cdfb9e7 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -11,7 +11,7 @@ "vscode-css-languageservice": "^3.0.6", "vscode-emmet-helper": "^1.1.34", "vscode-html-languageservice": "^2.0.16", - "vscode-languageserver": "^4.0.0-next.4", + "vscode-languageserver": "4.0.0-next.4", "vscode-languageserver-types": "^3.6.0-next.1", "vscode-nls": "^3.2.1", "vscode-uri": "^1.0.1" diff --git a/extensions/html/server/yarn.lock b/extensions/html/server/yarn.lock index 13bfdd813f7..f594dc4cf3c 100644 --- a/extensions/html/server/yarn.lock +++ b/extensions/html/server/yarn.lock @@ -56,7 +56,7 @@ vscode-languageserver-types@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3" -vscode-languageserver@^4.0.0-next.4: +vscode-languageserver@4.0.0-next.4: version "4.0.0-next.4" resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.4.tgz#162440b15bedaab07e1676f046e4d9b8578b3d92" dependencies: diff --git a/extensions/html/yarn.lock b/extensions/html/yarn.lock index 293e9314996..9c554a123f2 100644 --- a/extensions/html/yarn.lock +++ b/extensions/html/yarn.lock @@ -38,7 +38,7 @@ vscode-jsonrpc@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe" -vscode-languageclient@^4.0.0-next.9: +vscode-languageclient@4.0.0-next.9: version "4.0.0-next.9" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a" dependencies: diff --git a/extensions/json/package.json b/extensions/json/package.json index 0f976e9b2f6..e5bbf304d4c 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -165,7 +165,7 @@ }, "dependencies": { "vscode-extension-telemetry": "0.0.13", - "vscode-languageclient": "^4.0.0-next.9", + "vscode-languageclient": "4.0.0-next.9", "vscode-nls": "^3.2.1" }, "devDependencies": { diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json index 34d3b370410..c597f26e5cc 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -11,7 +11,7 @@ "jsonc-parser": "^1.0.1", "request-light": "^0.2.2", "vscode-json-languageservice": "^3.0.7", - "vscode-languageserver": "^4.0.0-next.3", + "vscode-languageserver": "4.0.0-next.3", "vscode-nls": "^3.2.1", "vscode-uri": "^1.0.1" }, diff --git a/extensions/json/server/yarn.lock b/extensions/json/server/yarn.lock index 169311dfdf0..ac7f8a50213 100644 --- a/extensions/json/server/yarn.lock +++ b/extensions/json/server/yarn.lock @@ -88,7 +88,7 @@ vscode-languageserver-types@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3" -vscode-languageserver@^4.0.0-next.3: +vscode-languageserver@4.0.0-next.3: version "4.0.0-next.3" resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.3.tgz#89a9ce5078e3a86a78e3551c3766194ce4295611" dependencies: diff --git a/extensions/json/yarn.lock b/extensions/json/yarn.lock index 293e9314996..9c554a123f2 100644 --- a/extensions/json/yarn.lock +++ b/extensions/json/yarn.lock @@ -38,7 +38,7 @@ vscode-jsonrpc@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe" -vscode-languageclient@^4.0.0-next.9: +vscode-languageclient@4.0.0-next.9: version "4.0.0-next.9" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a" dependencies: From 0faa5d117cced88ab9d507cadf697c2acd3dcbff Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 22 Feb 2018 14:12:57 +0100 Subject: [PATCH 48/53] folding provider: first version --- extensions/json/client/src/jsonMain.ts | 66 +++++++- extensions/json/server/src/jsonServerMain.ts | 132 ++++++++++++++- src/vs/editor/common/modes.ts | 66 ++++++++ src/vs/editor/contrib/folding/folding.ts | 36 +++- src/vs/editor/contrib/folding/foldingModel.ts | 68 ++++---- .../editor/contrib/folding/foldingRanges.ts | 4 +- .../contrib/folding/hiddenRangeModel.ts | 4 +- .../contrib/folding/indentRangeProvider.ts | 32 ++-- .../contrib/folding/syntaxRangeProvider.ts | 154 ++++++++++++++++++ .../contrib/folding/test/foldingModel.test.ts | 4 +- .../standalone/browser/standaloneLanguages.ts | 8 + src/vs/monaco.d.ts | 3 + src/vs/vscode.proposed.d.ts | 74 +++++++++ .../mainThreadLanguageFeatures.ts | 11 ++ src/vs/workbench/api/node/extHost.api.impl.ts | 8 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 + .../api/node/extHostLanguageFeatures.ts | 31 +++- .../api/node/extHostTypeConverters.ts | 8 + src/vs/workbench/api/node/extHostTypes.ts | 43 +++++ 19 files changed, 689 insertions(+), 65 deletions(-) create mode 100644 src/vs/editor/contrib/folding/syntaxRangeProvider.ts diff --git a/extensions/json/client/src/jsonMain.ts b/extensions/json/client/src/jsonMain.ts index 9491dc03e3e..949a1207c0a 100644 --- a/extensions/json/client/src/jsonMain.ts +++ b/extensions/json/client/src/jsonMain.ts @@ -8,8 +8,8 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; +import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList as VSFoldingRangeList, FoldingRange as VSFoldingRange } from 'vscode'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, TextDocumentIdentifier } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; import { hash } from './utils/hash'; @@ -30,6 +30,57 @@ namespace SchemaAssociationNotification { export const type: NotificationType = new NotificationType('json/schemaAssociations'); } +interface FoldingRangeList { + /** + * The folding ranges. + */ + ranges: FoldingRange[]; +} + +export enum FoldingRangeType { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' +} + +interface FoldingRange { + + /** + * The start line number + */ + startLine: number; + + /** + * The end line number + */ + endLine: number; + + /** + * The actual color value for this color range. + */ + type?: FoldingRangeType; +} + +interface FoldingRangeRequest { + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; +} + +namespace FoldingRangesRequest { + export const type: RequestType = new RequestType('textDocument/foldingRanges'); +} + interface IPackageInfo { name: string; version: string; @@ -124,6 +175,17 @@ export function activate(context: ExtensionContext) { toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri))); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + + languages.registerFoldingProvider(documentSelector, { + provideFoldingRanges(document: TextDocument) { + return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => { + if (res && Array.isArray(res.ranges)) { + return new VSFoldingRangeList(res.ranges.map(r => new VSFoldingRange(r.startLine, r.endLine, r.type))); + } + return null; + }); + } + }); }); let languageConfiguration: LanguageConfiguration = { diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts index 1adbcfbf546..bd3a7fb03f0 100644 --- a/extensions/json/server/src/jsonServerMain.ts +++ b/extensions/json/server/src/jsonServerMain.ts @@ -7,7 +7,7 @@ import { createConnection, IConnection, TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentIdentifier } from 'vscode-languageserver'; import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; @@ -20,6 +20,7 @@ import Strings = require('./utils/strings'); import { formatError, runSafe, runSafeAsync } from './utils/errors'; import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; +import { createScanner, SyntaxKind } from 'jsonc-parser'; interface ISchemaAssociations { [pattern: string]: string[]; @@ -37,6 +38,57 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } +interface FoldingRangeList { + /** + * The folding ranges. + */ + ranges: FoldingRange[]; +} + +export enum FoldingRangeType { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' +} + +interface FoldingRange { + + /** + * The start line number + */ + startLine: number; + + /** + * The end line number + */ + endLine: number; + + /** + * The actual color value for this color range. + */ + type?: FoldingRangeType | string; +} + +interface FoldingRangeRequest { + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; +} + +namespace FoldingRangesRequest { + export const type: RequestType = new RequestType('textDocument/foldingRanges'); +} + // Create a connection for the server let connection: IConnection = createConnection(); @@ -347,7 +399,83 @@ connection.onRequest(ColorPresentationRequest.type, params => { return languageService.getColorPresentations(document, jsonDocument, params.color, params.range); } return []; - }, [], `Error while computing color presentationsd for ${params.textDocument.uri}`); + }, [], `Error while computing color presentations for ${params.textDocument.uri}`); +}); + +connection.onRequest(FoldingRangesRequest.type, params => { + return runSafe(() => { + let document = documents.get(params.textDocument.uri); + if (document) { + let ranges: FoldingRange[] = []; + let stack: FoldingRange[] = []; + let prevStart = -1; + let scanner = createScanner(document.getText(), false); + let token = scanner.scan(); + while (token !== SyntaxKind.EOF) { + switch (token) { + case SyntaxKind.OpenBraceToken: + case SyntaxKind.OpenBracketToken: { + let startLine = document.positionAt(scanner.getTokenOffset()).line; + let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' }; + stack.push(range); + } + break; + case SyntaxKind.CloseBraceToken: + case SyntaxKind.CloseBracketToken: { + let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array'; + if (stack.length > 0 && stack[stack.length - 1].type === type) { + let range = stack.pop(); + let line = document.positionAt(scanner.getTokenOffset()).line; + if (range && line > range.startLine + 1 && prevStart !== range.startLine) { + range.endLine = line - 1; + ranges.push(range); + prevStart = range.startLine; + } + } + } + break; + case SyntaxKind.BlockCommentTrivia: { + let startLine = document.positionAt(scanner.getTokenOffset()).line; + let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line; + if (startLine < endLine) { + ranges.push({ startLine, endLine, type: FoldingRangeType.Comment }); + prevStart = startLine; + } + } + break; + case SyntaxKind.LineCommentTrivia: { + let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength()); + let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/); + if (m) { + let line = document.positionAt(scanner.getTokenOffset()).line; + if (m[1]) { // start pattern match + let range = { startLine: line, endLine: line, type: FoldingRangeType.Region }; + stack.push(range); + } else { + let i = stack.length - 1; + while (i >= 0 && stack[i].type !== FoldingRangeType.Region) { + i--; + } + if (i >= 0) { + let range = stack[i]; + stack.length = i; + if (line > range.startLine && prevStart !== range.startLine) { + range.endLine = line; + ranges.push(range); + prevStart = range.startLine; + } + } + } + } + } + break; + } + token = scanner.scan(); + } + return { ranges }; + } + return null; + }, null, `Error while computing folding ranges for ${params.textDocument.uri}`); }); // Listen on the connection diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index c7fd1b7b243..33c9cbcce55 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -826,6 +826,67 @@ export interface DocumentColorProvider { provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable; } +/** + * A provider of colors for editor models. + */ +/** + * @internal + */ +export interface FoldingProvider { + /** + * Provides the color ranges for a specific model. + */ + provideFoldingRanges(model: model.ITextModel, token: CancellationToken): IFoldingRangeList | Thenable; +} +/** + * @internal + */ +export interface IFoldingRangeList { + + ranges: IFoldingRange[]; +} +/** + * @internal + */ +export interface IFoldingRange { + + /** + * The start line number + */ + startLineNumber: number; + + /** + * The end line number + */ + endLineNumber: number; + + /** + * The optional type of the folding range + */ + type?: FoldingRangeType | string; + + // auto-collapse + // header span + +} +/** + * @internal + */ +export enum FoldingRangeType { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' +} + /** * @internal */ @@ -971,6 +1032,11 @@ export const LinkProviderRegistry = new LanguageFeatureRegistry(); */ export const ColorProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const FoldingProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 9fd6f41c706..2eaa4756dba 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -19,17 +19,24 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel'; import { FoldingDecorationProvider } from './foldingDecorations'; +import { FoldingRegions } from './foldingRanges'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel'; import { IRange } from 'vs/editor/common/core/range'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { computeRanges as computeIndentRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; +import { IndentRangeProvider } from 'vs/editor/contrib/folding/indentRangeProvider'; import { IPosition } from 'vs/editor/common/core/position'; +import { FoldingProviderRegistry } from 'vs/editor/common/modes'; +import { SyntaxRangeProvider } from './syntaxRangeProvider'; export const ID = 'editor.contrib.folding'; +export interface RangeProvider { + compute(editorModel: ITextModel): TPromise; +} + export class FoldingController implements IEditorContribution { static MAX_FOLDING_REGIONS = 5000; @@ -48,6 +55,8 @@ export class FoldingController implements IEditorContribution { private foldingModel: FoldingModel; private hiddenRangeModel: HiddenRangeModel; + private rangeProvider: RangeProvider; + private foldingModelPromise: TPromise; private updateScheduler: Delayer; @@ -69,6 +78,7 @@ export class FoldingController implements IEditorContribution { this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this.globalToDispose.push(FoldingProviderRegistry.onDidChange(() => this.onModelChanged())); this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { if (e.contribInfo) { @@ -167,17 +177,22 @@ export class FoldingController implements IEditorContribution { this.foldingModelPromise = null; this.hiddenRangeModel = null; this.cursorChangedScheduler = null; + this.rangeProvider = null; } }); this.onModelContentChanged(); } - private computeRanges(editorModel: ITextModel) { - let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id); - let offSide = foldingRules && foldingRules.offSide; - let markers = foldingRules && foldingRules.markers; - let ranges = computeIndentRanges(editorModel, offSide, markers); - return ranges; + private getRangeProvider(): RangeProvider { + if (!this.rangeProvider) { + let foldingProviders = FoldingProviderRegistry.ordered(this.foldingModel.textModel); + if (foldingProviders.length) { + this.rangeProvider = new SyntaxRangeProvider(foldingProviders); + } else { + this.rangeProvider = new IndentRangeProvider(); + } + } + return this.rangeProvider; } public getFoldingModel() { @@ -191,9 +206,12 @@ export class FoldingController implements IEditorContribution { // some cursors might have moved into hidden regions, make sure they are in expanded regions let selections = this.editor.getSelections(); let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : []; - this.foldingModel.update(this.computeRanges(this.foldingModel.textModel), selectionLineNumbers); + return this.getRangeProvider().compute(this.foldingModel.textModel).then(foldingRanges => { + this.foldingModel.update(foldingRanges, selectionLineNumbers); + return this.foldingModel; + }); } - return this.foldingModel; + return null; }); } } diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 33e1826061c..5fbb9f2289a 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -5,7 +5,7 @@ import { ITextModel, IModelDecorationOptions, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import Event, { Emitter } from 'vs/base/common/event'; -import { FoldingRanges, ILineRange, FoldingRegion } from './foldingRanges'; +import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges'; export interface IDecorationProvider { getDecorationOption(isCollapsed: boolean): IModelDecorationOptions; @@ -24,13 +24,13 @@ export class FoldingModel { private _textModel: ITextModel; private _decorationProvider: IDecorationProvider; - private _ranges: FoldingRanges; + private _regions: FoldingRegions; private _editorDecorationIds: string[]; private _isInitialized: boolean; private _updateEventEmitter = new Emitter(); - public get ranges(): FoldingRanges { return this._ranges; } + public get regions(): FoldingRegions { return this._regions; } public get onDidChange(): Event { return this._updateEventEmitter.event; } public get textModel() { return this._textModel; } public get isInitialized() { return this._isInitialized; } @@ -38,7 +38,7 @@ export class FoldingModel { constructor(textModel: ITextModel, decorationProvider: IDecorationProvider) { this._textModel = textModel; this._decorationProvider = decorationProvider; - this._ranges = new FoldingRanges(new Uint32Array(0), new Uint32Array(0)); + this._regions = new FoldingRegions(new Uint32Array(0), new Uint32Array(0)); this._editorDecorationIds = []; this._isInitialized = false; } @@ -54,8 +54,8 @@ export class FoldingModel { let editorDecorationId = this._editorDecorationIds[index]; if (editorDecorationId && !processed[editorDecorationId]) { processed[editorDecorationId] = true; - let newCollapseState = !this._ranges.isCollapsed(index); - this._ranges.setCollapsed(index, newCollapseState); + let newCollapseState = !this._regions.isCollapsed(index); + this._regions.setCollapsed(index, newCollapseState); accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState)); } } @@ -63,7 +63,7 @@ export class FoldingModel { this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions }); } - public update(newRanges: FoldingRanges, blockedLineNumers: number[] = []): void { + public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void { let newEditorDecorations = []; let isBlocked = (startLineNumber, endLineNumber) => { @@ -76,11 +76,11 @@ export class FoldingModel { }; let initRange = (index: number, isCollapsed: boolean) => { - let startLineNumber = newRanges.getStartLineNumber(index); - if (isCollapsed && isBlocked(startLineNumber, newRanges.getEndLineNumber(index))) { + let startLineNumber = newRegions.getStartLineNumber(index); + if (isCollapsed && isBlocked(startLineNumber, newRegions.getEndLineNumber(index))) { isCollapsed = false; } - newRanges.setCollapsed(index, isCollapsed); + newRegions.setCollapsed(index, isCollapsed); let maxColumn = this._textModel.getLineMaxColumn(startLineNumber); let decorationRange = { startLineNumber: startLineNumber, @@ -93,8 +93,8 @@ export class FoldingModel { let i = 0; let nextCollapsed = () => { - while (i < this._ranges.length) { - let isCollapsed = this._ranges.isCollapsed(i); + while (i < this._regions.length) { + let isCollapsed = this._regions.isCollapsed(i); i++; if (isCollapsed) { return i - 1; @@ -105,14 +105,14 @@ export class FoldingModel { let k = 0; let collapsedIndex = nextCollapsed(); - while (collapsedIndex !== -1 && k < newRanges.length) { + while (collapsedIndex !== -1 && k < newRegions.length) { // get the latest range let decRange = this._textModel.getDecorationRange(this._editorDecorationIds[collapsedIndex]); if (decRange) { let collapsedStartLineNumber = decRange.startLineNumber; if (this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.startColumn) { // test that the decoration is still at the end otherwise it got deleted - while (k < newRanges.length) { - let startLineNumber = newRanges.getStartLineNumber(k); + while (k < newRegions.length) { + let startLineNumber = newRegions.getStartLineNumber(k); if (collapsedStartLineNumber >= startLineNumber) { initRange(k, collapsedStartLineNumber === startLineNumber); k++; @@ -124,13 +124,13 @@ export class FoldingModel { } collapsedIndex = nextCollapsed(); } - while (k < newRanges.length) { + while (k < newRegions.length) { initRange(k, false); k++; } this._editorDecorationIds = this._decorationProvider.deltaDecorations(this._editorDecorationIds, newEditorDecorations); - this._ranges = newRanges; + this._regions = newRegions; this._isInitialized = true; this._updateEventEmitter.fire({ model: this }); } @@ -140,12 +140,12 @@ export class FoldingModel { */ public getMemento(): CollapseMemento { let collapsedRanges: ILineRange[] = []; - for (let i = 0; i < this._ranges.length; i++) { - if (this._ranges.isCollapsed(i)) { + for (let i = 0; i < this._regions.length; i++) { + if (this._regions.isCollapsed(i)) { let range = this._textModel.getDecorationRange(this._editorDecorationIds[i]); if (range) { let startLineNumber = range.startLineNumber; - let endLineNumber = range.endLineNumber + this._ranges.getEndLineNumber(i) - this._ranges.getStartLineNumber(i); + let endLineNumber = range.endLineNumber + this._regions.getEndLineNumber(i) - this._regions.getStartLineNumber(i); collapsedRanges.push({ startLineNumber, endLineNumber }); } } @@ -179,11 +179,11 @@ export class FoldingModel { getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] { let result: FoldingRegion[] = []; - if (this._ranges) { - let index = this._ranges.findRange(lineNumber); + if (this._regions) { + let index = this._regions.findRange(lineNumber); let level = 1; while (index >= 0) { - let current = this._ranges.toRegion(index); + let current = this._regions.toRegion(index); if (!filter || filter(current, level)) { result.push(current); } @@ -195,10 +195,10 @@ export class FoldingModel { } getRegionAtLine(lineNumber: number): FoldingRegion { - if (this._ranges) { - let index = this._ranges.findRange(lineNumber); + if (this._regions) { + let index = this._regions.findRange(lineNumber); if (index >= 0) { - return this._ranges.toRegion(index); + return this._regions.toRegion(index); } } return null; @@ -210,9 +210,9 @@ export class FoldingModel { let levelStack: FoldingRegion[] = trackLevel ? [] : null; let index = region ? region.regionIndex + 1 : 0; let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; - for (let i = index, len = this._ranges.length; i < len; i++) { - let current = this._ranges.toRegion(i); - if (this._ranges.getStartLineNumber(i) < endLineNumber) { + for (let i = index, len = this._regions.length; i < len; i++) { + let current = this._regions.toRegion(i); + if (this._regions.getStartLineNumber(i) < endLineNumber) { if (trackLevel) { while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) { levelStack.pop(); @@ -296,13 +296,13 @@ export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: n */ export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, regExp: RegExp, doCollapse: boolean): void { let editorModel = foldingModel.textModel; - let ranges = foldingModel.ranges; + let regions = foldingModel.regions; let toToggle = []; - for (let i = ranges.length - 1; i >= 0; i--) { - if (doCollapse !== ranges.isCollapsed(i)) { - let startLineNumber = ranges.getStartLineNumber(i); + for (let i = regions.length - 1; i >= 0; i--) { + if (doCollapse !== regions.isCollapsed(i)) { + let startLineNumber = regions.getStartLineNumber(i); if (regExp.test(editorModel.getLineContent(startLineNumber))) { - toToggle.push(ranges.toRegion(i)); + toToggle.push(regions.toRegion(i)); } } } diff --git a/src/vs/editor/contrib/folding/foldingRanges.ts b/src/vs/editor/contrib/folding/foldingRanges.ts index 666e174f1b2..613fe4c169b 100644 --- a/src/vs/editor/contrib/folding/foldingRanges.ts +++ b/src/vs/editor/contrib/folding/foldingRanges.ts @@ -15,7 +15,7 @@ export const MAX_LINE_NUMBER = 0xFFFFFF; const MASK_INDENT = 0xFF000000; -export class FoldingRanges { +export class FoldingRegions { private _startIndexes: Uint32Array; private _endIndexes: Uint32Array; private _collapseStates: Uint32Array; @@ -138,7 +138,7 @@ export class FoldingRanges { export class FoldingRegion { - constructor(private ranges: FoldingRanges, private index: number) { + constructor(private ranges: FoldingRegions, private index: number) { } public get startLineNumber() { diff --git a/src/vs/editor/contrib/folding/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/hiddenRangeModel.ts index fee0f5108c2..0acb3f59648 100644 --- a/src/vs/editor/contrib/folding/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/hiddenRangeModel.ts @@ -23,7 +23,7 @@ export class HiddenRangeModel { this._foldingModel = model; this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges()); this._hiddenRanges = []; - if (model.ranges.length) { + if (model.regions.length) { this.updateHiddenRanges(); } } @@ -37,7 +37,7 @@ export class HiddenRangeModel { let lastCollapsedStart = Number.MAX_VALUE; let lastCollapsedEnd = -1; - let ranges = this._foldingModel.ranges; + let ranges = this._foldingModel.regions; for (; i < ranges.length; i++) { if (!ranges.isCollapsed(i)) { continue; diff --git a/src/vs/editor/contrib/folding/indentRangeProvider.ts b/src/vs/editor/contrib/folding/indentRangeProvider.ts index 04259b2c8b8..9ec730af0e2 100644 --- a/src/vs/editor/contrib/folding/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/indentRangeProvider.ts @@ -7,25 +7,37 @@ import { ITextModel } from 'vs/editor/common/model'; import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration'; -import { FoldingRanges, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges'; +import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { RangeProvider } from './folding'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000; +export class IndentRangeProvider implements RangeProvider { + compute(editorModel: ITextModel): TPromise { + let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id); + let offSide = foldingRules && foldingRules.offSide; + let markers = foldingRules && foldingRules.markers; + return TPromise.as(computeRanges(editorModel, offSide, markers)); + } +} + // public only for testing export class RangesCollector { private _startIndexes: number[]; private _endIndexes: number[]; private _indentOccurrences: number[]; private _length: number; - private _FoldingRangesLimit: number; + private _foldingRangesLimit: number; - constructor(FoldingRangesLimit: number) { + constructor(foldingRangesLimit: number) { this._startIndexes = []; this._endIndexes = []; this._indentOccurrences = []; this._length = 0; - this._FoldingRangesLimit = FoldingRangesLimit; + this._foldingRangesLimit = foldingRangesLimit; } public insertFirst(startLineNumber: number, endLineNumber: number, indent: number) { @@ -42,7 +54,7 @@ export class RangesCollector { } public toIndentRanges(model: ITextModel) { - if (this._length <= this._FoldingRangesLimit) { + if (this._length <= this._foldingRangesLimit) { // reverse and create arrays of the exact length let startIndexes = new Uint32Array(this._length); let endIndexes = new Uint32Array(this._length); @@ -50,14 +62,14 @@ export class RangesCollector { startIndexes[k] = this._startIndexes[i]; endIndexes[k] = this._endIndexes[i]; } - return new FoldingRanges(startIndexes, endIndexes); + return new FoldingRegions(startIndexes, endIndexes); } else { let entries = 0; let maxIndent = this._indentOccurrences.length; for (let i = 0; i < this._indentOccurrences.length; i++) { let n = this._indentOccurrences[i]; if (n) { - if (n + entries > this._FoldingRangesLimit) { + if (n + entries > this._foldingRangesLimit) { maxIndent = i; break; } @@ -78,7 +90,7 @@ export class RangesCollector { k++; } } - return new FoldingRanges(startIndexes, endIndexes); + return new FoldingRegions(startIndexes, endIndexes); } } @@ -87,9 +99,9 @@ export class RangesCollector { interface PreviousRegion { indent: number; line: number; marker: boolean; } -export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, FoldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRanges { +export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions { const tabSize = model.getOptions().tabSize; - let result = new RangesCollector(FoldingRangesLimit); + let result = new RangesCollector(foldingRangesLimit); let pattern = void 0; if (markers) { diff --git a/src/vs/editor/contrib/folding/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts new file mode 100644 index 00000000000..345f08a12f3 --- /dev/null +++ b/src/vs/editor/contrib/folding/syntaxRangeProvider.ts @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { FoldingProvider, IFoldingRange } from 'vs/editor/common/modes'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { ITextModel } from 'vs/editor/common/model'; +import { RangeProvider } from './folding'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges'; + +const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000; + +export interface IFoldingRangeData extends IFoldingRange { + rank: number; +} + +export class SyntaxRangeProvider implements RangeProvider { + + constructor(private providers: FoldingProvider[]) { + } + + compute(model: ITextModel): TPromise { + return collectSyntaxRanges(this.providers, model).then(ranges => { + return sanitizeRanges(ranges); + }); + } + +} + +function collectSyntaxRanges(providers: FoldingProvider[], model: ITextModel): TPromise { + const rangeData: IFoldingRangeData[] = []; + let promises = providers.map((provider, rank) => asWinJsPromise(token => provider.provideFoldingRanges(model, token)).then(list => { + if (list && Array.isArray(list.ranges)) { + for (let r of list.ranges) { + rangeData.push({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, rank, type: r.type }); + } + } + }, onUnexpectedExternalError)); + + return TPromise.join(promises).then(() => { + return rangeData; + }); +} + +export class RangesCollector { + private _startIndexes: number[]; + private _endIndexes: number[]; + private _nestingLevels: number[]; + private _nestingLevelCounts: number[]; + private _length: number; + private _foldingRangesLimit: number; + + constructor(foldingRangesLimit: number) { + this._startIndexes = []; + this._endIndexes = []; + this._nestingLevels = []; + this._nestingLevelCounts = []; + this._length = 0; + this._foldingRangesLimit = foldingRangesLimit; + } + + public add(startLineNumber: number, endLineNumber: number, nestingLevel: number) { + if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) { + return; + } + let index = this._length; + this._startIndexes[index] = startLineNumber; + this._endIndexes[index] = endLineNumber; + this._nestingLevels[index] = nestingLevel; + this._length++; + if (nestingLevel < 30) { + this._nestingLevelCounts[nestingLevel] = (this._nestingLevelCounts[nestingLevel] || 0) + 1; + } + } + + public toIndentRanges() { + if (this._length <= this._foldingRangesLimit) { + let startIndexes = new Uint32Array(this._length); + let endIndexes = new Uint32Array(this._length); + for (let i = 0; i < this._length; i++) { + startIndexes[i] = this._startIndexes[i]; + endIndexes[i] = this._endIndexes[i]; + } + return new FoldingRegions(startIndexes, endIndexes); + } else { + let entries = 0; + let maxLevel = this._nestingLevelCounts.length; + for (let i = 0; i < this._nestingLevelCounts.length; i++) { + let n = this._nestingLevelCounts[i]; + if (n) { + if (n + entries > this._foldingRangesLimit) { + maxLevel = i; + break; + } + entries += n; + } + } + let startIndexes = new Uint32Array(entries); + let endIndexes = new Uint32Array(entries); + for (let i = 0, k = 0; i < this._length; i++) { + let level = this._nestingLevels[i]; + if (level < maxLevel) { + startIndexes[k] = this._startIndexes[i]; + endIndexes[k] = this._endIndexes[i]; + k++; + } + } + return new FoldingRegions(startIndexes, endIndexes); + } + + } +} + +export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions { + + let sorted = rangeData.sort((d1, d2) => { + let diff = d1.startLineNumber - d2.startLineNumber; + if (diff === 0) { + diff = d1.rank - d2.rank; + } + return diff; + }); + let collector = new RangesCollector(MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT); + + let top: IFoldingRangeData = null; + let previous = []; + for (let entry of sorted) { + if (!top) { + top = entry; + collector.add(entry.startLineNumber, entry.endLineNumber, previous.length); + } else { + if (entry.startLineNumber > top.startLineNumber) { + if (entry.endLineNumber <= top.endLineNumber) { + previous.push(top); + top = entry; + collector.add(entry.startLineNumber, entry.endLineNumber, previous.length); + } else if (entry.startLineNumber > top.endLineNumber) { + do { + top = previous.pop(); + } while (top && entry.startLineNumber > top.endLineNumber); + previous.push(top); + top = entry; + collector.add(entry.startLineNumber, entry.endLineNumber, previous.length); + } + } + } + } + return collector.toIndentRanges(); +} \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/test/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/foldingModel.test.ts index 0b415eae211..a77fbf071c1 100644 --- a/src/vs/editor/contrib/folding/test/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingModel.test.ts @@ -61,7 +61,7 @@ suite('Folding Model', () => { function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) { let actualRanges = []; - let actual = foldingModel.ranges; + let actual = foldingModel.regions; for (let i = 0; i < actual.length; i++) { if (actual.isCollapsed(i)) { actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i))); @@ -72,7 +72,7 @@ suite('Folding Model', () => { function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) { let actualRanges = []; - let actual = foldingModel.ranges; + let actual = foldingModel.regions; for (let i = 0; i < actual.length; i++) { actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i))); } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index eded61cea6f..55ce345642e 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -389,6 +389,14 @@ export function registerColorProvider(languageId: string, provider: modes.Docume return modes.ColorProviderRegistry.register(languageId, provider); } +/** + * Register a folding provider + */ +/*export function registerFoldingProvider(languageId: string, provider: modes.FoldingProvider): IDisposable { + return modes.FoldingProviderRegistry.register(languageId, provider); +}*/ + + /** * Contains additional diagnostic information about the context in which * a [code action](#CodeActionProvider.provideCodeActions) is run. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0a1cc793efd..9165cba0dc6 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4047,6 +4047,9 @@ declare module monaco.languages { */ export function registerColorProvider(languageId: string, provider: DocumentColorProvider): IDisposable; + /** + * Register a folding provider + */ /** * Contains additional diagnostic information about the context in which * a [code action](#CodeActionProvider.provideCodeActions) is run. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a3be8f61888..5061c95a0c9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -7,6 +7,63 @@ declare module 'vscode' { + export class FoldingRangeList { + + /** + * The folding ranges. + */ + ranges: FoldingRange[]; + + /** + * Creates mew folding range list. + * + * @param ranges The folding ranges + */ + constructor(ranges: FoldingRange[]); + } + + + export class FoldingRange { + + /** + * The start line number (0-based) + */ + startLine: number; + + /** + * The end line number (0-based) + */ + endLine: number; + + /** + * The actual color value for this color range. + */ + type?: FoldingRangeType | string; + + /** + * Creates a new folding range. + * + * @param startLineNumber The first line of the fold + * @param type The last line of the fold + */ + constructor(startLineNumber: number, endLineNumber: number, type?: FoldingRangeType); + } + + export enum FoldingRangeType { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' + } + // export enum FileErrorCodes { // /** // * Not owner. @@ -340,10 +397,27 @@ declare module 'vscode' { } export namespace languages { + + /** + * Register a folding provider. + * + * Multiple folding can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A folding provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFoldingProvider(selector: DocumentSelector, provider: FoldingProvider): Disposable; + export interface RenameProvider2 extends RenameProvider { resolveInitialRenameValue?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } } + export interface FoldingProvider { + provideFoldingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + } /** * Represents the validation type of the Source Control input. diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 8d5bf82d505..3ca33d51e0a 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -346,6 +346,17 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }); } + // --- folding + + $registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void { + const proxy = this._proxy; + this._registrations[handle] = modes.FoldingProviderRegistry.register(toLanguageSelector(selector), { + provideFoldingRanges: (model, token) => { + return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri)); + } + }); + } + // --- configuration private static _reviveRegExp(regExp: ISerializedRegExp): RegExp { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 466456f5bc3..863b1bfc5ab 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -292,6 +292,9 @@ export function createApiFactory( registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { return extHostLanguageFeatures.registerColorProvider(selector, provider); }, + registerFoldingProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable => { + return extHostLanguageFeatures.registerFoldingProvider(selector, provider); + }), setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); } @@ -634,7 +637,10 @@ export function createApiFactory( RelativePattern: extHostTypes.RelativePattern, FileChangeType: extHostTypes.FileChangeType, - FileType: extHostTypes.FileType + FileType: extHostTypes.FileType, + FoldingRangeList: extHostTypes.FoldingRangeList, + FoldingRange: extHostTypes.FoldingRange, + FoldingRangeType: extHostTypes.FoldingRangeType }; }; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b4715e17ff2..2c694546d36 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -282,6 +282,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void; $registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void; $registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void; + $registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void; $setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void; } @@ -692,6 +693,7 @@ export interface ExtHostLanguageFeaturesShape { $resolveDocumentLink(handle: number, link: modes.ILink): TPromise; $provideDocumentColors(handle: number, resource: UriComponents): TPromise; $provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise; + $provideFoldingRanges(handle: number, resource: UriComponents): TPromise; } export interface ExtHostQuickOpenShape { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 56d191ee98b..8a270631e0b 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -804,10 +804,29 @@ class ColorProviderAdapter { } } +class FoldingProviderAdapter { + + constructor( + private _documents: ExtHostDocuments, + private _provider: vscode.FoldingProvider + ) { } + + provideFoldingRanges(resource: URI): TPromise { + const doc = this._documents.getDocumentData(resource).document; + return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, token)).then(list => { + if (!Array.isArray(list.ranges)) { + return void 0; + } + return TypeConverters.FoldingRangeList.from(list); + }); + } +} + type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter - | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter; + | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter + | ColorProviderAdapter | FoldingProviderAdapter; export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { @@ -1108,6 +1127,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo)); } + registerFoldingProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable { + const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider)); + this._proxy.$registerFoldingProvider(handle, selector); + return this._createDisposable(handle); + } + + $provideFoldingRanges(handle: number, resource: UriComponents): TPromise { + return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource))); + } + // --- configuration private static _serializeRegExp(regExp: RegExp): ISerializedRegExp { diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 428d05ab635..c15538d7339 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -586,6 +586,14 @@ export namespace ProgressLocation { } } +export namespace FoldingRangeList { + export function from(rangeList: vscode.FoldingRangeList): modes.IFoldingRangeList { + return { + ranges: rangeList.ranges.map(r => ({ startLineNumber: r.startLine + 1, endLineNumber: r.endLine + 1 })) + }; + } +} + export function toTextEditorOptions(options?: vscode.TextDocumentShowOptions): ITextEditorOptions { if (options) { return { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 2cac1f11910..41bf5746751 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1659,3 +1659,46 @@ export enum FileType { } //#endregion + +//#region folding api + +export class FoldingRangeList { + + ranges: FoldingRange[]; + + constructor(ranges: FoldingRange[]) { + this.ranges = ranges; + } +} + +export class FoldingRange { + + startLine: number; + + endLine: number; + + type?: FoldingRangeType; + + constructor(startLine: number, endLine: number, type?: FoldingRangeType) { + this.startLine = startLine; + this.endLine = endLine; + this.type = type; + } +} + +export enum FoldingRangeType { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' +} + +//#endregion \ No newline at end of file From d127e5777cc8d98e1033dd6b1dbe71837499d94a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Feb 2018 15:42:35 +0100 Subject: [PATCH 49/53] Implement #42770 --- src/vs/base/browser/ui/splitview/panelview.ts | 4 ++ .../browser/parts/sidebar/sidebarPart.ts | 23 +--------- .../browser/parts/views/panelViewlet.ts | 29 ++++++++++-- .../browser/parts/views/viewsViewlet.ts | 46 +++++++++++++------ src/vs/workbench/browser/viewlet.ts | 23 +++++++++- .../parts/debug/browser/debugViewlet.ts | 4 +- .../electron-browser/extensionsViewlet.ts | 4 +- .../files/electron-browser/explorerViewlet.ts | 4 +- .../parts/scm/electron-browser/scmViewlet.ts | 6 ++- .../parts/search/browser/searchViewlet.ts | 4 +- src/vs/workbench/test/browser/viewlet.test.ts | 2 +- 11 files changed, 101 insertions(+), 48 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index eafc85d142a..74bce829de6 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -49,6 +49,10 @@ export abstract class Panel implements IView { private _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; + get element(): HTMLElement { + return this.el; + } + get draggableElement(): HTMLElement { return this.header; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 6e9105368a2..7976f3547bb 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -7,12 +7,11 @@ import 'vs/css!./media/sidebarpart'; import { TPromise } from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import { Registry } from 'vs/platform/registry/common/platform'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action } from 'vs/base/common/actions'; import { CompositePart } from 'vs/workbench/browser/parts/compositePart'; import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService'; import { IViewlet } from 'vs/workbench/common/viewlet'; @@ -26,7 +25,6 @@ import Event from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme'; -import { ToggleSidebarVisibilityAction } from 'vs/workbench/browser/actions/toggleSidebarVisibility'; import { Dimension } from 'vs/base/browser/builder'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -134,25 +132,6 @@ export class SidebarPart extends CompositePart { return super.layout(dimension); } - - protected getTitleAreaContextMenuActions(): IAction[] { - const contextMenuActions = super.getTitleAreaContextMenuActions(); - if (contextMenuActions.length) { - contextMenuActions.push(new Separator()); - } - contextMenuActions.push(this.createHideSideBarAction()); - return contextMenuActions; - } - - private createHideSideBarAction(): IAction { - return { - id: ToggleSidebarVisibilityAction.ID, - label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), - enabled: true, - run: () => this.partService.setSideBarHidden(true) - }; - } - } class FocusSideBarAction extends Action { diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index 22c581580ec..2d24634db83 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -11,7 +11,7 @@ import { ColorIdentifier, contrastBorder } from 'vs/platform/theme/common/colorR import { attachStyler, IColorMapping, IThemable } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme'; import { Dimension, Builder } from 'vs/base/browser/builder'; -import { append, $, trackFocus, toggleClass } from 'vs/base/browser/dom'; +import { append, $, trackFocus, toggleClass, EventType, isAncestor } from 'vs/base/browser/dom'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { firstIndex } from 'vs/base/common/arrays'; import { IAction, IActionRunner } from 'vs/base/common/actions'; @@ -26,6 +26,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export interface IPanelColors extends IColorMapping { dropBackground?: ColorIdentifier; @@ -165,10 +167,12 @@ export class PanelViewlet extends Viewlet { constructor( id: string, private options: IViewsViewletOptions, + @IPartService partService: IPartService, + @IContextMenuService protected contextMenuService: IContextMenuService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService ) { - super(id, telemetryService, themeService); + super(id, partService, telemetryService, themeService); } async create(parent: Builder): TPromise { @@ -176,7 +180,26 @@ export class PanelViewlet extends Viewlet { const container = parent.getHTMLElement(); this.panelview = this._register(new PanelView(container, this.options)); - this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel)); + this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel))); + this._register(parent.on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); + } + + private showContextMenu(event: StandardMouseEvent): void { + event.stopPropagation(); + event.preventDefault(); + + for (const panelItem of this.panelItems) { + // Do not show context menu if requested from inside panel views + if (isAncestor(event.target, panelItem.panel.element)) { + return; + } + } + + let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.as(this.getContextMenuActions()) + }); } getTitle(): string { diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 4ecac43df70..d96df6d36cc 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import * as DOM from 'vs/base/browser/dom'; @@ -11,7 +10,7 @@ import { $, Dimension, Builder } from 'vs/base/browser/builder'; import { Scope } from 'vs/workbench/common/memento'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { firstIndex } from 'vs/base/common/arrays'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -31,6 +30,8 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; import Event, { Emitter } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { localize } from 'vs/nls'; export interface IViewOptions extends IPanelOptions { id: string; @@ -200,6 +201,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { id: string, private location: ViewLocation, private showHeaderInTitleWhenSingleView: boolean, + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService protected storageService: IStorageService, @IInstantiationService protected instantiationService: IInstantiationService, @@ -208,7 +210,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { @IContextMenuService protected contextMenuService: IContextMenuService, @IExtensionService protected extensionService: IExtensionService ) { - super(id, { showHeaderInTitleWhenSingleView, dnd: true }, telemetryService, themeService); + super(id, { showHeaderInTitleWhenSingleView, dnd: true }, partService, contextMenuService, telemetryService, themeService); this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE); } @@ -232,7 +234,8 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { } getContextMenuActions(): IAction[] { - return this.getViewDescriptorsFromRegistry(true) + const result: IAction[] = []; + const viewToggleActions = this.getViewDescriptorsFromRegistry(true) .filter(viewDescriptor => viewDescriptor.canToggleVisibility && this.contextKeyService.contextMatchesRules(viewDescriptor.when)) .map(viewDescriptor => ({ id: `${viewDescriptor.id}.toggleVisibility`, @@ -241,6 +244,13 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { enabled: true, run: () => this.toggleViewVisibility(viewDescriptor.id) })); + result.push(...viewToggleActions); + const parentActions = super.getContextMenuActions(); + if (viewToggleActions.length && parentActions.length) { + result.push(new Separator()); + } + result.push(...parentActions); + return result; } setVisible(visible: boolean): TPromise { @@ -481,9 +491,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.draggableElement, DOM.EventType.CONTEXT_MENU, e => { e.stopPropagation(); e.preventDefault(); - if (viewDescriptor.canToggleVisibility) { - this.onContextMenu(new StandardMouseEvent(e), view); - } + this.onContextMenu(new StandardMouseEvent(e), viewDescriptor); })); } } @@ -498,19 +506,26 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet { } } - private onContextMenu(event: StandardMouseEvent, view: ViewsViewletPanel): void { + private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { event.stopPropagation(); event.preventDefault(); + const actions: IAction[] = []; + actions.push({ + id: `${viewDescriptor.id}.removeView`, + label: localize('hideView', "Hide"), + enabled: viewDescriptor.canToggleVisibility, + run: () => this.toggleViewVisibility(viewDescriptor.id) + }); + const otherActions = this.getContextMenuActions(); + if (otherActions.length) { + actions.push(...[new Separator(), ...otherActions]); + } + let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.as([{ - id: `${view.id}.removeView`, - label: nls.localize('hideView', "Hide"), - enabled: true, - run: () => this.toggleViewVisibility(view.id) - }]), + getActions: () => TPromise.as(actions) }); } @@ -610,6 +625,7 @@ export class PersistentViewsViewlet extends ViewsViewlet { location: ViewLocation, private readonly viewletStateStorageId: string, showHeaderInTitleWhenSingleView: boolean, + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IInstantiationService instantiationService: IInstantiationService, @@ -619,7 +635,7 @@ export class PersistentViewsViewlet extends ViewsViewlet { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(id, location, showHeaderInTitleWhenSingleView, telemetryService, storageService, instantiationService, themeService, contextKeyService, contextMenuService, extensionService); + super(id, location, showHeaderInTitleWhenSingleView, partService, telemetryService, storageService, instantiationService, themeService, contextKeyService, contextMenuService, extensionService); this.hiddenViewsStorageId = `${this.viewletStateStorageId}.hidden`; this._register(this.onDidChangeViewVisibilityState(id => this.onViewVisibilityChanged(id))); } diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index bbf35cdb272..ae4dc027589 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -7,19 +7,40 @@ import nls = require('vs/nls'); import { TPromise } from 'vs/base/common/winjs.base'; import DOM = require('vs/base/browser/dom'); import { Registry } from 'vs/platform/registry/common/platform'; -import { Action } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { ToggleSidebarVisibilityAction } from 'vs/workbench/browser/actions/toggleSidebarVisibility'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export abstract class Viewlet extends Composite implements IViewlet { + constructor(id: string, + private partService: IPartService, + telemetryService: ITelemetryService, + themeService: IThemeService + ) { + super(id, telemetryService, themeService); + } + public getOptimalWidth(): number { return null; } + + public getContextMenuActions(): IAction[] { + return [{ + id: ToggleSidebarVisibilityAction.ID, + label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + enabled: true, + run: () => this.partService.setSideBarHidden(true) + }]; + } } /** diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index eddef39bcdd..3f1885ea3e3 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -26,6 +26,7 @@ import { ViewLocation } from 'vs/workbench/common/views'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; export class DebugViewlet extends PersistentViewsViewlet { @@ -35,6 +36,7 @@ export class DebugViewlet extends PersistentViewsViewlet { private panelListeners = new Map(); constructor( + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IProgressService private progressService: IProgressService, @IDebugService private debugService: IDebugService, @@ -46,7 +48,7 @@ export class DebugViewlet extends PersistentViewsViewlet { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); this.progressRunner = null; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 594a6114a88..9abba273904 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -55,6 +55,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IChoiceService } from 'vs/platform/dialogs/common/dialogs'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -88,6 +89,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens private disposables: IDisposable[] = []; constructor( + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IProgressService private progressService: IProgressService, @IInstantiationService instantiationService: IInstantiationService, @@ -104,7 +106,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); this.registerViews(); this.searchDelayer = new ThrottledDelayer(500); diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index 7dd040a28c2..ef92afe2742 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -34,6 +34,7 @@ import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/commo import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -150,6 +151,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet implements IExplorer private viewletVisibleContextKey: IContextKey; constructor( + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IStorageService protected storageService: IStorageService, @@ -162,7 +164,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet implements IExplorer @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(VIEWLET_ID, ViewLocation.Explorer, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); + super(VIEWLET_ID, ViewLocation.Explorer, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService); this.viewletState = new FileViewletState(); this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 7f179fcb8df..7c5758e181c 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -57,6 +57,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ThrottledDelayer } from 'vs/base/common/async'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; export interface ISpliceEvent { index: number; @@ -1047,6 +1048,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { get selectedRepositories(): ISCMRepository[] { return this.repositoryPanels.map(p => p.repository); } constructor( + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @ISCMService protected scmService: ISCMService, @IInstantiationService protected instantiationService: IInstantiationService, @@ -1054,7 +1056,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService protected keybindingService: IKeybindingService, @INotificationService protected notificationService: INotificationService, - @IContextMenuService private contextMenuService: IContextMenuService, + @IContextMenuService protected contextMenuService: IContextMenuService, @IThemeService protected themeService: IThemeService, @ICommandService protected commandService: ICommandService, @IEditorGroupService protected editorGroupService: IEditorGroupService, @@ -1064,7 +1066,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel { @IExtensionService extensionService: IExtensionService, @IConfigurationService private configurationService: IConfigurationService, ) { - super(VIEWLET_ID, { showHeaderInTitleWhenSingleView: true }, telemetryService, themeService); + super(VIEWLET_ID, { showHeaderInTitleWhenSingleView: true }, partService, contextMenuService, telemetryService, themeService); this.menus = instantiationService.createInstance(SCMMenus, undefined); this.menus.onDidChangeTitle(this.updateTitleArea, this, this.disposables); diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 173957be352..24e553b9c14 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -59,6 +59,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { SimpleFileResourceDragAndDrop } from 'vs/workbench/browser/dnd'; import { IConfirmation, IConfirmationService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; export class SearchViewlet extends Viewlet { @@ -103,6 +104,7 @@ export class SearchViewlet extends Viewlet { private searchWithoutFolderMessageBuilder: Builder; constructor( + @IPartService partService: IPartService, @ITelemetryService telemetryService: ITelemetryService, @IFileService private fileService: IFileService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @@ -122,7 +124,7 @@ export class SearchViewlet extends Viewlet { @IPreferencesService private preferencesService: IPreferencesService, @IThemeService protected themeService: IThemeService ) { - super(Constants.VIEWLET_ID, telemetryService, themeService); + super(Constants.VIEWLET_ID, partService, telemetryService, themeService); this.viewletVisible = Constants.SearchViewletVisibleKey.bindTo(contextKeyService); this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService); diff --git a/src/vs/workbench/test/browser/viewlet.test.ts b/src/vs/workbench/test/browser/viewlet.test.ts index f8b54dfda12..e649533f6cc 100644 --- a/src/vs/workbench/test/browser/viewlet.test.ts +++ b/src/vs/workbench/test/browser/viewlet.test.ts @@ -15,7 +15,7 @@ suite('Workbench Viewlet', () => { class TestViewlet extends Viewlet { constructor() { - super('id', null, null); + super('id', null, null, null); } public layout(dimension: any): void { From aeaf907cf1b7e7b2687f0cb2212d55e17bc4161b Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 22 Feb 2018 15:56:40 +0100 Subject: [PATCH 50/53] center title when using centered layout --- .../workbench/browser/parts/editor/media/titlecontrol.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 7f7fe4a23c7..3a1d53f23c8 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -11,6 +11,11 @@ flex: 1; } +.monaco-workbench > .part.editor > .content > .one-editor-silo.centered > .container > .title .title-label { + flex-direction: row; + justify-content: center; +} + .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a, .monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a { text-decoration: none; @@ -104,4 +109,4 @@ .vs-dark .monaco-workbench .show-group-editors-action, .hc-black .monaco-workbench .show-group-editors-action { background: url('stackview-inverse.svg') center center no-repeat; -} \ No newline at end of file +} From aacda6e647a65e0108a7e82fb54ba4fe1dbdd6f1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 22 Feb 2018 15:44:50 +0100 Subject: [PATCH 51/53] fixes #44177 --- src/vs/base/browser/ui/list/listView.ts | 9 ++++--- src/vs/base/browser/ui/list/listWidget.ts | 30 +---------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 886f8d3ca4c..a5ed34f7367 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -295,11 +295,10 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer.appendChild(item.row.domNode); } - const renderer = this.renderers.get(item.templateId); - item.row.domNode.style.top = `${this.elementTop(index)}px`; item.row.domNode.style.height = `${item.size}px`; - item.row.domNode.setAttribute('data-index', `${index}`); - item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); + this.updateItemInDOM(item, index); + + const renderer = this.renderers.get(item.templateId); renderer.renderElement(item.element, index, item.row.templateData); } @@ -307,6 +306,8 @@ export class ListView implements ISpliceable, IDisposable { item.row.domNode.style.top = `${this.elementTop(index)}px`; item.row.domNode.setAttribute('data-index', `${index}`); item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); + item.row.domNode.setAttribute('aria-setsize', `${this.length}`); + item.row.domNode.setAttribute('aria-posinset', `${index + 1}`); } private removeItemFromDOM(item: IItem): void { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 97e46087653..9aca42e7f3a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -203,32 +203,6 @@ class FocusTrait extends Trait { } } -class Aria implements IRenderer, ISpliceable { - - private length = 0; - - get templateId(): string { - return 'aria'; - } - - splice(start: number, deleteCount: number, elements: T[]): void { - this.length += elements.length - deleteCount; - } - - renderTemplate(container: HTMLElement): HTMLElement { - return container; - } - - renderElement(element: T, index: number, container: HTMLElement): void { - container.setAttribute('aria-setsize', `${this.length}`); - container.setAttribute('aria-posinset', `${index + 1}`); - } - - disposeTemplate(container: HTMLElement): void { - // noop - } -} - /** * The TraitSpliceable is used as a util class to be able * to preserve traits across splice calls, given an identity @@ -781,13 +755,12 @@ export class List implements ISpliceable, IDisposable { renderers: IRenderer[], options: IListOptions = DefaultOptions ) { - const aria = new Aria(); this.focus = new FocusTrait(i => this.getElementDomId(i)); this.selection = new Trait('selected'); mixin(options, defaultStyles, false); - renderers = renderers.map(r => new PipelineRenderer(r.templateId, [aria, this.focus.renderer, this.selection.renderer, r])); + renderers = renderers.map(r => new PipelineRenderer(r.templateId, [this.focus.renderer, this.selection.renderer, r])); this.view = new ListView(container, delegate, renderers, options); this.view.domNode.setAttribute('role', 'tree'); @@ -797,7 +770,6 @@ export class List implements ISpliceable, IDisposable { this.styleElement = DOM.createStyleSheet(this.view.domNode); this.spliceable = new CombinedSpliceable([ - aria, new TraitSpliceable(this.focus, this.view, options.identityProvider), new TraitSpliceable(this.selection, this.view, options.identityProvider), this.view From be7434b93ee2f725904b2b723fa43807d1c1b7aa Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 22 Feb 2018 15:58:06 +0100 Subject: [PATCH 52/53] fixes #44179 --- src/vs/base/browser/ui/list/listWidget.ts | 25 +++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9aca42e7f3a..fe92a7203f0 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -21,6 +21,7 @@ import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; +import { clamp } from 'vs/base/common/numbers'; export interface IIdentityProvider { (element: T): string; @@ -952,12 +953,9 @@ export class List implements ISpliceable, IDisposable { const elementHeight = this.view.elementHeight(index); if (isNumber(relativeTop)) { - relativeTop = relativeTop < 0 ? 0 : relativeTop; - relativeTop = relativeTop > 1 ? 1 : relativeTop; - // y = mx + b const m = elementHeight - this.view.renderHeight; - this.view.setScrollTop(m * relativeTop + elementTop); + this.view.setScrollTop(m * clamp(relativeTop, 0, 1) + elementTop); } else { const viewItemBottom = elementTop + elementHeight; const wrapperBottom = scrollTop + this.view.renderHeight; @@ -970,6 +968,24 @@ export class List implements ISpliceable, IDisposable { } } + /** + * Returns the relative position of an element rendered in the list. + * Returns `null` if the element isn't *entirely* in the visible viewport. + */ + getRelativeTop(index: number): number | null { + const scrollTop = this.view.getScrollTop(); + const elementTop = this.view.elementTop(index); + const elementHeight = this.view.elementHeight(index); + + if (elementTop < scrollTop || elementTop + elementHeight > scrollTop + this.view.renderHeight) { + return null; + } + + // y = mx + b + const m = elementHeight - this.view.renderHeight; + return Math.abs((scrollTop - elementTop) / m); + } + private getElementDomId(index: number): string { return `${this.idPrefix}_${index}`; } @@ -1069,6 +1085,7 @@ export class List implements ISpliceable, IDisposable { if (focus.length > 0) { this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0])); + console.log(this.getRelativeTop(focus[0])); } else { this.view.domNode.removeAttribute('aria-activedescendant'); } From 787378a250185715584b321fda76845ac41efbe6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 22 Feb 2018 16:22:02 +0100 Subject: [PATCH 53/53] list: keep element order in DOM fixes #44193 --- src/vs/base/browser/ui/list/listView.ts | 45 ++++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index a5ed34f7367..f0f96b1a236 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -155,7 +155,7 @@ export class ListView implements ISpliceable, IDisposable { const removeRange = intersect(previousRenderRange, deleteRange); for (let i = removeRange.start; i < removeRange.end; i++) { - this.removeItemFromDOM(this.items[i]); + this.removeItemFromDOM(i); } const previousRestRange: IRange = { start: start + deleteCount, end: this.items.length }; @@ -188,19 +188,20 @@ export class ListView implements ISpliceable, IDisposable { const removeRange = removeRanges[r]; for (let i = removeRange.start; i < removeRange.end; i++) { - this.removeItemFromDOM(this.items[i]); + this.removeItemFromDOM(i); } } const unrenderedRestRanges = previousUnrenderedRestRanges.map(r => shift(r, delta)); const elementsRange = { start, end: start + elements.length }; const insertRanges = [elementsRange, ...unrenderedRestRanges].map(r => intersect(renderRange, r)); + const beforeElement = this.getNextToLastElement(insertRanges); for (let r = 0; r < insertRanges.length; r++) { const insertRange = insertRanges[r]; for (let i = insertRange.start; i < insertRange.end; i++) { - this.insertItemInDOM(this.items[i], i); + this.insertItemInDOM(i, beforeElement); } } @@ -259,16 +260,17 @@ export class ListView implements ISpliceable, IDisposable { const rangesToInsert = relativeComplement(renderRange, previousRenderRange); const rangesToRemove = relativeComplement(previousRenderRange, renderRange); + const beforeElement = this.getNextToLastElement(rangesToInsert); for (const range of rangesToInsert) { for (let i = range.start; i < range.end; i++) { - this.insertItemInDOM(this.items[i], i); + this.insertItemInDOM(i, beforeElement); } } for (const range of rangesToRemove) { for (let i = range.start; i < range.end; i++) { - this.removeItemFromDOM(this.items[i], ); + this.removeItemFromDOM(i); } } @@ -286,13 +288,19 @@ export class ListView implements ISpliceable, IDisposable { // DOM operations - private insertItemInDOM(item: IItem, index: number): void { + private insertItemInDOM(index: number, beforeElement: HTMLElement | null): void { + const item = this.items[index]; + if (!item.row) { item.row = this.cache.alloc(item.templateId); } if (!item.row.domNode.parentElement) { - this.rowsContainer.appendChild(item.row.domNode); + if (beforeElement) { + this.rowsContainer.insertBefore(item.row.domNode, beforeElement); + } else { + this.rowsContainer.appendChild(item.row.domNode); + } } item.row.domNode.style.height = `${item.size}px`; @@ -310,7 +318,8 @@ export class ListView implements ISpliceable, IDisposable { item.row.domNode.setAttribute('aria-posinset', `${index + 1}`); } - private removeItemFromDOM(item: IItem): void { + private removeItemFromDOM(index: number): void { + const item = this.items[index]; this.cache.release(item.row); item.row = null; } @@ -458,6 +467,26 @@ export class ListView implements ISpliceable, IDisposable { }; } + private getNextToLastElement(ranges: IRange[]): HTMLElement | null { + const lastRange = ranges[ranges.length - 1]; + + if (!lastRange) { + return null; + } + + const nextToLastItem = this.items[lastRange.end]; + + if (!nextToLastItem) { + return null; + } + + if (!nextToLastItem.row) { + return null; + } + + return nextToLastItem.row.domNode; + } + // Dispose dispose() {