Initial VS Code 1.19 source merge (#571)

* Initial 1.19 xcopy

* Fix yarn build

* Fix numerous build breaks

* Next batch of build break fixes

* More build break fixes

* Runtime breaks

* Additional post merge fixes

* Fix windows setup file

* Fix test failures.

* Update license header blocks to refer to source eula
This commit is contained in:
Karl Burtram
2018-01-28 23:37:17 -08:00
committed by GitHub
parent 9a1ac20710
commit 251ae01c3e
8009 changed files with 93378 additions and 35634 deletions

View File

@@ -9,13 +9,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { BackupModelTracker } from 'vs/workbench/parts/backup/common/backupModelTracker';
import { BackupRestorer } from 'vs/workbench/parts/backup/common/backupRestorer';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// Register Backup Model Tracker
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
BackupModelTracker
);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupModelTracker, LifecyclePhase.Starting);
// Register Backup Restorer
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
BackupRestorer
);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BackupRestorer, LifecyclePhase.Starting);

View File

@@ -50,7 +50,7 @@ export class BackupModelTracker implements IWorkbenchContribution {
this.toDispose.push(this.untitledEditorService.onDidDisposeModel((e) => this.discardBackup(e)));
// Listen to config changes
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>())));
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getValue<IFilesConfiguration>())));
}
private onConfigurationChange(configuration: IFilesConfiguration): void {
@@ -92,8 +92,4 @@ export class BackupModelTracker implements IWorkbenchContribution {
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}
public getId(): string {
return 'vs.backup.backupModelTracker';
}
}

View File

@@ -9,7 +9,6 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import errors = require('vs/base/common/errors');
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
@@ -17,6 +16,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { Schemas } from 'vs/base/common/network';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
export class BackupRestorer implements IWorkbenchContribution {
@@ -26,18 +26,18 @@ export class BackupRestorer implements IWorkbenchContribution {
constructor(
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IPartService private partService: IPartService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IBackupFileService private backupFileService: IBackupFileService,
@ITextFileService private textFileService: ITextFileService,
@IEditorGroupService private groupService: IEditorGroupService
@IEditorGroupService private groupService: IEditorGroupService,
@ILifecycleService private lifecycleService: ILifecycleService
) {
this.restoreBackups();
}
private restoreBackups(): void {
if (this.backupFileService.backupEnabled) {
this.partService.joinCreation().then(() => {
this.lifecycleService.when(LifecyclePhase.Running).then(() => {
this.doRestoreBackups().done(null, errors.onUnexpectedError);
});
}
@@ -105,8 +105,4 @@ export class BackupRestorer implements IWorkbenchContribution {
return { resource, options };
}
public getId(): string {
return 'vs.backup.backupRestorer';
}
}

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { NodeCachedDataManager } from 'vs/workbench/parts/cache/node/nodeCachedDataManager';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// Register NodeCachedDataManager Contribution
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NodeCachedDataManager, LifecyclePhase.Eventually);

View File

@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { basename } from 'path';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }];
declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] };
export class NodeCachedDataManager implements IWorkbenchContribution {
private readonly _telemetryService: ITelemetryService;
constructor(
@ITelemetryService telemetryService: ITelemetryService
) {
this._telemetryService = telemetryService;
this._handleCachedDataInfo();
}
private _handleCachedDataInfo(): void {
let didRejectCachedData = false;
let didProduceCachedData = false;
for (const [err, data] of MonacoEnvironment.onNodeCachedData) {
// build summary
didRejectCachedData = didRejectCachedData || Boolean(err);
didProduceCachedData = didProduceCachedData || Boolean(data);
// log each failure separately
if (err) {
/* __GDPR__
"cachedDataError" : {
"errorCode" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"path": { "classification": "CustomerContent", "purpose": "PerformanceAndHealth" }
}
*/
this._telemetryService.publicLog('cachedDataError', {
errorCode: err.errorCode,
path: basename(err.path)
});
}
}
// log summary
/* __GDPR__
"cachedDataInfo" : {
"didRequestCachedData" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"didRejectCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"didProduceCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
this._telemetryService.publicLog('cachedDataInfo', {
didRequestCachedData: Boolean(global.require.getConfig().nodeCachedDataDir),
didRejectCachedData,
didProduceCachedData
});
global.require.config({ onNodeCachedData: undefined });
delete MonacoEnvironment.onNodeCachedData;
}
}

View File

@@ -15,14 +15,8 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IEditorService } from 'vs/platform/editor/common/editor';
import product from 'vs/platform/node/product';
interface ILegacyUse {
file: string;
lineNumber: number;
}
function ignore<T>(code: string, value: T = null): (err: any) => TPromise<T> {
return err => err.code === code ? TPromise.as<T>(value) : TPromise.wrapError<T>(err);
}
@@ -42,8 +36,7 @@ class InstallAction extends Action {
constructor(
id: string,
label: string,
@IMessageService private messageService: IMessageService,
@IEditorService private editorService: IEditorService
@IMessageService private messageService: IMessageService
) {
super(id, label);
}

View File

@@ -17,12 +17,11 @@ import { Widget } from 'vs/base/browser/ui/widget';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, CommonEditorRegistry, EditorAction, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, registerEditorContribution, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode';
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
@@ -31,15 +30,15 @@ import * as platform from 'vs/base/common/platform';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import URI from 'vs/base/common/uri';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey<boolean>('accessibilityHelpWidgetVisible', false);
@editorContribution
class AccessibilityHelpController extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.accessibilityHelpController';
private static readonly ID = 'editor.contrib.accessibilityHelpController';
public static get(editor: ICommonCodeEditor): AccessibilityHelpController {
public static get(editor: ICodeEditor): AccessibilityHelpController {
return editor.getContribution<AccessibilityHelpController>(AccessibilityHelpController.ID);
}
@@ -71,9 +70,9 @@ class AccessibilityHelpController extends Disposable implements IEditorContribut
class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
private static ID = 'editor.contrib.accessibilityHelpWidget';
private static WIDTH = 500;
private static HEIGHT = 300;
private static readonly ID = 'editor.contrib.accessibilityHelpWidget';
private static readonly WIDTH = 500;
private static readonly HEIGHT = 300;
private _editor: ICodeEditor;
private _domNode: FastDomNode<HTMLElement>;
@@ -192,7 +191,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
text += '\n\n' + nls.localize('status', "Status:");
const configuredValue = this._configurationService.getConfiguration<editorOptions.IEditorOptions>('editor').accessibilitySupport;
const configuredValue = this._configurationService.getValue<editorOptions.IEditorOptions>('editor').accessibilitySupport;
const actualValue = opts.accessibilitySupport;
const emergencyTurnOnMessage = (
@@ -277,7 +276,6 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
}
}
@editorAction
class ShowAccessibilityHelpAction extends EditorAction {
constructor() {
@@ -293,7 +291,7 @@ class ShowAccessibilityHelpAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = AccessibilityHelpController.get(editor);
if (controller) {
controller.show();
@@ -301,14 +299,17 @@ class ShowAccessibilityHelpAction extends EditorAction {
}
}
registerEditorContribution(AccessibilityHelpController);
registerEditorAction(ShowAccessibilityHelpAction);
const AccessibilityHelpCommand = EditorCommand.bindToContribution<AccessibilityHelpController>(AccessibilityHelpController.get);
CommonEditorRegistry.registerEditorCommand(new AccessibilityHelpCommand({
registerEditorCommand(new AccessibilityHelpCommand({
id: 'closeAccessibilityHelp',
precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE,
handler: x => x.hide(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(100),
weight: KeybindingsRegistry.WEIGHT.editorContrib(100),
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape]
}

View File

@@ -5,14 +5,13 @@
'use strict';
import * as nls from 'vs/nls';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IUntitledResourceInput } from 'vs/platform/editor/common/editor';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
class InspectKeyMap extends EditorAction {
constructor() {
@@ -24,7 +23,7 @@ class InspectKeyMap extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const keybindingService = accessor.get(IKeybindingService);
const editorService = accessor.get(IWorkbenchEditorService);
@@ -33,3 +32,5 @@ class InspectKeyMap extends EditorAction {
}
}
}
registerEditorAction(InspectKeyMap);

View File

@@ -344,6 +344,7 @@ export class LanguageConfigurationFileHandler {
const schemaId = 'vscode://schemas/language-configuration';
const schema: IJSONSchema = {
allowComments: true,
default: {
comments: {
blockComment: ['/*', '*/'],

View File

@@ -8,15 +8,14 @@ import { KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
/**
* Prevents the top-level menu from showing up when doing Alt + Click in the editor
*/
@editorContribution
export class MenuPreventer extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.menuPreventer';
private static readonly ID = 'editor.contrib.menuPreventer';
private _editor: ICodeEditor;
private _altListeningMouse: boolean;
@@ -62,3 +61,5 @@ export class MenuPreventer extends Disposable implements IEditorContribution {
return MenuPreventer.ID;
}
}
registerEditorContribution(MenuPreventer);

View File

@@ -7,20 +7,19 @@
import { clipboard } from 'electron';
import * as platform from 'vs/base/common/platform';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { Disposable } from 'vs/base/common/lifecycle';
import { EndOfLinePreference, IEditorContribution } from 'vs/editor/common/editorCommon';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Range } from 'vs/editor/common/core/range';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@editorContribution
export class SelectionClipboard extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.selectionClipboard';
private static readonly ID = 'editor.contrib.selectionClipboard';
constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService) {
super();
@@ -49,6 +48,10 @@ export class SelectionClipboard extends Disposable implements IEditorContributio
editor.setPosition(e.target.position);
}
if (e.target.type === MouseTargetType.SCROLLBAR) {
return;
}
process.nextTick(() => {
// TODO@Alex: electron weirdness: calling clipboard.readText('selection') generates a paste event, so no need to execute paste ourselves
clipboard.readText('selection');
@@ -101,3 +104,5 @@ export class SelectionClipboard extends Disposable implements IEditorContributio
super.dispose();
}
}
registerEditorContribution(SelectionClipboard);

View File

