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
@@ -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);
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
14
src/vs/workbench/parts/cache/node/cache.contribution.ts
vendored
Normal 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);
|
||||
66
src/vs/workbench/parts/cache/node/nodeCachedDataManager.ts
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -344,6 +344,7 @@ export class LanguageConfigurationFileHandler {
|
||||
|
||||
const schemaId = 'vscode://schemas/language-configuration';
|
||||
const schema: IJSONSchema = {
|
||||
allowComments: true,
|
||||
default: {
|
||||
comments: {
|
||||
blockComment: ['/*', '*/'],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}; }`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
248
src/vs/workbench/parts/debug/electron-browser/baseDebugView.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
518
src/vs/workbench/parts/debug/electron-browser/breakpointsView.ts
Normal 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);
|
||||
}
|
||||
543
src/vs/workbench/parts/debug/electron-browser/callStackView.ts
Normal 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);
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 += ' ';
|
||||
}
|
||||
|
||||
334
src/vs/workbench/parts/debug/electron-browser/variablesView.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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."),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}; }`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,10 +47,6 @@ export class OpenExtensionsFolderAction extends Action {
|
||||
return this.windowsService.showItemInFolder(itemToShow);
|
||||
});
|
||||
}
|
||||
|
||||
protected isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallVSIXAction extends Action {
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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; }
|
||||
}
|
||||
@@ -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 |
@@ -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 |
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
|
||||
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
|
||||
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
|
||||
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
|
||||
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
|
||||
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
|
||||
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
|
||||
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
|
||||
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
|
||||
E5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cA
|
||||
AAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAA
|
||||
AABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABIE5cAAAAAAABI
|
||||
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
|
||||
AQAAAAAAkIlLAAAAAAAAZOISAAAAAAAAmbgEAAAAAABAJi4BAAAAAACQiUsAAAAAAABk4hIAAAAA
|
||||
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 |
@@ -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,
|
||||
|
||||