@@ -11,10 +11,9 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { escape } from 'vs/base/common/strings';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Position } from 'vs/editor/common/core/position';
import { ICommonCodeEditor, IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions';
import { IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, ContentWidgetPositionPreference, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { TPromise } from 'vs/base/common/winjs.base';
import { IGrammar, StackElement, IToken } from 'vscode-textmate';
import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService';
@@ -30,12 +29,11 @@ import Severity from 'vs/base/common/severity';
import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
@editorContribution
class InspectTMScopesController extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.inspectTMScopes';
private static readonly ID = 'editor.contrib.inspectTMScopes';
public static get(editor: ICommonCodeEditor): InspectTMScopesController {
public static get(editor: ICodeEditor): InspectTMScopesController {
return editor.getContribution<InspectTMScopesController>(InspectTMScopesController.ID);
}
@@ -101,7 +99,6 @@ class InspectTMScopesController extends Disposable implements IEditorContributio
}
}
@editorAction
class InspectTMScopes extends EditorAction {
constructor() {
@@ -113,7 +110,7 @@ class InspectTMScopes extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = InspectTMScopesController.get(editor);
if (controller) {
controller.toggle();
@@ -173,7 +170,7 @@ function renderTokenText(tokenText: string): string {
class InspectTMScopesWidget extends Disposable implements IContentWidget {
private static _ID = 'editor.contrib.inspectTMScopesWidget';
private static readonly _ID = 'editor.contrib.inspectTMScopesWidget';
// Editor.IContentWidget.allowEditorOverflow
public readonly allowEditorOverflow = true;
@@ -376,6 +373,9 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
}
}
registerEditorContribution(InspectTMScopesController);
registerEditorAction(InspectTMScopes);
registerThemingParticipant((theme, collector) => {
let border = theme.getColor(editorHoverBorder);
if (border) {
@@ -387,4 +387,4 @@ registerThemingParticipant((theme, collector) => {
if (background) {
collector.addRule(`.monaco-editor .tm-inspect-widget { background-color: ${background}; }`);
}
});
});

View File

@@ -5,11 +5,10 @@
'use strict';
import * as nls from 'vs/nls';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
export class ToggleMinimapAction extends EditorAction {
constructor() {
@@ -21,7 +20,7 @@ export class ToggleMinimapAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const configurationService = accessor.get(IConfigurationService);
const newValue = !editor.getConfiguration().viewInfo.minimap.enabled;
@@ -29,3 +28,5 @@ export class ToggleMinimapAction extends EditorAction {
configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER);
}
}
registerEditorAction(ToggleMinimapAction);

View File

@@ -14,10 +14,10 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur
export class ToggleMultiCursorModifierAction extends Action {
public static ID = 'workbench.action.toggleMultiCursorModifier';
public static LABEL = nls.localize('toggleLocation', "Toggle Multi-Cursor Modifier");
public static readonly ID = 'workbench.action.toggleMultiCursorModifier';
public static readonly LABEL = nls.localize('toggleLocation', "Toggle Multi-Cursor Modifier");
private static multiCursorModifierConfigurationKey = 'editor.multiCursorModifier';
private static readonly multiCursorModifierConfigurationKey = 'editor.multiCursorModifier';
constructor(
id: string,
@@ -28,7 +28,7 @@ export class ToggleMultiCursorModifierAction extends Action {
}
public run(): TPromise<any> {
const editorConf = this.configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd');
return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER);

View File

@@ -5,11 +5,10 @@
'use strict';
import * as nls from 'vs/nls';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
export class ToggleRenderControlCharacterAction extends EditorAction {
constructor() {
@@ -21,7 +20,7 @@ export class ToggleRenderControlCharacterAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const configurationService = accessor.get(IConfigurationService);
let newRenderControlCharacters = !editor.getConfiguration().viewInfo.renderControlCharacters;
@@ -29,3 +28,5 @@ export class ToggleRenderControlCharacterAction extends EditorAction {
configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER);
}
}
registerEditorAction(ToggleRenderControlCharacterAction);

View File

@@ -5,11 +5,10 @@
'use strict';
import * as nls from 'vs/nls';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
export class ToggleRenderWhitespaceAction extends EditorAction {
constructor() {
@@ -21,7 +20,7 @@ export class ToggleRenderWhitespaceAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const configurationService = accessor.get(IConfigurationService);
let renderWhitespace = editor.getConfiguration().viewInfo.renderWhitespace;
@@ -35,3 +34,5 @@ export class ToggleRenderWhitespaceAction extends EditorAction {
configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER);
}
}
registerEditorAction(ToggleRenderWhitespaceAction);

View File

@@ -7,9 +7,9 @@
import 'vs/css!./media/codeEditor';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICommonCodeEditor, IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorContribution, IModel } from 'vs/editor/common/editorCommon';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -18,6 +18,7 @@ import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import { InternalEditorOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
const transientWordWrapState = 'transientWordWrapState';
const isWordWrapMinifiedKey = 'isWordWrapMinified';
@@ -53,7 +54,7 @@ function readTransientState(model: IModel, codeEditorService: ICodeEditorService
}
function readWordWrapState(model: IModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
const editorConfig = configurationService.getConfiguration(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean };
const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean };
let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : void 0;
// Compatibility with old true or false values
@@ -72,7 +73,7 @@ function readWordWrapState(model: IModel, configurationService: ITextResourceCon
};
}
function toggleWordWrap(editor: ICommonCodeEditor, state: IWordWrapState): IWordWrapState {
function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapState {
if (state.transientState) {
// toggle off => go to null
return {
@@ -113,7 +114,7 @@ function toggleWordWrap(editor: ICommonCodeEditor, state: IWordWrapState): IWord
};
}
function applyWordWrapState(editor: ICommonCodeEditor, state: IWordWrapState): void {
function applyWordWrapState(editor: ICodeEditor, state: IWordWrapState): void {
if (state.transientState) {
// toggle is on
editor.updateOptions({
@@ -130,7 +131,6 @@ function applyWordWrapState(editor: ICommonCodeEditor, state: IWordWrapState): v
});
}
@editorAction
class ToggleWordWrapAction extends EditorAction {
constructor() {
@@ -146,7 +146,7 @@ class ToggleWordWrapAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const editorConfiguration = editor.getConfiguration();
if (editorConfiguration.wrappingInfo.inDiffEditor) {
// Cannot change wrapping settings inside the diff editor
@@ -174,13 +174,12 @@ class ToggleWordWrapAction extends EditorAction {
}
}
@commonEditorContribution
class ToggleWordWrapController extends Disposable implements IEditorContribution {
private static _ID = 'editor.contrib.toggleWordWrapController';
private static readonly _ID = 'editor.contrib.toggleWordWrapController';
constructor(
private readonly editor: ICommonCodeEditor,
private readonly editor: ICodeEditor,
@IContextKeyService readonly contextKeyService: IContextKeyService,
@ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService,
@ICodeEditorService readonly codeEditorService: ICodeEditorService
@@ -250,6 +249,11 @@ function canToggleWordWrap(uri: URI): boolean {
return (uri.scheme !== 'output' && uri.scheme !== 'vscode');
}
registerEditorContribution(ToggleWordWrapController);
registerEditorAction(ToggleWordWrapAction);
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: 'editor.action.toggleWordWrap',

View File

@@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IMessageService } from 'vs/platform/message/common/message';
@@ -22,7 +22,7 @@ interface IStorageData {
}
class WordWrapMigrationStorage {
private static KEY = 'wordWrapMigration';
private static readonly KEY = 'wordWrapMigration';
private _storageService: IStorageService;
private _value: IStorageData;
@@ -54,10 +54,9 @@ class WordWrapMigrationStorage {
}
}
@editorContribution
class WordWrapMigrationController extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.wordWrapMigrationController';
private static readonly ID = 'editor.contrib.wordWrapMigrationController';
private static _checked = false;
constructor(
@@ -140,3 +139,5 @@ class WordWrapMigrationController extends Disposable implements IEditorContribut
});
}
}
registerEditorContribution(WordWrapMigrationController);

View File

@@ -13,7 +13,7 @@ import * as lifecycle from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IDebugService, IBreakpoint, IRawBreakpoint } from 'vs/workbench/parts/debug/common/debug';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';

View File

@@ -12,7 +12,6 @@ import * as dom from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugService } from 'vs/workbench/parts/debug/common/debug';
@@ -23,9 +22,9 @@ import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
const $ = dom.$;
export class StartDebugActionItem extends EventEmitter implements IActionItem {
export class StartDebugActionItem implements IActionItem {
private static SEPARATOR = '─────────';
private static readonly SEPARATOR = '─────────';
public actionRunner: IActionRunner;
private container: HTMLElement;
@@ -43,7 +42,6 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem {
@IConfigurationService private configurationService: IConfigurationService,
@ICommandService private commandService: ICommandService
) {
super();
this.toDispose = [];
this.selectBox = new SelectBox([], -1);
this.toDispose.push(attachSelectBoxStyler(this.selectBox, themeService, {

View File

@@ -14,7 +14,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 { IMessageService } from 'vs/platform/message/common/message';
import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IFunctionBreakpoint, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState }
import { IDebugService, State, IProcess, IThread, IEnablement, IBreakpoint, IStackFrame, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, IExpression, REPL_ID, ProcessState }
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';
@@ -77,7 +77,6 @@ export class ConfigureAction extends AbstractDebugAction {
constructor(id: string, label: string,
@IDebugService debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IMessageService private messageService: IMessageService
) {
super(id, label, 'debug-action configure', debugService, keybindingService);
@@ -133,28 +132,31 @@ export class StartAction extends AbstractDebugAction {
return false;
}
// Disabled if the launch drop down shows the launch config that is already running.
protected isEnabled(state: State): boolean {
const processes = this.debugService.getModel().getProcesses();
const selectedName = this.debugService.getConfigurationManager().selectedName;
const launch = this.debugService.getConfigurationManager().selectedLaunch;
public static isEnabled(debugService: IDebugService, contextService: IWorkspaceContextService, configName: string) {
const processes = debugService.getModel().getProcesses();
const launch = debugService.getConfigurationManager().selectedLaunch;
if (state === State.Initializing) {
if (debugService.state === State.Initializing) {
return false;
}
if (this.contextService && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && processes.length > 0) {
if (contextService && contextService.getWorkbenchState() === WorkbenchState.EMPTY && processes.length > 0) {
return false;
}
if (processes.some(p => p.getName(false) === selectedName && (!launch || p.session.root.uri.toString() === launch.workspace.uri.toString()))) {
if (processes.some(p => p.getName(false) === configName && (!launch || p.session.root.uri.toString() === launch.workspace.uri.toString()))) {
return false;
}
const compound = launch && launch.getCompound(selectedName);
const compound = launch && launch.getCompound(configName);
if (compound && compound.configurations && processes.some(p => compound.configurations.indexOf(p.getName(false)) !== -1)) {
return false;
}
return true;
}
// Disabled if the launch drop down shows the launch config that is already running.
protected isEnabled(state: State): boolean {
return StartAction.isEnabled(this.debugService, this.contextService, this.debugService.getConfigurationManager().selectedName);
}
}
export class RunAction extends StartAction {
@@ -529,25 +531,17 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction {
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, 'debug-action add-function-breakpoint', debugService, keybindingService);
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
}
public run(): TPromise<any> {
this.debugService.addFunctionBreakpoint();
return TPromise.as(null);
}
}
export class RenameFunctionBreakpointAction extends AbstractDebugAction {
static ID = 'workbench.debug.viewlet.action.renameFunctionBreakpointAction';
static LABEL = nls.localize('renameFunctionBreakpoint', "Rename Function Breakpoint");
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
super(id, label, null, debugService, keybindingService);
}
public run(fbp: IFunctionBreakpoint): TPromise<any> {
this.debugService.getViewModel().setSelectedFunctionBreakpoint(fbp);
return TPromise.as(null);
protected isEnabled(state: State): boolean {
return !this.debugService.getViewModel().getSelectedFunctionBreakpoint()
&& this.debugService.getModel().getFunctionBreakpoints().every(fbp => !!fbp.name);
}
}

View File

@@ -12,15 +12,13 @@ import * as builder from 'vs/base/browser/builder';
import * as dom from 'vs/base/browser/dom';
import * as arrays from 'vs/base/common/arrays';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAction } from 'vs/base/common/actions';
import { EventType } from 'vs/base/common/events';
import { IAction, IRunEvent } from 'vs/base/common/actions';
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
import { AbstractDebugAction, PauseAction, ContinueAction, StepBackAction, ReverseContinueAction, StopAction, DisconnectAction, StepOverAction, StepIntoAction, StepOutAction, RestartAction, FocusProcessAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { FocusProcessActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IMessageService } from 'vs/platform/message/common/message';
@@ -29,6 +27,8 @@ import { Themable } from 'vs/workbench/common/theme';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { localize } from 'vs/nls';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
const $ = builder.$;
const DEBUG_ACTIONS_WIDGET_POSITION_KEY = 'debug.actionswidgetposition';
@@ -40,7 +40,6 @@ export const debugToolBarBackground = registerColor('debugToolBar.background', {
}, localize('debugToolBarBackground', "Debug toolbar background color."));
export class DebugActionsWidget extends Themable implements IWorkbenchContribution {
private static ID = 'debug.actionsWidget';
private $el: builder.Builder;
private dragArea: builder.Builder;
@@ -55,11 +54,12 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
@IMessageService private messageService: IMessageService,
@ITelemetryService private telemetryService: ITelemetryService,
@IDebugService private debugService: IDebugService,
@IInstantiationService private instantiationService: IInstantiationService,
@IPartService private partService: IPartService,
@IStorageService private storageService: IStorageService,
@IConfigurationService private configurationService: IConfigurationService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IKeybindingService private keybindingService: IKeybindingService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
super(themeService);
@@ -75,7 +75,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
orientation: ActionsOrientation.HORIZONTAL,
actionItemProvider: (action: IAction) => {
if (action.id === FocusProcessAction.ID) {
return this.instantiationService.createInstance(FocusProcessActionItem, action);
return new FocusProcessActionItem(action, this.debugService, this.themeService);
}
return null;
@@ -94,7 +94,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
private registerListeners(): void {
this.toUnbind.push(this.debugService.onDidChangeState(state => this.update(state)));
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e)));
this.toUnbind.push(this.actionBar.actionRunner.addListener(EventType.RUN, (e: any) => {
this.toUnbind.push(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => {
// check for error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
this.messageService.show(severity.Error, e.error);
@@ -147,12 +147,6 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
private storePosition(): void {
const position = parseFloat(this.$el.getComputedStyle().left) / window.innerWidth;
this.storageService.store(DEBUG_ACTIONS_WIDGET_POSITION_KEY, position, StorageScope.WORKSPACE);
/* __GDPR__
"debug.actionswidgetposition" : {
"position" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog(DEBUG_ACTIONS_WIDGET_POSITION_KEY, { position });
}
protected updateStyles(): void {
@@ -191,10 +185,6 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
this.$el.style('left', `${x}px`);
}
public getId(): string {
return DebugActionsWidget.ID;
}
private onDidConfigurationChange(event: IConfigurationChangeEvent): void {
if (event.affectsConfiguration('debug.hideActionBar')) {
this.update(this.debugService.state);
@@ -202,7 +192,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
}
private update(state: State): void {
if (state === State.Inactive || state === State.Initializing || this.configurationService.getConfiguration<IDebugConfiguration>('debug').hideActionBar) {
if (state === State.Inactive || state === State.Initializing || this.configurationService.getValue<IDebugConfiguration>('debug').hideActionBar) {
return this.hide();
}
@@ -237,17 +227,17 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi
private getActions(): AbstractDebugAction[] {
if (!this.allActions) {
this.allActions = [];
this.allActions.push(this.instantiationService.createInstance(ContinueAction, ContinueAction.ID, ContinueAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(PauseAction, PauseAction.ID, PauseAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(StopAction, StopAction.ID, StopAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(StepOverAction, StepOverAction.ID, StepOverAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(StepOutAction, StepOutAction.ID, StepOutAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(StepBackAction, StepBackAction.ID, StepBackAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(ReverseContinueAction, ReverseContinueAction.ID, ReverseContinueAction.LABEL));
this.allActions.push(this.instantiationService.createInstance(FocusProcessAction, FocusProcessAction.ID, FocusProcessAction.LABEL));
this.allActions.push(new ContinueAction(ContinueAction.ID, ContinueAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new PauseAction(PauseAction.ID, PauseAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new StopAction(StopAction.ID, StopAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new DisconnectAction(DisconnectAction.ID, DisconnectAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new StepOverAction(StepOverAction.ID, StepOverAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new StepIntoAction(StepIntoAction.ID, StepIntoAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new StepOutAction(StepOutAction.ID, StepOutAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new RestartAction(RestartAction.ID, RestartAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new StepBackAction(StepBackAction.ID, StepBackAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new ReverseContinueAction(ReverseContinueAction.ID, ReverseContinueAction.LABEL, this.debugService, this.keybindingService));
this.allActions.push(new FocusProcessAction(FocusProcessAction.ID, FocusProcessAction.LABEL, this.debugService, this.keybindingService, this.editorService));
this.allActions.forEach(a => {
this.toUnbind.push(a);
});

View File

@@ -39,10 +39,6 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC
textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this);
}
public getId(): string {
return 'debug.contentprovider';
}
public provideTextContent(resource: uri): TPromise<IModel> {
let process: IProcess;

View File

@@ -7,15 +7,15 @@ import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { Range } from 'vs/editor/common/core/range';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ServicesAccessor, editorAction, EditorAction, CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { ServicesAccessor, registerEditorAction, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE } from 'vs/workbench/parts/debug/common/debug';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
class ToggleBreakpointAction extends EditorAction {
constructor() {
super({
@@ -30,7 +30,7 @@ class ToggleBreakpointAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<any> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<any> {
const debugService = accessor.get(IDebugService);
const position = editor.getPosition();
@@ -49,7 +49,7 @@ class ToggleBreakpointAction extends EditorAction {
}
}
function addColumnBreakpoint(accessor: ServicesAccessor, editor: ICommonCodeEditor, remove: boolean): TPromise<any> {
function addColumnBreakpoint(accessor: ServicesAccessor, editor: ICodeEditor, remove: boolean): TPromise<any> {
const debugService = accessor.get(IDebugService);
const position = editor.getPosition();
@@ -67,7 +67,6 @@ function addColumnBreakpoint(accessor: ServicesAccessor, editor: ICommonCodeEdit
return TPromise.as(null);
}
@editorAction
class ToggleColumnBreakpointAction extends EditorAction {
constructor() {
super({
@@ -82,13 +81,12 @@ class ToggleColumnBreakpointAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<any> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<any> {
return addColumnBreakpoint(accessor, editor, true);
}
}
// TODO@Isidor merge two column breakpoints actions together
@editorAction
class ToggleColumnBreakpointContextMenuAction extends EditorAction {
constructor() {
super({
@@ -103,12 +101,11 @@ class ToggleColumnBreakpointContextMenuAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<any> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<any> {
return addColumnBreakpoint(accessor, editor, false);
}
}
@editorAction
class ConditionalBreakpointAction extends EditorAction {
constructor() {
@@ -120,7 +117,7 @@ class ConditionalBreakpointAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const debugService = accessor.get(IDebugService);
const { lineNumber, column } = editor.getPosition();
@@ -131,7 +128,6 @@ class ConditionalBreakpointAction extends EditorAction {
}
@editorAction
class RunToCursorAction extends EditorAction {
constructor() {
@@ -147,7 +143,7 @@ class RunToCursorAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const debugService = accessor.get(IDebugService);
if (debugService.state !== State.Stopped) {
@@ -174,7 +170,6 @@ class RunToCursorAction extends EditorAction {
}
}
@editorAction
class SelectionToReplAction extends EditorAction {
constructor() {
@@ -190,7 +185,7 @@ class SelectionToReplAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const debugService = accessor.get(IDebugService);
const panelService = accessor.get(IPanelService);
@@ -201,7 +196,6 @@ class SelectionToReplAction extends EditorAction {
}
}
@editorAction
class SelectionToWatchExpressionsAction extends EditorAction {
constructor() {
@@ -217,7 +211,7 @@ class SelectionToWatchExpressionsAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const debugService = accessor.get(IDebugService);
const viewletService = accessor.get(IViewletService);
@@ -226,7 +220,6 @@ class SelectionToWatchExpressionsAction extends EditorAction {
}
}
@editorAction
class ShowDebugHoverAction extends EditorAction {
constructor() {
@@ -242,7 +235,7 @@ class ShowDebugHoverAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const position = editor.getPosition();
const word = editor.getModel().getWordAtPosition(position);
if (!word) {
@@ -261,7 +254,7 @@ class CloseBreakpointWidgetCommand extends EditorCommand {
id: 'closeBreakpointWidget',
precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE,
kbOpts: {
weight: CommonEditorRegistry.commandWeight(8),
weight: KeybindingsRegistry.WEIGHT.editorContrib(8),
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
@@ -269,9 +262,18 @@ class CloseBreakpointWidgetCommand extends EditorCommand {
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
return editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).closeBreakpointWidget();
}
}
CommonEditorRegistry.registerEditorCommand(new CloseBreakpointWidgetCommand());
registerEditorAction(ToggleBreakpointAction);
registerEditorAction(ToggleColumnBreakpointAction);
registerEditorAction(ToggleColumnBreakpointContextMenuAction);
registerEditorAction(ConditionalBreakpointAction);
registerEditorAction(RunToCursorAction);
registerEditorAction(SelectionToReplAction);
registerEditorAction(SelectionToWatchExpressionsAction);
registerEditorAction(ShowDebugHoverAction);
registerEditorCommand(new CloseBreakpointWidgetCommand());

View File

@@ -4,23 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as objects from 'vs/base/common/objects';
import * as lifecycle from 'vs/base/common/lifecycle';
import { Constants } from 'vs/editor/common/core/uint';
import { Range } from 'vs/editor/common/core/range';
import { IModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/editorCommon';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IDebugService, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, IBreakpoint, State } from 'vs/workbench/parts/debug/common/debug';
import { IModelService } from 'vs/editor/common/services/modelService';
import { MarkdownString } from 'vs/base/common/htmlContent';
interface IBreakpointDecoration {
decorationId: string;
modelId: string;
range: Range;
}
interface IDebugEditorModelData {
model: IModel;
toDispose: lifecycle.IDisposable[];
breakpointDecorationIds: string[];
breakpointModelIds: string[];
breakpointDecorationsAsMap: Map<string, Range>;
breakpointDecorations: IBreakpointDecoration[];
currentStackDecorations: string[];
dirty: boolean;
topStackFrameRange: Range;
@@ -44,14 +47,10 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
this.registerListeners();
}
public getId(): string {
return DebugEditorModelManager.ID;
}
public dispose(): void {
this.modelDataMap.forEach(modelData => {
lifecycle.dispose(modelData.toDispose);
modelData.model.deltaDecorations(modelData.breakpointDecorationIds, []);
modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), []);
modelData.model.deltaDecorations(modelData.currentStackDecorations, []);
});
this.toDispose = lifecycle.dispose(this.toDispose);
@@ -82,18 +81,13 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUrlStr));
const desiredDecorations = this.createBreakpointDecorations(model, breakpoints);
const breakPointDecorations = model.deltaDecorations([], desiredDecorations);
const breakpointDecorationIds = model.deltaDecorations([], desiredDecorations);
const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUrlStr))];
const breakpointDecorationsAsMap = new Map<string, Range>();
breakPointDecorations.forEach((decorationId, index) => breakpointDecorationsAsMap.set(decorationId, desiredDecorations[index].range));
this.modelDataMap.set(modelUrlStr, {
model: model,
toDispose: toDispose,
breakpointDecorationIds: breakPointDecorations,
breakpointModelIds: breakpoints.map(bp => bp.getId()),
breakpointDecorationsAsMap,
breakpointDecorations: breakpointDecorationIds.map((decorationId, index) => ({ decorationId, modelId: breakpoints[index].getId(), range: desiredDecorations[index].range })),
currentStackDecorations: currentStackDecorations,
dirty: false,
topStackFrameRange: undefined
@@ -188,17 +182,17 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
// breakpoints management. Represent data coming from the debug service and also send data back.
private onModelDecorationsChanged(modelUrlStr: string): void {
const modelData = this.modelDataMap.get(modelUrlStr);
if (modelData.breakpointDecorationsAsMap.size === 0 || this.ignoreDecorationsChangedEvent) {
if (modelData.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent) {
// I have no decorations
return;
}
let somethingChanged = false;
modelData.breakpointDecorationsAsMap.forEach((breakpointRange, decorationId) => {
modelData.breakpointDecorations.forEach(breakpointDecoration => {
if (somethingChanged) {
return;
}
const newBreakpointRange = modelData.model.getDecorationRange(decorationId);
if (newBreakpointRange && !breakpointRange.equalsRange(newBreakpointRange)) {
const newBreakpointRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) {
somethingChanged = true;
}
});
@@ -207,35 +201,28 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
return;
}
const data: IRawBreakpoint[] = [];
const data: { [id: string]: DebugProtocol.Breakpoint } = Object.create(null);
const breakpoints = this.debugService.getModel().getBreakpoints();
const modelUri = modelData.model.uri;
for (let i = 0, len = modelData.breakpointDecorationIds.length; i < len; i++) {
const decorationRange = modelData.model.getDecorationRange(modelData.breakpointDecorationIds[i]);
for (let i = 0, len = modelData.breakpointDecorations.length; i < len; i++) {
const breakpointDecoration = modelData.breakpointDecorations[i];
const decorationRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
// check if the line got deleted.
if (decorationRange && decorationRange.endColumn - decorationRange.startColumn > 0) {
const breakpoint = breakpoints.filter(bp => bp.getId() === modelData.breakpointModelIds[i]).pop();
if (decorationRange) {
const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.modelId).pop();
// since we know it is collapsed, it cannot grow to multiple lines
if (breakpoint) {
data.push({
lineNumber: decorationRange.startLineNumber,
enabled: breakpoint.enabled,
condition: breakpoint.condition,
hitCondition: breakpoint.hitCondition,
column: breakpoint.column ? decorationRange.startColumn : undefined
});
data[breakpoint.getId()] = {
line: decorationRange.startLineNumber,
column: breakpoint.column ? decorationRange.startColumn : undefined,
verified: breakpoint.verified
};
}
}
}
modelData.dirty = this.debugService.state !== State.Inactive;
const toRemove = this.debugService.getModel().getBreakpoints()
.filter(bp => bp.uri.toString() === modelUri.toString());
TPromise.join(toRemove.map(bp => this.debugService.removeBreakpoints(bp.getId()))).then(() => {
this.debugService.addBreakpoints(modelUri, data);
});
this.debugService.updateBreakpoints(modelUri, data);
}
private onBreakpointsChange(): void {
@@ -263,22 +250,24 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void {
const desiredDecorations = this.createBreakpointDecorations(modelData.model, newBreakpoints);
let breakpointDecorationIds: string[];
try {
this.ignoreDecorationsChangedEvent = true;
modelData.breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorationIds, desiredDecorations);
breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), desiredDecorations);
} finally {
this.ignoreDecorationsChangedEvent = false;
}
modelData.breakpointModelIds = newBreakpoints.map(nbp => nbp.getId());
modelData.breakpointDecorationsAsMap.clear();
modelData.breakpointDecorationIds.forEach((decorationId, index) => modelData.breakpointDecorationsAsMap.set(decorationId, desiredDecorations[index].range));
modelData.breakpointDecorations = breakpointDecorationIds.map((decorationId, index) =>
({ decorationId, modelId: newBreakpoints[index].getId(), range: desiredDecorations[index].range }));
}
private createBreakpointDecorations(model: IModel, breakpoints: IBreakpoint[]): { range: Range; options: IModelDecorationOptions; }[] {
return breakpoints.map((breakpoint) => {
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
const range = model.validateRange(
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
: new Range(breakpoint.lineNumber, 1, breakpoint.lineNumber, Constants.MAX_SAFE_SMALL_INTEGER) // Decoration has to have a width #20688
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
);
return {
options: this.getBreakpointDecorationOptions(breakpoint),
@@ -299,7 +288,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
!breakpoint.condition && !breakpoint.hitCondition ? DebugEditorModelManager.BREAKPOINT_DECORATION : null;
if (result) {
result = objects.clone(result);
result = objects.deepClone(result);
if (breakpoint.message) {
result.glyphMarginHoverMessage = new MarkdownString().appendText(breakpoint.message);
}

View File

@@ -9,12 +9,14 @@ import { TPromise } from 'vs/base/common/winjs.base';
import Quickopen = require('vs/workbench/browser/quickopen');
import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IDebugService, ILaunch } from 'vs/workbench/parts/debug/common/debug';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import * as errors from 'vs/base/common/errors';
import { QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { IMessageService } from 'vs/platform/message/common/message';
import { Severity } from 'vs/workbench/services/message/browser/messageList';
class AddConfigEntry extends Model.QuickOpenEntry {
@@ -46,7 +48,7 @@ class AddConfigEntry extends Model.QuickOpenEntry {
class StartDebugEntry extends Model.QuickOpenEntry {
constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) {
constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private messageService: IMessageService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) {
super(highlights);
}
@@ -63,12 +65,12 @@ class StartDebugEntry extends Model.QuickOpenEntry {
}
public run(mode: QuickOpen.Mode, context: Model.IContext): boolean {
if (mode === QuickOpen.Mode.PREVIEW) {
if (mode === QuickOpen.Mode.PREVIEW || !StartAction.isEnabled(this.debugService, this.contextService, this.configurationName)) {
return false;
}
// Run selected debug configuration
this.debugService.getConfigurationManager().selectConfiguration(this.launch, this.configurationName);
this.debugService.startDebugging(this.launch.workspace).done(undefined, errors.onUnexpectedError);
this.debugService.startDebugging(this.launch.workspace).done(undefined, e => this.messageService.show(Severity.Error, e));
return true;
}
@@ -80,10 +82,10 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler {
private autoFocusIndex: number;
constructor(
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IDebugService private debugService: IDebugService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@ICommandService private commandService: ICommandService
@ICommandService private commandService: ICommandService,
@IMessageService private messageService: IMessageService
) {
super();
}
@@ -104,7 +106,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler {
if (launch === configManager.selectedLaunch && config === configManager.selectedName) {
this.autoFocusIndex = configurations.length;
}
configurations.push(new StartDebugEntry(this.debugService, this.contextService, launch, config, highlights));
configurations.push(new StartDebugEntry(this.debugService, this.contextService, this.messageService, launch, config, highlights));
});
}
launches.forEach((l, index) => {

View File

@@ -10,22 +10,25 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, State, IDebugConfiguration } from 'vs/workbench/parts/debug/common/debug';
import { Themable, STATUS_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const $ = dom.$;
export class DebugStatus extends Themable implements IStatusbarItem {
private toDispose: IDisposable[];
private container: HTMLElement;
private statusBarItem: HTMLElement;
private label: HTMLElement;
private icon: HTMLElement;
private hidden = true;
private showInStatusBar: string;
constructor(
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IDebugService private debugService: IDebugService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService
) {
super(themeService);
this.toDispose = [];
@@ -33,9 +36,24 @@ export class DebugStatus extends Themable implements IStatusbarItem {
this.setLabel();
}));
this.toDispose.push(this.debugService.onDidChangeState(state => {
if (state !== State.Inactive && this.hidden) {
this.hidden = false;
this.render(this.container);
if (state !== State.Inactive && this.showInStatusBar === 'onFirstSessionStart') {
this.doRender();
}
}));
this.showInStatusBar = configurationService.getValue<IDebugConfiguration>('debug').showInStatusBar;
this.toDispose.push(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('debug.showInStatusBar')) {
this.showInStatusBar = configurationService.getValue<IDebugConfiguration>('debug').showInStatusBar;
if (this.showInStatusBar === 'never' && this.statusBarItem) {
this.statusBarItem.hidden = true;
} else {
if (this.statusBarItem) {
this.statusBarItem.hidden = false;
}
if (this.showInStatusBar === 'always') {
this.doRender();
}
}
}
}));
}
@@ -49,24 +67,30 @@ export class DebugStatus extends Themable implements IStatusbarItem {
public render(container: HTMLElement): IDisposable {
this.container = container;
if (!this.hidden) {
const statusBarItem = dom.append(container, $('.debug-statusbar-item'));
this.toDispose.push(dom.addDisposableListener(statusBarItem, 'click', () => {
if (this.showInStatusBar === 'always') {
this.doRender();
}
// noop, we render when we decide is best
return this;
}
private doRender(): void {
if (!this.statusBarItem && this.container) {
this.statusBarItem = dom.append(this.container, $('.debug-statusbar-item'));
this.toDispose.push(dom.addDisposableListener(this.statusBarItem, 'click', () => {
this.quickOpenService.show('debug ').done(undefined, errors.onUnexpectedError);
}));
statusBarItem.title = nls.localize('selectAndStartDebug', "Select and start debug configuration");
const a = dom.append(statusBarItem, $('a'));
this.statusBarItem.title = nls.localize('selectAndStartDebug', "Select and start debug configuration");
const a = dom.append(this.statusBarItem, $('a'));
this.icon = dom.append(a, $('.icon'));
this.label = dom.append(a, $('span.label'));
this.setLabel();
this.updateStyles();
}
return this;
}
private setLabel(): void {
if (this.label && !this.hidden) {
if (this.label && this.statusBarItem) {
const manager = this.debugService.getConfigurationManager();
const name = manager.selectedName || '';
this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedLaunch.workspace.name})` : name;

View File

@@ -10,7 +10,7 @@ import { Action, IAction } from 'vs/base/common/actions';
import * as DOM from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { PersistentViewsViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { PersistentViewsViewlet, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, VIEWLET_ID, State, VARIABLES_VIEW_ID, WATCH_VIEW_ID, CALLSTACK_VIEW_ID, BREAKPOINTS_VIEW_ID } from 'vs/workbench/parts/debug/common/debug';
import { StartAction, ToggleReplAction, ConfigureAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { StartDebugActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
@@ -25,11 +25,14 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ViewLocation } from 'vs/workbench/browser/parts/views/viewsRegistry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export class DebugViewlet extends PersistentViewsViewlet {
private startDebugActionItem: StartDebugActionItem;
private progressRunner: IProgressRunner;
private breakpointView: ViewsViewletPanel;
private panelListeners = new Map<string, IDisposable>();
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -105,6 +108,32 @@ export class DebugViewlet extends PersistentViewsViewlet {
this.progressRunner = null;
}
}
addPanel(panel: ViewsViewletPanel, size: number, index?: number): void {
super.addPanel(panel, size, index);
// attach event listener to
if (panel.id === BREAKPOINTS_VIEW_ID) {
this.breakpointView = panel;
this.updateBreakpointsMaxSize();
} else {
this.panelListeners.set(panel.id, panel.onDidChange(() => this.updateBreakpointsMaxSize()));
}
}
removePanel(panel: ViewsViewletPanel): void {
super.removePanel(panel);
dispose(this.panelListeners.get(panel.id));
this.panelListeners.delete(panel.id);
}
private updateBreakpointsMaxSize(): void {
if (this.breakpointView) {
// We need to update the breakpoints view since all other views are collapsed #25384
const allOtherCollapsed = this.views.every(view => !view.isExpanded() || view === this.breakpointView);
this.breakpointView.maximumBodySize = allOtherCollapsed ? Number.POSITIVE_INFINITY : this.breakpointView.minimumBodySize;
}
}
}
export class FocusVariablesViewAction extends Action {

View File

@@ -6,10 +6,9 @@
import 'vs/css!../browser/media/exceptionWidget';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IDebugService, IExceptionInfo } from 'vs/workbench/parts/debug/common/debug';
import { IExceptionInfo } from 'vs/workbench/parts/debug/common/debug';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
@@ -27,9 +26,7 @@ export class ExceptionWidget extends ZoneWidget {
private _backgroundColor: Color;
constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo, private lineNumber: number,
@IContextViewService private contextViewService: IContextViewService,
@IDebugService private debugService: IDebugService,
constructor(editor: ICodeEditor, private exceptionInfo: IExceptionInfo,
@IThemeService themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) {

View File

@@ -9,7 +9,6 @@ import * as errors from 'vs/base/common/errors';
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import * as nls from 'vs/nls';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export class LinkDetector {
private static FILE_LOCATION_PATTERNS: RegExp[] = [
@@ -23,8 +22,7 @@ export class LinkDetector {
];
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
// noop
}

View File

@@ -109,9 +109,13 @@
padding: 0 5px 0 5px;
}
.monaco-workbench .part.statusbar .debug-statusbar-item.hidden {
display: none;
}
.monaco-workbench .part.statusbar .debug-statusbar-item .icon {
-webkit-mask: url('continue.svg') no-repeat 50% 50%;
-webkit-mask-size: 18px;
-webkit-mask-size: 16px;
display: inline-block;
padding-right: 2px;
width: 16px;
@@ -142,6 +146,10 @@
color: #9B46B0;
}
.monaco-workbench .monaco-tree-row:not(.selected) .expression .name.virtual {
opacity: 0.5;
}
.monaco-workbench > .monaco-tree-row:not(.selected) .expression .value {
color: rgba(108, 108, 108, 0.8);
}

View File

@@ -110,6 +110,7 @@
}
.debug-viewlet .monaco-tree .monaco-tree-row.selected .line-number,
.debug-viewlet .monaco-list .monaco-list-row.selected .line-number,
.debug-viewlet .monaco-tree .monaco-tree-row.selected .thread > .state > .label,
.debug-viewlet .monaco-tree .monaco-tree-row.selected .process > .state > .label {
background-color: #ffffff;
@@ -347,6 +348,10 @@
/* Breakpoints */
.debug-viewlet .debug-breakpoints .monaco-list-row {
padding-left: 20px;
}
.debug-viewlet .debug-breakpoints .breakpoint {
display: flex;
padding-right: 0.8em;
@@ -367,7 +372,6 @@
overflow: hidden;
}
.debug-viewlet .debug-action.remove {
background: url('remove.svg') center center no-repeat;
}

View File

@@ -36,9 +36,9 @@ export const CONTEXT_NOT_IN_DEBUG_REPL: ContextKeyExpr = CONTEXT_IN_DEBUG_REPL.t
export const CONTEXT_ON_FIRST_DEBUG_REPL_LINE = new RawContextKey<boolean>('onFirsteDebugReplLine', false);
export const CONTEXT_ON_LAST_DEBUG_REPL_LINE = new RawContextKey<boolean>('onLastDebugReplLine', false);
export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('breakpointWidgetVisible', false);
export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', false);
export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', false);
export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', false);
export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', true);
export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', true);
export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', true);
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
export const DEBUG_SCHEME = 'debug';
@@ -288,7 +288,6 @@ export interface IViewModel extends ITreeElement {
onDidFocusProcess: Event<IProcess | undefined>;
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame, explicit: boolean }>;
onDidSelectExpression: Event<IExpression>;
onDidSelectFunctionBreakpoint: Event<IFunctionBreakpoint>;
}
export interface IModel extends ITreeElement {
@@ -300,11 +299,20 @@ export interface IModel extends ITreeElement {
getWatchExpressions(): IExpression[];
getReplElements(): IReplElement[];
onDidChangeBreakpoints: Event<void>;
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent>;
onDidChangeCallStack: Event<void>;
onDidChangeWatchExpressions: Event<IExpression>;
onDidChangeReplElements: Event<void>;
};
}
/**
* An event describing a change to the set of [breakpoints](#debug.Breakpoint).
*/
export interface IBreakpointsChangeEvent {
added?: (IBreakpoint | IFunctionBreakpoint)[];
removed?: (IBreakpoint | IFunctionBreakpoint)[];
changed?: (IBreakpoint | IFunctionBreakpoint)[];
}
// Debug enums
@@ -319,9 +327,11 @@ export enum State {
export interface IDebugConfiguration {
allowBreakpointsEverywhere: boolean;
openDebug: string;
openExplorerOnEnd: boolean;
inlineValues: boolean;
hideActionBar: boolean;
showInStatusBar: string;
internalConsoleOptions: string;
}
@@ -509,6 +519,11 @@ export interface IDebugService {
*/
addBreakpoints(uri: uri, rawBreakpoints: IRawBreakpoint[]): TPromise<void>;
/**
* Updates the breakpoints and notifies the debug adapter of breakpoint changes.
*/
updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void>;
/**
* Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint.
* Notifies debug adapter of breakpoint changes.

View File

@@ -20,7 +20,7 @@ import { ISuggestion } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import {
ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource,
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState, IBreakpointsChangeEvent
} from 'vs/workbench/parts/debug/common/debug';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -58,7 +58,7 @@ export class SimpleReplElement extends AbstractReplElement {
export class RawObjectReplElement extends AbstractReplElement implements IExpression {
private static MAX_CHILDREN = 1000; // upper bound of children per value
private static readonly MAX_CHILDREN = 1000; // upper bound of children per value
constructor(public name: string, public valueObj: any, source?: IReplElementSource, public annotation?: string) {
super(source);
@@ -104,7 +104,7 @@ export class ExpressionContainer implements IExpressionContainer {
public static allValues: Map<string, string> = new Map<string, string>();
// Use chunks to support variable paging #9537
private static BASE_CHUNK_SIZE = 100;
private static readonly BASE_CHUNK_SIZE = 100;
public valueChanged: boolean;
private _value: string;
@@ -159,7 +159,7 @@ export class ExpressionContainer implements IExpressionContainer {
for (let i = 0; i < numberOfChunks; i++) {
const start = this.startOfVariables + i * chunkSize;
const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize);
childrenArray.push(new Variable(this.process, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, null, true, start));
childrenArray.push(new Variable(this.process, this, this.reference, `[${start}..${start + count - 1}]`, '', '', null, count, { kind: 'virtual' }, null, true, start));
}
return childrenArray;
@@ -191,9 +191,9 @@ export class ExpressionContainer implements IExpressionContainer {
filter
}).then(response => {
return response && response.body && response.body.variables ? distinct(response.body.variables.filter(v => !!v && v.name), v => v.name).map(
v => new Variable(this.process, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.type)
v => new Variable(this.process, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)
) : [];
}, (e: Error) => [new Variable(this.process, this, 0, null, e.message, '', 0, 0, null, false)]) : TPromise.as([]);
}, (e: Error) => [new Variable(this.process, this, 0, null, e.message, '', 0, 0, { kind: 'virtual' }, null, false)]) : TPromise.as([]);
}
// The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked.
@@ -278,6 +278,7 @@ export class Variable extends ExpressionContainer implements IExpression {
value: string,
namedVariables: number,
indexedVariables: number,
public presentationHint: DebugProtocol.VariablePresentationHint,
public type: string = null,
public available = true,
startOfVariables = 0
@@ -739,7 +740,7 @@ export class Model implements IModel {
private toDispose: lifecycle.IDisposable[];
private replElements: IReplElement[];
private schedulers = new Map<string, RunOnceScheduler>();
private _onDidChangeBreakpoints: Emitter<void>;
private _onDidChangeBreakpoints: Emitter<IBreakpointsChangeEvent>;
private _onDidChangeCallStack: Emitter<void>;
private _onDidChangeWatchExpressions: Emitter<IExpression>;
private _onDidChangeREPLElements: Emitter<void>;
@@ -754,7 +755,7 @@ export class Model implements IModel {
this.processes = [];
this.replElements = [];
this.toDispose = [];
this._onDidChangeBreakpoints = new Emitter<void>();
this._onDidChangeBreakpoints = new Emitter<IBreakpointsChangeEvent>();
this._onDidChangeCallStack = new Emitter<void>();
this._onDidChangeWatchExpressions = new Emitter<IExpression>();
this._onDidChangeREPLElements = new Emitter<void>();
@@ -780,7 +781,7 @@ export class Model implements IModel {
this._onDidChangeCallStack.fire();
}
public get onDidChangeBreakpoints(): Event<void> {
public get onDidChangeBreakpoints(): Event<IBreakpointsChangeEvent> {
return this._onDidChangeBreakpoints.event;
}
@@ -867,9 +868,10 @@ export class Model implements IModel {
const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, undefined));
this.breakpoints = this.breakpoints.concat(newBreakpoints);
this.breakpointsActivated = true;
this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`);
this.sortAndDeDup();
if (fireEvent) {
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ added: newBreakpoints });
}
return newBreakpoints;
@@ -877,10 +879,11 @@ export class Model implements IModel {
public removeBreakpoints(toRemove: IBreakpoint[]): void {
this.breakpoints = this.breakpoints.filter(bp => !toRemove.some(toRemove => toRemove.getId() === bp.getId()));
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ removed: toRemove });
}
public updateBreakpoints(data: { [id: string]: DebugProtocol.Breakpoint }): void {
const updated: IBreakpoint[] = [];
this.breakpoints.forEach(bp => {
const bpData = data[bp.getId()];
if (bpData) {
@@ -888,45 +891,82 @@ export class Model implements IModel {
bp.endLineNumber = bpData.endLine;
bp.column = bpData.column;
bp.endColumn = bpData.endColumn;
bp.verified = bpData.verified;
bp.verified = bp.verified || bpData.verified;
bp.idFromAdapter = bpData.id;
bp.message = bpData.message;
bp.adapterData = bpData.source ? bpData.source.adapterData : bp.adapterData;
updated.push(bp);
}
});
this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`);
this.sortAndDeDup();
this._onDidChangeBreakpoints.fire({ changed: updated });
}
this._onDidChangeBreakpoints.fire();
private sortAndDeDup(): void {
this.breakpoints = this.breakpoints.sort((first, second) => {
if (first.uri.toString() !== second.uri.toString()) {
return resources.basenameOrAuthority(first.uri).localeCompare(resources.basenameOrAuthority(second.uri));
}
if (first.lineNumber === second.lineNumber) {
return first.column - second.column;
}
return first.lineNumber - second.lineNumber;
});
this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`);
}
public setEnablement(element: IEnablement, enable: boolean): void {
const changed: (IBreakpoint | IFunctionBreakpoint)[] = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint)) {
changed.push(element);
}
element.enabled = enable;
if (element instanceof Breakpoint && !element.enabled) {
const breakpoint = <Breakpoint>element;
breakpoint.verified = false;
}
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public enableOrDisableAllBreakpoints(enable: boolean): void {
const changed: (IBreakpoint | IFunctionBreakpoint)[] = [];
this.breakpoints.forEach(bp => {
if (bp.enabled !== enable) {
changed.push(bp);
}
bp.enabled = enable;
if (!enable) {
bp.verified = false;
}
});
this.functionBreakpoints.forEach(fbp => fbp.enabled = enable);
this.functionBreakpoints.forEach(fbp => {
if (fbp.enabled !== enable) {
changed.push(fbp);
}
fbp.enabled = enable;
});
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public addFunctionBreakpoint(functionName: string): void {
this.functionBreakpoints.push(new FunctionBreakpoint(functionName, true, null));
this._onDidChangeBreakpoints.fire();
public addFunctionBreakpoint(functionName: string): FunctionBreakpoint {
const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, null);
this.functionBreakpoints.push(newFunctionBreakpoint);
this._onDidChangeBreakpoints.fire({ added: [newFunctionBreakpoint] });
return newFunctionBreakpoint;
}
public updateFunctionBreakpoints(data: { [id: string]: { name?: string, verified?: boolean; id?: number; hitCondition?: string } }): void {
const changed: IFunctionBreakpoint[] = [];
this.functionBreakpoints.forEach(fbp => {
const fbpData = data[fbp.getId()];
if (fbpData) {
@@ -934,15 +974,25 @@ export class Model implements IModel {
fbp.verified = fbpData.verified;
fbp.idFromAdapter = fbpData.id;
fbp.hitCondition = fbpData.hitCondition;
changed.push(fbp);
}
});
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public removeFunctionBreakpoints(id?: string): void {
this.functionBreakpoints = id ? this.functionBreakpoints.filter(fbp => fbp.getId() !== id) : [];
this._onDidChangeBreakpoints.fire();
let removed: IFunctionBreakpoint[];
if (id) {
removed = this.functionBreakpoints.filter(fbp => fbp.getId() === id);
this.functionBreakpoints = this.functionBreakpoints.filter(fbp => fbp.getId() !== id);
} else {
removed = this.functionBreakpoints;
this.functionBreakpoints = [];
}
this._onDidChangeBreakpoints.fire({ removed: removed });
}
public getReplElements(): IReplElement[] {

View File

@@ -251,8 +251,8 @@ declare module DebugProtocol {
cwd: string;
/** List of arguments. The first argument is the command to run. */
args: string[];
/** Environment key-value pairs that are added to the default environment. */
env?: { [key: string]: string; };
/** Environment key-value pairs that are added to or removed from the default environment. */
env?: { [key: string]: string | null; };
}
/** Response to Initialize request. */
@@ -1275,7 +1275,18 @@ declare module DebugProtocol {
/** Optional properties of a variable that can be used to determine how to render the variable in the UI. */
export interface VariablePresentationHint {
/** The kind of variable. Before introducing additional values, try to use the listed values.
Values: 'property', 'method', 'class', 'data', 'event', 'baseClass', 'innerClass', 'interface', 'mostDerivedClass', etc.
Values:
'property': Indicates that the object is a property.
'method': Indicates that the object is a method.
'class': Indicates that the object is a class.
'data': Indicates that the object is data.
'event': Indicates that the object is an event.
'baseClass': Indicates that the object is a base class.
'innerClass': Indicates that the object is an inner class.
'interface': Indicates that the object is an interface.
'mostDerivedClass': Indicates that the object is the most derived class.
'virtual': Indicates that the object is virtual, that means it is a synthetic object introduced by the adapter for rendering purposes, e.g. an index range for large arrays.
etc.
*/
kind?: string;
/** Set of attributes represented as an array of strings. Before introducing additional values, try to use the listed values.

View File

@@ -15,16 +15,12 @@ export class ViewModel implements debug.IViewModel {
private _onDidFocusProcess: Emitter<debug.IProcess | undefined>;
private _onDidFocusStackFrame: Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>;
private _onDidSelectExpression: Emitter<debug.IExpression>;
private _onDidSelectFunctionBreakpoint: Emitter<debug.IFunctionBreakpoint>;
private multiProcessView: boolean;
public changedWorkbenchViewState: boolean;
constructor() {
this._onDidFocusProcess = new Emitter<debug.IProcess | undefined>();
this._onDidFocusStackFrame = new Emitter<{ stackFrame: debug.IStackFrame, explicit: boolean }>();
this._onDidSelectExpression = new Emitter<debug.IExpression>();
this._onDidSelectFunctionBreakpoint = new Emitter<debug.IFunctionBreakpoint>();
this.changedWorkbenchViewState = false;
this.multiProcessView = false;
}
@@ -80,11 +76,6 @@ export class ViewModel implements debug.IViewModel {
public setSelectedFunctionBreakpoint(functionBreakpoint: debug.IFunctionBreakpoint): void {
this.selectedFunctionBreakpoint = functionBreakpoint;
this._onDidSelectFunctionBreakpoint.fire(functionBreakpoint);
}
public get onDidSelectFunctionBreakpoint(): Event<debug.IFunctionBreakpoint> {
return this._onDidSelectFunctionBreakpoint.event;
}
public isMultiProcessView(): boolean {

View File

@@ -0,0 +1,248 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { IExpression, IDebugService, IEnablement } from 'vs/workbench/parts/debug/common/debug';
import { Expression, FunctionBreakpoint, Variable } from 'vs/workbench/parts/debug/common/debugModel';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITree, ContextMenuEvent, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { InputBox, IInputValidationOptions } from 'vs/base/browser/ui/inputbox/inputBox';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { once } from 'vs/base/common/functional';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
import { ClickBehavior, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { onUnexpectedError } from 'vs/base/common/errors';
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
export const twistiePixels = 20;
const booleanRegex = /^true|false$/i;
const stringRegex = /^(['"]).*\1$/;
const $ = dom.$;
export interface IRenderValueOptions {
preserveWhitespace?: boolean;
showChanged?: boolean;
maxValueLength?: number;
showHover?: boolean;
colorize?: boolean;
}
export interface IVariableTemplateData {
expression: HTMLElement;
name: HTMLElement;
value: HTMLElement;
}
export function renderViewTree(container: HTMLElement): HTMLElement {
const treeContainer = document.createElement('div');
dom.addClass(treeContainer, 'debug-view-content');
container.appendChild(treeContainer);
return treeContainer;
}
function replaceWhitespace(value: string): string {
const map: { [x: string]: string } = { '\n': '\\n', '\r': '\\r', '\t': '\\t' };
return value.replace(/[\n\r\t]/g, char => map[char]);
}
export function renderExpressionValue(expressionOrValue: IExpression | string, container: HTMLElement, options: IRenderValueOptions): void {
let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value;
// remove stale classes
container.className = 'value';
// when resolving expressions we represent errors from the server as a variable with name === null.
if (value === null || ((expressionOrValue instanceof Expression || expressionOrValue instanceof Variable) && !expressionOrValue.available)) {
dom.addClass(container, 'unavailable');
if (value !== Expression.DEFAULT_VALUE) {
dom.addClass(container, 'error');
}
}
if (options.colorize && typeof expressionOrValue !== 'string') {
if (expressionOrValue.type === 'number' || expressionOrValue.type === 'boolean' || expressionOrValue.type === 'string') {
dom.addClass(container, expressionOrValue.type);
} else if (!isNaN(+value)) {
dom.addClass(container, 'number');
} else if (booleanRegex.test(value)) {
dom.addClass(container, 'boolean');
} else if (stringRegex.test(value)) {
dom.addClass(container, 'string');
}
}
if (options.showChanged && (<any>expressionOrValue).valueChanged && value !== Expression.DEFAULT_VALUE) {
// value changed color has priority over other colors.
container.className = 'value changed';
}
if (options.maxValueLength && value.length > options.maxValueLength) {
value = value.substr(0, options.maxValueLength) + '...';
}
if (value && !options.preserveWhitespace) {
container.textContent = replaceWhitespace(value);
} else {
container.textContent = value;
}
if (options.showHover) {
container.title = value;
}
}
export function renderVariable(tree: ITree, variable: Variable, data: IVariableTemplateData, showChanged: boolean): void {
if (variable.available) {
data.name.textContent = replaceWhitespace(variable.name);
data.name.title = variable.type ? variable.type : variable.name;
dom.toggleClass(data.name, 'virtual', !!variable.presentationHint && variable.presentationHint.kind === 'virtual');
}
if (variable.value) {
data.name.textContent += variable.name ? ':' : '';
renderExpressionValue(variable, data.value, {
showChanged,
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
preserveWhitespace: false,
showHover: true,
colorize: true
});
} else {
data.value.textContent = '';
data.value.title = '';
}
}
export interface IRenameBoxOptions {
initialValue: string;
ariaLabel: string;
placeholder?: string;
validationOptions?: IInputValidationOptions;
}
export function renderRenameBox(debugService: IDebugService, contextViewService: IContextViewService, themeService: IThemeService, tree: ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
let inputBox = new InputBox(inputBoxContainer, contextViewService, {
validationOptions: options.validationOptions,
placeholder: options.placeholder,
ariaLabel: options.ariaLabel
});
const styler = attachInputBoxStyler(inputBox, themeService);
tree.setHighlight();
inputBox.value = options.initialValue ? options.initialValue : '';
inputBox.focus();
inputBox.select();
let disposed = false;
const toDispose: IDisposable[] = [inputBox, styler];
const wrapUp = once((renamed: boolean) => {
if (!disposed) {
disposed = true;
if (element instanceof Expression && renamed && inputBox.value) {
debugService.renameWatchExpression(element.getId(), inputBox.value).done(null, onUnexpectedError);
} else if (element instanceof Expression && !element.name) {
debugService.removeWatchExpressions(element.getId());
} else if (element instanceof FunctionBreakpoint && inputBox.value) {
debugService.renameFunctionBreakpoint(element.getId(), renamed ? inputBox.value : element.name).done(null, onUnexpectedError);
} else if (element instanceof FunctionBreakpoint && !element.name) {
debugService.removeFunctionBreakpoints(element.getId()).done(null, onUnexpectedError);
} else if (element instanceof Variable) {
element.errorMessage = null;
if (renamed && element.value !== inputBox.value) {
element.setVariable(inputBox.value)
// if everything went fine we need to refresh ui elements since the variable update can change watch and variables view
.done(() => {
tree.refresh(element, false);
debugService.evaluateWatchExpressions();
}, onUnexpectedError);
}
}
tree.clearHighlight();
tree.DOMFocus();
tree.setFocus(element);
// need to remove the input box since this template will be reused.
container.removeChild(inputBoxContainer);
dispose(toDispose);
}
});
toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
const isEscape = e.equals(KeyCode.Escape);
const isEnter = e.equals(KeyCode.Enter);
if (isEscape || isEnter) {
e.preventDefault();
e.stopPropagation();
wrapUp(isEnter);
}
}));
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
wrapUp(true);
}));
}
export class BaseDebugController extends DefaultController {
private contributedContextMenu: IMenu;
constructor(
private actionProvider: IActionProvider,
menuId: MenuId,
@IDebugService protected debugService: IDebugService,
@IWorkbenchEditorService protected editorService: IWorkbenchEditorService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IMenuService menuService: IMenuService
) {
super({ clickBehavior: ClickBehavior.ON_MOUSE_UP, keyboardSupport: false });
this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
}
public onContextMenu(tree: ITree, element: IEnablement, event: ContextMenuEvent, focusElement = true): boolean {
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
return false;
}
event.preventDefault();
event.stopPropagation();
if (focusElement) {
tree.setFocus(element);
}
if (this.actionProvider.hasSecondaryActions(tree, element)) {
const anchor = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => {
fillInActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions);
return actions;
}),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
tree.DOMFocus();
}
},
getActionsContext: () => element
});
return true;
}
return false;
}
protected getContext(element: any): any {
return undefined;
}
}

View File

@@ -0,0 +1,518 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as resources from 'vs/base/common/resources';
import * as dom from 'vs/base/browser/dom';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IAction, Action } from 'vs/base/common/actions';
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint } from 'vs/workbench/parts/debug/common/debugModel';
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Constants } from 'vs/editor/common/core/uint';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { getPathLabel } from 'vs/base/common/labels';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { basename } from 'vs/base/common/paths';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TPromise } from 'vs/base/common/winjs.base';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { IEditorService, IEditor } from 'vs/platform/editor/common/editor';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
import { ViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
const $ = dom.$;
export class BreakpointsView extends ViewsViewletPanel {
private static readonly MAX_VISIBLE_FILES = 9;
private static readonly MEMENTO = 'breakopintsview.memento';
private settings: any;
private list: WorkbenchList<IEnablement>;
private needsRefresh: boolean;
constructor(
options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService,
@IEditorService private editorService: IEditorService,
@IContextViewService private contextViewService: IContextViewService,
@IContextKeyService private contextKeyService: IContextKeyService
) {
super(options, keybindingService, contextMenuService);
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
this.settings = options.viewletSettings;
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-breakpoints');
const delegate = new BreakpointsDelegate(this.debugService);
this.list = new WorkbenchList<IEnablement>(container, delegate, [
this.instantiationService.createInstance(BreakpointsRenderer),
new ExceptionBreakpointsRenderer(this.debugService),
new FunctionBreakpointsRenderer(this.debugService),
new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService)
], {
identityProvider: element => element.getId(),
multipleSelectionSupport: false
}, this.contextKeyService, this.listService, this.themeService);
CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.list.contextKeyService);
this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
const handleBreakpointFocus = (preserveFocuse: boolean, sideBySide: boolean, selectFunctionBreakpoint: boolean) => {
const focused = this.list.getFocusedElements();
const element = focused.length ? focused[0] : undefined;
if (element instanceof Breakpoint) {
openBreakpointSource(element, sideBySide, preserveFocuse, this.debugService, this.editorService).done(undefined, onUnexpectedError);
}
if (selectFunctionBreakpoint && element instanceof FunctionBreakpoint && element !== this.debugService.getViewModel().getSelectedFunctionBreakpoint()) {
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
this.onBreakpointsChange();
}
};
this.disposables.push(this.list.onKeyUp(e => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
handleBreakpointFocus(false, event && (event.ctrlKey || event.metaKey), false);
}
}));
this.disposables.push(this.list.onMouseDblClick(e => {
handleBreakpointFocus(false, false, true);
}));
this.disposables.push(this.list.onMouseClick(e => {
handleBreakpointFocus(true, false, false);
}));
this.list.splice(0, this.list.length, this.elements);
}
protected layoutBody(size: number): void {
if (this.list) {
this.list.layout(size);
}
}
private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
const actions: IAction[] = [];
const element = e.element;
if (element instanceof Breakpoint) {
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editConditionalBreakpoint', "Edit Breakpoint..."), undefined, true, () => {
return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
const codeEditor = editor.getControl();
if (isCodeEditor(codeEditor)) {
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
}
});
}));
actions.push(new Separator());
}
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL, this.debugService, this.keybindingService));
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new Separator());
actions.push(new EnableAllBreakpointsAction(EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
actions.push(new DisableAllBreakpointsAction(DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
}
actions.push(new Separator());
actions.push(new ReapplyBreakpointsAction(ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL, this.debugService, this.keybindingService));
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => TPromise.as(actions),
getActionsContext: () => element
});
}
public getActions(): IAction[] {
return [
new AddFunctionBreakpointAction(AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL, this.debugService, this.keybindingService),
new ToggleBreakpointsActivatedAction(ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL, this.debugService, this.keybindingService),
new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)
];
}
public setExpanded(expanded: boolean): void {
super.setExpanded(expanded);
if (expanded && this.needsRefresh) {
this.onBreakpointsChange();
}
}
public setVisible(visible: boolean): TPromise<void> {
return super.setVisible(visible).then(() => {
if (visible && this.needsRefresh) {
this.onBreakpointsChange();
}
});
}
private onBreakpointsChange(): void {
if (this.isExpanded() && this.isVisible()) {
this.minimumBodySize = this.getExpandedBodySize();
if (this.maximumBodySize < Number.POSITIVE_INFINITY) {
this.maximumBodySize = this.minimumBodySize;
}
if (this.list) {
this.list.splice(0, this.list.length, this.elements);
this.needsRefresh = false;
}
} else {
this.needsRefresh = true;
}
}
private get elements(): IEnablement[] {
const model = this.debugService.getModel();
const elements = (<IEnablement[]>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints());
return elements;
}
private getExpandedBodySize(): number {
const model = this.debugService.getModel();
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length;
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
}
public shutdown(): void {
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
}
}
class BreakpointsDelegate implements IDelegate<IEnablement> {
constructor(private debugService: IDebugService) {
// noop
}
getHeight(element: IEnablement): number {
return 22;
}
getTemplateId(element: IEnablement): string {
if (element instanceof Breakpoint) {
return BreakpointsRenderer.ID;
}
if (element instanceof FunctionBreakpoint) {
const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
if (!element.name || (selected && selected.getId() === element.getId())) {
return FunctionBreakpointInputRenderer.ID;
}
return FunctionBreakpointsRenderer.ID;
}
if (element instanceof ExceptionBreakpoint) {
return ExceptionBreakpointsRenderer.ID;
}
return undefined;
}
}
interface IBaseBreakpointTemplateData {
breakpoint: HTMLElement;
name: HTMLElement;
checkbox: HTMLInputElement;
context: IEnablement;
toDispose: IDisposable[];
}
interface IBreakpointTemplateData extends IBaseBreakpointTemplateData {
lineNumber: HTMLElement;
filePath: HTMLElement;
}
interface IInputTemplateData {
inputBox: InputBox;
breakpoint: IFunctionBreakpoint;
reactedOnEvent: boolean;
toDispose: IDisposable[];
}
class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateData> {
constructor(
@IDebugService private debugService: IDebugService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
// noop
}
static ID = 'breakpoints';
get templateId() {
return BreakpointsRenderer.ID;
}
renderTemplate(container: HTMLElement): IBreakpointTemplateData {
const data: IBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));
data.checkbox = <HTMLInputElement>$('input');
data.checkbox.type = 'checkbox';
data.toDispose = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
}));
dom.append(data.breakpoint, data.checkbox);
data.name = dom.append(data.breakpoint, $('span.name'));
data.filePath = dom.append(data.breakpoint, $('span.file-path'));
const lineNumberContainer = dom.append(data.breakpoint, $('.line-number-container'));
data.lineNumber = dom.append(lineNumberContainer, $('span.line-number'));
return data;
}
renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
data.context = breakpoint;
dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated());
data.name.textContent = basename(getPathLabel(breakpoint.uri, this.contextService));
data.lineNumber.textContent = breakpoint.lineNumber.toString();
if (breakpoint.column) {
data.lineNumber.textContent += `:${breakpoint.column}`;
}
data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.contextService, this.environmentService);
data.checkbox.checked = breakpoint.enabled;
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
if (debugActive && !breakpoint.verified) {
dom.addClass(data.breakpoint, 'disabled');
if (breakpoint.message) {
data.breakpoint.title = breakpoint.message;
}
} else if (breakpoint.condition || breakpoint.hitCondition) {
data.breakpoint.title = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
}
}
disposeTemplate(templateData: IBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}
class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IBaseBreakpointTemplateData> {
constructor(
private debugService: IDebugService
) {
// noop
}
static ID = 'exceptionbreakpoints';
get templateId() {
return ExceptionBreakpointsRenderer.ID;
}
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
const data: IBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));
data.checkbox = <HTMLInputElement>$('input');
data.checkbox.type = 'checkbox';
data.toDispose = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
}));
dom.append(data.breakpoint, data.checkbox);
data.name = dom.append(data.breakpoint, $('span.name'));
dom.addClass(data.breakpoint, 'exception');
return data;
}
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
data.context = exceptionBreakpoint;
data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
data.breakpoint.title = data.name.textContent;
data.checkbox.checked = exceptionBreakpoint.enabled;
}
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}
class FunctionBreakpointsRenderer implements IRenderer<IFunctionBreakpoint, IBaseBreakpointTemplateData> {
constructor(
private debugService: IDebugService
) {
// noop
}
static ID = 'functionbreakpoints';
get templateId() {
return FunctionBreakpointsRenderer.ID;
}
renderTemplate(container: HTMLElement): IBaseBreakpointTemplateData {
const data: IBreakpointTemplateData = Object.create(null);
data.breakpoint = dom.append(container, $('.breakpoint'));
data.checkbox = <HTMLInputElement>$('input');
data.checkbox.type = 'checkbox';
data.toDispose = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
}));
dom.append(data.breakpoint, data.checkbox);
data.name = dom.append(data.breakpoint, $('span.name'));
return data;
}
renderElement(functionBreakpoint: IFunctionBreakpoint, index: number, data: IBaseBreakpointTemplateData): void {
data.context = functionBreakpoint;
data.name.textContent = functionBreakpoint.name;
data.checkbox.checked = functionBreakpoint.enabled;
data.breakpoint.title = functionBreakpoint.name;
// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
const process = this.debugService.getViewModel().focusedProcess;
dom.toggleClass(data.breakpoint, 'disalbed', (process && !process.session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
if (process && !process.session.capabilities.supportsFunctionBreakpoints) {
data.breakpoint.title = nls.localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type");
}
}
disposeTemplate(templateData: IBaseBreakpointTemplateData): void {
dispose(templateData.toDispose);
}
}
class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint, IInputTemplateData> {
constructor(
private debugService: IDebugService,
private contextViewService: IContextViewService,
private themeService: IThemeService
) {
// noop
}
static ID = 'functionbreakpointinput';
get templateId() {
return FunctionBreakpointInputRenderer.ID;
}
renderTemplate(container: HTMLElement): IInputTemplateData {
const template: IInputTemplateData = Object.create(null);
const inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"),
ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint")
});
const styler = attachInputBoxStyler(inputBox, this.themeService);
const toDispose: IDisposable[] = [inputBox, styler];
const wrapUp = (renamed: boolean) => {
if (!template.reactedOnEvent) {
template.reactedOnEvent = true;
this.debugService.getViewModel().setSelectedFunctionBreakpoint(undefined);
if (inputBox.value && (renamed || template.breakpoint.name)) {
this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name).done(null, onUnexpectedError);
} else {
this.debugService.removeFunctionBreakpoints(template.breakpoint.getId()).done(null, onUnexpectedError);
}
}
};
toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
const isEscape = e.equals(KeyCode.Escape);
const isEnter = e.equals(KeyCode.Enter);
if (isEscape || isEnter) {
e.preventDefault();
e.stopPropagation();
wrapUp(isEnter);
}
}));
toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
wrapUp(true);
}));
template.inputBox = inputBox;
template.toDispose = toDispose;
return template;
}
renderElement(functionBreakpoint: IFunctionBreakpoint, index: number, data: IInputTemplateData): void {
data.breakpoint = functionBreakpoint;
data.reactedOnEvent = false;
data.inputBox.value = functionBreakpoint.name || '';
data.inputBox.focus();
data.inputBox.select();
}
disposeTemplate(templateData: IInputTemplateData): void {
dispose(templateData.toDispose);
}
}
function openBreakpointSource(breakpoint: Breakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): TPromise<IEditor> {
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
return TPromise.as(null);
}
const selection = breakpoint.endLineNumber ? {
startLineNumber: breakpoint.lineNumber,
endLineNumber: breakpoint.endLineNumber,
startColumn: breakpoint.column,
endColumn: breakpoint.endColumn
} : {
startLineNumber: breakpoint.lineNumber,
startColumn: breakpoint.column || 1,
endLineNumber: breakpoint.lineNumber,
endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
};
return editorService.openEditor({
resource: breakpoint.uri,
options: {
preserveFocus,
selection,
revealIfVisible: true,
revealInCenterIfOutsideViewport: true,
pinned: !preserveFocus
}
}, sideBySide);
}

View File

@@ -0,0 +1,543 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, State, IStackFrame, IProcess, IThread } from 'vs/workbench/parts/debug/common/debug';
import { Thread, StackFrame, ThreadAndProcessIds, Process, Model } from 'vs/workbench/parts/debug/common/debugModel';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
import { ITree, IActionProvider, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAction, IActionItem } from 'vs/base/common/actions';
import { RestartAction, StopAction, ContinueAction, StepOverAction, StepIntoAction, StepOutAction, PauseAction, RestartFrameAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { CopyStackTraceAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const $ = dom.$;
export class CallStackView extends TreeViewsViewletPanel {
private static readonly MEMENTO = 'callstackview.memento';
private pauseMessage: HTMLSpanElement;
private pauseMessageLabel: HTMLSpanElement;
private onCallStackChangeScheduler: RunOnceScheduler;
private settings: any;
private needsRefresh: boolean;
constructor(
private options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService,
@IListService private listService: IListService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
this.onCallStackChangeScheduler = new RunOnceScheduler(() => {
let newTreeInput: any = this.debugService.getModel();
const processes = this.debugService.getModel().getProcesses();
if (!this.debugService.getViewModel().isMultiProcessView() && processes.length) {
const threads = processes[0].getAllThreads();
// Only show the threads in the call stack if there is more than 1 thread.
newTreeInput = threads.length === 1 ? threads[0] : processes[0];
}
// Only show the global pause message if we do not display threads.
// Otherwise there will be a pause message per thread and there is no need for a global one.
if (newTreeInput instanceof Thread && newTreeInput.stoppedDetails) {
this.pauseMessageLabel.textContent = newTreeInput.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", newTreeInput.stoppedDetails.reason);
if (newTreeInput.stoppedDetails.text) {
this.pauseMessageLabel.title = newTreeInput.stoppedDetails.text;
}
dom.toggleClass(this.pauseMessageLabel, 'exception', newTreeInput.stoppedDetails.reason === 'exception');
this.pauseMessage.hidden = false;
} else {
this.pauseMessage.hidden = true;
}
this.needsRefresh = false;
(this.tree.getInput() === newTreeInput ? this.tree.refresh() : this.tree.setInput(newTreeInput))
.done(() => this.updateTreeSelection(), errors.onUnexpectedError);
}, 50);
}
protected renderHeaderTitle(container: HTMLElement): void {
const title = dom.append(container, $('.title.debug-call-stack-title'));
const name = dom.append(title, $('span'));
name.textContent = this.options.name;
this.pauseMessage = dom.append(title, $('span.pause-message'));
this.pauseMessage.hidden = true;
this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label'));
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-call-stack');
this.treeContainer = renderViewTree(container);
const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService);
const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext);
this.tree = new WorkbenchTree(this.treeContainer, {
dataSource: new CallStackDataSource(),
renderer: this.instantiationService.createInstance(CallStackRenderer),
accessibilityProvider: this.instantiationService.createInstance(CallstackAccessibilityProvider),
controller
}, {
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"),
twistiePixels,
keyboardSupport: false
}, this.contextKeyService, this.listService, this.themeService);
this.disposables.push(this.tree.onDidChangeSelection(event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
const element = this.tree.getFocus();
if (element instanceof ThreadAndProcessIds) {
controller.showMoreStackFrames(this.tree, element);
} else if (element instanceof StackFrame) {
controller.focusStackFrame(element, event, false);
}
}
}));
this.disposables.push(this.debugService.getModel().onDidChangeCallStack(() => {
if (!this.isVisible()) {
this.needsRefresh = true;
return;
}
if (!this.onCallStackChangeScheduler.isScheduled()) {
this.onCallStackChangeScheduler.schedule();
}
}));
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
this.updateTreeSelection().done(undefined, errors.onUnexpectedError)));
// Schedule the update of the call stack tree if the viewlet is opened after a session started #14684
if (this.debugService.state === State.Stopped) {
this.onCallStackChangeScheduler.schedule();
}
}
private updateTreeSelection(): TPromise<void> {
if (!this.tree.getInput()) {
// Tree not initialized yet
return TPromise.as(null);
}
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
const thread = this.debugService.getViewModel().focusedThread;
const process = this.debugService.getViewModel().focusedProcess;
if (!thread) {
if (!process) {
this.tree.clearSelection();
return TPromise.as(null);
}
this.tree.setSelection([process]);
return this.tree.reveal(process);
}
return this.tree.expandAll([thread.process, thread]).then(() => {
if (!stackFrame) {
return TPromise.as(null);
}
this.tree.setSelection([stackFrame]);
return this.tree.reveal(stackFrame);
});
}
public setVisible(visible: boolean): TPromise<void> {
return super.setVisible(visible).then(() => {
if (visible && this.needsRefresh) {
this.onCallStackChangeScheduler.schedule();
}
});
}
public shutdown(): void {
this.settings[CallStackView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
class CallStackController extends BaseDebugController {
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
if (element instanceof ThreadAndProcessIds) {
return this.showMoreStackFrames(tree, element);
}
if (element instanceof StackFrame) {
super.onLeftClick(tree, element, event);
this.focusStackFrame(element, event, event.detail !== 2);
return true;
}
return super.onLeftClick(tree, element, event);
}
protected getContext(element: any): any {
if (element instanceof StackFrame) {
if (element.source.inMemory) {
return element.source.raw.path || element.source.reference;
}
return element.source.uri.toString();
}
if (element instanceof Thread) {
return element.threadId;
}
}
// user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree.
public showMoreStackFrames(tree: ITree, threadAndProcessIds: ThreadAndProcessIds): boolean {
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === threadAndProcessIds.processId).pop();
const thread = process && process.getThread(threadAndProcessIds.threadId);
if (thread) {
(<Thread>thread).fetchCallStack()
.done(() => tree.refresh(), errors.onUnexpectedError);
}
return true;
}
public focusStackFrame(stackFrame: IStackFrame, event: any, preserveFocus: boolean): void {
this.debugService.focusStackFrameAndEvaluate(stackFrame, undefined, true).then(() => {
const sideBySide = (event && (event.ctrlKey || event.metaKey));
return stackFrame.openInEditor(this.editorService, preserveFocus, sideBySide);
}, errors.onUnexpectedError);
}
}
class CallStackActionProvider implements IActionProvider {
constructor(private debugService: IDebugService, private keybindingService: IKeybindingService) {
// noop
}
public hasActions(tree: ITree, element: any): boolean {
return false;
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
return TPromise.as([]);
}
public hasSecondaryActions(tree: ITree, element: any): boolean {
return element !== tree.getInput();
}
public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
const actions: IAction[] = [];
if (element instanceof Process) {
actions.push(new RestartAction(RestartAction.ID, RestartAction.LABEL, this.debugService, this.keybindingService));
actions.push(new StopAction(StopAction.ID, StopAction.LABEL, this.debugService, this.keybindingService));
} else if (element instanceof Thread) {
const thread = <Thread>element;
if (thread.stopped) {
actions.push(new ContinueAction(ContinueAction.ID, ContinueAction.LABEL, this.debugService, this.keybindingService));
actions.push(new StepOverAction(StepOverAction.ID, StepOverAction.LABEL, this.debugService, this.keybindingService));
actions.push(new StepIntoAction(StepIntoAction.ID, StepIntoAction.LABEL, this.debugService, this.keybindingService));
actions.push(new StepOutAction(StepOutAction.ID, StepOutAction.LABEL, this.debugService, this.keybindingService));
} else {
actions.push(new PauseAction(PauseAction.ID, PauseAction.LABEL, this.debugService, this.keybindingService));
}
} else if (element instanceof StackFrame) {
if (element.thread.process.session.capabilities.supportsRestartFrame) {
actions.push(new RestartFrameAction(RestartFrameAction.ID, RestartFrameAction.LABEL, this.debugService, this.keybindingService));
}
actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL));
}
return TPromise.as(actions);
}
public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
return null;
}
}
class CallStackDataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
if (typeof element === 'string') {
return element;
}
return element.getId();
}
public hasChildren(tree: ITree, element: any): boolean {
return element instanceof Model || element instanceof Process || (element instanceof Thread && (<Thread>element).stopped);
}
public getChildren(tree: ITree, element: any): TPromise<any> {
if (element instanceof Thread) {
return this.getThreadChildren(element);
}
if (element instanceof Model) {
return TPromise.as(element.getProcesses());
}
const process = <IProcess>element;
return TPromise.as(process.getAllThreads());
}
private getThreadChildren(thread: Thread): TPromise<any> {
let callStack: any[] = thread.getCallStack();
let callStackPromise: TPromise<any> = TPromise.as(null);
if (!callStack || !callStack.length) {
callStackPromise = thread.fetchCallStack().then(() => callStack = thread.getCallStack());
}
return callStackPromise.then(() => {
if (callStack.length === 1 && thread.process.session.capabilities.supportsDelayedStackTraceLoading) {
// To reduce flashing of the call stack view simply append the stale call stack
// once we have the correct data the tree will refresh and we will no longer display it.
callStack = callStack.concat(thread.getStaleCallStack().slice(1));
}
if (thread.stoppedDetails && thread.stoppedDetails.framesErrorMessage) {
callStack = callStack.concat([thread.stoppedDetails.framesErrorMessage]);
}
if (thread.stoppedDetails && thread.stoppedDetails.totalFrames > callStack.length && callStack.length > 1) {
callStack = callStack.concat([new ThreadAndProcessIds(thread.process.getId(), thread.threadId)]);
}
return callStack;
});
}
public getParent(tree: ITree, element: any): TPromise<any> {
return TPromise.as(null);
}
}
interface IThreadTemplateData {
thread: HTMLElement;
name: HTMLElement;
state: HTMLElement;
stateLabel: HTMLSpanElement;
}
interface IProcessTemplateData {
process: HTMLElement;
name: HTMLElement;
state: HTMLElement;
stateLabel: HTMLSpanElement;
}
interface IErrorTemplateData {
label: HTMLElement;
}
interface ILoadMoreTemplateData {
label: HTMLElement;
}
interface IStackFrameTemplateData {
stackFrame: HTMLElement;
label: HTMLElement;
file: HTMLElement;
fileName: HTMLElement;
lineNumber: HTMLElement;
}
class CallStackRenderer implements IRenderer {
private static readonly THREAD_TEMPLATE_ID = 'thread';
private static readonly STACK_FRAME_TEMPLATE_ID = 'stackFrame';
private static readonly ERROR_TEMPLATE_ID = 'error';
private static readonly LOAD_MORE_TEMPLATE_ID = 'loadMore';
private static readonly PROCESS_TEMPLATE_ID = 'process';
constructor(
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
// noop
}
public getHeight(tree: ITree, element: any): number {
return 22;
}
public getTemplateId(tree: ITree, element: any): string {
if (element instanceof Process) {
return CallStackRenderer.PROCESS_TEMPLATE_ID;
}
if (element instanceof Thread) {
return CallStackRenderer.THREAD_TEMPLATE_ID;
}
if (element instanceof StackFrame) {
return CallStackRenderer.STACK_FRAME_TEMPLATE_ID;
}
if (typeof element === 'string') {
return CallStackRenderer.ERROR_TEMPLATE_ID;
}
return CallStackRenderer.LOAD_MORE_TEMPLATE_ID;
}
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) {
let data: IProcessTemplateData = Object.create(null);
data.process = dom.append(container, $('.process'));
data.name = dom.append(data.process, $('.name'));
data.state = dom.append(data.process, $('.state'));
data.stateLabel = dom.append(data.state, $('span.label'));
return data;
}
if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
let data: ILoadMoreTemplateData = Object.create(null);
data.label = dom.append(container, $('.load-more'));
return data;
}
if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
let data: ILoadMoreTemplateData = Object.create(null);
data.label = dom.append(container, $('.error'));
return data;
}
if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
let data: IThreadTemplateData = Object.create(null);
data.thread = dom.append(container, $('.thread'));
data.name = dom.append(data.thread, $('.name'));
data.state = dom.append(data.thread, $('.state'));
data.stateLabel = dom.append(data.state, $('span.label'));
return data;
}
let data: IStackFrameTemplateData = Object.create(null);
data.stackFrame = dom.append(container, $('.stack-frame'));
data.label = dom.append(data.stackFrame, $('span.label.expression'));
data.file = dom.append(data.stackFrame, $('.file'));
data.fileName = dom.append(data.file, $('span.file-name'));
const wrapper = dom.append(data.file, $('span.line-number-wrapper'));
data.lineNumber = dom.append(wrapper, $('span.line-number'));
return data;
}
public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
if (templateId === CallStackRenderer.PROCESS_TEMPLATE_ID) {
this.renderProcess(element, templateData);
} else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) {
this.renderThread(element, templateData);
} else if (templateId === CallStackRenderer.STACK_FRAME_TEMPLATE_ID) {
this.renderStackFrame(element, templateData);
} else if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) {
this.renderError(element, templateData);
} else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) {
this.renderLoadMore(element, templateData);
}
}
private renderProcess(process: IProcess, data: IProcessTemplateData): void {
data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process");
data.name.textContent = process.getName(this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE);
const stoppedThread = process.getAllThreads().filter(t => t.stopped).pop();
data.stateLabel.textContent = stoppedThread ? nls.localize('paused', "Paused")
: nls.localize({ key: 'running', comment: ['indicates state'] }, "Running");
}
private renderThread(thread: IThread, data: IThreadTemplateData): void {
data.thread.title = nls.localize('thread', "Thread");
data.name.textContent = thread.name;
if (thread.stopped) {
data.stateLabel.textContent = thread.stoppedDetails.description ||
thread.stoppedDetails.reason ? nls.localize({ key: 'pausedOn', comment: ['indicates reason for program being paused'] }, "Paused on {0}", thread.stoppedDetails.reason) : nls.localize('paused', "Paused");
} else {
data.stateLabel.textContent = nls.localize({ key: 'running', comment: ['indicates state'] }, "Running");
}
}
private renderError(element: string, data: IErrorTemplateData) {
data.label.textContent = element;
data.label.title = element;
}
private renderLoadMore(element: any, data: ILoadMoreTemplateData): void {
data.label.textContent = nls.localize('loadMoreStackFrames', "Load More Stack Frames");
}
private renderStackFrame(stackFrame: IStackFrame, data: IStackFrameTemplateData): void {
dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize');
dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label');
dom.toggleClass(data.stackFrame, 'subtle', stackFrame.presentationHint === 'subtle');
data.file.title = stackFrame.source.raw.path || stackFrame.source.name;
if (stackFrame.source.raw.origin) {
data.file.title += `\n${stackFrame.source.raw.origin}`;
}
data.label.textContent = stackFrame.name;
data.label.title = stackFrame.name;
data.fileName.textContent = getSourceName(stackFrame.source, this.contextService, this.environmentService);
if (stackFrame.range.startLineNumber !== undefined) {
data.lineNumber.textContent = `${stackFrame.range.startLineNumber}`;
if (stackFrame.range.startColumn) {
data.lineNumber.textContent += `:${stackFrame.range.startColumn}`;
}
dom.removeClass(data.lineNumber, 'unavailable');
} else {
dom.addClass(data.lineNumber, 'unavailable');
}
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
// noop
}
}
class CallstackAccessibilityProvider implements IAccessibilityProvider {
constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
// noop
}
public getAriaLabel(tree: ITree, element: any): string {
if (element instanceof Thread) {
return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (<Thread>element).name);
}
if (element instanceof StackFrame) {
return nls.localize('stackFrameAriaLabel', "Stack Frame {0} line {1} {2}, callstack, debug", (<StackFrame>element).name, (<StackFrame>element).range.startLineNumber, getSourceName((<StackFrame>element).source, this.contextService));
}
return null;
}
}
function getSourceName(source: Source, contextService: IWorkspaceContextService, environmentService?: IEnvironmentService): string {
if (source.name) {
return source.name;
}
return basenameOrAuthority(source.uri);
}

View File

@@ -16,7 +16,10 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensio
import { ToggleViewletAction, Extensions as ViewletExtensions, ViewletRegistry, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { TogglePanelAction, Extensions as PanelExtensions, PanelRegistry, PanelDescriptor } from 'vs/workbench/browser/panel';
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { VariablesView, WatchExpressionsView, CallStackView, BreakpointsView } from 'vs/workbench/parts/debug/electron-browser/debugViews';
import { VariablesView } from 'vs/workbench/parts/debug/electron-browser/variablesView';
import { BreakpointsView } from 'vs/workbench/parts/debug/electron-browser/breakpointsView';
import { WatchExpressionsView } from 'vs/workbench/parts/debug/electron-browser/watchExpressionsView';
import { CallStackView } from 'vs/workbench/parts/debug/electron-browser/callStackView';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import {
IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
@@ -46,10 +49,11 @@ import { DebugViewlet, FocusVariablesViewAction, FocusBreakpointsViewAction, Foc
import { Repl } from 'vs/workbench/parts/debug/electron-browser/repl';
import { DebugQuickOpenHandler } from 'vs/workbench/parts/debug/browser/debugQuickOpen';
import { DebugStatus } from 'vs/workbench/parts/debug/browser/debugStatus';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
class OpenDebugViewletAction extends ToggleViewletAction {
public static ID = VIEWLET_ID;
public static LABEL = nls.localize('toggleDebugViewlet', "Show Debug");
public static readonly ID = VIEWLET_ID;
public static readonly LABEL = nls.localize('toggleDebugViewlet', "Show Debug");
constructor(
id: string,
@@ -62,8 +66,8 @@ class OpenDebugViewletAction extends ToggleViewletAction {
}
class OpenDebugPanelAction extends TogglePanelAction {
public static ID = 'workbench.debug.action.toggleRepl';
public static LABEL = nls.localize('toggleDebugPanel', "Debug Console");
public static readonly ID = 'workbench.debug.action.toggleRepl';
public static readonly LABEL = nls.localize('toggleDebugPanel', "Debug Console");
constructor(
id: string,
@@ -113,10 +117,10 @@ const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryEx
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View"));
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugActionsWidget);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager, LifecyclePhase.Running);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugActionsWidget, LifecyclePhase.Running);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually);
const debugCategory = nls.localize('debugCategory', "Debug");
registry.registerWorkbenchAction(new SyncActionDescriptor(
@@ -187,7 +191,18 @@ configurationRegistry.registerConfiguration({
description: nls.localize({ comment: ['This is the description for a setting'], key: 'hideActionBar' }, "Controls if the floating debug action bar should be hidden"),
default: false
},
'debug.showInStatusBar': {
enum: ['never', 'always', 'onFirstSessionStart'],
enumDescriptions: [nls.localize('never', "Never show debug in status bar"), nls.localize('always', "Always show debug in status bar"), nls.localize('onFirstSessionStart', "Show debug in status bar only after debug was started for the first time")],
description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInStatusBar' }, "Controls when the debug status bar should be visible"),
default: 'onFirstSessionStart'
},
'debug.internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA,
'debug.openDebug': {
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
default: 'openOnFirstSessionStart',
description: nls.localize('openDebug', "Controls whether debug viewlet should be open on debugging session start.")
},
'launch': {
type: 'object',
description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces"),

View File

@@ -9,7 +9,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
import severity from 'vs/base/common/severity';
import { List } from 'vs/base/browser/ui/list/listWidget';
import * as errors from 'vs/base/common/errors';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService } from 'vs/platform/list/browser/listService';
@@ -19,13 +18,14 @@ import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel';
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export function registerCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.logToDebugConsole',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
handler(accessor: ServicesAccessor, value: string) {
handler: (accessor: ServicesAccessor, value: string) => {
if (typeof value === 'string') {
const debugService = accessor.get(IDebugService);
// Use warning as severity to get the orange color for messages coming from the debug extension
@@ -44,7 +44,7 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.getFocused();
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
@@ -64,7 +64,7 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.getFocused();
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
@@ -85,7 +85,7 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.getFocused();
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
@@ -106,7 +106,7 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.getFocused();
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
@@ -127,7 +127,7 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.getFocused();
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
@@ -172,7 +172,7 @@ export function registerCommands(): void {
return launch.openConfigFile(false).done(editor => {
if (editor) {
const codeEditor = <ICommonCodeEditor>editor.getControl();
const codeEditor = <ICodeEditor>editor.getControl();
if (codeEditor) {
return codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
}

View File

@@ -14,7 +14,7 @@ import * as objects from 'vs/base/common/objects';
import uri from 'vs/base/common/uri';
import * as paths from 'vs/base/common/paths';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { IModel, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { IModel } from 'vs/editor/common/editorCommon';
import { IEditor } from 'vs/platform/editor/common/editor';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
@@ -24,7 +24,6 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -33,6 +32,7 @@ import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
// debuggers extension point
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawAdapter[]>('debuggers', [], {
@@ -217,16 +217,14 @@ export class ConfigurationManager implements IConfigurationManager {
constructor(
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IFileService private fileService: IFileService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IConfigurationService private configurationService: IConfigurationService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
@IInstantiationService private instantiationService: IInstantiationService,
@ICommandService private commandService: ICommandService,
@IStorageService private storageService: IStorageService,
@ILifecycleService lifecycleService: ILifecycleService
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService private extensionService: IExtensionService
) {
this.providers = [];
this.adapters = [];
@@ -258,8 +256,11 @@ export class ConfigurationManager implements IConfigurationManager {
}
public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): TPromise<IConfig> {
// pipe the config through the promises sequentially
return this.providers.filter(p => p.type === type && p.resolveDebugConfiguration).reduce((promise, provider) => {
// pipe the config through the promises sequentially. append at the end the '*' types
const providers = this.providers.filter(p => p.type === type && p.resolveDebugConfiguration)
.concat(this.providers.filter(p => p.type === '*' && p.resolveDebugConfiguration));
return providers.reduce((promise, provider) => {
return promise.then(config => {
if (config) {
return provider.resolveDebugConfiguration(folderUri, config);
@@ -292,7 +293,7 @@ export class ConfigurationManager implements IConfigurationManager {
if (duplicate) {
duplicate.merge(rawAdapter, extension.description);
} else {
this.adapters.push(this.instantiationService.createInstance(Adapter, rawAdapter, extension.description));
this.adapters.push(new Adapter(rawAdapter, extension.description, this.configurationService, this.commandService));
}
});
});
@@ -379,11 +380,11 @@ export class ConfigurationManager implements IConfigurationManager {
public canSetBreakpointsIn(model: IModel): boolean {
const modeId = model ? model.getLanguageIdentifier().language : null;
if (!modeId || modeId === 'json') {
if (!modeId || modeId === 'jsonc') {
// do not allow breakpoints in our settings files
return false;
}
if (this.configurationService.getConfiguration<IDebugConfiguration>('debug').allowBreakpointsEverywhere) {
if (this.configurationService.getValue<IDebugConfiguration>('debug').allowBreakpointsEverywhere) {
return true;
}
@@ -395,34 +396,36 @@ export class ConfigurationManager implements IConfigurationManager {
}
public guessAdapter(type?: string): TPromise<Adapter> {
if (type) {
const adapter = this.getAdapter(type);
return TPromise.as(adapter);
}
return this.extensionService.activateByEvent('onDebugInitialConfigurations').then(() => this.extensionService.activateByEvent('onDebug').then(() => {
if (type) {
const adapter = this.getAdapter(type);
return TPromise.as(adapter);
}
const editor = this.editorService.getActiveEditor();
if (editor) {
const codeEditor = editor.getControl();
if (isCommonCodeEditor(codeEditor)) {
const model = codeEditor.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
const adapters = this.adapters.filter(a => a.languages && a.languages.indexOf(language) >= 0);
if (adapters.length === 1) {
return TPromise.as(adapters[0]);
const editor = this.editorService.getActiveEditor();
if (editor) {
const codeEditor = editor.getControl();
if (isCodeEditor(codeEditor)) {
const model = codeEditor.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
const adapters = this.adapters.filter(a => a.languages && a.languages.indexOf(language) >= 0);
if (adapters.length === 1) {
return TPromise.as(adapters[0]);
}
}
}
}
return this.quickOpenService.pick([...this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider), { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
.then(picked => {
if (picked instanceof Adapter) {
return picked;
}
if (picked) {
this.commandService.executeCommand('debug.installAdditionalDebuggers');
}
return undefined;
});
return this.quickOpenService.pick([...this.adapters.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider), { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
.then(picked => {
if (picked instanceof Adapter) {
return picked;
}
if (picked) {
this.commandService.executeCommand('debug.installAdditionalDebuggers');
}
return undefined;
});
}));
}
private store(): void {
@@ -445,14 +448,13 @@ class Launch implements ILaunch {
@IFileService private fileService: IFileService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IConfigurationService private configurationService: IConfigurationService,
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
@IExtensionService private extensionService: IExtensionService
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService
) {
// noop
}
public getCompound(name: string): ICompound {
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
if (!config || !config.compounds) {
return null;
}
@@ -461,7 +463,7 @@ class Launch implements ILaunch {
}
public getConfigurationNames(): string[] {
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
const config = this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri });
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
return [];
} else {
@@ -478,7 +480,7 @@ class Launch implements ILaunch {
}
public getConfiguration(name: string): IConfig {
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
const config = objects.deepClone(this.configurationService.getValue<IGlobalConfig>('launch', { resource: this.workspace.uri }));
if (!config || !config.configurations) {
return null;
}
@@ -487,7 +489,7 @@ class Launch implements ILaunch {
}
public resolveConfiguration(config: IConfig): TPromise<IConfig> {
const result = objects.clone(config) as IConfig;
const result = objects.deepClone(config) as IConfig;
// Set operating system specific properties #1873
const setOSProperties = (flag: boolean, osConfig: IEnvConfig) => {
if (flag && osConfig) {
@@ -514,59 +516,57 @@ class Launch implements ILaunch {
}
public openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
return this.extensionService.activateByEvent('onDebug').then(() => {
const resource = this.uri;
let configFileCreated = false;
const resource = this.uri;
let configFileCreated = false;
return this.fileService.resolveContent(resource).then(content => content, err => {
return this.fileService.resolveContent(resource).then(content => content, err => {
// launch.json not found: create one by collecting launch configs from debugConfigProviders
// launch.json not found: create one by collecting launch configs from debugConfigProviders
return this.configurationManager.guessAdapter(type).then(adapter => {
if (adapter) {
return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type).then(initialConfigs => {
return adapter.getInitialConfigurationContent(initialConfigs);
});
} else {
return undefined;
}
}).then(content => {
if (!content) {
return undefined;
}
configFileCreated = true;
return this.fileService.updateContent(resource, content).then(() => {
// convert string into IContent; see #32135
return { value: content };
return this.configurationManager.guessAdapter(type).then(adapter => {
if (adapter) {
return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type).then(initialConfigs => {
return adapter.getInitialConfigurationContent(initialConfigs);
});
});
} else {
return undefined;
}
}).then(content => {
if (!content) {
return undefined;
}
const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`);
let startLineNumber = 1;
for (let i = 0; i < index; i++) {
if (content.value.charAt(i) === '\n') {
startLineNumber++;
}
}
const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined;
return this.editorService.openEditor({
resource: resource,
options: {
forceOpen: true,
selection,
pinned: configFileCreated, // pin only if config file is created #8727
revealIfVisible: true
},
}, sideBySide);
}, (error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error));
configFileCreated = true;
return this.fileService.updateContent(resource, content).then(() => {
// convert string into IContent; see #32135
return { value: content };
});
});
}).then(content => {
if (!content) {
return undefined;
}
const index = content.value.indexOf(`"${this.configurationManager.selectedName}"`);
let startLineNumber = 1;
for (let i = 0; i < index; i++) {
if (content.value.charAt(i) === '\n') {
startLineNumber++;
}
}
const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined;
return this.editorService.openEditor({
resource: resource,
options: {
forceOpen: true,
selection,
pinned: configFileCreated, // pin only if config file is created #8727
revealIfVisible: true
},
}, sideBySide);
}, (error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error));
});
}
}

View File

@@ -18,9 +18,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { StandardTokenType } from 'vs/editor/common/modes';
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IDecorationOptions, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -34,12 +34,13 @@ import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CON
import { BreakpointWidget } from 'vs/workbench/parts/debug/browser/breakpointWidget';
import { ExceptionWidget } from 'vs/workbench/parts/debug/browser/exceptionWidget';
import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { IListService } from 'vs/platform/list/browser/listService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Position } from 'vs/editor/common/core/position';
import { CoreEditingCommands } from 'vs/editor/common/controller/coreCommands';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { first } from 'vs/base/common/arrays';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IListService } from 'vs/platform/list/browser/listService';
const HOVER_DELAY = 300;
const LAUNCH_JSON_REGEX = /launch\.json$/;
@@ -49,7 +50,6 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We
const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added
const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped
@editorContribution
export class DebugEditorContribution implements IDebugEditorContribution {
private toDispose: lifecycle.IDisposable[];
@@ -79,10 +79,11 @@ export class DebugEditorContribution implements IDebugEditorContribution {
@ITelemetryService private telemetryService: ITelemetryService,
@IListService listService: IListService,
@IConfigurationService private configurationService: IConfigurationService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IKeybindingService private keybindingService: IKeybindingService
) {
this.breakpointHintDecoration = [];
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, listService, this.instantiationService, themeService);
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService, themeService, contextKeyService, listService);
this.toDispose = [];
this.showHoverScheduler = new RunOnceScheduler(() => this.showHover(this.hoverRange, false), HOVER_DELAY);
this.hideHoverScheduler = new RunOnceScheduler(() => this.hoverWidget.hide(), HOVER_DELAY);
@@ -97,12 +98,12 @@ export class DebugEditorContribution implements IDebugEditorContribution {
private getContextMenuActions(breakpoints: IBreakpoint[], uri: uri, lineNumber: number): TPromise<(IAction | ContextSubMenu)[]> {
const actions: (IAction | ContextSubMenu)[] = [];
if (breakpoints.length === 1) {
actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
actions.push(this.instantiationService.createInstance(EditConditionalBreakpointAction, EditConditionalBreakpointAction.ID, EditConditionalBreakpointAction.LABEL, this.editor));
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL, this.debugService, this.keybindingService));
actions.push(new EditConditionalBreakpointAction(EditConditionalBreakpointAction.ID, EditConditionalBreakpointAction.LABEL, this.editor, this.debugService, this.keybindingService));
if (breakpoints[0].enabled) {
actions.push(this.instantiationService.createInstance(DisableBreakpointAction, DisableBreakpointAction.ID, DisableBreakpointAction.LABEL));
actions.push(new DisableBreakpointAction(DisableBreakpointAction.ID, DisableBreakpointAction.LABEL, this.debugService, this.keybindingService));
} else {
actions.push(this.instantiationService.createInstance(EnableBreakpointAction, EnableBreakpointAction.ID, EnableBreakpointAction.LABEL));
actions.push(new EnableBreakpointAction(EnableBreakpointAction.ID, EnableBreakpointAction.LABEL, this.debugService, this.keybindingService));
}
} else if (breakpoints.length > 1) {
const sorted = breakpoints.sort((first, second) => first.column - second.column);
@@ -139,7 +140,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
true,
() => this.debugService.addBreakpoints(uri, [{ lineNumber }])
));
actions.push(this.instantiationService.createInstance(AddConditionalBreakpointAction, AddConditionalBreakpointAction.ID, AddConditionalBreakpointAction.LABEL, this.editor, lineNumber));
actions.push(new AddConditionalBreakpointAction(AddConditionalBreakpointAction.ID, AddConditionalBreakpointAction.LABEL, this.editor, lineNumber, this.debugService, this.keybindingService));
}
return TPromise.as(actions);
@@ -200,7 +201,12 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e)));
this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(e)));
this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => {
const rect = this.hoverWidget.getDomNode().getBoundingClientRect();
const hoverDomNode = this.hoverWidget.getDomNode();
if (!hoverDomNode) {
return;
}
const rect = hoverDomNode.getBoundingClientRect();
// Only hide the hover widget if the editor mouse leave event is outside the hover widget #3528
if (e.event.posx < rect.left || e.event.posx > rect.right || e.event.posy < rect.top || e.event.posy > rect.bottom) {
this.hideHoverWidget();
@@ -389,7 +395,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.exceptionWidget.dispose();
}
this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo, lineNumber);
this.exceptionWidget = this.instantiationService.createInstance(ExceptionWidget, this.editor, exceptionInfo);
this.exceptionWidget.show({ lineNumber, column }, 0);
}
@@ -469,7 +475,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
// Inline Decorations
private updateInlineDecorations(stackFrame: IStackFrame): void {
const model = this.editor.getModel();
if (!this.configurationService.getConfiguration<IDebugConfiguration>('debug').inlineValues ||
if (!this.configurationService.getValue<IDebugConfiguration>('debug').inlineValues ||
!model || !stackFrame || model.uri.toString() !== stackFrame.source.uri.toString()) {
if (!this.removeInlineValuesScheduler.isScheduled()) {
this.removeInlineValuesScheduler.schedule();
@@ -623,3 +629,5 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.toDispose = lifecycle.dispose(this.toDispose);
}
}
registerEditorContribution(DebugEditorContribution);

View File

@@ -10,7 +10,6 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import * as dom from 'vs/base/browser/dom';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { DefaultController, ICancelableEvent, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
@@ -20,25 +19,27 @@ import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPosit
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/parts/debug/common/debug';
import { Expression } from 'vs/workbench/parts/debug/common/debugModel';
import { VariablesRenderer, renderExpressionValue, VariablesDataSource } from 'vs/workbench/parts/debug/electron-browser/debugViewer';
import { IListService } from 'vs/platform/list/browser/listService';
import { renderExpressionValue } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
import { VariablesDataSource, VariablesRenderer } from 'vs/workbench/parts/debug/electron-browser/variablesView';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { attachListStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
const $ = dom.$;
const MAX_ELEMENTS_SHOWN = 18;
export class DebugHoverWidget implements IContentWidget {
public static ID = 'debug.hoverWidget';
public static readonly ID = 'debug.hoverWidget';
// editor.IContentWidget.allowEditorOverflow
public allowEditorOverflow = true;
private _isVisible: boolean;
private domNode: HTMLElement;
private tree: ITree;
private tree: WorkbenchTree;
private showAtPosition: Position;
private highlightDecorations: string[];
private complexValueContainer: HTMLElement;
@@ -52,13 +53,34 @@ export class DebugHoverWidget implements IContentWidget {
constructor(
private editor: ICodeEditor,
private debugService: IDebugService,
private listService: IListService,
instantiationService: IInstantiationService,
private themeService: IThemeService
private instantiationService: IInstantiationService,
private themeService: IThemeService,
private contextKeyService: IContextKeyService,
private listService: IListService
) {
this.toDispose = [];
this.create(instantiationService);
this.registerListeners();
this._isVisible = false;
this.showAtPosition = null;
this.highlightDecorations = [];
}
private create(): void {
this.domNode = $('.debug-hover-widget');
this.complexValueContainer = dom.append(this.domNode, $('.complex-value'));
this.complexValueTitle = dom.append(this.complexValueContainer, $('.title'));
this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree'));
this.treeContainer.setAttribute('role', 'tree');
this.tree = new WorkbenchTree(this.treeContainer, {
dataSource: new VariablesDataSource(),
renderer: this.instantiationService.createInstance(VariablesHoverRenderer),
controller: new DebugHoverController(this.editor)
}, {
indentPixels: 6,
twistiePixels: 15,
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"),
keyboardSupport: false
}, this.contextKeyService, this.listService, this.themeService);
this.valueContainer = $('.value');
this.valueContainer.tabIndex = 0;
@@ -67,33 +89,8 @@ export class DebugHoverWidget implements IContentWidget {
this.domNode.appendChild(this.scrollbar.getDomNode());
this.toDispose.push(this.scrollbar);
this._isVisible = false;
this.showAtPosition = null;
this.highlightDecorations = [];
this.editor.addContentWidget(this);
this.editor.applyFontInfo(this.domNode);
}
private create(instantiationService: IInstantiationService): void {
this.domNode = $('.debug-hover-widget');
this.complexValueContainer = dom.append(this.domNode, $('.complex-value'));
this.complexValueTitle = dom.append(this.complexValueContainer, $('.title'));
this.treeContainer = dom.append(this.complexValueContainer, $('.debug-hover-tree'));
this.treeContainer.setAttribute('role', 'tree');
this.tree = new Tree(this.treeContainer, {
dataSource: new VariablesDataSource(),
renderer: instantiationService.createInstance(VariablesHoverRenderer),
controller: new DebugHoverController(this.editor)
}, {
indentPixels: 6,
twistiePixels: 15,
ariaLabel: nls.localize('treeAriaLabel', "Debug Hover"),
keyboardSupport: false
});
this.toDispose.push(attachListStyler(this.tree, this.themeService));
this.toDispose.push(this.listService.register(this.tree));
this.toDispose.push(attachStylerCallback(this.themeService, { editorHoverBackground, editorHoverBorder }, colors => {
this.domNode.style.backgroundColor = colors.editorHoverBackground;
if (colors.editorHoverBorder) {
@@ -102,13 +99,16 @@ export class DebugHoverWidget implements IContentWidget {
this.domNode.style.border = null;
}
}));
this.registerListeners();
this.editor.addContentWidget(this);
}
private registerListeners(): void {
this.toDispose.push(this.tree.addListener('item:expanded', () => {
this.toDispose.push(this.tree.onDidExpandItem(() => {
this.layoutTree();
}));
this.toDispose.push(this.tree.addListener('item:collapsed', () => {
this.toDispose.push(this.tree.onDidCollapseItem(() => {
this.layoutTree();
}));
@@ -237,7 +237,8 @@ export class DebugHoverWidget implements IContentWidget {
}
private findExpressionInStackFrame(namesToFind: string[], expressionRange: Range): TPromise<IExpression> {
return this.debugService.getViewModel().focusedStackFrame.getMostSpecificScopes(expressionRange)
return this.debugService.getViewModel().focusedStackFrame.getScopes()
.then(scopes => scopes.filter(s => !s.expensive))
.then(scopes => TPromise.join(scopes.map(scope => this.doFindExpression(scope, namesToFind))))
.then(expressions => expressions.filter(exp => !!exp))
// only show if all expressions found have the same value
@@ -245,6 +246,10 @@ export class DebugHoverWidget implements IContentWidget {
}
private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): TPromise<void> {
if (!this.domNode) {
this.create();
}
this.showAtPosition = position;
this._isVisible = true;
this.stoleFocus = focus;

View File

@@ -26,11 +26,10 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import * as debug from 'vs/workbench/parts/debug/common/debug';
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
@@ -39,7 +38,7 @@ import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { ITaskService, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -53,6 +52,7 @@ import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EX
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { TaskEvent, TaskEventKind } from 'vs/workbench/parts/tasks/common/tasks';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
@@ -60,11 +60,6 @@ const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
interface StartSessionResult {
status: 'ok' | 'initialConfiguration' | 'saveConfiguration';
content?: string;
};
export class DebugService implements debug.IDebugService {
public _serviceBrand: any;
@@ -84,6 +79,7 @@ export class DebugService implements debug.IDebugService {
private debugState: IContextKey<string>;
private breakpointsToSendOnResourceSaved: Set<string>;
private launchJsonChanged: boolean;
private firstSessionStart: boolean;
private previousState: debug.State;
constructor(
@@ -94,20 +90,18 @@ export class DebugService implements debug.IDebugService {
@IPanelService private panelService: IPanelService,
@IMessageService private messageService: IMessageService,
@IPartService private partService: IPartService,
@IWindowsService private windowsService: IWindowsService,
@IWindowService private windowService: IWindowService,
@IBroadcastService private broadcastService: IBroadcastService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IContextKeyService contextKeyService: IContextKeyService,
@ILifecycleService lifecycleService: ILifecycleService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionService private extensionService: IExtensionService,
@IMarkerService private markerService: IMarkerService,
@ITaskService private taskService: ITaskService,
@IFileService private fileService: IFileService,
@IConfigurationService private configurationService: IConfigurationService,
@ICommandService private commandService: ICommandService
@IConfigurationService private configurationService: IConfigurationService
) {
this.toDispose = [];
this.toDisposeOnSessionEnd = new Map<string, lifecycle.IDisposable[]>();
@@ -129,14 +123,15 @@ export class DebugService implements debug.IDebugService {
this.loadExceptionBreakpoints(), this.loadWatchExpressions());
this.toDispose.push(this.model);
this.viewModel = new ViewModel();
this.firstSessionStart = true;
this.registerListeners(lifecycleService);
this.registerListeners();
}
private registerListeners(lifecycleService: ILifecycleService): void {
private registerListeners(): void {
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
lifecycleService.onShutdown(this.store, this);
lifecycleService.onShutdown(this.dispose, this);
this.lifecycleService.onShutdown(this.store, this);
this.lifecycleService.onShutdown(this.dispose, this);
this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this));
}
@@ -330,6 +325,7 @@ export class DebugService implements debug.IDebugService {
this.updateStateAndEmit(session.getId(), debug.State.Running);
}));
let outputPromises: TPromise<void>[] = [];
this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
if (!event.body) {
return;
@@ -347,24 +343,26 @@ export class DebugService implements debug.IDebugService {
return;
}
// Make sure to append output in the correct order by properly waiting on preivous promises #33822
const waitFor = outputPromises.slice();
const source = event.body.source ? {
lineNumber: event.body.line,
column: event.body.column,
source: process.getSource(event.body.source)
} : undefined;
if (event.body.variablesReference) {
const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid());
container.getChildren().then(children => {
children.forEach(child => {
outputPromises.push(container.getChildren().then(children => {
return TPromise.join(waitFor).then(() => children.forEach(child => {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
child.name = null;
this.logToRepl(child, outputSeverity, source);
});
});
}));
}));
} else if (typeof event.body.output === 'string') {
this.logToRepl(event.body.output, outputSeverity, source);
TPromise.join(waitFor).then(() => this.logToRepl(event.body.output, outputSeverity, source));
}
TPromise.join(outputPromises).then(() => outputPromises = []);
}));
this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
@@ -393,9 +391,7 @@ export class DebugService implements debug.IDebugService {
}
}
// For compatibilty reasons check if wrong reason and source not present
// TODO@Isidor clean up these checks in October
if (event.body.reason === 'changed' || (event.body.reason === 'new' && !event.body.breakpoint.source) || event.body.reason === 'update') {
if (event.body.reason === 'changed') {
if (breakpoint) {
if (!breakpoint.column) {
event.body.breakpoint.column = undefined;
@@ -411,7 +407,7 @@ export class DebugService implements debug.IDebugService {
this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
process && this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && process.configuration.noDebug) {
process && process.session.root && process.configuration.noDebug) {
this.broadcastService.broadcast({
channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
payload: [process.session.root.uri.fsPath]
@@ -578,12 +574,18 @@ export class DebugService implements debug.IDebugService {
return this.sendBreakpoints(uri);
}
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void> {
this.model.updateBreakpoints(data);
return this.sendBreakpoints(uri);
}
public removeBreakpoints(id?: string): TPromise<any> {
const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath)));
const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri);
this.model.removeBreakpoints(toRemove);
return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
}
@@ -593,7 +595,8 @@ export class DebugService implements debug.IDebugService {
}
public addFunctionBreakpoint(): void {
this.model.addFunctionBreakpoint('');
const newFunctionBreakpoint = this.model.addFunctionBreakpoint('');
this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint);
}
public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
@@ -607,10 +610,6 @@ export class DebugService implements debug.IDebugService {
}
public addReplExpression(name: string): TPromise<void> {
/* __GDPR__
"debugService/addReplExpression" : {}
*/
this.telemetryService.publicLog('debugService/addReplExpression');
return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
@@ -653,10 +652,11 @@ export class DebugService implements debug.IDebugService {
// make sure to save all files and that the configuration is up to date
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() =>
this.extensionService.onReady().then(() => {
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
if (this.model.getProcesses().length === 0) {
this.removeReplExpressions();
this.allProcesses.clear();
this.model.getBreakpoints().forEach(bp => bp.verified = false);
}
this.launchJsonChanged = false;
const manager = this.getConfigurationManager();
@@ -686,7 +686,9 @@ export class DebugService implements debug.IDebugService {
return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
}
if (configOrName && !config) {
return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName)));
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName) :
nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist.");
return TPromise.wrapError(new Error(message));
}
// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
@@ -711,23 +713,27 @@ export class DebugService implements debug.IDebugService {
};
return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() =>
this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => {
// a falsy config indicates an aborted launch
if (config && config.type) {
return this.createProcess(root, config, sessionId);
}
(type ? this.extensionService.activateByEvent(`onDebugResolve:${type}`) : TPromise.as(null)).then(() =>
this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => {
// a falsy config indicates an aborted launch
if (config && config.type) {
return this.createProcess(root, config, sessionId);
}
if (launch) {
return launch.openConfigFile(false, type).then(editor => undefined);
}
return <any>launch.openConfigFile(false, type); // cast to ignore weird compile error
})
).then(() => wrapUpState(), err => {
wrapUpState();
return <any>TPromise.wrapError(err);
});
return undefined;
})
).then(() => wrapUpState(), err => {
wrapUpState();
return <any>TPromise.wrapError(err);
}));
})
)));
}
private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<void> {
return this.textFileService.saveAll().then(() =>
(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
if (!resolvedConfig) {
@@ -749,12 +755,13 @@ export class DebugService implements debug.IDebugService {
return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
}
this.toDisposeOnSessionEnd.set(sessionId, []);
const debugAnywayAction = new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
this.messageService.hideAll();
return this.doCreateProcess(root, resolvedConfig, sessionId);
});
return this.runPreLaunchTask(root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
return this.runPreLaunchTask(sessionId, root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
const successExitCode = taskSummary && taskSummary.exitCode === 0;
const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
@@ -841,7 +848,6 @@ export class DebugService implements debug.IDebugService {
const process = this.model.addProcess(configuration, session);
this.allProcesses.set(process.getId(), process);
this.toDisposeOnSessionEnd.set(session.getId(), []);
if (client) {
this.toDisposeOnSessionEnd.get(session.getId()).push(client);
}
@@ -867,16 +873,17 @@ export class DebugService implements debug.IDebugService {
this._onDidNewProcess.fire(process);
this.focusStackFrameAndEvaluate(null, process);
const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').internalConsoleOptions;
if (internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && internalConsoleOptions === 'openOnFirstSessionStart')) {
const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getValue<debug.IDebugConfiguration>('debug').internalConsoleOptions;
if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
}
if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || this.contextService.getWorkbenchState() === WorkbenchState.EMPTY)) {
// We only want to change the workbench view state on the first debug session #5738 and if the side bar is not hidden
this.viewModel.changedWorkbenchViewState = true;
const openDebugOptions = this.configurationService.getValue<debug.IDebugConfiguration>('debug').openDebug;
// Open debug viewlet based on the visibility of the side bar and openDebug setting
if (openDebugOptions === 'openOnSessionStart' || (openDebugOptions === 'openOnFirstSessionStart' && this.firstSessionStart)) {
this.viewletService.openViewlet(debug.VIEWLET_ID);
}
this.firstSessionStart = false;
this.debugType.set(configuration.type);
if (this.model.getProcesses().length > 1) {
@@ -902,7 +909,7 @@ export class DebugService implements debug.IDebugService {
watchExpressionsCount: this.model.getWatchExpressions().length,
extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
isBuiltin: adapter.extensionDescription.isBuiltin,
launchJsonExists: this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root.uri })
launchJsonExists: root && !!this.configurationService.getValue<debug.IGlobalConfig>('launch', { resource: root.uri })
});
}).then(() => process, (error: any) => {
if (error instanceof Error && error.message === 'Canceled') {
@@ -941,7 +948,7 @@ export class DebugService implements debug.IDebugService {
});
}
private runPreLaunchTask(root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
private runPreLaunchTask(sessionId: string, root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
if (!taskName) {
return TPromise.as(null);
}
@@ -952,6 +959,17 @@ export class DebugService implements debug.IDebugService {
return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
}
function once(kind: TaskEventKind, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (e.kind === kind) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
let taskStarted = false;
const promise = this.taskService.getActiveTasks().then(tasks => {
@@ -959,11 +977,16 @@ export class DebugService implements debug.IDebugService {
// task is already running - nothing to do.
return TPromise.as(null);
}
this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Active, () => taskStarted = true));
this.toDisposeOnSessionEnd.get(sessionId).push(
once(TaskEventKind.Active, this.taskService.onDidStateChange)(() => {
taskStarted = true;
})
);
const taskPromise = this.taskService.run(task);
if (task.isBackground) {
return new TPromise((c, e) => this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null))));
return new TPromise((c, e) => this.toDisposeOnSessionEnd.get(sessionId).push(
once(TaskEventKind.Inactive, this.taskService.onDidStateChange)(() => c(null)))
);
}
return taskPromise;
@@ -989,43 +1012,45 @@ export class DebugService implements debug.IDebugService {
}
public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
if (process.session.capabilities.supportsRestartRequest) {
return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
}
const focusedProcess = this.viewModel.focusedProcess;
const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
return process.session.disconnect(true).then(() => {
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
return this.broadcastService.broadcast({
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
payload: [process.session.root.uri.fsPath]
});
return this.textFileService.saveAll().then(() => {
if (process.session.capabilities.supportsRestartRequest) {
return <TPromise>process.session.custom('restart', null);
}
const focusedProcess = this.viewModel.focusedProcess;
const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
return new TPromise<void>((c, e) => {
setTimeout(() => {
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
let config = process.configuration;
if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
this.launchJsonChanged = false;
config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
// Take the type from the process since the debug extension might overwrite it #21316
config.type = process.configuration.type;
config.noDebug = process.configuration.noDebug;
}
config.__restart = restartData;
this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
}, 300);
});
}).then(() => {
if (preserveFocus) {
// Restart should preserve the focused process
const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
this.focusStackFrameAndEvaluate(null, restartedProcess);
return process.session.disconnect(true).then(() => {
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
return this.broadcastService.broadcast({
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
payload: [process.session.root.uri.fsPath]
});
}
}
return new TPromise<void>((c, e) => {
setTimeout(() => {
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
let config = process.configuration;
if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
this.launchJsonChanged = false;
config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
// Take the type from the process since the debug extension might overwrite it #21316
config.type = process.configuration.type;
config.noDebug = process.configuration.noDebug;
}
config.__restart = restartData;
this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
}, 300);
});
}).then(() => {
if (preserveFocus) {
// Restart should preserve the focused process
const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
this.focusStackFrameAndEvaluate(null, restartedProcess);
}
}
});
});
}
@@ -1089,7 +1114,7 @@ export class DebugService implements debug.IDebugService {
this.debugType.reset();
this.viewModel.setMultiProcessView(false);
if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
}
}
@@ -1216,8 +1241,11 @@ export class DebugService implements debug.IDebugService {
}
private onFileChanges(fileChangesEvent: FileChangesEvent): void {
this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
const toRemove = this.model.getBreakpoints().filter(bp =>
fileChangesEvent.contains(bp.uri, FileChangeType.DELETED));
if (toRemove.length) {
this.model.removeBreakpoints(toRemove);
}
fileChangesEvent.getUpdated().forEach(event => {
if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,514 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as resources from 'vs/base/common/resources';
import { RunOnceScheduler, sequence } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import * as builder from 'vs/base/browser/builder';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { EventType } from 'vs/base/common/events';
import { IAction } from 'vs/base/common/actions';
import { prepareActions } from 'vs/workbench/browser/actions';
import { IHighlightEvent, ITree } from 'vs/base/parts/tree/browser/tree';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, State, IBreakpoint, IExpression, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
import { Expression, Variable, ExceptionBreakpoint, FunctionBreakpoint, Thread, StackFrame, Breakpoint, ThreadAndProcessIds } from 'vs/workbench/parts/debug/common/debugModel';
import * as viewer from 'vs/workbench/parts/debug/electron-browser/debugViewer';
import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IListService } from 'vs/platform/list/browser/listService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
function renderViewTree(container: HTMLElement): HTMLElement {
const treeContainer = document.createElement('div');
dom.addClass(treeContainer, 'debug-view-content');
container.appendChild(treeContainer);
return treeContainer;
}
const $ = builder.$;
const twistiePixels = 20;
export class VariablesView extends ViewsViewletPanel {
private static MEMENTO = 'variablesview.memento';
private onFocusStackFrameScheduler: RunOnceScheduler;
private variablesFocusedContext: IContextKey<boolean>;
private settings: any;
private expandedElements: any[];
constructor(
private options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@ITelemetryService private telemetryService: ITelemetryService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
this.variablesFocusedContext = CONTEXT_VARIABLES_FOCUSED.bindTo(contextKeyService);
this.expandedElements = [];
// Use scheduler to prevent unnecessary flashing
this.onFocusStackFrameScheduler = new RunOnceScheduler(() => {
// Remember expanded elements when there are some (otherwise don't override/erase the previous ones)
const expanded = this.tree.getExpandedElements();
if (expanded.length > 0) {
this.expandedElements = expanded;
}
// Always clear tree highlight to avoid ending up in a broken state #12203
this.tree.clearHighlight();
this.tree.refresh().then(() => {
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
return sequence(this.expandedElements.map(e => () => this.tree.expand(e))).then(() => {
// If there is no preserved expansion state simply expand the first scope
if (stackFrame && this.tree.getExpandedElements().length === 0) {
return stackFrame.getScopes().then(scopes => {
if (scopes.length > 0 && !scopes[0].expensive) {
return this.tree.expand(scopes[0]);
}
return undefined;
});
}
return undefined;
});
}).done(null, errors.onUnexpectedError);
}, 400);
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-variables');
this.treeContainer = renderViewTree(container);
this.tree = new Tree(this.treeContainer, {
dataSource: new viewer.VariablesDataSource(),
renderer: this.instantiationService.createInstance(viewer.VariablesRenderer),
accessibilityProvider: new viewer.VariablesAccessibilityProvider(),
controller: this.instantiationService.createInstance(viewer.VariablesController, new viewer.VariablesActionProvider(this.instantiationService), MenuId.DebugVariablesContext)
}, {
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
twistiePixels,
keyboardSupport: false
});
this.disposables.push(attachListStyler(this.tree, this.themeService));
this.disposables.push(this.listService.register(this.tree, [this.variablesFocusedContext]));
const viewModel = this.debugService.getViewModel();
this.tree.setInput(viewModel);
const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
this.toolbar.setActions(prepareActions([collapseAction]))();
this.disposables.push(viewModel.onDidFocusStackFrame(sf => {
// Refresh the tree immediately if it is not visible.
// Otherwise postpone the refresh until user stops stepping.
if (!this.tree.getContentHeight() || sf.explicit) {
this.onFocusStackFrameScheduler.schedule(0);
} else {
this.onFocusStackFrameScheduler.schedule();
}
}));
this.disposables.push(this.debugService.onDidChangeState(state => {
collapseAction.enabled = state === State.Running || state === State.Stopped;
}));
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
if (!expression || !(expression instanceof Variable)) {
return;
}
this.tree.refresh(expression, false).then(() => {
this.tree.setHighlight(expression);
this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => {
if (!e.highlight) {
this.debugService.getViewModel().setSelectedExpression(null);
}
});
}).done(null, errors.onUnexpectedError);
}));
}
public shutdown(): void {
this.settings[VariablesView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
export class WatchExpressionsView extends ViewsViewletPanel {
private static MEMENTO = 'watchexpressionsview.memento';
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
private toReveal: IExpression;
private watchExpressionsFocusedContext: IContextKey<boolean>;
private settings: any;
constructor(
private options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
// only expand when a new watch expression is added.
if (we instanceof Expression) {
this.setExpanded(true);
}
}));
this.watchExpressionsFocusedContext = CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(contextKeyService);
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
this.tree.refresh().done(() => {
return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true);
}, errors.onUnexpectedError);
}, 50);
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-watch');
this.treeContainer = renderViewTree(container);
const actionProvider = new viewer.WatchExpressionsActionProvider(this.instantiationService);
this.tree = new Tree(this.treeContainer, {
dataSource: new viewer.WatchExpressionsDataSource(),
renderer: this.instantiationService.createInstance(viewer.WatchExpressionsRenderer, actionProvider, this.actionRunner),
accessibilityProvider: new viewer.WatchExpressionsAccessibilityProvider(),
controller: this.instantiationService.createInstance(viewer.WatchExpressionsController, actionProvider, MenuId.DebugWatchContext),
dnd: this.instantiationService.createInstance(viewer.WatchExpressionsDragAndDrop)
}, {
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
twistiePixels,
keyboardSupport: false
});
this.disposables.push(attachListStyler(this.tree, this.themeService));
this.disposables.push(this.listService.register(this.tree, [this.watchExpressionsFocusedContext]));
this.tree.setInput(this.debugService.getModel());
const addWatchExpressionAction = this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL);
const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, true, 'explorer-action collapse-explorer');
const removeAllWatchExpressionsAction = this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL);
this.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
this.onWatchExpressionsUpdatedScheduler.schedule();
}
this.toReveal = we;
}));
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
if (!expression || !(expression instanceof Expression)) {
return;
}
this.tree.refresh(expression, false).then(() => {
this.tree.setHighlight(expression);
this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => {
if (!e.highlight) {
this.debugService.getViewModel().setSelectedExpression(null);
}
});
}).done(null, errors.onUnexpectedError);
}));
}
public shutdown(): void {
this.settings[WatchExpressionsView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
export class CallStackView extends ViewsViewletPanel {
private static MEMENTO = 'callstackview.memento';
private pauseMessage: builder.Builder;
private pauseMessageLabel: builder.Builder;
private onCallStackChangeScheduler: RunOnceScheduler;
private settings: any;
constructor(
private options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@ITelemetryService private telemetryService: ITelemetryService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
this.onCallStackChangeScheduler = new RunOnceScheduler(() => {
let newTreeInput: any = this.debugService.getModel();
const processes = this.debugService.getModel().getProcesses();
if (!this.debugService.getViewModel().isMultiProcessView() && processes.length) {
const threads = processes[0].getAllThreads();
// Only show the threads in the call stack if there is more than 1 thread.
newTreeInput = threads.length === 1 ? threads[0] : processes[0];
}
// Only show the global pause message if we do not display threads.
// Otherwise there will be a pause message per thread and there is no need for a global one.
if (newTreeInput instanceof Thread && newTreeInput.stoppedDetails) {
this.pauseMessageLabel.text(newTreeInput.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", newTreeInput.stoppedDetails.reason));
if (newTreeInput.stoppedDetails.text) {
this.pauseMessageLabel.title(newTreeInput.stoppedDetails.text);
}
newTreeInput.stoppedDetails.reason === 'exception' ? this.pauseMessageLabel.addClass('exception') : this.pauseMessageLabel.removeClass('exception');
this.pauseMessage.show();
} else {
this.pauseMessage.hide();
}
(this.tree.getInput() === newTreeInput ? this.tree.refresh() : this.tree.setInput(newTreeInput))
.done(() => this.updateTreeSelection(), errors.onUnexpectedError);
}, 50);
}
protected renderHeaderTitle(container: HTMLElement): void {
const title = $('.title.debug-call-stack-title').appendTo(container);
$('span').text(this.options.name).appendTo(title);
this.pauseMessage = $('span.pause-message').appendTo(title);
this.pauseMessage.hide();
this.pauseMessageLabel = $('span.label').appendTo(this.pauseMessage);
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-call-stack');
this.treeContainer = renderViewTree(container);
const actionProvider = this.instantiationService.createInstance(viewer.CallStackActionProvider);
const controller = this.instantiationService.createInstance(viewer.CallStackController, actionProvider, MenuId.DebugCallStackContext);
this.tree = new Tree(this.treeContainer, {
dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource),
renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider),
controller
}, {
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'callStackAriaLabel' }, "Debug Call Stack"),
twistiePixels,
keyboardSupport: false
});
this.disposables.push(attachListStyler(this.tree, this.themeService));
this.disposables.push(this.listService.register(this.tree));
this.disposables.push(this.tree.addListener('selection', event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
const element = this.tree.getFocus();
if (element instanceof ThreadAndProcessIds) {
controller.showMoreStackFrames(this.tree, element);
} else if (element instanceof StackFrame) {
controller.focusStackFrame(element, event, false);
}
}
}));
this.disposables.push(this.debugService.getModel().onDidChangeCallStack(() => {
if (!this.onCallStackChangeScheduler.isScheduled()) {
this.onCallStackChangeScheduler.schedule();
}
}));
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
this.updateTreeSelection().done(undefined, errors.onUnexpectedError)));
// Schedule the update of the call stack tree if the viewlet is opened after a session started #14684
if (this.debugService.state === State.Stopped) {
this.onCallStackChangeScheduler.schedule();
}
}
private updateTreeSelection(): TPromise<void> {
if (!this.tree.getInput()) {
// Tree not initialized yet
return TPromise.as(null);
}
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
const thread = this.debugService.getViewModel().focusedThread;
const process = this.debugService.getViewModel().focusedProcess;
if (!thread) {
if (!process) {
this.tree.clearSelection();
return TPromise.as(null);
}
this.tree.setSelection([process]);
return this.tree.reveal(process);
}
return this.tree.expandAll([thread.process, thread]).then(() => {
if (!stackFrame) {
return TPromise.as(null);
}
this.tree.setSelection([stackFrame]);
return this.tree.reveal(stackFrame);
});
}
public shutdown(): void {
this.settings[CallStackView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
export class BreakpointsView extends ViewsViewletPanel {
private static MAX_VISIBLE_FILES = 9;
private static MEMENTO = 'breakopintsview.memento';
private breakpointsFocusedContext: IContextKey<boolean>;
private settings: any;
constructor(
private options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService
) {
super({
...(options as IViewOptions),
ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section")
}, keybindingService, contextMenuService);
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
this.settings = options.viewletSettings;
this.breakpointsFocusedContext = CONTEXT_BREAKPOINTS_FOCUSED.bindTo(contextKeyService);
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-breakpoints');
this.treeContainer = renderViewTree(container);
const actionProvider = new viewer.BreakpointsActionProvider(this.instantiationService, this.debugService);
const controller = this.instantiationService.createInstance(viewer.BreakpointsController, actionProvider, MenuId.DebugBreakpointsContext);
this.tree = new Tree(this.treeContainer, {
dataSource: new viewer.BreakpointsDataSource(),
renderer: this.instantiationService.createInstance(viewer.BreakpointsRenderer, actionProvider, this.actionRunner),
accessibilityProvider: this.instantiationService.createInstance(viewer.BreakpointsAccessibilityProvider),
controller,
sorter: {
compare(tree: ITree, element: any, otherElement: any): number {
const first = <IBreakpoint>element;
const second = <IBreakpoint>otherElement;
if (first instanceof ExceptionBreakpoint) {
return -1;
}
if (second instanceof ExceptionBreakpoint) {
return 1;
}
if (first instanceof FunctionBreakpoint) {
return -1;
}
if (second instanceof FunctionBreakpoint) {
return 1;
}
if (first.uri.toString() !== second.uri.toString()) {
return resources.basenameOrAuthority(first.uri).localeCompare(resources.basenameOrAuthority(second.uri));
}
if (first.lineNumber === second.lineNumber) {
return first.column - second.column;
}
return first.lineNumber - second.lineNumber;
}
}
}, {
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'breakpointsAriaTreeLabel' }, "Debug Breakpoints"),
twistiePixels,
keyboardSupport: false
});
this.disposables.push(attachListStyler(this.tree, this.themeService));
this.disposables.push(this.listService.register(this.tree, [this.breakpointsFocusedContext]));
this.disposables.push(this.tree.addListener('selection', event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
const element = this.tree.getFocus();
if (element instanceof Breakpoint) {
controller.openBreakpointSource(element, event, false);
}
}
}));
const debugModel = this.debugService.getModel();
this.tree.setInput(debugModel);
this.disposables.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => {
if (!fbp || !(fbp instanceof FunctionBreakpoint)) {
return;
}
this.tree.refresh(fbp, false).then(() => {
this.tree.setHighlight(fbp);
this.tree.addOneTimeListener(EventType.HIGHLIGHT, (e: IHighlightEvent) => {
if (!e.highlight) {
this.debugService.getViewModel().setSelectedFunctionBreakpoint(null);
}
});
}).done(null, errors.onUnexpectedError);
}));
}
public getActions(): IAction[] {
return [
this.instantiationService.createInstance(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL),
this.instantiationService.createInstance(ToggleBreakpointsActivatedAction, ToggleBreakpointsActivatedAction.ID, ToggleBreakpointsActivatedAction.ACTIVATE_LABEL),
this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL)
];
}
private onBreakpointsChange(): void {
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
if (this.tree) {
this.tree.refresh();
}
}
private getExpandedBodySize(): number {
const model = this.debugService.getModel();
const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length;
return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22;
}
public shutdown(): void {
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}

View File

@@ -16,14 +16,13 @@ import { isMacintosh } from 'vs/base/common/platform';
import { CancellationToken } from 'vs/base/common/cancellation';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ITree, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest';
import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController';
import { IReadOnlyModel, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { Position } from 'vs/editor/common/core/position';
import * as modes from 'vs/editor/common/modes';
import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { IModelService } from 'vs/editor/common/services/modelService';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -38,11 +37,12 @@ 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';
import { IListService } from 'vs/platform/list/browser/listService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { clipboard } from 'electron';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
import { memoize } from 'vs/base/common/decorators';
const $ = dom.$;
@@ -65,16 +65,16 @@ export interface IPrivateReplService {
export class Repl extends Panel implements IPrivateReplService {
public _serviceBrand: any;
private static HALF_WIDTH_TYPICAL = 'n';
private static readonly HALF_WIDTH_TYPICAL = 'n';
private static HISTORY: ReplHistory;
private static REFRESH_DELAY = 500; // delay in ms to refresh the repl for new elements to show
private static REPL_INPUT_INITIAL_HEIGHT = 19;
private static REPL_INPUT_MAX_HEIGHT = 170;
private static readonly REFRESH_DELAY = 500; // delay in ms to refresh the repl for new elements to show
private static readonly REPL_INPUT_INITIAL_HEIGHT = 19;
private static readonly REPL_INPUT_MAX_HEIGHT = 170;
private tree: ITree;
private renderer: ReplExpressionsRenderer;
private characterWidthSurveyor: HTMLElement;
private container: HTMLElement;
private treeContainer: HTMLElement;
private replInput: ReplInputEditor;
private replInputContainer: HTMLElement;
@@ -129,30 +129,20 @@ export class Repl extends Panel implements IPrivateReplService {
public create(parent: Builder): TPromise<void> {
super.create(parent);
const container = dom.append(parent.getHTMLElement(), $('.repl'));
this.treeContainer = dom.append(container, $('.repl-tree'));
this.createReplInput(container);
this.characterWidthSurveyor = dom.append(container, $('.surveyor'));
this.characterWidthSurveyor.textContent = Repl.HALF_WIDTH_TYPICAL;
for (let i = 0; i < 10; i++) {
this.characterWidthSurveyor.textContent += this.characterWidthSurveyor.textContent;
}
this.characterWidthSurveyor.style.fontSize = isMacintosh ? '12px' : '14px';
this.container = dom.append(parent.getHTMLElement(), $('.repl'));
this.treeContainer = dom.append(this.container, $('.repl-tree'));
this.createReplInput(this.container);
this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer);
const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.instantiationService), MenuId.DebugConsoleContext);
controller.toFocusOnClick = this.replInput;
this.tree = new Tree(this.treeContainer, {
this.tree = new WorkbenchTree(this.treeContainer, {
dataSource: new ReplExpressionsDataSource(),
renderer: this.renderer,
accessibilityProvider: new ReplExpressionsAccessibilityProvider(),
controller
}, replTreeOptions);
this.toUnbind.push(attachListStyler(this.tree, this.themeService));
this.toUnbind.push(this.listService.register(this.tree));
}, replTreeOptions, this.contextKeyService, this.listService, this.themeService);
if (!Repl.HISTORY) {
Repl.HISTORY = new ReplHistory(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')));
@@ -246,7 +236,7 @@ export class Repl extends Panel implements IPrivateReplService {
public layout(dimension: Dimension): void {
this.dimension = dimension;
if (this.tree) {
this.renderer.setWidth(dimension.width - 25, this.characterWidthSurveyor.clientWidth / this.characterWidthSurveyor.textContent.length);
this.renderer.setWidth(dimension.width - 25, this.characterWidth);
const treeHeight = dimension.height - this.replInputHeight;
this.treeContainer.style.height = `${treeHeight}px`;
this.tree.layout(treeHeight);
@@ -256,6 +246,18 @@ export class Repl extends Panel implements IPrivateReplService {
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();
}
@@ -312,7 +314,6 @@ export class Repl extends Panel implements IPrivateReplService {
}
}
@editorAction
class ReplHistoryPreviousAction extends EditorAction {
constructor() {
@@ -332,12 +333,11 @@ class ReplHistoryPreviousAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise<void> {
accessor.get(IPrivateReplService).navigateHistory(true);
}
}
@editorAction
class ReplHistoryNextAction extends EditorAction {
constructor() {
@@ -357,12 +357,11 @@ class ReplHistoryNextAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise<void> {
accessor.get(IPrivateReplService).navigateHistory(false);
}
}
@editorAction
class AcceptReplInputAction extends EditorAction {
constructor() {
@@ -378,25 +377,12 @@ class AcceptReplInputAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise<void> {
SuggestController.get(editor).acceptSelectedSuggestion();
accessor.get(IPrivateReplService).acceptReplInput();
}
}
const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(SuggestController.get);
CommonEditorRegistry.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
}
}));
@editorAction
export class ReplCopyAllAction extends EditorAction {
constructor() {
@@ -408,7 +394,24 @@ export class ReplCopyAllAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void | TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise<void> {
clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent());
}
}
registerEditorAction(ReplHistoryPreviousAction);
registerEditorAction(ReplHistoryNextAction);
registerEditorAction(AcceptReplInputAction);
registerEditorAction(ReplCopyAllAction);
const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(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
}
}));

View File

@@ -4,9 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorContributionCtor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorExtensionsRegistry, IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -15,9 +14,9 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
// Allowed Editor Contributions:
import { MenuPreventer } from 'vs/workbench/parts/codeEditor/electron-browser/menuPreventer';
import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard';
import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu';
import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu';
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { TabCompletionController } from 'vs/workbench/parts/snippets/electron-browser/tabCompletion';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -46,6 +45,6 @@ export class ReplInputEditor extends CodeEditorWidget {
}
protected _getActions(): EditorAction[] {
return CommonEditorRegistry.getEditorActions();
return EditorExtensionsRegistry.getEditorActions();
}
}

View File

@@ -17,7 +17,7 @@ import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer
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/electron-browser/debugViewer';
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
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';
@@ -41,7 +41,7 @@ export class ReplExpressionsDataSource implements IDataSource {
return TPromise.as(element.getReplElements());
}
if (element instanceof RawObjectReplElement) {
return TPromise.as(element.getChildren());
return element.getChildren();
}
if (element instanceof SimpleReplElement) {
return TPromise.as(null);
@@ -80,12 +80,12 @@ interface IRawObjectReplTemplateData {
export class ReplExpressionsRenderer implements IRenderer {
private static VARIABLE_TEMPLATE_ID = 'variable';
private static EXPRESSION_TEMPLATE_ID = 'expressionRepl';
private static SIMPLE_REPL_ELEMENT_TEMPLATE_ID = 'simpleReplElement';
private static RAW_OBJECT_REPL_ELEMENT_TEMPLATE_ID = 'rawObject';
private static readonly VARIABLE_TEMPLATE_ID = 'variable';
private static readonly EXPRESSION_TEMPLATE_ID = 'expressionRepl';
private static readonly SIMPLE_REPL_ELEMENT_TEMPLATE_ID = 'simpleReplElement';
private static readonly RAW_OBJECT_REPL_ELEMENT_TEMPLATE_ID = 'rawObject';
private static LINE_HEIGHT_PX = 18;
private static readonly LINE_HEIGHT_PX = 18;
private width: number;
private characterWidth: number;
@@ -298,34 +298,46 @@ export class ReplExpressionsRenderer implements IRenderer {
if (text.charCodeAt(i) === 27) {
let index = i;
let chr = (++index < len ? text.charAt(index) : null);
let codes = [];
if (chr && chr === '[') {
let code: string = null;
chr = (++index < len ? text.charAt(index) : null);
if (chr && chr >= '0' && chr <= '9') {
code = chr;
while (chr !== 'm' && codes.length <= 7) {
chr = (++index < len ? text.charAt(index) : null);
}
if (chr && chr >= '0' && chr <= '9') {
code += chr;
chr = (++index < len ? text.charAt(index) : null);
}
if (chr && chr >= '0' && chr <= '9') {
code = chr;
chr = (++index < len ? text.charAt(index) : null);
}
if (code === null) {
code = '0';
if (chr && chr >= '0' && chr <= '9') {
code += chr;
chr = (++index < len ? text.charAt(index) : null);
}
if (code === null) {
code = '0';
}
codes.push(code);
}
if (chr === 'm') { // set text color/mode.
code = null;
// only respect text-foreground ranges and ignore the values for "black" & "white" because those
// only make sense in combination with text-background ranges which we currently not support
let parsedMode = parseInt(code, 10);
let token = document.createElement('span');
if ((parsedMode >= 30 && parsedMode <= 37) || (parsedMode >= 90 && parsedMode <= 97)) {
token.className = 'code' + parsedMode;
} else if (parsedMode === 1) {
token.className = 'code-bold';
token.className = '';
while (codes.length > 0) {
code = codes.pop();
let parsedMode = parseInt(code, 10);
if (token.className.length > 0) {
token.className += ' ';
}
if ((parsedMode >= 30 && parsedMode <= 37) || (parsedMode >= 90 && parsedMode <= 97)) {
token.className += 'code' + parsedMode;
} else if (parsedMode === 1) {
token.className += 'code-bold';
}
}
// we need a tokens container now

View File

@@ -34,7 +34,6 @@ 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 static ID = 'debug.statusbarColorProvider';
constructor(
@IThemeService themeService: IThemeService,
@@ -103,10 +102,6 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
return process && process.configuration && process.configuration.noDebug;
}
public getId(): string {
return StatusBarColorProvider.ID;
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

View File

@@ -11,7 +11,7 @@ import { ITerminalService, ITerminalInstance, ITerminalConfiguration } from 'vs/
import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const enum ShellType { cmd, powershell, bash };
const enum ShellType { cmd, powershell, bash }
export class TerminalSupport {
@@ -61,7 +61,7 @@ export class TerminalSupport {
// get the shell configuration for the current platform
let shell: string;
const shell_config = (<ITerminalConfiguration>configurationService.getConfiguration<any>().terminal.integrated).shell;
const shell_config = (<ITerminalConfiguration>configurationService.getValue<any>().terminal.integrated).shell;
if (platform.isWindows) {
shell = shell_config.windows;
shellType = ShellType.cmd;
@@ -102,7 +102,12 @@ export class TerminalSupport {
}
if (args.env) {
for (let key in args.env) {
command += `$env:${key}='${args.env[key]}'; `;
const value = args.env[key];
if (value === null) {
command += `Remove-Item env:${key}; `;
} else {
command += `\${env:${key}}='${value}'; `;
}
}
}
if (args.args && args.args.length > 0) {
@@ -127,7 +132,12 @@ export class TerminalSupport {
if (args.env) {
command += 'cmd /C "';
for (let key in args.env) {
command += `set "${key}=${args.env[key]}" && `;
const value = args.env[key];
if (value === null) {
command += `set "${key}=" && `;
} else {
command += `set "${key}=${args.env[key]}" && `;
}
}
}
for (let a of args.args) {
@@ -151,7 +161,12 @@ export class TerminalSupport {
if (args.env) {
command += 'env';
for (let key in args.env) {
command += ` "${key}=${args.env[key]}"`;
const value = args.env[key];
if (value === null) {
command += ` -u "${key}"`;
} else {
command += ` "${key}=${value}"`;
}
}
command += ' ';
}

View File

@@ -0,0 +1,334 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { RunOnceScheduler, sequence } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import { prepareActions } from 'vs/workbench/browser/actions';
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, State, CONTEXT_VARIABLES_FOCUSED, IExpression } from 'vs/workbench/parts/debug/common/debug';
import { Variable, Scope } from 'vs/workbench/parts/debug/common/debugModel';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { once } from 'vs/base/common/event';
import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction, IActionItem } from 'vs/base/common/actions';
import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
const $ = dom.$;
export class VariablesView extends TreeViewsViewletPanel {
private static readonly MEMENTO = 'variablesview.memento';
private onFocusStackFrameScheduler: RunOnceScheduler;
private settings: any;
private expandedElements: any[];
private needsRefresh: boolean;
constructor(
options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IListService private listService: IListService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IThemeService private themeService: IThemeService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
this.expandedElements = [];
// Use scheduler to prevent unnecessary flashing
this.onFocusStackFrameScheduler = new RunOnceScheduler(() => {
// Remember expanded elements when there are some (otherwise don't override/erase the previous ones)
const expanded = this.tree.getExpandedElements();
if (expanded.length > 0) {
this.expandedElements = expanded;
}
// Always clear tree highlight to avoid ending up in a broken state #12203
this.tree.clearHighlight();
this.needsRefresh = false;
this.tree.refresh().then(() => {
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
return sequence(this.expandedElements.map(e => () => this.tree.expand(e))).then(() => {
// If there is no preserved expansion state simply expand the first scope
if (stackFrame && this.tree.getExpandedElements().length === 0) {
return stackFrame.getScopes().then(scopes => {
if (scopes.length > 0 && !scopes[0].expensive) {
return this.tree.expand(scopes[0]);
}
return undefined;
});
}
return undefined;
});
}).done(null, errors.onUnexpectedError);
}, 400);
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-variables');
this.treeContainer = renderViewTree(container);
this.tree = new WorkbenchTree(this.treeContainer, {
dataSource: new VariablesDataSource(),
renderer: this.instantiationService.createInstance(VariablesRenderer),
accessibilityProvider: new VariablesAccessibilityProvider(),
controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext)
}, {
ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"),
twistiePixels,
keyboardSupport: false
}, this.contextKeyService, this.listService, this.themeService);
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
const viewModel = this.debugService.getViewModel();
this.tree.setInput(viewModel);
const collapseAction = new CollapseAction(this.tree, false, 'explorer-action collapse-explorer');
this.toolbar.setActions(prepareActions([collapseAction]))();
this.disposables.push(viewModel.onDidFocusStackFrame(sf => {
if (!this.isVisible() || !this.isExpanded()) {
this.needsRefresh = true;
return;
}
// Refresh the tree immediately if it is not visible.
// Otherwise postpone the refresh until user stops stepping.
if (!this.tree.getContentHeight() || sf.explicit) {
this.onFocusStackFrameScheduler.schedule(0);
} else {
this.onFocusStackFrameScheduler.schedule();
}
}));
this.disposables.push(this.debugService.onDidChangeState(state => {
collapseAction.enabled = state === State.Running || state === State.Stopped;
}));
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
if (!expression || !(expression instanceof Variable)) {
return;
}
this.tree.refresh(expression, false).then(() => {
this.tree.setHighlight(expression);
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
if (!e.highlight) {
this.debugService.getViewModel().setSelectedExpression(null);
}
});
}).done(null, errors.onUnexpectedError);
}));
}
public setExpanded(expanded: boolean): void {
super.setExpanded(expanded);
if (expanded && this.needsRefresh) {
this.onFocusStackFrameScheduler.schedule();
}
}
public setVisible(visible: boolean): TPromise<void> {
return super.setVisible(visible).then(() => {
if (visible && this.needsRefresh) {
this.onFocusStackFrameScheduler.schedule();
}
});
}
public shutdown(): void {
this.settings[VariablesView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
class VariablesActionProvider implements IActionProvider {
constructor(private debugService: IDebugService, private keybindingService: IKeybindingService) {
// noop
}
public hasActions(tree: ITree, element: any): boolean {
return false;
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
return TPromise.as([]);
}
public hasSecondaryActions(tree: ITree, element: any): boolean {
// Only show context menu on "real" variables. Not on array chunk nodes.
return element instanceof Variable && !!element.value;
}
public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
const actions: IAction[] = [];
const variable = <Variable>element;
actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService));
actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, this.debugService));
actions.push(new Separator());
actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService));
return TPromise.as(actions);
}
public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
return null;
}
}
export class VariablesDataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
return element.getId();
}
public hasChildren(tree: ITree, element: any): boolean {
if (element instanceof ViewModel || element instanceof Scope) {
return true;
}
let variable = <Variable>element;
return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null');
}
public getChildren(tree: ITree, element: any): TPromise<any> {
if (element instanceof ViewModel) {
const focusedStackFrame = (<ViewModel>element).focusedStackFrame;
return focusedStackFrame ? focusedStackFrame.getScopes() : TPromise.as([]);
}
let scope = <Scope>element;
return scope.getChildren();
}
public getParent(tree: ITree, element: any): TPromise<any> {
return TPromise.as(null);
}
}
interface IScopeTemplateData {
name: HTMLElement;
}
export class VariablesRenderer implements IRenderer {
private static readonly SCOPE_TEMPLATE_ID = 'scope';
private static readonly VARIABLE_TEMPLATE_ID = 'variable';
constructor(
@IDebugService private debugService: IDebugService,
@IContextViewService private contextViewService: IContextViewService,
@IThemeService private themeService: IThemeService
) {
// noop
}
public getHeight(tree: ITree, element: any): number {
return 22;
}
public getTemplateId(tree: ITree, element: any): string {
if (element instanceof Scope) {
return VariablesRenderer.SCOPE_TEMPLATE_ID;
}
if (element instanceof Variable) {
return VariablesRenderer.VARIABLE_TEMPLATE_ID;
}
return null;
}
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
let data: IScopeTemplateData = Object.create(null);
data.name = dom.append(container, $('.scope'));
return data;
}
let data: IVariableTemplateData = Object.create(null);
data.expression = dom.append(container, $('.expression'));
data.name = dom.append(data.expression, $('span.name'));
data.value = dom.append(data.expression, $('span.value'));
return data;
}
public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
this.renderScope(element, templateData);
} else {
const variable = <Variable>element;
if (variable === this.debugService.getViewModel().getSelectedExpression() || variable.errorMessage) {
renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, variable, (<IVariableTemplateData>templateData).expression, {
initialValue: variable.value,
ariaLabel: nls.localize('variableValueAriaLabel', "Type new variable value"),
validationOptions: {
validation: (value: string) => variable.errorMessage ? ({ content: variable.errorMessage }) : null
}
});
} else {
renderVariable(tree, variable, templateData, true);
}
}
}
private renderScope(scope: Scope, data: IScopeTemplateData): void {
data.name.textContent = scope.name;
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
// noop
}
}
class VariablesAccessibilityProvider implements IAccessibilityProvider {
public getAriaLabel(tree: ITree, element: any): string {
if (element instanceof Scope) {
return nls.localize('variableScopeAriaLabel', "Scope {0}, variables, debug", (<Scope>element).name);
}
if (element instanceof Variable) {
return nls.localize('variableAriaLabel', "{0} value {1}, variables, debug", (<Variable>element).name, (<Variable>element).value);
}
return null;
}
}
class VariablesController extends BaseDebugController {
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
// double click on primitive value: open input box to be able to set the value
const process = this.debugService.getViewModel().focusedProcess;
if (element instanceof Variable && event.detail === 2 && process && process.session.capabilities.supportsSetVariable) {
const expression = <IExpression>element;
this.debugService.getViewModel().setSelectedExpression(expression);
return true;
}
return super.onLeftClick(tree, element, event);
}
}

View File

@@ -0,0 +1,395 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { prepareActions } from 'vs/workbench/browser/actions';
import { IHighlightEvent, IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
import { TreeViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, IExpression, CONTEXT_WATCH_EXPRESSIONS_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
import { Expression, Variable, Model } from 'vs/workbench/parts/debug/common/debugModel';
import { AddWatchExpressionAction, RemoveAllWatchExpressionsAction, EditWatchExpressionAction, RemoveWatchExpressionAction } from 'vs/workbench/parts/debug/browser/debugActions';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { once } from 'vs/base/common/event';
import { IAction, IActionItem } from 'vs/base/common/actions';
import { CopyValueAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { IMouseEvent, DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
import { IVariableTemplateData, renderVariable, renderRenameBox, renderExpressionValue, BaseDebugController, twistiePixels, renderViewTree } from 'vs/workbench/parts/debug/electron-browser/baseDebugView';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
const $ = dom.$;
const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
export class WatchExpressionsView extends TreeViewsViewletPanel {
private static readonly MEMENTO = 'watchexpressionsview.memento';
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
private toReveal: IExpression;
private settings: any;
private needsRefresh: boolean;
constructor(
options: IViewletViewOptions,
@IContextMenuService contextMenuService: IContextMenuService,
@IDebugService private debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IListService private listService: IListService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
this.settings = options.viewletSettings;
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
// only expand when a new watch expression is added.
if (we instanceof Expression) {
this.setExpanded(true);
}
}));
this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => {
this.needsRefresh = false;
this.tree.refresh().done(() => {
return this.toReveal instanceof Expression ? this.tree.reveal(this.toReveal) : TPromise.as(true);
}, errors.onUnexpectedError);
}, 50);
}
public renderBody(container: HTMLElement): void {
dom.addClass(container, 'debug-watch');
this.treeContainer = renderViewTree(container);
const actionProvider = new WatchExpressionsActionProvider(this.debugService, this.keybindingService);
this.tree = new WorkbenchTree(this.treeContainer, {
dataSource: new WatchExpressionsDataSource(),
renderer: this.instantiationService.createInstance(WatchExpressionsRenderer),
accessibilityProvider: new WatchExpressionsAccessibilityProvider(),
controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext),
dnd: new WatchExpressionsDragAndDrop(this.debugService)
}, {
ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"),
twistiePixels,
keyboardSupport: false
}, this.contextKeyService, this.listService, this.themeService);
CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);
this.tree.setInput(this.debugService.getModel());
const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService);
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
const removeAllWatchExpressionsAction = new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService);
this.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
if (!this.isExpanded() || !this.isVisible()) {
this.needsRefresh = true;
return;
}
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
this.onWatchExpressionsUpdatedScheduler.schedule();
}
this.toReveal = we;
}));
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
if (!expression || !(expression instanceof Expression)) {
return;
}
this.tree.refresh(expression, false).then(() => {
this.tree.setHighlight(expression);
once(this.tree.onDidChangeHighlight)((e: IHighlightEvent) => {
if (!e.highlight) {
this.debugService.getViewModel().setSelectedExpression(null);
}
});
}).done(null, errors.onUnexpectedError);
}));
}
public setExpanded(expanded: boolean): void {
super.setExpanded(expanded);
if (expanded && this.needsRefresh) {
this.onWatchExpressionsUpdatedScheduler.schedule();
}
}
public setVisible(visible: boolean): TPromise<void> {
return super.setVisible(visible).then(() => {
if (visible && this.needsRefresh) {
this.onWatchExpressionsUpdatedScheduler.schedule();
}
});
}
public shutdown(): void {
this.settings[WatchExpressionsView.MEMENTO] = !this.isExpanded();
super.shutdown();
}
}
class WatchExpressionsActionProvider implements IActionProvider {
constructor(private debugService: IDebugService, private keybindingService: IKeybindingService) {
// noop
}
public hasActions(tree: ITree, element: any): boolean {
return element instanceof Expression && !!element.name;
}
public hasSecondaryActions(tree: ITree, element: any): boolean {
return true;
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
return TPromise.as([]);
}
public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
const actions: IAction[] = [];
if (element instanceof Expression) {
const expression = <Expression>element;
actions.push(new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
actions.push(new EditWatchExpressionAction(EditWatchExpressionAction.ID, EditWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
if (!expression.hasChildren) {
actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, expression.value, this.debugService));
}
actions.push(new Separator());
actions.push(new RemoveWatchExpressionAction(RemoveWatchExpressionAction.ID, RemoveWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
actions.push(new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService));
} else {
actions.push(new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService));
if (element instanceof Variable) {
const variable = <Variable>element;
if (!variable.hasChildren) {
actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable.value, this.debugService));
}
actions.push(new Separator());
}
actions.push(new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService));
}
return TPromise.as(actions);
}
public getActionItem(tree: ITree, element: any, action: IAction): IActionItem {
return null;
}
}
class WatchExpressionsDataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
return element.getId();
}
public hasChildren(tree: ITree, element: any): boolean {
if (element instanceof Model) {
return true;
}
const watchExpression = <Expression>element;
return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null');
}
public getChildren(tree: ITree, element: any): TPromise<any> {
if (element instanceof Model) {
return TPromise.as((<Model>element).getWatchExpressions());
}
let expression = <Expression>element;
return expression.getChildren();
}
public getParent(tree: ITree, element: any): TPromise<any> {
return TPromise.as(null);
}
}
interface IWatchExpressionTemplateData {
watchExpression: HTMLElement;
expression: HTMLElement;
name: HTMLSpanElement;
value: HTMLSpanElement;
}
class WatchExpressionsRenderer implements IRenderer {
private static readonly WATCH_EXPRESSION_TEMPLATE_ID = 'watchExpression';
private static readonly VARIABLE_TEMPLATE_ID = 'variables';
private toDispose: IDisposable[];
constructor(
@IDebugService private debugService: IDebugService,
@IContextViewService private contextViewService: IContextViewService,
@IThemeService private themeService: IThemeService
) {
this.toDispose = [];
}
public getHeight(tree: ITree, element: any): number {
return 22;
}
public getTemplateId(tree: ITree, element: any): string {
if (element instanceof Expression) {
return WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID;
}
return WatchExpressionsRenderer.VARIABLE_TEMPLATE_ID;
}
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
const createVariableTemplate = ((data: IVariableTemplateData, container: HTMLElement) => {
data.expression = dom.append(container, $('.expression'));
data.name = dom.append(data.expression, $('span.name'));
data.value = dom.append(data.expression, $('span.value'));
});
if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
const data: IWatchExpressionTemplateData = Object.create(null);
data.watchExpression = dom.append(container, $('.watch-expression'));
createVariableTemplate(data, data.watchExpression);
return data;
}
const data: IVariableTemplateData = Object.create(null);
createVariableTemplate(data, container);
return data;
}
public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
if (templateId === WatchExpressionsRenderer.WATCH_EXPRESSION_TEMPLATE_ID) {
this.renderWatchExpression(tree, element, templateData);
} else {
renderVariable(tree, element, templateData, true);
}
}
private renderWatchExpression(tree: ITree, watchExpression: IExpression, data: IWatchExpressionTemplateData): void {
let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
if ((selectedExpression instanceof Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof Expression && !watchExpression.name)) {
renderRenameBox(this.debugService, this.contextViewService, this.themeService, tree, watchExpression, data.expression, {
initialValue: watchExpression.name,
placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),
ariaLabel: nls.localize('watchExpressionInputAriaLabel', "Type watch expression")
});
}
data.name.textContent = watchExpression.name;
if (watchExpression.value) {
data.name.textContent += ':';
renderExpressionValue(watchExpression, data.value, {
showChanged: true,
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
preserveWhitespace: false,
showHover: true,
colorize: true
});
data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value;
}
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
// noop
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}
}
class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider {
public getAriaLabel(tree: ITree, element: any): string {
if (element instanceof Expression) {
return nls.localize('watchExpressionAriaLabel', "{0} value {1}, watch, debug", (<Expression>element).name, (<Expression>element).value);
}
if (element instanceof Variable) {
return nls.localize('watchVariableAriaLabel', "{0} value {1}, watch, debug", (<Variable>element).name, (<Variable>element).value);
}
return null;
}
}
class WatchExpressionsController extends BaseDebugController {
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent): boolean {
// double click on primitive value: open input box to be able to select and copy value.
if (element instanceof Expression && event.detail === 2) {
const expression = <IExpression>element;
this.debugService.getViewModel().setSelectedExpression(expression);
return true;
}
return super.onLeftClick(tree, element, event);
}
}
class WatchExpressionsDragAndDrop extends DefaultDragAndDrop {
constructor(private debugService: IDebugService) {
super();
}
public getDragURI(tree: ITree, element: Expression): string {
if (!(element instanceof Expression)) {
return null;
}
return element.getId();
}
public getDragLabel(tree: ITree, elements: Expression[]): string {
if (elements.length > 1) {
return String(elements.length);
}
return elements[0].name;
}
public onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): IDragOverReaction {
if (target instanceof Expression || target instanceof Model) {
return {
accept: true,
autoExpand: false
};
}
return DRAG_OVER_REJECT;
}
public drop(tree: ITree, data: IDragAndDropData, target: Expression | Model, originalEvent: DragMouseEvent): void {
const draggedData = data.getData();
if (Array.isArray(draggedData)) {
const draggedElement = <Expression>draggedData[0];
const watches = this.debugService.getModel().getWatchExpressions();
const position = target instanceof Model ? watches.length - 1 : watches.indexOf(target);
this.debugService.moveWatchExpression(draggedElement.getId(), position);
}
}
}

View File

@@ -15,14 +15,12 @@ import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
export class Adapter {
constructor(private rawAdapter: IRawAdapter, public extensionDescription: IExtensionDescription,
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
@IConfigurationService private configurationService: IConfigurationService,
@ICommandService private commandService: ICommandService
) {
@@ -35,7 +33,7 @@ export class Adapter {
public getAdapterExecutable(root: IWorkspaceFolder, verifyAgainstFS = true): TPromise<IAdapterExecutable> {
if (this.rawAdapter.adapterExecutableCommand) {
if (this.rawAdapter.adapterExecutableCommand && root) {
return this.commandService.executeCommand<IAdapterExecutable>(this.rawAdapter.adapterExecutableCommand, root.uri.toString()).then(ad => {
return this.verifyAdapterDetails(ad, verifyAgainstFS);
});
@@ -159,13 +157,13 @@ export class Adapter {
].join('\n');
// fix formatting
const editorConfig = this.configurationService.getConfiguration<any>();
const editorConfig = this.configurationService.getValue<any>();
if (editorConfig.editor && editorConfig.editor.insertSpaces) {
content = content.replace(new RegExp('\t', 'g'), strings.repeat(' ', editorConfig.editor.tabSize));
}
return TPromise.as(content);
};
}
public getSchemaAttributes(): IJSONSchema[] {
if (!this.rawAdapter.configurationAttributes) {
@@ -210,7 +208,7 @@ export class Adapter {
};
properties['internalConsoleOptions'] = INTERNAL_CONSOLE_OPTIONS_SCHEMA;
const osProperties = objects.clone(properties);
const osProperties = objects.deepClone(properties);
properties['windows'] = {
type: 'object',
description: nls.localize('debugWindowsConfiguration', "Windows specific launch configuration attributes."),

View File

@@ -9,7 +9,7 @@ import { canceled } from 'vs/base/common/errors';
export abstract class V8Protocol {
private static TWO_CRLF = '\r\n\r\n';
private static readonly TWO_CRLF = '\r\n\r\n';
private outputStream: stream.Writable;
private sequence: number;

View File

@@ -42,7 +42,6 @@ suite('Debug - View Model', () => {
});
test('multi process view and changed workbench state', () => {
assert.equal(model.changedWorkbenchViewState, false);
assert.equal(model.isMultiProcessView(), false);
model.setMultiProcessView(true);
assert.equal(model.isMultiProcessView(), true);

View File

@@ -44,6 +44,10 @@ export class MockDebugService implements debug.IDebugService {
return TPromise.as(null);
}
public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }): TPromise<void> {
return TPromise.as(null);
}
public enableOrDisableBreakpoints(enabled: boolean): TPromise<void> {
return TPromise.as(null);
}

View File

@@ -44,7 +44,7 @@ suite('Debug - Adapter', () => {
setup(() => {
adapter = new Adapter(rawAdapter, { extensionFolderPath, id: 'adapter', name: 'myAdapter', version: '1.0.0', publisher: 'vscode', isBuiltin: false, engines: null },
null, new TestConfigurationService(), null);
new TestConfigurationService(), null);
});
teardown(() => {

View File

@@ -8,14 +8,13 @@
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
const EMMET_COMMANDS_PREFIX = '>Emmet: ';
@editorAction
class ShowEmmetCommandsAction extends EditorAction {
constructor() {
@@ -27,9 +26,11 @@ class ShowEmmetCommandsAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const quickOpenService = accessor.get(IQuickOpenService);
quickOpenService.show(EMMET_COMMANDS_PREFIX);
return TPromise.as(null);
}
}
}
registerEditorAction(ShowEmmetCommandsAction);

View File

@@ -6,12 +6,11 @@
'use strict';
import nls = require('vs/nls');
import { EmmetEditorAction } from 'vs/workbench/parts/emmet/electron-browser/emmetActions';
import { editorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@editorAction
class ExpandAbbreviationAction extends EmmetEditorAction {
constructor() {
@@ -32,4 +31,6 @@ class ExpandAbbreviationAction extends EmmetEditorAction {
});
}
}
}
registerEditorAction(ExpandAbbreviationAction);

View File

@@ -5,13 +5,13 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { EditorAction, ServicesAccessor, IActionOptions } from 'vs/editor/common/editorCommonExtensions';
import { EditorAction, ServicesAccessor, IActionOptions } from 'vs/editor/browser/editorExtensions';
import { grammarsExtPoint, ITMSyntaxExtensionPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IExtensionService, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
interface ModeScopeMap {
[key: string]: string;
@@ -78,7 +78,7 @@ export abstract class EmmetEditorAction extends EditorAction {
return this._lastGrammarContributions;
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const extensionService = accessor.get(IExtensionService);
const modeService = accessor.get(IModeService);
const commandService = accessor.get(ICommandService);
@@ -94,7 +94,7 @@ export abstract class EmmetEditorAction extends EditorAction {
}
public static getLanguage(languageIdentifierResolver: ILanguageIdentifierResolver, editor: ICommonCodeEditor, grammars: IGrammarContributions) {
public static getLanguage(languageIdentifierResolver: ILanguageIdentifierResolver, editor: ICodeEditor, grammars: IGrammarContributions) {
let position = editor.getSelection().getStartPosition();
editor.getModel().tokenizeIfCheap(position.lineNumber);
let languageId = editor.getModel().getLanguageIdAtPosition(position.lineNumber, position.column);
@@ -127,5 +127,3 @@ export abstract class EmmetEditorAction extends EditorAction {
}

View File

@@ -6,7 +6,7 @@
'use strict';
import { IGrammarContributions, ILanguageIdentifierResolver, EmmetEditorAction } from 'vs/workbench/parts/emmet/electron-browser/emmetActions';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import assert = require('assert');
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
@@ -43,7 +43,7 @@ class MockGrammarContributions implements IGrammarContributions {
suite('Emmet', () => {
test('Get language mode and parent mode for emmet', () => {
withMockCodeEditor([], {}, (editor) => {
withTestCodeEditor([], {}, (editor) => {
function testIsEnabled(mode: string, scopeName: string, expectedLanguage?: string, expectedParentLanguage?: string) {
const languageIdentifier = new LanguageIdentifier(mode, 73);

View File

@@ -120,10 +120,10 @@ export abstract class AbstractOpenInTerminalAction extends Action {
export class OpenConsoleAction extends AbstractOpenInTerminalAction {
public static ID = 'workbench.action.terminal.openNativeConsole';
public static Label = env.isWindows ? nls.localize('globalConsoleActionWin', "Open New Command Prompt") :
public static readonly ID = 'workbench.action.terminal.openNativeConsole';
public static readonly Label = env.isWindows ? nls.localize('globalConsoleActionWin', "Open New Command Prompt") :
nls.localize('globalConsoleActionMacLinux', "Open New Terminal");
public static ScopedLabel = env.isWindows ? nls.localize('scopedConsoleActionWin', "Open in Command Prompt") :
public static readonly ScopedLabel = env.isWindows ? nls.localize('scopedConsoleActionWin', "Open in Command Prompt") :
nls.localize('scopedConsoleActionMacLinux', "Open in Terminal");
constructor(
@@ -147,8 +147,8 @@ export class OpenConsoleAction extends AbstractOpenInTerminalAction {
export class OpenIntegratedTerminalAction extends AbstractOpenInTerminalAction {
public static ID = 'workbench.action.terminal.openFolderInIntegratedTerminal';
public static Label = nls.localize('openFolderInIntegratedTerminal', "Open in Terminal");
public static readonly ID = 'workbench.action.terminal.openFolderInIntegratedTerminal';
public static readonly Label = nls.localize('openFolderInIntegratedTerminal', "Open in Terminal");
constructor(
id: string,
@@ -196,7 +196,7 @@ export class ExplorerViewerActionContributor extends ActionBarContributor {
resource = resources.dirname(resource);
}
const configuration = this.configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this.configurationService.getValue<ITerminalConfiguration>();
const explorerKind = configuration.terminal.explorerKind;
if (explorerKind === 'integrated') {

View File

@@ -28,7 +28,7 @@ enum WinSpawnType {
export class WinTerminalService implements ITerminalService {
public _serviceBrand: any;
private static CMD = 'cmd.exe';
private static readonly CMD = 'cmd.exe';
constructor(
@IConfigurationService private _configurationService: IConfigurationService
@@ -36,7 +36,7 @@ export class WinTerminalService implements ITerminalService {
}
public openTerminal(cwd?: string): void {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd)
.done(null, errors.onUnexpectedError);
@@ -44,7 +44,7 @@ export class WinTerminalService implements ITerminalService {
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise<void> {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
const terminalConfig = configuration.terminal.external;
const exec = terminalConfig.windowsExec || DEFAULT_TERMINAL_WINDOWS;
@@ -60,6 +60,9 @@ export class WinTerminalService implements ITerminalService {
// merge environment variables into a copy of the process.env
const env = assign({}, process.env, envVars);
// delete environment variables that have a null value
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
const options: any = {
cwd: dir,
env: env,
@@ -114,21 +117,21 @@ export class WinTerminalService implements ITerminalService {
export class MacTerminalService implements ITerminalService {
public _serviceBrand: any;
private static OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X
private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X
constructor(
@IConfigurationService private _configurationService: IConfigurationService
) { }
public openTerminal(cwd?: string): void {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
this.spawnTerminal(cp, configuration, cwd).done(null, errors.onUnexpectedError);
}
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise<void> {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
const terminalConfig = configuration.terminal.external;
const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX;
@@ -155,8 +158,14 @@ export class MacTerminalService implements ITerminalService {
if (envVars) {
for (let key in envVars) {
osaArgs.push('-e');
osaArgs.push(key + '=' + envVars[key]);
const value = envVars[key];
if (value === null) {
osaArgs.push('-u');
osaArgs.push(key);
} else {
osaArgs.push('-e');
osaArgs.push(`${key}=${value}`);
}
}
}
@@ -199,7 +208,7 @@ export class MacTerminalService implements ITerminalService {
export class LinuxTerminalService implements ITerminalService {
public _serviceBrand: any;
private static WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue...");
private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue...");
constructor(
@IConfigurationService private _configurationService: IConfigurationService
@@ -207,7 +216,7 @@ export class LinuxTerminalService implements ITerminalService {
public openTerminal(cwd?: string): void {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
this.spawnTerminal(cp, configuration, cwd)
.done(null, errors.onUnexpectedError);
@@ -215,7 +224,7 @@ export class LinuxTerminalService implements ITerminalService {
public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): TPromise<void> {
const configuration = this._configurationService.getConfiguration<ITerminalConfiguration>();
const configuration = this._configurationService.getValue<ITerminalConfiguration>();
const terminalConfig = configuration.terminal.external;
const execPromise = terminalConfig.linuxExec ? TPromise.as(terminalConfig.linuxExec) : DEFAULT_TERMINAL_LINUX_READY;
@@ -239,6 +248,9 @@ export class LinuxTerminalService implements ITerminalService {
// merge environment variables into a copy of the process.env
const env = assign({}, process.env, envVars);
// delete environment variables that have a null value
Object.keys(env).filter(v => env[v] === null).forEach(key => delete env[key]);
const options: any = {
cwd: dir,
env: env

View File

@@ -10,9 +10,9 @@ import { WinTerminalService, LinuxTerminalService, MacTerminalService } from 'vs
import { DEFAULT_TERMINAL_WINDOWS, DEFAULT_TERMINAL_LINUX_READY, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal';
suite('Execution - TerminalService', () => {
let mockOnExit;
let mockOnError;
let mockConfig;
let mockOnExit: Function;
let mockOnError: Function;
let mockConfig: any;
setup(() => {
mockConfig = {
@@ -25,22 +25,22 @@ suite('Execution - TerminalService', () => {
}
}
};
mockOnExit = s => s;
mockOnError = e => e;
mockOnExit = (s: any) => s;
mockOnError = (e: any) => e;
});
test(`WinTerminalService - uses terminal from configuration`, done => {
let testShell = 'cmd';
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(command, testShell, 'shell should equal expected');
equal(args[args.length - 1], mockConfig.terminal.external.windowsExec, 'terminal should equal expected');
equal(opts.cwd, testCwd, 'opts.cwd should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -59,12 +59,12 @@ suite('Execution - TerminalService', () => {
let testShell = 'cmd';
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(args[args.length - 1], DEFAULT_TERMINAL_WINDOWS, 'terminal should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -84,12 +84,12 @@ suite('Execution - TerminalService', () => {
let testShell = 'cmd';
let testCwd = 'c:/foo';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(opts.cwd, 'C:/foo', 'cwd should be uppercase regardless of the case that\'s passed in');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -109,12 +109,12 @@ suite('Execution - TerminalService', () => {
mockConfig.terminal.external.windowsExec = 'cmder';
let testCwd = 'c:/foo';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
deepEqual(args, ['C:/foo']);
equal(opts, undefined);
done();
return { on: (evt) => evt };
return { on: (evt: any) => evt };
}
};
let testService = new WinTerminalService(mockConfig);
@@ -131,12 +131,12 @@ suite('Execution - TerminalService', () => {
test(`MacTerminalService - uses terminal from configuration`, done => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(args[1], mockConfig.terminal.external.osxExec, 'terminal should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -153,12 +153,12 @@ suite('Execution - TerminalService', () => {
test(`MacTerminalService - uses default terminal when configuration.terminal.external.osxExec is undefined`, done => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(args[1], DEFAULT_TERMINAL_OSX, 'terminal should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -176,13 +176,13 @@ suite('Execution - TerminalService', () => {
test(`LinuxTerminalService - uses terminal from configuration`, done => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(command, mockConfig.terminal.external.linuxExec, 'terminal should equal expected');
equal(opts.cwd, testCwd, 'opts.cwd should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};
@@ -200,12 +200,12 @@ suite('Execution - TerminalService', () => {
DEFAULT_TERMINAL_LINUX_READY.then(defaultTerminalLinux => {
let testCwd = 'path/to/workspace';
let mockSpawner = {
spawn: (command, args, opts) => {
spawn: (command: any, args: any, opts: any) => {
// assert
equal(command, defaultTerminalLinux, 'terminal should equal expected');
done();
return {
on: (evt) => evt
on: (evt: any) => evt
};
}
};

View File

@@ -55,8 +55,8 @@ export class DataSource implements IDataSource {
export class Renderer implements IRenderer {
private static EXTENSION_TEMPLATE_ID = 'extension-template';
private static UNKNOWN_EXTENSION_TEMPLATE_ID = 'unknown-extension-template';
private static readonly EXTENSION_TEMPLATE_ID = 'extension-template';
private static readonly UNKNOWN_EXTENSION_TEMPLATE_ID = 'unknown-extension-template';
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
}

View File

@@ -12,12 +12,12 @@ import { marked } from 'vs/base/common/marked/marked';
import { always } from 'vs/base/common/async';
import * as arrays from 'vs/base/common/arrays';
import { OS } from 'vs/base/common/platform';
import Event, { Emitter, once, fromEventEmitter, chain } from 'vs/base/common/event';
import Event, { Emitter, once, chain } from 'vs/base/common/event';
import Cache from 'vs/base/common/cache';
import { Action } from 'vs/base/common/actions';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import Severity from 'vs/base/common/severity';
import { IDisposable, empty, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { Builder } from 'vs/base/browser/builder';
import { domEvent } from 'vs/base/browser/event';
import { append, $, addClass, removeClass, finalHandler, join } from 'vs/base/browser/dom';
@@ -25,17 +25,15 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionGalleryService, IExtensionManifest, IKeyBinding, IView, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManifest, IKeyBinding, IView, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies } from 'vs/workbench/parts/extensions/common/extensions';
import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/browser/dependenciesViewer';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITemplateData } from 'vs/workbench/parts/extensions/browser/extensionsList';
import { RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
import { EditorOptions } from 'vs/workbench/common/editor';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, BuiltinStatusLabelAction, ReloadAction, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
import WebView from 'vs/workbench/parts/html/browser/webview';
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -44,17 +42,16 @@ import { IMessageService } from 'vs/platform/message/common/message';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { Position } from 'vs/platform/editor/common/editor';
import { IListService } from 'vs/platform/list/browser/listService';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextKeyService, RawContextKey, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Command, ICommandOptions } from 'vs/editor/common/editorCommonExtensions';
import { Command, ICommandOptions } from 'vs/editor/browser/editorExtensions';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Color } from 'vs/base/common/color';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
/** A context key that is set when an extension editor webview has focus. */
export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey<boolean>('extensionEditorWebviewFocus', undefined);
@@ -155,18 +152,18 @@ export class ExtensionEditor extends BaseEditor {
private icon: HTMLImageElement;
private name: HTMLElement;
private identifier: HTMLElement;
private preview: HTMLElement;
private license: HTMLElement;
private publisher: HTMLElement;
private installCount: HTMLElement;
private rating: HTMLElement;
private repository: HTMLElement;
private description: HTMLElement;
private extensionActionBar: ActionBar;
private navbar: NavBar;
private content: HTMLElement;
private recommendation: HTMLElement;
private header: HTMLElement;
private _highlight: ITemplateData;
private highlightDisposable: IDisposable;
private extensionReadme: Cache<string>;
private extensionChangelog: Cache<string>;
@@ -183,8 +180,6 @@ export class ExtensionEditor extends BaseEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@@ -199,15 +194,13 @@ export class ExtensionEditor extends BaseEditor {
@IExtensionTipsService private extensionTipsService: IExtensionTipsService
) {
super(ExtensionEditor.ID, telemetryService, themeService);
this._highlight = null;
this.highlightDisposable = empty;
this.disposables = [];
this.extensionReadme = null;
this.extensionChangelog = null;
this.extensionManifest = null;
this.extensionDependencies = null;
this.contextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.bindTo(contextKeyService);
this.findInputFocusContextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(contextKeyService);
this.contextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS.bindTo(this.contextKeyService);
this.findInputFocusContextKey = KEYBINDING_CONTEXT_EXTENSIONEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(this.contextKeyService);
}
createEditor(parent: Builder): void {
@@ -222,6 +215,7 @@ export class ExtensionEditor extends BaseEditor {
const title = append(details, $('.title'));
this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") }));
this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") }));
this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") }));
const subtitle = append(details, $('.subtitle'));
this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name") }));
@@ -230,6 +224,10 @@ export class ExtensionEditor extends BaseEditor {
this.rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating") }));
this.repository = append(subtitle, $('span.repository.clickable'));
this.repository.textContent = localize('repository', 'Repository');
this.repository.style.display = 'none';
this.license = append(subtitle, $('span.license.clickable'));
this.license.textContent = localize('license', 'License');
this.license.style.display = 'none';
@@ -253,7 +251,7 @@ export class ExtensionEditor extends BaseEditor {
this.recommendation = append(details, $('.recommendation'));
chain(fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run'))
chain(this.extensionActionBar.onDidRun)
.map(({ error }) => error)
.filter(error => !!error)
.on(this.onError, this, this.disposables);
@@ -289,6 +287,11 @@ export class ExtensionEditor extends BaseEditor {
this.name.textContent = extension.displayName;
this.identifier.textContent = extension.id;
if (extension.preview) {
this.preview.textContent = localize('preview', "Preview");
} else {
this.preview.textContent = null;
}
this.publisher.textContent = extension.publisherDisplayName;
this.description.textContent = extension.description;
@@ -319,6 +322,15 @@ export class ExtensionEditor extends BaseEditor {
}
}
if (extension.repository) {
this.repository.onclick = finalHandler(() => window.open(extension.repository));
this.repository.style.display = 'initial';
}
else {
this.repository.onclick = null;
this.repository.style.display = 'none';
}
const install = this.instantiationService.createInstance(InstallWidget, this.installCount, { extension });
this.transientDisposables.push(install);
@@ -326,6 +338,7 @@ export class ExtensionEditor extends BaseEditor {
this.transientDisposables.push(ratings);
const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, true);
const installAction = this.instantiationService.createInstance(CombinedInstallAction);
const updateAction = this.instantiationService.createInstance(UpdateAction);
const enableAction = this.instantiationService.createInstance(EnableAction);
@@ -334,14 +347,15 @@ export class ExtensionEditor extends BaseEditor {
installAction.extension = extension;
builtinStatusAction.extension = extension;
maliciousStatusAction.extension = extension;
updateAction.extension = extension;
enableAction.extension = extension;
disableAction.extension = extension;
reloadAction.extension = extension;
this.extensionActionBar.clear();
this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction], { icon: true, label: true });
this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction);
this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, builtinStatusAction, maliciousStatusAction], { icon: true, label: true });
this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, builtinStatusAction, maliciousStatusAction);
this.navbar.clear();
this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables);
@@ -366,12 +380,6 @@ export class ExtensionEditor extends BaseEditor {
}
}
hideFind(): void {
if (this.activeWebview) {
this.activeWebview.hideFind();
}
}
public showNextFindTerm() {
if (this.activeWebview) {
this.activeWebview.showNextFindTerm();
@@ -466,6 +474,8 @@ export class ExtensionEditor extends BaseEditor {
append(this.content, scrollableContent.getDomNode());
this.contentDisposables.push(scrollableContent);
}
}, () => {
append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions");
}));
}
@@ -503,7 +513,7 @@ export class ExtensionEditor extends BaseEditor {
private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
const renderer = this.instantiationService.createInstance(Renderer);
const controller = this.instantiationService.createInstance(Controller);
const tree = new Tree(container, {
const tree = new WorkbenchTree(container, {
dataSource: new DataSource(),
renderer,
controller
@@ -511,20 +521,16 @@ export class ExtensionEditor extends BaseEditor {
indentPixels: 40,
twistiePixels: 20,
keyboardSupport: false
});
this.contentDisposables.push(attachListStyler(tree, this.themeService));
}, this.contextKeyService, this.listService, this.themeService);
tree.setInput(extensionDependencies);
this.contentDisposables.push(tree.addListener('selection', event => {
this.contentDisposables.push(tree.onDidChangeSelection(event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
controller.openExtension(tree, false);
}
}));
this.contentDisposables.push(this.listService.register(tree));
return tree;
}
@@ -895,7 +901,6 @@ export class ExtensionEditor extends BaseEditor {
}
dispose(): void {
this._highlight = null;
this.transientDisposables = dispose(this.transientDisposables);
this.disposables = dispose(this.disposables);
super.dispose();
@@ -927,31 +932,6 @@ const showCommand = new ShowExtensionEditorFindCommand({
});
KeybindingsRegistry.registerCommandAndKeybindingRule(showCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class HideExtensionEditorFindCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = this.getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.hideFind();
}
}
private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor {
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor() as ExtensionEditor;
if (activeEditor instanceof ExtensionEditor) {
return activeEditor;
}
return null;
}
}
const hideCommand = new ShowExtensionEditorFindCommand({
id: 'editor.action.extensioneditor.hidefind',
precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class ShowExtensionEditorFindTermCommand extends Command {
constructor(opts: ICommandOptions, private _next: boolean) {
super(opts);

View File

@@ -14,10 +14,10 @@ import Event from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
@@ -41,14 +41,17 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions';
import Severity from 'vs/base/common/severity';
import { PagedModel } from 'vs/base/common/paging';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
export class InstallAction extends Action {
private static InstallLabel = localize('installAction', "Install");
private static InstallingLabel = localize('installing', "Installing");
private static readonly InstallLabel = localize('installAction', "Install");
private static readonly InstallingLabel = localize('installing', "Installing");
private static Class = 'extension-action prominent install';
private static InstallingClass = 'extension-action install installing';
private static readonly Class = 'extension-action prominent install';
private static readonly InstallingClass = 'extension-action install installing';
private disposables: IDisposable[] = [];
private _extension: IExtension;
@@ -98,11 +101,11 @@ export class InstallAction extends Action {
export class UninstallAction extends Action {
private static UninstallLabel = localize('uninstallAction', "Uninstall");
private static UninstallingLabel = localize('Uninstalling', "Uninstalling");
private static readonly UninstallLabel = localize('uninstallAction', "Uninstall");
private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling");
private static UninstallClass = 'extension-action uninstall';
private static UnInstallingClass = 'extension-action uninstall uninstalling';
private static readonly UninstallClass = 'extension-action uninstall';
private static readonly UnInstallingClass = 'extension-action uninstall uninstalling';
private disposables: IDisposable[] = [];
private _extension: IExtension;
@@ -110,9 +113,7 @@ export class UninstallAction extends Action {
set extension(extension: IExtension) { this._extension = extension; this.update(); }
constructor(
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IMessageService private messageService: IMessageService,
@IInstantiationService private instantiationService: IInstantiationService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false);
@@ -165,7 +166,7 @@ export class UninstallAction extends Action {
export class CombinedInstallAction extends Action {
private static NoExtensionClass = 'extension-action prominent install no-extension';
private static readonly NoExtensionClass = 'extension-action prominent install no-extension';
private installAction: InstallAction;
private uninstallAction: UninstallAction;
private disposables: IDisposable[] = [];
@@ -241,9 +242,9 @@ export class CombinedInstallAction extends Action {
export class UpdateAction extends Action {
private static EnabledClass = 'extension-action prominent update';
private static DisabledClass = `${UpdateAction.EnabledClass} disabled`;
private static Label = localize('updateAction', "Update");
private static readonly EnabledClass = 'extension-action prominent update';
private static readonly DisabledClass = `${UpdateAction.EnabledClass} disabled`;
private static readonly Label = localize('updateAction', "Update");
private disposables: IDisposable[] = [];
private _extension: IExtension;
@@ -334,7 +335,7 @@ export class DropDownMenuActionItem extends ActionItem {
private getActions(): IAction[] {
let actions: IAction[] = [];
const menuActionGroups = this.menuActionGroups.filter(group => group.some(action => action.enabled));
const menuActionGroups = this.menuActionGroups;
for (const menuActions of menuActionGroups) {
actions = [...actions, ...menuActions, new Separator()];
}
@@ -351,8 +352,8 @@ export class ManageExtensionAction extends Action {
static ID = 'extensions.manage';
private static Class = 'extension-action manage';
private static HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
private static readonly Class = 'extension-action manage';
private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`;
private _actionItem: DropDownMenuActionItem;
get actionItem(): IActionItem { return this._actionItem; }
@@ -363,21 +364,19 @@ export class ManageExtensionAction extends Action {
set extension(extension: IExtension) { this._extension = extension; this._actionItem.extension = extension; this.update(); }
constructor(
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super(ManageExtensionAction.ID);
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [
[
instantiationService.createInstance(EnableForWorkspaceAction, localize('enableForWorkspaceAction.label', "Enable (Workspace)")),
instantiationService.createInstance(EnableGloballyAction, localize('enableAlwaysAction.label', "Enable (Always)"))
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL),
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)
],
[
instantiationService.createInstance(DisableForWorkspaceAction, localize('disableForWorkspaceAction.label', "Disable (Workspace)")),
instantiationService.createInstance(DisableGloballyAction, localize('disableAlwaysAction.label', "Disable (Always)"))
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL),
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)
],
[
instantiationService.createInstance(UninstallAction)
@@ -415,7 +414,7 @@ export class ManageExtensionAction extends Action {
export class EnableForWorkspaceAction extends Action implements IExtensionAction {
static ID = 'extensions.enableForWorkspace';
static LABEL = localize('enableForWorkspaceAction', "Workspace");
static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)");
private disposables: IDisposable[] = [];
@@ -426,8 +425,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction
constructor(label: string,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IInstantiationService private instantiationService: IInstantiationService
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
) {
super(EnableForWorkspaceAction.ID, label);
@@ -439,12 +437,12 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction
private update(): void {
this.enabled = false;
if (this.extension) {
this.enabled = !this.extension.disabledGlobally && this.extension.disabledForWorkspace && this.extensionEnablementService.canEnable(this.extension);
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extensionEnablementService.canChangeEnablement();
}
}
run(): TPromise<any> {
return this.extensionsWorkbenchService.setEnablement(this.extension, true, true);
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.WorkspaceEnabled);
}
dispose(): void {
@@ -456,7 +454,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction
export class EnableGloballyAction extends Action implements IExtensionAction {
static ID = 'extensions.enableGlobally';
static LABEL = localize('enableGloballyAction', "Always");
static LABEL = localize('enableGloballyAction', "Enable");
private disposables: IDisposable[] = [];
@@ -466,8 +464,7 @@ export class EnableGloballyAction extends Action implements IExtensionAction {
constructor(label: string,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IInstantiationService private instantiationService: IInstantiationService
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
) {
super(EnableGloballyAction.ID, label);
@@ -478,12 +475,12 @@ export class EnableGloballyAction extends Action implements IExtensionAction {
private update(): void {
this.enabled = false;
if (this.extension) {
this.enabled = this.extension.disabledGlobally && this.extensionEnablementService.canEnable(this.extension);
this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extensionEnablementService.canChangeEnablement();
}
}
run(): TPromise<any> {
return this.extensionsWorkbenchService.setEnablement(this.extension, true, false);
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.Enabled);
}
dispose(): void {
@@ -495,11 +492,13 @@ export class EnableGloballyAction extends Action implements IExtensionAction {
export class EnableAction extends Action {
static ID = 'extensions.enable';
private static EnabledClass = 'extension-action prominent enable';
private static DisabledClass = `${EnableAction.EnabledClass} disabled`;
private static readonly EnabledClass = 'extension-action prominent enable';
private static readonly DisabledClass = `${EnableAction.EnabledClass} disabled`;
private disposables: IDisposable[] = [];
private _enableActions: IExtensionAction[];
private _actionItem: DropDownMenuActionItem;
get actionItem(): IActionItem { return this._actionItem; }
@@ -510,17 +509,15 @@ export class EnableAction extends Action {
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false);
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [
[
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL),
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)
]
]);
this._enableActions = [
instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL),
instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL)
];
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._enableActions]);
this.disposables.push(this._actionItem);
this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
@@ -534,7 +531,7 @@ export class EnableAction extends Action {
return;
}
this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.disabledGlobally || this.extension.disabledForWorkspace) && this.extensionEnablementService.canEnable(this.extension);
this.enabled = this.extension.state === ExtensionState.Installed && this._enableActions.some(e => e.enabled);
this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass;
}
@@ -553,7 +550,7 @@ export class EnableAction extends Action {
export class DisableForWorkspaceAction extends Action implements IExtensionAction {
static ID = 'extensions.disableForWorkspace';
static LABEL = localize('disableForWorkspaceAction', "Workspace");
static LABEL = localize('disableForWorkspaceAction', "Disable (Workspace)");
private disposables: IDisposable[] = [];
@@ -563,8 +560,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
constructor(label: string,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IInstantiationService private instantiationService: IInstantiationService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(DisableForWorkspaceAction.ID, label);
@@ -576,12 +572,12 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
private update(): void {
this.enabled = false;
if (this.extension && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace;
this.enabled = this.extension.type !== LocalExtensionType.System && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled);
}
}
run(): TPromise<any> {
return this.extensionsWorkbenchService.setEnablement(this.extension, false, true);
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.WorkspaceDisabled);
}
dispose(): void {
@@ -593,7 +589,7 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio
export class DisableGloballyAction extends Action implements IExtensionAction {
static ID = 'extensions.disableGlobally';
static LABEL = localize('disableGloballyAction', "Always");
static LABEL = localize('disableGloballyAction', "Disable");
private disposables: IDisposable[] = [];
@@ -602,8 +598,7 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
set extension(extension: IExtension) { this._extension = extension; this.update(); }
constructor(label: string,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IInstantiationService private instantiationService: IInstantiationService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(DisableGloballyAction.ID, label);
@@ -614,12 +609,12 @@ export class DisableGloballyAction extends Action implements IExtensionAction {
private update(): void {
this.enabled = false;
if (this.extension) {
this.enabled = this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace;
this.enabled = this.extension.type !== LocalExtensionType.System && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled);
}
}
run(): TPromise<any> {
return this.extensionsWorkbenchService.setEnablement(this.extension, false, false);
return this.extensionsWorkbenchService.setEnablement(this.extension, EnablementState.Disabled);
}
dispose(): void {
@@ -632,10 +627,11 @@ export class DisableAction extends Action {
static ID = 'extensions.disable';
private static EnabledClass = 'extension-action disable';
private static DisabledClass = `${DisableAction.EnabledClass} disabled`;
private static readonly EnabledClass = 'extension-action disable';
private static readonly DisabledClass = `${DisableAction.EnabledClass} disabled`;
private disposables: IDisposable[] = [];
private _disableActions: IExtensionAction[];
private _actionItem: DropDownMenuActionItem;
get actionItem(): IActionItem { return this._actionItem; }
@@ -649,12 +645,11 @@ export class DisableAction extends Action {
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
) {
super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false);
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [
[
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL),
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)
]
]);
this._disableActions = [
instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL),
instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL)
];
this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._disableActions]);
this.disposables.push(this._actionItem);
this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update()));
@@ -668,7 +663,7 @@ export class DisableAction extends Action {
return;
}
this.enabled = this.extension.state === ExtensionState.Installed && this.extension.type !== LocalExtensionType.System && !this.extension.disabledGlobally && !this.extension.disabledForWorkspace;
this.enabled = this.extension.state === ExtensionState.Installed && this.extension.type !== LocalExtensionType.System && this._disableActions.some(a => a.enabled);
this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass;
}
@@ -789,22 +784,22 @@ export class UpdateAllAction extends Action {
export class ReloadAction extends Action {
private static EnabledClass = 'extension-action reload';
private static DisabledClass = `${ReloadAction.EnabledClass} disabled`;
private static readonly EnabledClass = 'extension-action reload';
private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`;
private disposables: IDisposable[] = [];
private _extension: IExtension;
get extension(): IExtension { return this._extension; }
set extension(extension: IExtension) { this._extension = extension; this.update(); }
reloadMessaage: string = '';
reloadMessage: string = '';
private throttler: Throttler;
constructor(
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IMessageService private messageService: IMessageService,
@IWindowService private windowService: IWindowService,
@IExtensionService private extensionService: IExtensionService
@IExtensionService private extensionService: IExtensionService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
) {
super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false);
this.throttler = new Throttler();
@@ -817,7 +812,7 @@ export class ReloadAction extends Action {
this.throttler.queue(() => {
this.enabled = false;
this.tooltip = '';
this.reloadMessaage = '';
this.reloadMessage = '';
if (!this.extension) {
return TPromise.wrap<void>(null);
}
@@ -835,7 +830,7 @@ export class ReloadAction extends Action {
private computeReloadState(runningExtensions: IExtensionDescription[]): void {
const isInstalled = this.extensionsWorkbenchService.local.some(e => e.id === this.extension.id);
const isUninstalled = this.extension.state === ExtensionState.Uninstalled;
const isDisabled = this.extension.disabledForWorkspace || this.extension.disabledGlobally;
const isDisabled = !this.extensionEnablementService.isEnabled({ id: this.extension.id, uuid: this.extension.uuid });
const filteredExtensions = runningExtensions.filter(e => areSameExtensions(e, this.extension));
const isExtensionRunning = filteredExtensions.length > 0;
@@ -846,7 +841,7 @@ export class ReloadAction extends Action {
// Requires reload to run the updated extension
this.enabled = true;
this.tooltip = localize('postUpdateTooltip', "Reload to update");
this.reloadMessaage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName);
this.reloadMessage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName);
return;
}
@@ -854,7 +849,7 @@ export class ReloadAction extends Action {
// Requires reload to enable the extension
this.enabled = true;
this.tooltip = localize('postEnableTooltip', "Reload to activate");
this.reloadMessaage = localize('postEnableMessage', "Reload this window to activate the extension '{0}'?", this.extension.displayName);
this.reloadMessage = localize('postEnableMessage', "Reload this window to activate the extension '{0}'?", this.extension.displayName);
return;
}
@@ -862,7 +857,7 @@ export class ReloadAction extends Action {
// Requires reload to disable the extension
this.enabled = true;
this.tooltip = localize('postDisableTooltip', "Reload to deactivate");
this.reloadMessaage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName);
this.reloadMessage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName);
return;
}
return;
@@ -872,7 +867,7 @@ export class ReloadAction extends Action {
// Requires reload to deactivate the extension
this.enabled = true;
this.tooltip = localize('postUninstallTooltip', "Reload to deactivate");
this.reloadMessaage = localize('postUninstallMessage', "Reload this window to deactivate the uninstalled extension '{0}'?", this.extension.displayName);
this.reloadMessage = localize('postUninstallMessage', "Reload this window to deactivate the uninstalled extension '{0}'?", this.extension.displayName);
return;
}
}
@@ -910,8 +905,7 @@ export class ShowEnabledExtensionsAction extends Action {
constructor(
id: string,
label: string,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
@IViewletService private viewletService: IViewletService
) {
super(id, label, 'clear-extensions', true);
}
@@ -934,8 +928,7 @@ export class ShowInstalledExtensionsAction extends Action {
constructor(
id: string,
label: string,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
@IViewletService private viewletService: IViewletService
) {
super(id, label, 'clear-extensions', true);
}
@@ -958,8 +951,7 @@ export class ShowDisabledExtensionsAction extends Action {
constructor(
id: string,
label: string,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
@IViewletService private viewletService: IViewletService
) {
super(id, label, 'null', true);
}
@@ -985,8 +977,7 @@ export class ClearExtensionsInputAction extends Action {
id: string,
label: string,
onSearchChange: Event<string>,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
@IViewletService private viewletService: IViewletService
) {
super(id, label, 'clear-extensions', true);
this.enabled = false;
@@ -1032,10 +1023,6 @@ export class ShowOutdatedExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class ShowPopularExtensionsAction extends Action {
@@ -1059,10 +1046,6 @@ export class ShowPopularExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class ShowRecommendedExtensionsAction extends Action {
@@ -1086,10 +1069,6 @@ export class ShowRecommendedExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class InstallWorkspaceRecommendedExtensionsAction extends Action {
@@ -1163,10 +1142,6 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action {
});
}
protected isEnabled(): boolean {
return this.enabled;
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
@@ -1179,6 +1154,7 @@ export class InstallRecommendedExtensionAction extends Action {
static LABEL = localize('installRecommendedExtension', "Install Recommended Extension");
private extensionId: string;
private disposables: IDisposable[] = [];
constructor(
extensionId: string,
@@ -1188,6 +1164,11 @@ export class InstallRecommendedExtensionAction extends Action {
) {
super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, null);
this.extensionId = extensionId;
this.extensionsWorkbenchService.onChange(() => this.update(), this, this.disposables);
}
private update(): void {
this.enabled = !this.extensionsWorkbenchService.local.some(x => x.id.toLowerCase() === this.extensionId.toLowerCase());
}
run(): TPromise<any> {
@@ -1210,11 +1191,8 @@ export class InstallRecommendedExtensionAction extends Action {
});
}
protected isEnabled(): boolean {
return !this.extensionsWorkbenchService.local.some(x => x.id.toLowerCase() === this.extensionId.toLowerCase());
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
@@ -1223,7 +1201,6 @@ export class InstallRecommendedExtensionAction extends Action {
export class ShowRecommendedKeymapExtensionsAction extends Action {
static ID = 'workbench.extensions.action.showRecommendedKeymapExtensions';
static LABEL = localize('showRecommendedKeymapExtensions', "Show Recommended Keymaps");
static SHORT_LABEL = localize('showRecommendedKeymapExtensionsShort', "Keymaps");
constructor(
@@ -1242,16 +1219,11 @@ export class ShowRecommendedKeymapExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class ShowLanguageExtensionsAction extends Action {
static ID = 'workbench.extensions.action.showLanguageExtensions';
static LABEL = localize('showLanguageExtensions', "Show Language Extensions");
static SHORT_LABEL = localize('showLanguageExtensionsShort', "Language Extensions");
constructor(
@@ -1270,16 +1242,11 @@ export class ShowLanguageExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class ShowAzureExtensionsAction extends Action {
static ID = 'workbench.extensions.action.showAzureExtensions';
static LABEL = localize('showAzureExtensions', "Show Azure Extensions");
static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions");
constructor(
@@ -1298,10 +1265,6 @@ export class ShowAzureExtensionsAction extends Action {
viewlet.focus();
});
}
protected isEnabled(): boolean {
return true;
}
}
export class ChangeSortAction extends Action {
@@ -1341,9 +1304,52 @@ export class ChangeSortAction extends Action {
viewlet.focus();
});
}
}
protected isEnabled(): boolean {
return true;
export class ConfigureRecommendedExtensionsCommandsContributor extends Disposable implements IWorkbenchContribution {
private workspaceContextKey = new RawContextKey<boolean>('workspaceRecommendations', true);
private workspaceFolderContextKey = new RawContextKey<boolean>('workspaceFolderRecommendations', true);
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService
) {
super();
const boundWorkspaceContextKey = this.workspaceContextKey.bindTo(contextKeyService);
boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE);
this._register(workspaceContextService.onDidChangeWorkbenchState(() => boundWorkspaceContextKey.set(workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE)));
const boundWorkspaceFolderContextKey = this.workspaceFolderContextKey.bindTo(contextKeyService);
boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0);
this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => boundWorkspaceFolderContextKey.set(workspaceContextService.getWorkspace().folders.length > 0)));
this.registerCommands();
}
private registerCommands(): void {
CommandsRegistry.registerCommand(ConfigureWorkspaceRecommendedExtensionsAction.ID, serviceAccessor => {
serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL).run();
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: ConfigureWorkspaceRecommendedExtensionsAction.ID,
title: `${ExtensionsLabel}: ${ConfigureWorkspaceRecommendedExtensionsAction.LABEL}`,
},
when: this.workspaceContextKey
});
CommandsRegistry.registerCommand(ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, serviceAccessor => {
serviceAccessor.get(IInstantiationService).createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL).run();
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: ConfigureWorkspaceFolderRecommendedExtensionsAction.ID,
title: `${ExtensionsLabel}: ${ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL}`,
},
when: this.workspaceFolderContextKey
});
}
}
@@ -1367,20 +1373,22 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
protected openExtensionsFile(extensionsFileResource: URI): TPromise<any> {
return this.getOrCreateExtensionsFile(extensionsFileResource)
.then(({ created }) => {
return this.editorService.openEditor({
resource: extensionsFileResource,
options: {
forceOpen: true,
pinned: created
},
});
}, error => TPromise.wrapError(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));
.then(({ created, content }) =>
this.getSelectionPosition(content, extensionsFileResource, ['recommendations'])
.then(selection => this.editorService.openEditor({
resource: extensionsFileResource,
options: {
forceOpen: true,
pinned: created,
selection
}
})),
error => TPromise.wrapError(new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error))));
}
protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): TPromise<any> {
return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
.then(content => this.getSelectionPosition(content))
.then(content => this.getSelectionPosition(content.value, content.resource, ['extensions', 'recommendations']))
.then(selection => this.editorService.openEditor({
resource: workspaceConfigurationFile,
options: {
@@ -1402,12 +1410,14 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
});
}
private getSelectionPosition(content: IContent): TPromise<ITextEditorSelection> {
const tree = json.parseTree(content.value);
const node = json.findNodeAtLocation(tree, ['extensions', 'recommendations']);
private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): TPromise<ITextEditorSelection> {
const tree = json.parseTree(content);
const node = json.findNodeAtLocation(tree, path);
if (node && node.parent.children[1]) {
const offset = node.parent.children[1].offset;
return this.textModelResolverService.createModelReference(content.resource)
const recommendationsValueNode = node.parent.children[1];
const lastExtensionNode = recommendationsValueNode.children && recommendationsValueNode.children.length ? recommendationsValueNode.children[recommendationsValueNode.children.length - 1] : null;
const offset = lastExtensionNode ? lastExtensionNode.offset + lastExtensionNode.length : recommendationsValueNode.offset + 1;
return this.textModelResolverService.createModelReference(resource)
.then(reference => {
const position = reference.object.textEditorModel.getPositionAt(offset);
reference.dispose();
@@ -1422,12 +1432,12 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
return TPromise.as(null);
}
private getOrCreateExtensionsFile(extensionsFileResource: URI): TPromise<{ created: boolean, extensionsFileResource: URI }> {
private getOrCreateExtensionsFile(extensionsFileResource: URI): TPromise<{ created: boolean, extensionsFileResource: URI, content: string }> {
return this.fileService.resolveContent(extensionsFileResource).then(content => {
return { created: false, extensionsFileResource };
return { created: false, extensionsFileResource, content: content.value };
}, err => {
return this.fileService.updateContent(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
return { created: true, extensionsFileResource };
return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
});
});
}
@@ -1458,7 +1468,7 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfi
this.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
}
public run(event: any): TPromise<any> {
public run(): TPromise<any> {
switch (this.contextService.getWorkbenchState()) {
case WorkbenchState.FOLDER:
// {{SQL CARBON EDIT}}
@@ -1522,7 +1532,7 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac
export class BuiltinStatusLabelAction extends Action {
private static Class = 'extension-action built-in-status';
private static readonly Class = 'built-in-status';
private _extension: IExtension;
get extension(): IExtension { return this._extension; }
@@ -1545,6 +1555,34 @@ export class BuiltinStatusLabelAction extends Action {
}
}
export class MaliciousStatusLabelAction extends Action {
private static readonly Class = 'malicious-status';
private _extension: IExtension;
get extension(): IExtension { return this._extension; }
set extension(extension: IExtension) { this._extension = extension; this.update(); }
constructor(long: boolean) {
const tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
const label = long ? tooltip : localize('malicious', "Malicious");
super('extensions.install', label, '', false);
this.tooltip = localize('malicious tooltip', "This extension was reported to be malicious.");
}
private update(): void {
if (this.extension && this.extension.isMalicious) {
this.class = `${MaliciousStatusLabelAction.Class} malicious`;
} else {
this.class = `${MaliciousStatusLabelAction.Class} not-malicious`;
}
}
run(): TPromise<any> {
return TPromise.as(null);
}
}
export class DisableAllAction extends Action {
static ID = 'workbench.extensions.action.disableAll';
@@ -1554,8 +1592,7 @@ export class DisableAllAction extends Action {
constructor(
id: string = DisableAllAction.ID, label: string = DisableAllAction.LABEL,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(id, label);
this.update();
@@ -1563,11 +1600,11 @@ export class DisableAllAction extends Action {
}
private update(): void {
this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally);
this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled));
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false)));
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled)));
}
dispose(): void {
@@ -1586,8 +1623,7 @@ export class DisableAllWorkpsaceAction extends Action {
constructor(
id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(id, label);
this.update();
@@ -1596,11 +1632,11 @@ export class DisableAllWorkpsaceAction extends Action {
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && !e.disabledForWorkspace && !e.disabledGlobally);
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled));
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, false, true)));
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled)));
}
dispose(): void {
@@ -1627,11 +1663,11 @@ export class EnableAllAction extends Action {
}
private update(): void {
this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e) && e.disabledGlobally);
this.enabled = this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canChangeEnablement() && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true)));
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Enabled)));
}
dispose(): void {
@@ -1660,11 +1696,11 @@ export class EnableAllWorkpsaceAction extends Action {
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canEnable(e) && !e.disabledGlobally && e.disabledForWorkspace);
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => this.extensionEnablementService.canChangeEnablement() && (e.enablementState === EnablementState.Disabled || e.enablementState === EnablementState.WorkspaceDisabled));
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, true, true)));
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceEnabled)));
}
dispose(): void {
@@ -1742,4 +1778,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (extensionButtonProminentHoverBackground) {
collector.addRule(`.monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`);
}
});
});

View File

@@ -16,11 +16,9 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { once } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground } from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { InstallAction, UpdateAction, BuiltinStatusLabelAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, MaliciousStatusLabelAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Label, RatingsWidget, InstallWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets';
import { EventType } from 'vs/base/common/events';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -50,7 +48,6 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IMessageService private messageService: IMessageService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private extensionService: IExtensionService,
@@ -91,20 +88,21 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
return null;
}
});
actionbar.addListener(EventType.RUN, ({ error }) => error && this.messageService.show(Severity.Error, error));
actionbar.onDidRun(({ error }) => error && this.messageService.show(Severity.Error, error));
const versionWidget = this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version);
const installCountWidget = this.instantiationService.createInstance(InstallWidget, installCount, { small: true });
const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true });
const builtinStatusAction = this.instantiationService.createInstance(BuiltinStatusLabelAction);
const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, false);
const installAction = this.instantiationService.createInstance(InstallAction);
const updateAction = this.instantiationService.createInstance(UpdateAction);
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const manageAction = this.instantiationService.createInstance(ManageExtensionAction);
actionbar.push([reloadAction, updateAction, installAction, builtinStatusAction, manageAction], actionOptions);
const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
actionbar.push([reloadAction, updateAction, installAction, builtinStatusAction, maliciousStatusAction, manageAction], actionOptions);
const disposables = [versionWidget, installCountWidget, ratingsWidget, builtinStatusAction, maliciousStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
return {
root, element, icon, name, installCount, ratings, author, description, disposables,
@@ -114,6 +112,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
installCountWidget.extension = extension;
ratingsWidget.extension = extension;
builtinStatusAction.extension = extension;
maliciousStatusAction.extension = extension;
installAction.extension = extension;
updateAction.extension = extension;
reloadAction.extension = extension;

View File

@@ -11,6 +11,7 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
class SimpleEntry extends QuickOpenEntry {
@@ -75,7 +76,8 @@ export class GalleryExtensionsHandler extends QuickOpenHandler {
constructor(
@IViewletService private viewletService: IViewletService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@IExtensionManagementService private extensionsService: IExtensionManagementService
@IExtensionManagementService private extensionsService: IExtensionManagementService,
@IMessageService private messageService: IMessageService
) {
super();
}
@@ -97,7 +99,8 @@ export class GalleryExtensionsHandler extends QuickOpenHandler {
return this.viewletService.openViewlet(VIEWLET_ID, true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => viewlet.search(`@id:${text}`))
.done(() => this.extensionsService.installFromGallery(galleryExtension));
.then(() => this.extensionsService.installFromGallery(galleryExtension))
.done(null, err => this.messageService.show(Severity.Error, err));
};
entries.push(new SimpleEntry(label, action));

View File

@@ -10,6 +10,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtension, IExtensionsWorkbenchService } from '../common/extensions';
import { append, $, addClass } from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
export interface IOptions {
extension?: IExtension;
@@ -142,6 +143,7 @@ export class RatingsWidget implements IDisposable {
const count = append(this.container, $('span.count'));
count.textContent = String(rating);
this.container.title = this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('ratedBySingleUser', "Rated by 1 user");
} else {
for (let i = 1; i <= 5; i++) {
if (rating >= i) {

View File

@@ -30,19 +30,24 @@
.monaco-action-bar .action-item.disabled .action-label.extension-action.enable,
.monaco-action-bar .action-item.disabled .action-label.extension-action.disable,
.monaco-action-bar .action-item.disabled .action-label.extension-action.reload,
.monaco-action-bar .action-item.disabled .action-label.extension-action.built-in-status.user {
.monaco-action-bar .action-item.disabled .action-label.built-in-status.user,
.monaco-action-bar .action-item.disabled .action-label.malicious-status.not-malicious {
display: none;
}
.monaco-action-bar .action-item .action-label.extension-action.built-in-status {
.monaco-action-bar .action-item .action-label.built-in-status
.monaco-action-bar .action-item .action-label.malicious-status {
border-radius: 4px;
color: inherit;
background-color: transparent;
opacity: 0.9;
font-style: italic;
padding: 0 5px;
line-height: initial;
}
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-action.built-in-status {
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.built-in-status,
.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status {
font-weight: normal;
}

View File

@@ -15,7 +15,6 @@
.extension-editor > .header {
display: flex;
height: 128px;
background: rgba(128, 128, 128, 0.15);
padding: 20px;
overflow: hidden;
font-size: 14px;
@@ -62,6 +61,19 @@
white-space: nowrap;
}
.vs .extension-editor > .header > .details > .title > .preview {
color: white;
}
.extension-editor > .header > .details > .title > .preview {
background: rgb(214, 63, 38);
font-size: 10px;
font-style: italic;
margin-left: 10px;
padding: 0px 4px;
border-radius: 4px;
}
.extension-editor > .header > .details > .subtitle {
padding-top: 6px;
white-space: nowrap;
@@ -324,4 +336,4 @@
font-size: 90%;
font-weight: 600;
opacity: 0.6;
}
}

View File

@@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import Event from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPager } from 'vs/base/common/paging';
import { IQueryOptions, IExtensionManifest, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
export const VIEWLET_ID = 'workbench.view.extensions';
@@ -36,6 +36,7 @@ export interface IExtension {
latestVersion: string;
description: string;
url: string;
repository: string;
iconUrl: string;
iconUrlFallback: string;
licenseUrl: string;
@@ -43,13 +44,14 @@ export interface IExtension {
rating: number;
ratingCount: number;
outdated: boolean;
disabledGlobally: boolean;
disabledForWorkspace: boolean;
enablementState: EnablementState;
dependencies: string[];
telemetryData: any;
preview: boolean;
getManifest(): TPromise<IExtensionManifest>;
getReadme(): TPromise<string>;
getChangelog(): TPromise<string>;
isMalicious: boolean;
}
export interface IExtensionDependencies {
@@ -74,7 +76,7 @@ export interface IExtensionsWorkbenchService {
install(vsix: string): TPromise<void>;
install(extension: IExtension, promptToInstallDependencies?: boolean): TPromise<void>;
uninstall(extension: IExtension): TPromise<void>;
setEnablement(extension: IExtension, enable: boolean, workspace?: boolean): TPromise<void>;
setEnablement(extension: IExtension, enablementState: EnablementState): TPromise<void>;
loadDependencies(extension: IExtension): TPromise<IExtensionDependencies>;
open(extension: IExtension, sideByside?: boolean): TPromise<any>;
checkForUpdates(): TPromise<void>;
@@ -87,4 +89,4 @@ export const AutoUpdateConfigurationKey = 'extensions.autoUpdate';
export interface IExtensionsConfiguration {
autoUpdate: boolean;
ignoreRecommendations: boolean;
}
}

View File

@@ -10,6 +10,7 @@ import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/co
export const ExtensionsConfigurationSchemaId = 'vscode://schemas/extensions';
export const ExtensionsConfigurationSchema: IJSONSchema = {
id: ExtensionsConfigurationSchemaId,
allowComments: true,
type: 'object',
title: localize('app.extensions.json.title', "Extensions"),
properties: {

View File

@@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import Event, { Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/platform/extensions/common/extensions';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { append, $, addDisposableListener } from 'vs/base/browser/dom';
import { StatusbarAlignment, IStatusbarRegistry, StatusbarItemDescriptor, Extensions, IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionHostProfileService, ProfileSessionState, RuntimeExtensionsInput } from 'vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
_serviceBrand: any;
private readonly _onDidChangeState: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
private readonly _onDidChangeLastProfile: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeLastProfile: Event<void> = this._onDidChangeLastProfile.event;
private _profile: IExtensionHostProfile;
private _profileSession: ProfileSession;
private _state: ProfileSessionState;
public get state() { return this._state; }
public get lastProfile() { return this._profile; }
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
this._profile = null;
this._profileSession = null;
this._setState(ProfileSessionState.None);
}
private _setState(state: ProfileSessionState): void {
if (this._state === state) {
return;
}
this._state = state;
if (this._state === ProfileSessionState.Running) {
ProfileExtHostStatusbarItem.instance.show(() => {
this.stopProfiling();
this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true });
});
} else if (this._state === ProfileSessionState.Stopping) {
ProfileExtHostStatusbarItem.instance.hide();
}
this._onDidChangeState.fire(void 0);
}
public startProfiling(): void {
if (this._state !== ProfileSessionState.None) {
return;
}
this._setState(ProfileSessionState.Starting);
this._extensionService.startExtensionHostProfile().then((value) => {
this._profileSession = value;
this._setState(ProfileSessionState.Running);
}, (err) => {
onUnexpectedError(err);
this._setState(ProfileSessionState.None);
});
}
public stopProfiling(): void {
if (this._state !== ProfileSessionState.Running) {
return;
}
this._setState(ProfileSessionState.Stopping);
this._profileSession.stop().then((result) => {
this._setLastProfile(result);
this._setState(ProfileSessionState.None);
}, (err) => {
onUnexpectedError(err);
this._setState(ProfileSessionState.None);
});
this._profileSession = null;
}
private _setLastProfile(profile: IExtensionHostProfile) {
this._profile = profile;
this._onDidChangeLastProfile.fire(void 0);
}
public getLastProfile(): IExtensionHostProfile {
return this._profile;
}
public clearLastProfile(): void {
this._setLastProfile(null);
}
}
export class ProfileExtHostStatusbarItem implements IStatusbarItem {
public static instance: ProfileExtHostStatusbarItem;
private toDispose: IDisposable[];
private statusBarItem: HTMLElement;
private label: HTMLElement;
private timeStarted: number;
private labelUpdater: number;
private clickHandler: () => void;
constructor() {
ProfileExtHostStatusbarItem.instance = this;
this.toDispose = [];
this.timeStarted = 0;
}
public show(clickHandler: () => void) {
this.clickHandler = clickHandler;
if (this.timeStarted === 0) {
this.timeStarted = new Date().getTime();
this.statusBarItem.hidden = false;
this.labelUpdater = setInterval(() => {
this.updateLabel();
}, 1000);
this.updateLabel();
}
}
public hide() {
this.clickHandler = null;
this.statusBarItem.hidden = true;
this.timeStarted = 0;
clearInterval(this.labelUpdater);
this.labelUpdater = null;
}
public render(container: HTMLElement): IDisposable {
if (!this.statusBarItem && container) {
this.statusBarItem = append(container, $('.profileExtHost-statusbar-item'));
this.toDispose.push(addDisposableListener(this.statusBarItem, 'click', () => {
if (this.clickHandler) {
this.clickHandler();
}
}));
this.statusBarItem.title = nls.localize('selectAndStartDebug', "Click to stop profiling.");
const a = append(this.statusBarItem, $('a'));
append(a, $('.icon'));
this.label = append(a, $('span.label'));
this.updateLabel();
this.statusBarItem.hidden = true;
}
return this;
}
private updateLabel() {
let label = 'Profiling Extension Host';
if (this.timeStarted > 0) {
let secondsRecoreded = (new Date().getTime() - this.timeStarted) / 1000;
label = `Profiling Extension Host (${Math.round(secondsRecoreded)} sec)`;
}
this.label.textContent = label;
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
}
}
Registry.as<IStatusbarRegistry>(Extensions.Statusbar).registerStatusbarItem(
new StatusbarItemDescriptor(ProfileExtHostStatusbarItem, StatusbarAlignment.RIGHT)
);

View File

@@ -222,11 +222,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
private _suggest(model: IModel): void {
const uri = model.uri;
if (!uri) {
return;
}
if (uri.scheme === Schemas.inMemory || uri.scheme === Schemas.internal || uri.scheme === Schemas.vscode) {
if (!uri || uri.scheme !== Schemas.file) {
return;
}
@@ -250,7 +246,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
StorageScope.GLOBAL
);
const config = this.configurationService.getConfiguration<IExtensionsConfiguration>(ConfigurationKey);
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
if (config.ignoreRecommendations) {
return;
@@ -346,7 +342,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return;
}
const config = this.configurationService.getConfiguration<IExtensionsConfiguration>(ConfigurationKey);
const config = this.configurationService.getValue<IExtensionsConfiguration>(ConfigurationKey);
if (config.ignoreRecommendations) {
return;
@@ -358,7 +354,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
this.extensionsService.getInstalled(LocalExtensionType.User).done(local => {
const recommendations = allRecommendations
.filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id));
.filter(id => local.every(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}` !== id));
if (!recommendations.length) {
return;

View File

@@ -21,34 +21,41 @@ import { VIEWLET_ID, IExtensionsWorkbenchService } from '../common/extensions';
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction,
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction,
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor
} from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { ExtensionEditor } from 'vs/workbench/parts/extensions/browser/extensionEditor';
import { StatusUpdater, ExtensionsViewlet } from 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet';
import { StatusUpdater, ExtensionsViewlet, MaliciousExtensionChecker } from 'vs/workbench/parts/extensions/electron-browser/extensionsViewlet';
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeymapExtensions, BetterMergeDisabled } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils';
import { adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { GalleryExtensionsHandler, ExtensionsHandler } from 'vs/workbench/parts/extensions/browser/extensionsQuickOpen';
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { RuntimeExtensionsEditor, RuntimeExtensionsInput, ShowRuntimeExtensionsAction, IExtensionHostProfileService } from 'vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor';
import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { ExtensionHostProfileService } from 'vs/workbench/parts/extensions/electron-browser/extensionProfileService';
// Singletons
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
registerSingleton(IExtensionTipsService, ExtensionTipsService);
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(StatusUpdater);
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions);
workbenchRegistry.registerWorkbenchContribution(BetterMergeDisabled);
workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Running);
workbenchRegistry.registerWorkbenchContribution(MaliciousExtensionChecker, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCommandsContributor, LifecyclePhase.Eventually);
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Running);
workbenchRegistry.registerWorkbenchContribution(BetterMergeDisabled, LifecyclePhase.Running);
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel(ExtensionsChannelId, ExtensionsLabel);
@@ -86,6 +93,29 @@ const editorDescriptor = new EditorDescriptor(
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(editorDescriptor, [new SyncDescriptor(ExtensionsInput)]);
// Running Extensions Editor
const runtimeExtensionsEditorDescriptor = new EditorDescriptor(
RuntimeExtensionsEditor,
RuntimeExtensionsEditor.ID,
localize('runtimeExtension', "Running Extensions")
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(runtimeExtensionsEditorDescriptor, [new SyncDescriptor(RuntimeExtensionsInput)]);
class RuntimeExtensionsInputFactory implements IEditorInputFactory {
serialize(editorInput: EditorInput): string {
return '';
}
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
return new RuntimeExtensionsInput();
}
}
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(RuntimeExtensionsInput.ID, RuntimeExtensionsInputFactory);
// Viewlet
const viewletDescriptor = new ViewletDescriptor(
ExtensionsViewlet,
@@ -140,12 +170,6 @@ actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: U
const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
const configureWorkspaceExtensionsDescriptor = new SyncActionDescriptor(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL);
actionRegistry.registerWorkbenchAction(configureWorkspaceExtensionsDescriptor, 'Extensions: Configure Recommended Extensions (Workspace)', ExtensionsLabel);
const configureWorkspaceFolderRecommendationsDescriptor = new SyncActionDescriptor(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL);
actionRegistry.registerWorkbenchAction(configureWorkspaceFolderRecommendationsDescriptor, 'Extensions: Configure Recommended Extensions (Workspace Folder)', ExtensionsLabel);
const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL);
actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel);
@@ -166,6 +190,7 @@ actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer"));
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
.registerConfiguration({
@@ -181,7 +206,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
},
'extensions.ignoreRecommendations': {
type: 'boolean',
description: localize('extensionsIgnoreRecommendations', "Ignore extension recommendations"),
description: localize('extensionsIgnoreRecommendations', "If set to true, the notifications for extension recommendations will stop showing up."),
default: false
}
}
@@ -198,4 +223,4 @@ CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccess
if (extension.length === 1) {
extensionService.open(extension[0]).done(null, errors.onUnexpectedError);
}
});
});

View File

@@ -47,10 +47,6 @@ export class OpenExtensionsFolderAction extends Action {
return this.windowsService.showItemInFolder(itemToShow);
});
}
protected isEnabled(): boolean {
return true;
}
}
export class InstallVSIXAction extends Action {

View File

@@ -12,7 +12,7 @@ import { onUnexpectedError, canceled } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService, LocalExtensionType, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService, LocalExtensionType, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -20,7 +20,8 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message';
import { Action } from 'vs/base/common/actions';
import { BetterMergeDisabledNowKey, BetterMergeId, getIdAndVersionFromLocalExtensionId, areSameExtensions, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { BetterMergeDisabledNowKey, BetterMergeId, areSameExtensions, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
export interface IExtensionStatus {
identifier: IExtensionIdentifier;
@@ -49,16 +50,12 @@ export class KeymapExtensions implements IWorkbenchContribution {
);
}
getId(): string {
return 'vs.extensions.keymapExtensions';
}
private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): TPromise<void> {
return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => {
const keymaps = extensions.filter(extension => isKeymapExtension(this.tipsService, extension));
const extension = arrays.first(keymaps, extension => extension.identifier.id === extensionIdentifier.id);
const extension = arrays.first(keymaps, extension => stripVersion(extension.identifier.id) === extensionIdentifier.id);
if (extension && extension.globallyEnabled) {
const otherKeymaps = keymaps.filter(extension => extension.identifier.id !== extensionIdentifier.id && extension.globallyEnabled);
const otherKeymaps = keymaps.filter(extension => stripVersion(extension.identifier.id) !== extensionIdentifier.id && extension.globallyEnabled);
if (otherKeymaps.length) {
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
}
@@ -68,25 +65,8 @@ export class KeymapExtensions implements IWorkbenchContribution {
}
private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): TPromise<void> {
/* __GDPR__FRAGMENT__
"KeyMapsData" : {
"newKeymap" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"oldKeymaps": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
const telemetryData: { [key: string]: any; } = {
newKeymap: newKeymap.identifier,
oldKeymaps: oldKeymaps.map(k => k.identifier)
};
/* __GDPR__
"disableOtherKeymapsConfirmation" : {
"${include}": [
"${KeyMapsData}"
]
}
*/
this.telemetryService.publicLog('disableOtherKeymapsConfirmation', telemetryData);
const message = localize('disableOtherKeymapsConfirmation', "Disable other keymaps ({0}) to avoid conflicts between keybindings?", oldKeymaps.map(k => `'${k.local.manifest.displayName}'`).join(', '));
const options = [
localize('yes', "Yes"),
@@ -95,19 +75,22 @@ export class KeymapExtensions implements IWorkbenchContribution {
return this.choiceService.choose(Severity.Info, message, options, 1, false)
.then(value => {
const confirmed = value === 0;
telemetryData['confirmed'] = confirmed;
const telemetryData: { [key: string]: any; } = {
newKeymap: newKeymap.identifier,
oldKeymaps: oldKeymaps.map(k => k.identifier),
confirmed
};
/* __GDPR__
"disableOtherKeymaps" : {
"confirmed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"${include}": [
"${KeyMapsData}"
]
"newKeymap" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"oldKeymaps": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"confirmed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('disableOtherKeymaps', telemetryData);
if (confirmed) {
return TPromise.join(oldKeymaps.map(keymap => {
return this.extensionEnablementService.setEnablement(keymap.local.identifier, false);
return this.extensionEnablementService.setEnablement(keymap.local.identifier, EnablementState.Disabled);
}));
}
return undefined;
@@ -142,20 +125,22 @@ export function getInstalledExtensions(accessor: ServicesAccessor): TPromise<IEx
const extensionService = accessor.get(IExtensionManagementService);
const extensionEnablementService = accessor.get(IExtensionEnablementService);
return extensionService.getInstalled().then(extensions => {
const globallyDisabled = extensionEnablementService.getGloballyDisabledExtensions();
return extensions.map(extension => {
return {
identifier: { id: adoptToGalleryExtensionId(extension.identifier.id), uuid: extension.identifier.uuid },
local: extension,
globallyEnabled: globallyDisabled.every(disabled => !areSameExtensions(disabled, extension.identifier))
};
});
return extensionEnablementService.getDisabledExtensions()
.then(disabledExtensions => {
return extensions.map(extension => {
return {
identifier: { id: adoptToGalleryExtensionId(stripVersion(extension.identifier.id)), uuid: extension.identifier.uuid },
local: extension,
globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.identifier))
};
});
});
});
}
export function isKeymapExtension(tipsService: IExtensionTipsService, extension: IExtensionStatus): boolean {
const cats = extension.local.manifest.categories;
return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().indexOf(extension.identifier.id) !== -1;
return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().indexOf(stripVersion(extension.identifier.id)) !== -1;
}
function stripVersion(id: string): string {
@@ -171,48 +156,22 @@ export class BetterMergeDisabled implements IWorkbenchContribution {
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
extensionService.onReady().then(() => {
extensionService.whenInstalledExtensionsRegistered().then(() => {
if (storageService.getBoolean(BetterMergeDisabledNowKey, StorageScope.GLOBAL, false)) {
storageService.remove(BetterMergeDisabledNowKey, StorageScope.GLOBAL);
/* __GDPR__
"betterMergeDisabled" : {}
*/
telemetryService.publicLog('betterMergeDisabled');
messageService.show(Severity.Info, {
message: localize('betterMergeDisabled', "The Better Merge extension is now built-in, the installed extension was disabled and can be uninstalled."),
actions: [
new Action('uninstall', localize('uninstall', "Uninstall"), null, true, () => {
/* __GDPR__
"betterMergeUninstall" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryService.publicLog('betterMergeUninstall', {
outcome: 'uninstall',
});
return extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
return Promise.all(extensions.filter(e => stripVersion(e.identifier.id) === BetterMergeId)
.map(e => extensionManagementService.uninstall(e, true)));
});
}),
new Action('later', localize('later', "Later"), null, true, () => {
/* __GDPR__
"betterMergeUninstall" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryService.publicLog('betterMergeUninstall', {
outcome: 'later',
});
return TPromise.as(true);
})
new Action('later', localize('later', "Later"), null, true)
]
});
}
});
}
getId(): string {
return 'vs.extensions.betterMergeDisabled';
}
}

View File

@@ -51,6 +51,9 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { getGalleryExtensionIdFromLocal, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ILogService } from 'vs/platform/log/common/log';
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
interface SearchInputEvent extends Event {
target: HTMLInputElement;
@@ -85,7 +88,6 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
@IInstantiationService instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorInputService: IEditorGroupService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IMessageService private messageService: IMessageService,
@IViewletService private viewletService: IViewletService,
@@ -106,7 +108,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
this.disposables.push(this.viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
@@ -432,10 +434,6 @@ export class StatusUpdater implements IWorkbenchContribution {
extensionsWorkbenchService.onChange(this.onServiceChange, this, this.disposables);
}
getId(): string {
return 'vs.extensions.statusupdater';
}
private onServiceChange(): void {
dispose(this.badgeHandle);
@@ -457,3 +455,49 @@ export class StatusUpdater implements IWorkbenchContribution {
dispose(this.badgeHandle);
}
}
export class MaliciousExtensionChecker implements IWorkbenchContribution {
private disposables: IDisposable[];
constructor(
@IExtensionManagementService private extensionsManagementService: IExtensionManagementService,
@IInstantiationService private instantiationService: IInstantiationService,
@ILogService private logService: ILogService,
@IMessageService private messageService: IMessageService
) {
this.loopCheckForMaliciousExtensions();
}
private loopCheckForMaliciousExtensions(): void {
this.checkForMaliciousExtensions()
.then(() => TPromise.timeout(1000 * 60 * 5)) // every five minutes
.then(() => this.loopCheckForMaliciousExtensions());
}
private checkForMaliciousExtensions(): TPromise<any> {
return this.extensionsManagementService.getExtensionsReport().then(report => {
const maliciousSet = getMaliciousExtensionsSet(report);
return this.extensionsManagementService.getInstalled(LocalExtensionType.User).then(installed => {
const maliciousExtensions = installed
.filter(e => maliciousSet.has(getGalleryExtensionIdFromLocal(e)));
if (maliciousExtensions.length) {
return TPromise.join(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => {
this.messageService.show(Severity.Warning, {
message: localize('malicious warning', "We have uninstalled '{0}' which was reported to be malicious.", getGalleryExtensionIdFromLocal(e)),
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, localize('reloadNow', "Reload Now"))]
});
})));
} else {
return TPromise.as(null);
}
});
}, err => this.logService.error(err));
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -14,33 +14,29 @@ import { isPromiseCanceledError, create as createError } from 'vs/base/common/er
import Severity from 'vs/base/common/severity';
import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging';
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { append, $, toggleClass } from 'vs/base/browser/dom';
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Delegate, Renderer } from 'vs/workbench/parts/extensions/browser/extensionsList';
import { IExtension, IExtensionsWorkbenchService } from '../common/extensions';
import { Query } from '../common/extensionQuery';
import { IListService } from 'vs/platform/list/browser/listService';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler';
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { attachBadgeStyler } from 'vs/platform/theme/common/styler';
import { IViewletViewOptions, IViewOptions, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { EventType } from 'vs/base/common/events';
import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions';
import { WorkbenchPagedList, IListService } from 'vs/platform/list/browser/listService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
export class ExtensionsListView extends ViewsViewletPanel {
@@ -48,7 +44,7 @@ export class ExtensionsListView extends ViewsViewletPanel {
private extensionsList: HTMLElement;
private badge: CountBadge;
protected badgeContainer: HTMLElement;
private list: PagedList<IExtension>;
private list: WorkbenchPagedList<IExtension>;
constructor(
private options: IViewletViewOptions,
@@ -58,16 +54,14 @@ export class ExtensionsListView extends ViewsViewletPanel {
@IInstantiationService protected instantiationService: IInstantiationService,
@IListService private listService: IListService,
@IThemeService private themeService: IThemeService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionService private extensionService: IExtensionService,
@ICommandService private commandService: ICommandService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorInputService: IEditorGroupService,
@IExtensionTipsService private tipsService: IExtensionTipsService,
@IModeService private modeService: IModeService,
@ITelemetryService private telemetryService: ITelemetryService,
@IProgressService private progressService: IProgressService
@IContextKeyService private contextKeyService: IContextKeyService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService);
}
@@ -86,13 +80,10 @@ export class ExtensionsListView extends ViewsViewletPanel {
this.messageBox = append(container, $('.message'));
const delegate = new Delegate();
const renderer = this.instantiationService.createInstance(Renderer);
this.list = new PagedList(this.extensionsList, delegate, [renderer], {
this.list = new WorkbenchPagedList(this.extensionsList, delegate, [renderer], {
ariaLabel: localize('extensions', "Extensions"),
keyboardSupport: false
});
this.disposables.push(attachListStyler(this.list.widget, this.themeService));
this.disposables.push(this.listService.register(this.list.widget));
}, this.contextKeyService, this.listService, this.themeService);
chain(this.list.onSelectionChange)
.map(e => e.elements[0])
@@ -163,27 +154,10 @@ export class ExtensionsListView extends ViewsViewletPanel {
let result = await this.extensionsWorkbenchService.queryLocal();
switch (options.sortBy) {
case SortBy.InstallCount:
result = result.sort((e1, e2) => e2.installCount - e1.installCount);
break;
case SortBy.AverageRating:
case SortBy.WeightedRating:
result = result.sort((e1, e2) => e2.rating - e1.rating);
break;
default:
result = result.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));
break;
}
if (options.sortOrder === SortOrder.Descending) {
result = result.reverse();
}
result = result
.filter(e => e.type === LocalExtensionType.User && e.name.toLowerCase().indexOf(value) > -1);
return new PagedModel(result);
return new PagedModel(this.sortExtensions(result, options));
}
const idMatch = /@id:([a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*)/.exec(value);
@@ -196,18 +170,18 @@ export class ExtensionsListView extends ViewsViewletPanel {
}
if (/@outdated/i.test(value)) {
value = value.replace(/@outdated/g, '').trim().toLowerCase();
value = value.replace(/@outdated/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const local = await this.extensionsWorkbenchService.queryLocal();
const result = local
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(extension => extension.outdated && extension.name.toLowerCase().indexOf(value) > -1);
return new PagedModel(result);
return new PagedModel(this.sortExtensions(result, options));
}
if (/@disabled/i.test(value)) {
value = value.replace(/@disabled/g, '').trim().toLowerCase();
value = value.replace(/@disabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase();
const local = await this.extensionsWorkbenchService.queryLocal();
const runningExtensions = await this.extensionService.getExtensions();
@@ -216,22 +190,22 @@ export class ExtensionsListView extends ViewsViewletPanel {
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(e => runningExtensions.every(r => !areSameExtensions(r, e)) && e.name.toLowerCase().indexOf(value) > -1);
return new PagedModel(result);
return new PagedModel(this.sortExtensions(result, options));
}
if (/@enabled/i.test(value)) {
value = value ? value.replace(/@enabled/g, '').trim().toLowerCase() : '';
value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : '';
const local = await this.extensionsWorkbenchService.queryLocal();
const result = local
let result = local
.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName))
.filter(e => e.type === LocalExtensionType.User &&
!(e.disabledForWorkspace || e.disabledGlobally) &&
(e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) &&
e.name.toLowerCase().indexOf(value) > -1
);
return new PagedModel(result);
return new PagedModel(this.sortExtensions(result, options));
}
if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) {
@@ -286,6 +260,25 @@ export class ExtensionsListView extends ViewsViewletPanel {
return new PagedModel(pager);
}
private sortExtensions(extensions: IExtension[], options: IQueryOptions): IExtension[] {
switch (options.sortBy) {
case SortBy.InstallCount:
extensions = extensions.sort((e1, e2) => e2.installCount - e1.installCount);
break;
case SortBy.AverageRating:
case SortBy.WeightedRating:
extensions = extensions.sort((e1, e2) => e2.rating - e1.rating);
break;
default:
extensions = extensions.sort((e1, e2) => e1.displayName.localeCompare(e2.displayName));
break;
}
if (options.sortOrder === SortOrder.Descending) {
extensions = extensions.reverse();
}
return extensions;
}
private getAllRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase();
@@ -411,12 +404,6 @@ export class ExtensionsListView extends ViewsViewletPanel {
const value = query.value.replace(/@recommended:keymaps/g, '').trim().toLowerCase();
const names = this.tipsService.getKeymapRecommendations()
.filter(name => name.toLowerCase().indexOf(value) > -1);
/* __GDPR__
"extensionKeymapRecommendations:open" : {
"count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('extensionKeymapRecommendations:open', { count: names.length });
if (!names.length) {
return TPromise.as(new PagedModel([]));
@@ -559,7 +546,7 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView {
const actionbar = new ActionBar(listActionBar, {
animated: false
});
actionbar.addListener(EventType.RUN, ({ error }) => error && this.messageService.show(Severity.Error, error));
actionbar.onDidRun(({ error }) => error && this.messageService.show(Severity.Error, error));
const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL);
const configureWorkspaceFolderAction = this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL);

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#C5C5C5" cx="8" cy="8" r="6"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#424242" cx="8" cy="8" r="6"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#c10" cx="8" cy="8" r="6"/></svg>

After

Width:  |  Height:  |  Size: 164 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle fill="#E51400" cx="8" cy="8" r="6"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd {
background-color: #f5f5f5;
}
.runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row:hover:not(.odd) {
background-color: transparent;
}
.runtime-extensions-editor .extension {
display: flex;
padding-left: 20px;
padding-right: 20px;
}
.runtime-extensions-editor .extension .desc {
flex: 1;
padding: 4px 0;
}
.runtime-extensions-editor .extension .desc .name {
font-weight: bold;
}
.runtime-extensions-editor .extension .time {
padding: 4px;
text-align: right;
}
.runtime-extensions-editor .extension .desc .msg-label {
padding-left: 4px;
}
.runtime-extensions-editor .extension .profile-timeline {
width: 100px;
margin: auto;
padding: 0 4px;
}
.runtime-extensions-editor .extension .profile-timeline-svg {
width: 100%;
pointer-events: none;
}
.runtime-extensions-editor .extension .profile-timeline rect {
fill: rgb(181, 181, 255);
}
.runtime-extensions-editor .extension > .icon {
width: 42px;
height: 42px;
padding: 10px 14px 10px 0;
}
.monaco-action-bar .extension-host-profile-start {
background: url('profile-start.svg') center center no-repeat;
}
.monaco-action-bar .extension-host-profile-stop {
background: url('profile-stop.svg') center center no-repeat;
}
.vs-dark .monaco-action-bar .extension-host-profile-start,
.hc-black .monaco-action-bar .extension-host-profile-start {
background: url('profile-start-inverse.svg') center center no-repeat;
}
.vs-dark .monaco-action-bar .extension-host-profile-stop,
.hc-black .monaco-action-bar .extension-host-profile-stop {
background: url('profile-stop-inverse.svg') center center no-repeat;
animation:fade 1000ms infinite;
-webkit-animation:fade 1000ms infinite;
}
.monaco-action-bar .save-extension-host-profile {
background: url('save.svg') center center no-repeat;
}
.vs-dark .monaco-action-bar .save-extension-host-profile,
.hc-black .monaco-action-bar .save-extension-host-profile {
background: url('save-inverse.svg') center center no-repeat;
}
.vs-dark .runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd,
.hc-black .runtime-extensions-editor .monaco-list .monaco-list-rows > .monaco-list-row.odd {
background-color: #262829;
}
.runtime-extensions-editor .monaco-action-bar {
padding-top: 21px;
flex-shrink: 0;
}
.runtime-extensions-editor .monaco-action-bar.hidden {
visibility: hidden;
}
.monaco-workbench .part.statusbar .profileExtHost-statusbar-item .icon {
background: url('profile-stop.svg') no-repeat;
display: inline-block;
padding-right: 2px;
padding-bottom: 2px;
width: 16px;
height: 16px;
vertical-align: middle;
animation:fade 1000ms infinite;
-webkit-animation:fade 1000ms infinite;
}
@keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.5; }
to { opacity: 1.0; }
}
@-webkit-keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.5; }
to { opacity: 1.0; }
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#1E1E1E" d="M16 2.6L13.4 0H6v6H0v10h10v-6h6z"/><g fill="#C5C5C5"><path d="M5 7h1v2H5zM11 1h1v2h-1zM13 1v3H9V1H7v5h.4L10 8.6V9h5V3zM7 10H3V7H1v8h8V9L7 7z"/></g></svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><path fill="#F6F6F6" d="M16 2.6L13.4 0H6v6H0v10h10v-6h6z"/><g fill="#424242"><path d="M5 7h1v2H5zM11 1h1v2h-1zM13 1v3H9V1H7v5h.4L10 8.6V9h5V3zM7 10H3V7H1v8h8V9L7 7z"/></g></svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -0,0 +1,593 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/runtimeExtensionsEditor';
import * as nls from 'vs/nls';
import * as os from 'os';
import product from 'vs/platform/node/product';
import URI from 'vs/base/common/uri';
import { EditorInput } from 'vs/workbench/common/editor';
import pkg from 'vs/platform/node/package';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action, IAction } from 'vs/base/common/actions';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService, IExtensionDescription, IExtensionsStatus, IExtensionHostProfile } from 'vs/platform/extensions/common/extensions';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
import { append, $, addClass, toggleClass } from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { clipboard } from 'electron';
import { LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { writeFile } from 'vs/base/node/pfs';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { memoize } from 'vs/base/common/decorators';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import Event from 'vs/base/common/event';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const enum ProfileSessionState {
None = 0,
Starting = 1,
Running = 2,
Stopping = 3
}
export interface IExtensionHostProfileService {
_serviceBrand: any;
readonly onDidChangeState: Event<void>;
readonly onDidChangeLastProfile: Event<void>;
readonly state: ProfileSessionState;
readonly lastProfile: IExtensionHostProfile;
startProfiling(): void;
stopProfiling(): void;
clearLastProfile(): void;
}
interface IExtensionProfileInformation {
/**
* segment when the extension was running.
* 2*i = segment start time
* 2*i+1 = segment end time
*/
segments: number[];
/**
* total time when the extension was running.
* (sum of all segment lengths).
*/
totalTime: number;
}
interface IRuntimeExtension {
originalIndex: number;
description: IExtensionDescription;
marketplaceInfo: IExtension;
status: IExtensionsStatus;
profileInfo: IExtensionProfileInformation;
}
export class RuntimeExtensionsEditor extends BaseEditor {
static ID: string = 'workbench.editor.runtimeExtensions';
private _list: WorkbenchList<IRuntimeExtension>;
private _profileInfo: IExtensionHostProfile;
private _elements: IRuntimeExtension[];
private _extensionsDescriptions: IExtensionDescription[];
private _updateSoon: RunOnceScheduler;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService private readonly _extensionService: IExtensionService,
@IListService private readonly _listService: IListService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IMessageService private readonly _messageService: IMessageService,
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(RuntimeExtensionsEditor.ID, telemetryService, themeService);
this._list = null;
this._profileInfo = this._extensionHostProfileService.lastProfile;
this._register(this._extensionHostProfileService.onDidChangeLastProfile(() => {
this._profileInfo = this._extensionHostProfileService.lastProfile;
this._updateExtensions();
}));
this._elements = null;
this._extensionsDescriptions = [];
this._updateExtensions();
this._updateSoon = this._register(new RunOnceScheduler(() => this._updateExtensions(), 200));
this._extensionService.getExtensions().then((extensions) => {
// We only deal with extensions with source code!
this._extensionsDescriptions = extensions.filter((extension) => {
return !!extension.main;
});
this._updateExtensions();
});
this._register(this._extensionService.onDidChangeExtensionsStatus(() => this._updateSoon.schedule()));
}
private _updateExtensions(): void {
this._elements = this._resolveExtensions();
if (this._list) {
this._list.splice(0, this._list.length, this._elements);
}
}
private _resolveExtensions(): IRuntimeExtension[] {
let marketplaceMap: { [id: string]: IExtension; } = Object.create(null);
for (let extension of this._extensionsWorkbenchService.local) {
marketplaceMap[extension.id] = extension;
}
let statusMap = this._extensionService.getExtensionsStatus();
// group profile segments by extension
let segments: { [id: string]: number[]; } = Object.create(null);
if (this._profileInfo) {
let currentStartTime = this._profileInfo.startTime;
for (let i = 0, len = this._profileInfo.deltas.length; i < len; i++) {
const id = this._profileInfo.ids[i];
const delta = this._profileInfo.deltas[i];
let extensionSegments = segments[id];
if (!extensionSegments) {
extensionSegments = [];
segments[id] = extensionSegments;
}
extensionSegments.push(currentStartTime);
currentStartTime = currentStartTime + delta;
extensionSegments.push(currentStartTime);
}
}
let result: IRuntimeExtension[] = [];
for (let i = 0, len = this._extensionsDescriptions.length; i < len; i++) {
const extensionDescription = this._extensionsDescriptions[i];
let profileInfo: IExtensionProfileInformation = null;
if (this._profileInfo) {
let extensionSegments = segments[extensionDescription.id] || [];
let extensionTotalTime = 0;
for (let j = 0, lenJ = extensionSegments.length / 2; j < lenJ; j++) {
const startTime = extensionSegments[2 * j];
const endTime = extensionSegments[2 * j + 1];
extensionTotalTime += (endTime - startTime);
}
profileInfo = {
segments: extensionSegments,
totalTime: extensionTotalTime
};
}
result[i] = {
originalIndex: i,
description: extensionDescription,
marketplaceInfo: marketplaceMap[extensionDescription.id],
status: statusMap[extensionDescription.id],
profileInfo: profileInfo
};
}
result = result.filter((element) => element.status.activationTimes);
if (this._profileInfo) {
// sort descending by time spent in the profiler
result = result.sort((a, b) => {
if (a.profileInfo.totalTime === b.profileInfo.totalTime) {
return a.originalIndex - b.originalIndex;
}
return b.profileInfo.totalTime - a.profileInfo.totalTime;
});
}
return result;
}
protected createEditor(parent: Builder): void {
const container = parent.getHTMLElement();
addClass(container, 'runtime-extensions-editor');
const TEMPLATE_ID = 'runtimeExtensionElementTemplate';
const delegate = new class implements IDelegate<IRuntimeExtension>{
getHeight(element: IRuntimeExtension): number {
return 62;
}
getTemplateId(element: IRuntimeExtension): string {
return TEMPLATE_ID;
}
};
interface IRuntimeExtensionTemplateData {
root: HTMLElement;
element: HTMLElement;
name: HTMLElement;
activationTime: HTMLElement;
profileTime: HTMLElement;
profileTimeline: HTMLElement;
msgIcon: HTMLElement;
msgLabel: HTMLElement;
actionbar: ActionBar;
disposables: IDisposable[];
elementDisposables: IDisposable[];
}
const renderer: IRenderer<IRuntimeExtension, IRuntimeExtensionTemplateData> = {
templateId: TEMPLATE_ID,
renderTemplate: (root: HTMLElement): IRuntimeExtensionTemplateData => {
const element = append(root, $('.extension'));
const desc = append(element, $('div.desc'));
const name = append(desc, $('div.name'));
const msgContainer = append(desc, $('div.msg'));
const msgIcon = append(msgContainer, $('.'));
const msgLabel = append(msgContainer, $('span.msg-label'));
const timeContainer = append(element, $('.time'));
const activationTime = append(timeContainer, $('div.activation-time'));
const profileTime = append(timeContainer, $('div.profile-time'));
const profileTimeline = append(element, $('div.profile-timeline'));
const actionbar = new ActionBar(element, {
animated: false
});
actionbar.onDidRun(({ error }) => error && this._messageService.show(Severity.Error, error));
actionbar.push(new ReportExtensionIssueAction(), { icon: true, label: true });
const disposables = [actionbar];
return {
root,
element,
name,
actionbar,
activationTime,
profileTime,
profileTimeline,
msgIcon,
msgLabel,
disposables,
elementDisposables: []
};
},
renderElement: (element: IRuntimeExtension, index: number, data: IRuntimeExtensionTemplateData): void => {
data.elementDisposables = dispose(data.elementDisposables);
toggleClass(data.root, 'odd', index % 2 === 1);
data.name.textContent = element.marketplaceInfo ? element.marketplaceInfo.displayName : element.description.displayName;
const activationTimes = element.status.activationTimes;
let syncTime = activationTimes.codeLoadingTime + activationTimes.activateCallTime;
data.activationTime.textContent = activationTimes.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`;
data.actionbar.context = element;
toggleClass(data.actionbar.getContainer().getHTMLElement(), 'hidden', element.marketplaceInfo && element.marketplaceInfo.type === LocalExtensionType.User && (!element.description.repository || !element.description.repository.url));
let title: string;
if (activationTimes.activationEvent === '*') {
title = nls.localize('starActivation', "Activated on start-up");
} else if (/^workspaceContains:/.test(activationTimes.activationEvent)) {
let fileNameOrGlob = activationTimes.activationEvent.substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
title = nls.localize('workspaceContainsGlobActivation', "Activated because a file matching {0} exists in your workspace", fileNameOrGlob);
} else {
title = nls.localize('workspaceContainsFileActivation', "Activated because file {0} exists in your workspace", fileNameOrGlob);
}
} else if (/^onLanguage:/.test(activationTimes.activationEvent)) {
let language = activationTimes.activationEvent.substr('onLanguage:'.length);
title = nls.localize('languageActivation', "Activated because you opened a {0} file", language);
} else {
title = nls.localize('workspaceGenericActivation', "Activated on {0}", activationTimes.activationEvent);
}
data.activationTime.title = title;
if (!isFalsyOrEmpty(element.status.runtimeErrors)) {
data.msgIcon.className = 'octicon octicon-bug';
data.msgLabel.textContent = nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length);
} else if (element.status.messages && element.status.messages.length > 0) {
data.msgIcon.className = 'octicon octicon-alert';
data.msgLabel.textContent = element.status.messages[0].message;
} else {
data.msgIcon.className = '';
data.msgLabel.textContent = '';
}
if (this._profileInfo) {
data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`;
const elementSegments = element.profileInfo.segments;
let inner = '<rect x="0" y="99" width="100" height="1" />';
for (let i = 0, len = elementSegments.length / 2; i < len; i++) {
const absoluteStart = elementSegments[2 * i];
const absoluteEnd = elementSegments[2 * i + 1];
const start = absoluteStart - this._profileInfo.startTime;
const end = absoluteEnd - this._profileInfo.startTime;
const absoluteDuration = this._profileInfo.endTime - this._profileInfo.startTime;
const xStart = start / absoluteDuration * 100;
const xEnd = end / absoluteDuration * 100;
inner += `<rect x="${xStart}" y="0" width="${xEnd - xStart}" height="100" />`;
}
let svg = `<svg class="profile-timeline-svg" preserveAspectRatio="none" height="16" viewBox="0 0 100 100">${inner}</svg>`;
data.profileTimeline.innerHTML = svg;
data.profileTimeline.style.display = 'inherit';
} else {
data.profileTime.textContent = '';
data.profileTimeline.innerHTML = '';
}
},
disposeTemplate: (data: IRuntimeExtensionTemplateData): void => {
data.disposables = dispose(data.disposables);
}
};
this._list = new WorkbenchList<IRuntimeExtension>(container, delegate, [renderer], {
multipleSelectionSupport: false
}, this._contextKeyService, this._listService, this.themeService);
this._list.splice(0, this._list.length, this._elements);
this._list.onContextMenu((e) => {
const actions: IAction[] = [];
actions.push(this.saveExtensionHostProfileAction, this.extensionHostProfileAction);
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => TPromise.as(actions)
});
});
}
public getActions(): IAction[] {
return [
this.saveExtensionHostProfileAction,
this.extensionHostProfileAction
];
}
@memoize
private get extensionHostProfileAction(): IAction {
return this._instantiationService.createInstance(ExtensionHostProfileAction, ExtensionHostProfileAction.ID, ExtensionHostProfileAction.LABEL_START);
}
@memoize
private get saveExtensionHostProfileAction(): IAction {
return this._instantiationService.createInstance(SaveExtensionHostProfileAction, SaveExtensionHostProfileAction.ID, SaveExtensionHostProfileAction.LABEL);
}
public layout(dimension: Dimension): void {
this._list.layout(dimension.height);
}
}
export class RuntimeExtensionsInput extends EditorInput {
static ID = 'workbench.runtimeExtensions.input';
constructor() {
super();
}
getTypeId(): string {
return RuntimeExtensionsInput.ID;
}
getName(): string {
return nls.localize('extensionsInputName', "Running Extensions");
}
matches(other: any): boolean {
if (!(other instanceof RuntimeExtensionsInput)) {
return false;
}
return true;
}
resolve(refresh?: boolean): TPromise<any> {
return TPromise.as(null);
}
supportsSplitEditor(): boolean {
return false;
}
getResource(): URI {
return URI.from({
scheme: 'runtime-extensions',
path: 'default'
});
}
}
export class ShowRuntimeExtensionsAction extends Action {
static ID = 'workbench.action.showRuntimeExtensions';
static LABEL = nls.localize('showRuntimeExtensions', "Show Running Extensions");
constructor(
id: string, label: string,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super(id, label);
}
public run(e?: any): TPromise<any> {
return this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true });
}
}
class ReportExtensionIssueAction extends Action {
static ID = 'workbench.extensions.action.reportExtensionIssue';
static LABEL = nls.localize('reportExtensionIssue', "Report Issue");
constructor(
id: string = ReportExtensionIssueAction.ID, label: string = ReportExtensionIssueAction.LABEL
) {
super(id, label, 'extension-action report-issue');
}
run(extension: IRuntimeExtension): TPromise<any> {
clipboard.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```');
window.open(this.generateNewIssueUrl(extension));
return TPromise.as(null);
}
private generateNewIssueUrl(extension: IRuntimeExtension): string {
let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === LocalExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined;
if (!!baseUrl) {
baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`;
} else {
baseUrl = product.reportIssueUrl;
}
const osVersion = `${os.type()} ${os.arch()} ${os.release()}`;
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
const body = encodeURIComponent(
`- Extension Name: ${extension.description.name}
- Extension Version: ${extension.description.version}
- OS Version: ${osVersion}
- VSCode version: ${pkg.version}` + '\n\n We have written the needed data into your clipboard. Please paste:'
);
return `${baseUrl}${queryStringPrefix}body=${body}`;
}
}
class ExtensionHostProfileAction extends Action {
static ID = 'workbench.extensions.action.extensionHostProfile';
static LABEL_START = nls.localize('extensionHostProfileStart', "Start Extension Host Profile");
static LABEL_STOP = nls.localize('extensionHostProfileStop', "Stop Extension Host Profile");
static STOP_CSS_CLASS = 'extension-host-profile-stop';
static START_CSS_CLASS = 'extension-host-profile-start';
constructor(
id: string = ExtensionHostProfileAction.ID, label: string = ExtensionHostProfileAction.LABEL_START,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(id, label, ExtensionHostProfileAction.START_CSS_CLASS);
this._extensionHostProfileService.onDidChangeState(() => this._update());
}
private _update(): void {
const state = this._extensionHostProfileService.state;
if (state === ProfileSessionState.Running) {
this.class = ExtensionHostProfileAction.STOP_CSS_CLASS;
this.label = ExtensionHostProfileAction.LABEL_STOP;
} else {
this.class = ExtensionHostProfileAction.START_CSS_CLASS;
this.label = ExtensionHostProfileAction.LABEL_START;
}
}
run(): TPromise<any> {
const state = this._extensionHostProfileService.state;
if (state === ProfileSessionState.Running) {
this._extensionHostProfileService.stopProfiling();
} else if (state === ProfileSessionState.None) {
this._extensionHostProfileService.startProfiling();
}
return TPromise.as(null);
}
}
class SaveExtensionHostProfileAction extends Action {
static LABEL = nls.localize('saveExtensionHostProfile', "Save Extension Host Profile");
static ID = 'workbench.extensions.action.saveExtensionHostProfile';
constructor(
id: string = SaveExtensionHostProfileAction.ID, label: string = SaveExtensionHostProfileAction.LABEL,
@IWindowService private readonly _windowService: IWindowService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(id, label, 'save-extension-host-profile', false);
this.enabled = (this._extensionHostProfileService.lastProfile !== null);
this._extensionHostProfileService.onDidChangeLastProfile(() => {
this.enabled = (this._extensionHostProfileService.lastProfile !== null);
});
}
async run(): TPromise<any> {
let picked = this._windowService.showSaveDialog({
title: 'Save Extension Host Profile',
buttonLabel: 'Save',
defaultPath: `CPU-${new Date().toISOString().replace(/[\-:]/g, '')}.cpuprofile`,
filters: [{
name: 'CPU Profiles',
extensions: ['cpuprofile', 'txt']
}]
});
if (!picked) {
return;
}
const profileInfo = this._extensionHostProfileService.lastProfile;
let dataToWrite: object = profileInfo.data;
if (this._environmentService.isBuilt) {
const profiler = await import('v8-inspect-profiler');
// when running from a not-development-build we remove
// absolute filenames because we don't want to reveal anything
// about users. We also append the `.txt` suffix to make it
// easier to attach these files to GH issues
let tmp = profiler.rewriteAbsolutePaths({ profile: dataToWrite }, 'piiRemoved');
dataToWrite = tmp.profile;
picked = picked + '.txt';
}
return writeFile(picked, JSON.stringify(profileInfo.data, null, '\t'));
}
}

View File

@@ -20,9 +20,9 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import {
IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest,
InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionTipsService, IExtensionIdentifier
InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowService } from 'vs/platform/windows/common/windows';
@@ -33,21 +33,19 @@ import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenc
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IURLService } from 'vs/platform/url/common/url';
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import product from 'vs/platform/node/product';
interface IExtensionStateProvider {
(extension: Extension): ExtensionState;
interface IExtensionStateProvider<T> {
(extension: Extension): T;
}
class Extension implements IExtension {
public disabledGlobally = false;
public disabledForWorkspace = false;
public enablementState: EnablementState = EnablementState.Enabled;
constructor(
private galleryService: IExtensionGalleryService,
private stateProvider: IExtensionStateProvider,
private stateProvider: IExtensionStateProvider<ExtensionState>,
public local: ILocalExtension,
public gallery: IGalleryExtension,
private telemetryService: ITelemetryService
@@ -141,6 +139,10 @@ class Extension implements IExtension {
return require.toUrl('../browser/media/defaultIcon.png');
}
get repository(): string {
return this.gallery && this.gallery.assets.repository.uri;
}
get licenseUrl(): string {
return this.gallery && this.gallery.assets.license && this.gallery.assets.license.uri;
}
@@ -149,6 +151,8 @@ class Extension implements IExtension {
return this.stateProvider(this);
}
public isMalicious: boolean = false;
get installCount(): number {
return this.gallery ? this.gallery.installCount : null;
}
@@ -175,19 +179,28 @@ class Extension implements IExtension {
}
}
get preview(): boolean {
return this.gallery ? this.gallery.preview : false;
}
private isGalleryOutdated(): boolean {
return this.local && this.gallery && semver.gt(this.local.manifest.version, this.gallery.version);
}
getManifest(): TPromise<IExtensionManifest> {
if (this.gallery) {
if (this.gallery && !this.isGalleryOutdated()) {
if (this.gallery.assets.manifest) {
return this.galleryService.getManifest(this.gallery);
}
this.telemetryService.publicLog('extensions:NotFoundManifest', this.telemetryData);
return TPromise.wrapError<IExtensionManifest>(new Error('not available'));
}
return TPromise.as(this.local.manifest);
}
getReadme(): TPromise<string> {
if (this.gallery) {
if (this.gallery && !this.isGalleryOutdated()) {
if (this.gallery.assets.readme) {
return this.galleryService.getReadme(this.gallery);
}
@@ -203,7 +216,7 @@ class Extension implements IExtension {
}
getChangelog(): TPromise<string> {
if (this.gallery && this.gallery.assets.changelog) {
if (this.gallery && this.gallery.assets.changelog && !this.isGalleryOutdated()) {
return this.galleryService.getChangelog(this.gallery);
}
@@ -224,12 +237,12 @@ class Extension implements IExtension {
get dependencies(): string[] {
const { local, gallery } = this;
if (gallery && !this.isGalleryOutdated()) {
return gallery.properties.dependencies;
}
if (local && local.manifest.extensionDependencies) {
return local.manifest.extensionDependencies;
}
if (gallery) {
return gallery.properties.dependencies;
}
return [];
}
}
@@ -305,10 +318,10 @@ function toTelemetryEventName(operation: Operation) {
export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
private static SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
private static readonly SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
_serviceBrand: any;
private stateProvider: IExtensionStateProvider;
private stateProvider: IExtensionStateProvider<ExtensionState>;
private installing: IActiveExtension[] = [];
private uninstalling: IActiveExtension[] = [];
private installed: Extension[] = [];
@@ -332,8 +345,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
@IChoiceService private choiceService: IChoiceService,
@IURLService urlService: IURLService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IExtensionTipsService private tipsService: IExtensionTipsService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IWindowService private windowService: IWindowService
) {
this.stateProvider = ext => this.getExtensionState(ext);
@@ -374,13 +385,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
queryLocal(): TPromise<IExtension[]> {
return this.extensionService.getInstalled().then(result => {
const installedById = index(this.installed, e => e.local.identifier.id);
const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions();
const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions();
this.installed = result.map(local => {
const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, local, null, this.telemetryService);
extension.local = local;
extension.disabledGlobally = globallyDisabledExtensions.some(d => areSameExtensions(d, extension));
extension.disabledForWorkspace = workspaceDisabledExtensions.some(d => areSameExtensions(d, extension));
extension.enablementState = this.extensionEnablementService.getEnablementState({ id: extension.id, uuid: extension.uuid });
return extension;
});
@@ -390,15 +398,19 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
}
queryGallery(options: IQueryOptions = {}): TPromise<IPager<IExtension>> {
return this.galleryService.query(options)
.then(result => mapPager(result, gallery => this.fromGallery(gallery)))
.then(null, err => {
if (/No extension gallery service configured/.test(err.message)) {
return TPromise.as(singlePagePager([]));
}
return this.extensionService.getExtensionsReport().then(report => {
const maliciousSet = getMaliciousExtensionsSet(report);
return TPromise.wrapError<IPager<IExtension>>(err);
});
return this.galleryService.query(options)
.then(result => mapPager(result, gallery => this.fromGallery(gallery, maliciousSet)))
.then(null, err => {
if (/No extension gallery service configured/.test(err.message)) {
return TPromise.as(singlePagePager([]));
}
return TPromise.wrapError<IPager<IExtension>>(err);
});
});
}
loadDependencies(extension: IExtension): TPromise<IExtensionDependencies> {
@@ -406,45 +418,47 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return TPromise.wrap<IExtensionDependencies>(null);
}
return this.galleryService.getAllDependencies((<Extension>extension).gallery)
.then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension)))
.then(extensions => [...this.local, ...extensions])
.then(extensions => {
const map = new Map<string, IExtension>();
for (const extension of extensions) {
map.set(extension.id, extension);
}
return new ExtensionDependencies(extension, extension.id, map);
});
return this.extensionService.getExtensionsReport().then(report => {
const maliciousSet = getMaliciousExtensionsSet(report);
return this.galleryService.loadAllDependencies((<Extension>extension).dependencies.map(id => <IExtensionIdentifier>{ id }))
.then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet)))
.then(extensions => [...this.local, ...extensions])
.then(extensions => {
const map = new Map<string, IExtension>();
for (const extension of extensions) {
map.set(extension.id, extension);
}
return new ExtensionDependencies(extension, extension.id, map);
});
});
}
open(extension: IExtension, sideByside: boolean = false): TPromise<any> {
/* __GDPR__
"extensionGallery:open" : {
"${include}": [
"${GalleryExtensionTelemetryData}"
]
}
*/
this.telemetryService.publicLog('extensionGallery:open', extension.telemetryData);
return this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), null, sideByside);
}
private fromGallery(gallery: IGalleryExtension): Extension {
const installed = this.getInstalledExtensionMatchingGallery(gallery);
private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): Extension {
let result = this.getInstalledExtensionMatchingGallery(gallery);
if (installed) {
if (result) {
// Loading the compatible version only there is an engine property
// Otherwise falling back to old way so that we will not make many roundtrips
if (gallery.properties.engine) {
this.galleryService.loadCompatibleVersion(gallery).then(compatible => this.syncLocalWithGalleryExtension(installed, compatible));
this.galleryService.loadCompatibleVersion(gallery)
.then(compatible => compatible ? this.syncLocalWithGalleryExtension(result, compatible) : null);
} else {
this.syncLocalWithGalleryExtension(installed, gallery);
this.syncLocalWithGalleryExtension(result, gallery);
}
return installed;
} else {
result = new Extension(this.galleryService, this.stateProvider, null, gallery, this.telemetryService);
}
return new Extension(this.galleryService, this.stateProvider, null, gallery, this.telemetryService);
if (maliciousExtensionSet.has(result.id)) {
result.isMalicious = true;
}
return result;
}
private getInstalledExtensionMatchingGallery(gallery: IGalleryExtension): Extension {
@@ -531,6 +545,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return false;
}
if (extension.isMalicious) {
return false;
}
return !!(extension as Extension).gallery;
}
@@ -543,6 +561,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return undefined;
}
if (extension.isMalicious) {
return TPromise.wrapError<void>(new Error(nls.localize('malicious', "This extension is reported to be malicious.")));
}
const ext = extension as Extension;
const gallery = ext.gallery;
@@ -553,12 +575,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return this.extensionService.installFromGallery(gallery);
}
setEnablement(extension: IExtension, enable: boolean, workspace: boolean = false): TPromise<void> {
setEnablement(extension: IExtension, enablementState: EnablementState): TPromise<void> {
if (extension.type === LocalExtensionType.System) {
return TPromise.wrap<void>(void 0);
}
return this.promptAndSetEnablement(extension, enable, workspace).then(reload => {
const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled;
return this.promptAndSetEnablement(extension, enablementState, enable).then(reload => {
/* __GDPR__
"extension:enable" : {
"${include}": [
@@ -593,19 +616,19 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
}
private promptAndSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise<any> {
const allDependencies = this.getDependenciesRecursively(extension, this.local, enable, workspace, []);
private promptAndSetEnablement(extension: IExtension, enablementState: EnablementState, enable: boolean): TPromise<any> {
const allDependencies = this.getDependenciesRecursively(extension, this.local, enablementState, []);
if (allDependencies.length > 0) {
if (enable) {
return this.promptForDependenciesAndEnable(extension, allDependencies, workspace);
return this.promptForDependenciesAndEnable(extension, allDependencies, enablementState, enable);
} else {
return this.promptForDependenciesAndDisable(extension, allDependencies, workspace);
return this.promptForDependenciesAndDisable(extension, allDependencies, enablementState, enable);
}
}
return this.checkAndSetEnablement(extension, [], enable, workspace);
return this.checkAndSetEnablement(extension, [], enablementState, enable);
}
private promptForDependenciesAndEnable(extension: IExtension, dependencies: IExtension[], workspace: boolean): TPromise<any> {
private promptForDependenciesAndEnable(extension: IExtension, dependencies: IExtension[], enablementState: EnablementState, enable: boolean): TPromise<any> {
const message = nls.localize('enableDependeciesConfirmation', "Enabling '{0}' also enable its dependencies. Would you like to continue?", extension.displayName);
const options = [
nls.localize('enable', "Yes"),
@@ -614,13 +637,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return this.choiceService.choose(Severity.Info, message, options, 1, true)
.then<void>(value => {
if (value === 0) {
return this.checkAndSetEnablement(extension, dependencies, true, workspace);
return this.checkAndSetEnablement(extension, dependencies, enablementState, enable);
}
return TPromise.as(null);
});
}
private promptForDependenciesAndDisable(extension: IExtension, dependencies: IExtension[], workspace: boolean): TPromise<void> {
private promptForDependenciesAndDisable(extension: IExtension, dependencies: IExtension[], enablementState: EnablementState, enable: boolean): TPromise<void> {
const message = nls.localize('disableDependeciesConfirmation', "Would you like to disable '{0}' only or its dependencies also?", extension.displayName);
const options = [
nls.localize('disableOnly', "Only"),
@@ -630,26 +653,26 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
return this.choiceService.choose(Severity.Info, message, options, 2, true)
.then<void>(value => {
if (value === 0) {
return this.checkAndSetEnablement(extension, [], false, workspace);
return this.checkAndSetEnablement(extension, [], enablementState, enable);
}
if (value === 1) {
return this.checkAndSetEnablement(extension, dependencies, false, workspace);
return this.checkAndSetEnablement(extension, dependencies, enablementState, enable);
}
return TPromise.as(null);
});
}
private checkAndSetEnablement(extension: IExtension, dependencies: IExtension[], enable: boolean, workspace: boolean): TPromise<any> {
private checkAndSetEnablement(extension: IExtension, dependencies: IExtension[], enablementState: EnablementState, enable: boolean): TPromise<any> {
if (!enable) {
let dependents = this.getDependentsAfterDisablement(extension, dependencies, this.local, workspace);
let dependents = this.getDependentsAfterDisablement(extension, dependencies, this.local, enablementState);
if (dependents.length) {
return TPromise.wrapError<void>(new Error(this.getDependentsErrorMessage(extension, dependents)));
}
}
return TPromise.join([extension, ...dependencies].map(e => this.doSetEnablement(e, enable, workspace)));
return TPromise.join([extension, ...dependencies].map(e => this.doSetEnablement(e, enablementState)));
}
private getDependenciesRecursively(extension: IExtension, installed: IExtension[], enable: boolean, workspace: boolean, checked: IExtension[]): IExtension[] {
private getDependenciesRecursively(extension: IExtension, installed: IExtension[], enablementState: EnablementState, checked: IExtension[]): IExtension[] {
if (checked.indexOf(extension) !== -1) {
return [];
}
@@ -659,19 +682,19 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
}
const dependenciesToDisable = installed.filter(i => {
// Do not include extensions which are already disabled and request is to disable
if (!enable && (workspace ? i.disabledForWorkspace : i.disabledGlobally)) {
if (i.enablementState === enablementState && (i.enablementState === EnablementState.WorkspaceDisabled || i.enablementState === EnablementState.Disabled)) {
return false;
}
return i.type === LocalExtensionType.User && extension.dependencies.indexOf(i.id) !== -1;
});
const depsOfDeps = [];
for (const dep of dependenciesToDisable) {
depsOfDeps.push(...this.getDependenciesRecursively(dep, installed, enable, workspace, checked));
depsOfDeps.push(...this.getDependenciesRecursively(dep, installed, enablementState, checked));
}
return [...dependenciesToDisable, ...depsOfDeps];
}
private getDependentsAfterDisablement(extension: IExtension, dependencies: IExtension[], installed: IExtension[], workspace: boolean): IExtension[] {
private getDependentsAfterDisablement(extension: IExtension, dependencies: IExtension[], installed: IExtension[], enablementState: EnablementState): IExtension[] {
return installed.filter(i => {
if (i.dependencies.length === 0) {
return false;
@@ -679,8 +702,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
if (i === extension) {
return false;
}
const disabled = workspace ? i.disabledForWorkspace : i.disabledGlobally;
if (disabled) {
if (i.enablementState === EnablementState.WorkspaceDisabled || i.enablementState === EnablementState.Disabled) {
return false;
}
if (dependencies.indexOf(i) !== -1) {
@@ -707,17 +729,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
extension.displayName, dependents[0].displayName, dependents[1].displayName);
}
private doSetEnablement(extension: IExtension, enable: boolean, workspace: boolean): TPromise<boolean> {
if (workspace) {
return this.extensionEnablementService.setEnablement(extension, enable, workspace);
}
const globalElablement = this.extensionEnablementService.setEnablement(extension, enable, false);
if (enable && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
const workspaceEnablement = this.extensionEnablementService.setEnablement(extension, enable, true);
return TPromise.join([globalElablement, workspaceEnablement]).then(values => values[0] || values[1]);
}
return globalElablement;
private doSetEnablement(extension: IExtension, enablementState: EnablementState): TPromise<boolean> {
return this.extensionEnablementService.setEnablement(extension, enablementState);
}
get allowedBadgeProviders(): string[] {
@@ -756,9 +769,16 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
if (extension) {
this.installing = installing ? this.installing.filter(e => e !== installing) : this.installing;
if (!error) {
if (error) {
if (extension.gallery) {
// Updating extension can be only a gallery extension
const installed = this.installed.filter(e => e.id === extension.id)[0];
if (installed && installing) {
installing.operation = Operation.Updating;
}
}
} else {
extension.local = local;
const installed = this.installed.filter(e => e.id === extension.id)[0];
if (installed) {
if (installing) {
@@ -815,11 +835,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
private onEnablementChanged(extensionIdentifier: IExtensionIdentifier) {
const [extension] = this.local.filter(e => areSameExtensions(e, extensionIdentifier));
if (extension) {
const globallyDisabledExtensions = this.extensionEnablementService.getGloballyDisabledExtensions();
const workspaceDisabledExtensions = this.extensionEnablementService.getWorkspaceDisabledExtensions();
extension.disabledGlobally = globallyDisabledExtensions.some(disabled => areSameExtensions(disabled, extension));
extension.disabledForWorkspace = workspaceDisabledExtensions.some(disabled => areSameExtensions(disabled, extension));
this._onChange.fire();
const enablementState = this.extensionEnablementService.getEnablementState({ id: extension.id, uuid: extension.uuid });
if (enablementState !== extension.enablementState) {
extension.enablementState = enablementState;
this._onChange.fire();
}
}
}
@@ -933,4 +953,4 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
this.syncDelayer.cancel();
this.disposables = dispose(this.disposables);
}
}
}

View File

@@ -14,7 +14,7 @@ import * as ExtensionsActions from 'vs/workbench/parts/extensions/browser/extens
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
@@ -71,6 +71,7 @@ suite('ExtensionsActions Test', () => {
setup(() => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []);
instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());
instantiationService.stub(IExtensionService, { getExtensions: () => TPromise.wrap([]) });
(<TestExtensionEnablementService>instantiationService.get(IExtensionEnablementService)).reset();
@@ -509,44 +510,44 @@ suite('ExtensionsActions Test', () => {
});
});
test('Test EnableForWorkspaceAction when there extension is disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
test('Test EnableForWorkspaceAction when the extension is disabled globally', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => {
testObject.extension = extensions[0];
assert.ok(!testObject.enabled);
done();
});
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
testObject.extension = extensions[0];
assert.ok(testObject.enabled);
});
});
test('Test EnableForWorkspaceAction when extension is disabled for workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
test('Test EnableForWorkspaceAction when extension is disabled for workspace', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => {
testObject.extension = extensions[0];
assert.ok(testObject.enabled);
done();
});
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
testObject.extension = extensions[0];
assert.ok(testObject.enabled);
});
});
test('Test EnableForWorkspaceAction when the extension is disabled in both', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
test('Test EnableForWorkspaceAction when the extension is disabled globally and workspace', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.EnableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.EnableForWorkspaceAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => {
testObject.extension = extensions[0];
assert.ok(!testObject.enabled);
done();
});
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
testObject.extension = extensions[0];
assert.ok(testObject.enabled);
});
});
test('Test EnableGloballyAction when there is no extension', () => {
@@ -568,20 +569,20 @@ suite('ExtensionsActions Test', () => {
});
test('Test EnableGloballyAction when the extension is disabled for workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
instantiationService.get(IExtensionsWorkbenchService).queryLocal().done(extensions => {
testObject.extension = extensions[0];
assert.ok(!testObject.enabled);
assert.ok(testObject.enabled);
done();
});
});
test('Test EnableGloballyAction when the extension is disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -594,8 +595,8 @@ suite('ExtensionsActions Test', () => {
});
test('Test EnableGloballyAction when the extension is disabled in both', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.EnableGloballyAction = instantiationService.createInstance(ExtensionsActions.EnableGloballyAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -626,7 +627,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test EnableAction when extension is installed and disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -639,7 +640,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test EnableAction when extension is installed and disabled for workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.EnableAction = instantiationService.createInstance(ExtensionsActions.EnableAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -698,7 +699,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableForWorkspaceAction when the extension is disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -711,7 +712,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableForWorkspaceAction when the extension is disabled workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -742,7 +743,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableGloballyAction when the extension is disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -755,7 +756,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableGloballyAction when the extension is disabled for workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id');
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -798,7 +799,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableAction when extension is installed and disabled globally', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -811,7 +812,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test DisableAction when extension is installed and disabled for workspace', (done) => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction);
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
@@ -970,7 +971,7 @@ suite('ExtensionsActions Test', () => {
assert.ok(testObject.enabled);
assert.equal('Reload to activate', testObject.tooltip);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessage);
done();
});
});
@@ -1005,7 +1006,7 @@ suite('ExtensionsActions Test', () => {
assert.ok(testObject.enabled);
assert.equal('Reload to deactivate', testObject.tooltip);
assert.equal(`Reload this window to deactivate the uninstalled extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to deactivate the uninstalled extension 'a'?`, testObject.reloadMessage);
done();
});
});
@@ -1045,7 +1046,7 @@ suite('ExtensionsActions Test', () => {
assert.ok(testObject.enabled);
assert.equal('Reload to update', testObject.tooltip);
assert.equal(`Reload this window to activate the updated extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to activate the updated extension 'a'?`, testObject.reloadMessage);
done();
});
@@ -1053,7 +1054,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is updated when not running', (done) => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a', { version: '1.0.1' });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
@@ -1078,11 +1079,11 @@ suite('ExtensionsActions Test', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
workbenchService.queryLocal().done(extensions => {
testObject.extension = extensions[0];
workbenchService.setEnablement(extensions[0], false);
workbenchService.setEnablement(extensions[0], EnablementState.Disabled);
assert.ok(testObject.enabled);
assert.equal('Reload to deactivate', testObject.tooltip);
assert.equal(`Reload this window to deactivate the extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to deactivate the extension 'a'?`, testObject.reloadMessage);
done();
});
});
@@ -1095,8 +1096,8 @@ suite('ExtensionsActions Test', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
workbenchService.queryLocal().done(extensions => {
testObject.extension = extensions[0];
workbenchService.setEnablement(extensions[0], false);
workbenchService.setEnablement(extensions[0], true);
workbenchService.setEnablement(extensions[0], EnablementState.Disabled);
workbenchService.setEnablement(extensions[0], EnablementState.Enabled);
assert.ok(!testObject.enabled);
done();
@@ -1105,33 +1106,33 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is enabled when not running', (done) => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
workbenchService.queryLocal().done(extensions => {
testObject.extension = extensions[0];
workbenchService.setEnablement(extensions[0], true);
workbenchService.setEnablement(extensions[0], EnablementState.Enabled);
assert.ok(testObject.enabled);
assert.equal('Reload to activate', testObject.tooltip);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessage);
done();
});
});
test('Test ReloadAction when extension enablement is toggled when not running', (done) => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a');
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
workbenchService.queryLocal().done(extensions => {
testObject.extension = extensions[0];
workbenchService.setEnablement(extensions[0], true);
workbenchService.setEnablement(extensions[0], false);
workbenchService.setEnablement(extensions[0], EnablementState.Enabled);
workbenchService.setEnablement(extensions[0], EnablementState.Disabled);
assert.ok(!testObject.enabled);
done();
@@ -1140,7 +1141,7 @@ suite('ExtensionsActions Test', () => {
test('Test ReloadAction when extension is updated when not running and enabled', (done) => {
instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.b' }]);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction);
const local = aLocalExtension('a', { version: '1.0.1' });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
@@ -1151,11 +1152,11 @@ suite('ExtensionsActions Test', () => {
const gallery = aGalleryExtension('a', { identifier: local.identifier, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, gallery });
didInstallEvent.fire({ identifier: gallery.identifier, gallery, local: aLocalExtension('a', gallery, gallery) });
workbenchService.setEnablement(extensions[0], true);
workbenchService.setEnablement(extensions[0], EnablementState.Enabled);
assert.ok(testObject.enabled);
assert.equal('Reload to activate', testObject.tooltip);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessaage);
assert.equal(`Reload this window to activate the extension 'a'?`, testObject.reloadMessage);
done();
});
});

View File

@@ -15,7 +15,7 @@ import { IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/parts/
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
import {
IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension,
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier
DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService';
@@ -74,6 +74,7 @@ suite('ExtensionsWorkbenchService Test', () => {
setup(() => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []);
instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());
instantiationService.stubPromise(IChoiceService, 'choose', 0);
(<TestExtensionEnablementService>instantiationService.get(IExtensionEnablementService)).reset();
@@ -102,7 +103,8 @@ suite('ExtensionsWorkbenchService Test', () => {
changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' },
download: { uri: 'uri:download', fallbackUri: 'fallback:download' },
icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' },
license: { uri: 'uri:license', fallbackUri: 'fallback:license' }
license: { uri: 'uri:license', fallbackUri: 'fallback:license' },
repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' },
});
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
@@ -250,7 +252,8 @@ suite('ExtensionsWorkbenchService Test', () => {
changelog: { uri: 'uri:changelog', fallbackUri: 'fallback:changlog' },
download: { uri: 'uri:download', fallbackUri: 'fallback:download' },
icon: { uri: 'uri:icon', fallbackUri: 'fallback:icon' },
license: { uri: 'uri:license', fallbackUri: 'fallback:license' }
license: { uri: 'uri:license', fallbackUri: 'fallback:license' },
repository: { uri: 'uri:repository', fallbackUri: 'fallback:repository' },
});
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local1, local2]);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery1));
@@ -277,7 +280,7 @@ suite('ExtensionsWorkbenchService Test', () => {
assert.equal(4, actual.rating);
assert.equal(100, actual.ratingCount);
assert.equal(true, actual.outdated);
assert.deepEqual(['pub.1', 'pub.2'], actual.dependencies);
assert.deepEqual(['pub.1'], actual.dependencies);
actual = actuals[1];
assert.equal(LocalExtensionType.System, actual.type);
@@ -463,7 +466,7 @@ suite('ExtensionsWorkbenchService Test', () => {
test('test one level extension dependencies without cycle', () => {
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.c', 'pub.d'] })));
instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('b'), aGalleryExtension('c'), aGalleryExtension('d')]);
instantiationService.stubPromise(IExtensionGalleryService, 'loadAllDependencies', [aGalleryExtension('b'), aGalleryExtension('c'), aGalleryExtension('d')]);
return testObject.queryGallery().then(page => {
const extension = page.firstPage[0];
@@ -502,7 +505,7 @@ suite('ExtensionsWorkbenchService Test', () => {
test('test one level extension dependencies with cycle', () => {
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.a'] })));
instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('b'), aGalleryExtension('a')]);
instantiationService.stubPromise(IExtensionGalleryService, 'loadAllDependencies', [aGalleryExtension('b'), aGalleryExtension('a')]);
return testObject.queryGallery().then(page => {
const extension = page.firstPage[0];
@@ -534,7 +537,7 @@ suite('ExtensionsWorkbenchService Test', () => {
test('test one level extension dependencies with missing dependencies', () => {
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.a'] })));
instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('a')]);
instantiationService.stubPromise(IExtensionGalleryService, 'loadAllDependencies', [aGalleryExtension('a')]);
return testObject.queryGallery().then(page => {
const extension = page.firstPage[0];
@@ -568,7 +571,7 @@ suite('ExtensionsWorkbenchService Test', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.inbuilt', 'pub.a'] })));
instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [aGalleryExtension('a')]);
instantiationService.stubPromise(IExtensionGalleryService, 'loadAllDependencies', [aGalleryExtension('a')]);
return testObject.queryGallery().then(page => {
const extension = page.firstPage[0];
@@ -603,7 +606,7 @@ suite('ExtensionsWorkbenchService Test', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.c'] })));
instantiationService.stubPromise(IExtensionGalleryService, 'getAllDependencies', [
instantiationService.stubPromise(IExtensionGalleryService, 'loadAllDependencies', [
aGalleryExtension('b', {}, { dependencies: ['pub.d', 'pub.e'] }),
aGalleryExtension('d', {}, { dependencies: ['pub.f', 'pub.c'] }),
aGalleryExtension('e')]);
@@ -691,292 +694,282 @@ suite('ExtensionsWorkbenchService Test', () => {
});
});
test('test disabled flags are false for uninstalled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false, true);
test('test uninstalled extensions are always enabled', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a')));
return testObject.queryGallery().then(pagedResponse => {
const actual = pagedResponse.firstPage[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Enabled);
});
});
test('test disabled flags are false for installed enabled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false, true);
test('test enablement state installed enabled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Enabled);
});
test('test disabled for workspace is set', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.e' }, false, true);
test('test workspace disabled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.e' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
const actual = testObject.local[0];
assert.ok(actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
});
test('test disabled globally is set', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false, true);
test('test globally disabled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.d' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Disabled);
});
test('test disable flags are updated for user extensions', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false, true);
test('test enablement state is updated for user extensions', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false, true);
testObject.setEnablement(testObject.local[0], EnablementState.WorkspaceDisabled);
const actual = testObject.local[0];
assert.ok(actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.WorkspaceDisabled);
});
test('test enable extension globally when extension is disabled for workspace', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], true);
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Enabled);
});
test('test disable extension globally should not disable for workspace', () => {
test('test disable extension globally', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Disabled);
});
test('test disabled flags are not updated for system extensions', () => {
test('test system extensions are always enabled', () => {
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', {}, { type: LocalExtensionType.System })]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(!actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Enabled);
});
test('test disabled flags are updated on change from outside', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false, true);
test('test enablement state is updated on change from outside', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
const actual = testObject.local[0];
assert.ok(!actual.disabledForWorkspace);
assert.ok(actual.disabledGlobally);
assert.equal(actual.enablementState, EnablementState.Disabled);
});
test('test disable extension with dependencies disable only itself', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.ok(!testObject.local[1].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
});
test('test disable extension with dependencies disable all', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.ok(testObject.local[1].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
});
test('test disable extension fails if extension is a dependent of other', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
return testObject.setEnablement(testObject.local[1], false).then(() => assert.fail('Should fail'), error => assert.ok(true));
return testObject.setEnablement(testObject.local[1], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
});
test('test disable extension does not fail if its dependency is a dependent of other but chosen to disable only itself', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
});
test('test disable extension fails if its dependency is a dependent of other', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
return testObject.setEnablement(testObject.local[0], false).then(() => assert.fail('Should fail'), error => assert.ok(true));
return testObject.setEnablement(testObject.local[0], EnablementState.Disabled).then(() => assert.fail('Should fail'), error => assert.ok(true));
});
test('test disable extension if its dependency is a dependent of other disabled extension', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.ok(testObject.local[1].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
});
test('test disable extension if its dependencys dependency is itself', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.a'] }), aLocalExtension('c')]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.ok(testObject.local[1].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
});
test('test disable extension if its dependency is dependent and is disabled', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c', { extensionDependencies: ['pub.b'] })]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
});
test('test disable extension with cyclic dependencies', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Enabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Enabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]);
instantiationService.stubPromise(IChoiceService, 'choose', 1);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(testObject.local[0].disabledGlobally);
assert.ok(testObject.local[1].disabledGlobally);
assert.ok(testObject.local[2].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Disabled);
});
test('test enable extension with dependencies enable all', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b'), aLocalExtension('c')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], true);
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
assert.ok(!testObject.local[0].disabledGlobally);
assert.ok(!testObject.local[1].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
});
test('test enable extension with cyclic dependencies', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', { extensionDependencies: ['pub.b'] }), aLocalExtension('b', { extensionDependencies: ['pub.c'] }), aLocalExtension('c', { extensionDependencies: ['pub.a'] })]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
testObject.setEnablement(testObject.local[0], true);
testObject.setEnablement(testObject.local[0], EnablementState.Enabled);
assert.ok(!testObject.local[0].disabledGlobally);
assert.ok(!testObject.local[1].disabledGlobally);
assert.ok(!testObject.local[2].disabledGlobally);
assert.equal(testObject.local[0].enablementState, EnablementState.Enabled);
assert.equal(testObject.local[1].enablementState, EnablementState.Enabled);
assert.equal(testObject.local[2].enablementState, EnablementState.Enabled);
});
test('test change event is fired when disablement flags are changed', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
const target = sinon.spy();
testObject.onChange(target);
testObject.setEnablement(testObject.local[0], false);
testObject.setEnablement(testObject.local[0], EnablementState.Disabled);
assert.ok(target.calledOnce);
});
test('test change event is fired when disablement flags are changed from outside', () => {
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, false, true);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.c' }, EnablementState.Disabled);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.b' }, EnablementState.WorkspaceDisabled);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]);
testObject = instantiationService.createInstance(ExtensionsWorkbenchService);
const target = sinon.spy();
testObject.onChange(target);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, false);
instantiationService.get(IExtensionEnablementService).setEnablement({ id: 'pub.a' }, EnablementState.Disabled);
assert.ok(target.calledOnce);
});
@@ -996,7 +989,8 @@ suite('ExtensionsWorkbenchService Test', () => {
icon: null,
license: null,
manifest: null,
readme: null
readme: null,
repository: null
};
function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: IGalleryExtensionAssets = noAssets): IGalleryExtension {

View File

@@ -44,7 +44,7 @@ class TwitterFeedbackService implements IFeedbackService {
length += ` via @${TwitterFeedbackService.VIA_NAME}`.length;
}
return 140 - length;
return 280 - length;
}
}
@@ -88,4 +88,4 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem {
return null;
}
}
}

View File

@@ -163,6 +163,7 @@
background-image: url('twitter.svg');
background-color: #007ACC;
background-position: left;
background-size: 20px;
background-repeat: no-repeat;
padding-left: 30px;
padding-right: 12px;

View File

@@ -1,549 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<image style="overflow:visible;" width="1687" height="1687" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABpcAAAaXCAYAAACT+jYAAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAeGhJREFUeNrs3f1xE1nWwOHD1P5v
vxFYG4E9EaCJADYCiwjWG8GYCGAiQEQwEAEigsERICIYOwK/fadbZfFhfCS3pP54nqouU7M7g31s
ULd+fW8/ub29DQAAAAAAAMj4xQgAAAAAAADIEpcAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
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
AACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlL
AAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAA
AEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOIS
AAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAA
AJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgE
AAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAA
AGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4B
AAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAA
AJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsA
AAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAA
QCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIA
AAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAA
kIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQA
AAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAA
ZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEA
AAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAA
mbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAA
AAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABA
Ji4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAA
AAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQ
iUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAA
AAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk
4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAA
AAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZ
uAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAA
AAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAm
LgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAA
AAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJ
SwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAA
AABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTi
EgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAA
AACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4
BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAA
AABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYu
AQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAA
AACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlL
AAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAA
AEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOIS
AAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAA
AJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgE
AAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAA
AGTiEgAAAAAAAJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4B
AAAAAACQiUsAAAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAA
AJm4BAAAAAAAQCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsA
AAAAAABk4hIAAAAAAACZuAQAAAAAAEAmLgEAAAAAAJCJSwAAAAAAAGTiEgAAAAAAAJm4BAAAAAAA
QCYuAQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIA
AAAAAACZuAQAAAAAAEAmLgEAAAAAAJD9D/btHPQiCVCiAAAAAElFTkSuQmCC" transform="matrix(9.484292e-03 0 0 9.484292e-03 0 0)">
</image>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>BrandTwitter_white_16x</title><path d="M16,16H0V0H16Z" fill="#f6f6f6" opacity="0"/><path d="M14.362,4.737l.01.425A9.276,9.276,0,0,1,5.032,14.5,9.293,9.293,0,0,1,0,13.027a6.67,6.67,0,0,0,.783.046A6.584,6.584,0,0,0,4.86,11.667a3.286,3.286,0,0,1-3.066-2.28,3.284,3.284,0,0,0,1.482-.056A3.284,3.284,0,0,1,.642,6.113V6.071a3.269,3.269,0,0,0,1.487.41A3.285,3.285,0,0,1,1.114,2.1,9.316,9.316,0,0,0,7.88,5.529a3.284,3.284,0,0,1,5.593-2.993,6.577,6.577,0,0,0,2.085-.8,3.292,3.292,0,0,1-1.443,1.816A6.574,6.574,0,0,0,16,3.038a6.685,6.685,0,0,1-1.638,1.7Z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 632 B

View File

@@ -16,7 +16,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows';
*/
export class BinaryFileEditor extends BaseBinaryResourceEditor {
public static ID = BINARY_FILE_EDITOR_ID;
public static readonly ID = BINARY_FILE_EDITOR_ID;
constructor(
@ITelemetryService telemetryService: ITelemetryService,

Some files were not shown because too many files have changed in this diff Show More