Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
@@ -7,17 +7,21 @@ import 'vs/css!../browser/media/debug.contribution';
|
||||
import 'vs/css!../browser/media/debugHover';
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actionRegistry';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actions';
|
||||
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 { 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 } from 'vs/workbench/parts/debug/common/debug';
|
||||
import {
|
||||
IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
|
||||
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID
|
||||
} from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager';
|
||||
@@ -34,7 +38,14 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
|
||||
import * as debugCommands from 'vs/workbench/parts/debug/electron-browser/debugCommands';
|
||||
import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/parts/debug/electron-browser/statusbarColorProvider';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry';
|
||||
import { ViewLocation, ViewsRegistry } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { DebugViewlet, FocusVariablesViewAction, FocusBreakpointsViewAction, FocusCallStackViewAction, FocusWatchViewAction } from 'vs/workbench/parts/debug/browser/debugViewlet';
|
||||
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';
|
||||
|
||||
class OpenDebugViewletAction extends ToggleViewletAction {
|
||||
public static ID = VIEWLET_ID;
|
||||
@@ -66,8 +77,7 @@ class OpenDebugPanelAction extends TogglePanelAction {
|
||||
|
||||
// register viewlet
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor(
|
||||
'vs/workbench/parts/debug/browser/debugViewlet',
|
||||
'DebugViewlet',
|
||||
DebugViewlet,
|
||||
VIEWLET_ID,
|
||||
nls.localize('debug', "Debug"),
|
||||
'debug',
|
||||
@@ -83,8 +93,7 @@ const openPanelKb: IKeybindings = {
|
||||
|
||||
// register repl panel
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
|
||||
'vs/workbench/parts/debug/electron-browser/repl',
|
||||
'Repl',
|
||||
Repl,
|
||||
REPL_ID,
|
||||
nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'),
|
||||
'repl',
|
||||
@@ -94,10 +103,10 @@ Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescri
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).setDefaultPanelId(REPL_ID);
|
||||
|
||||
// Register default debug views
|
||||
ViewsRegistry.registerViews([{ id: 'workbench.debug.variablesView', name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, size: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: 'workbench.debug.watchExpressionsView', name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, size: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: 'workbench.debug.callStackView', name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, size: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: 'workbench.debug.breakPointsView', name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, size: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, size: 40, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, size: 10, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, size: 30, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, size: 20, location: ViewLocation.Debug, canToggleVisibility: true }]);
|
||||
|
||||
// register action to open viewlet
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions);
|
||||
@@ -123,19 +132,24 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(PauseAction, PauseActi
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Without Debugging', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Without Debugging', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, FocusReplAction.ID, FocusReplAction.LABEL), 'Debug: Focus Debug Console', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusVariablesViewAction, FocusVariablesViewAction.ID, FocusVariablesViewAction.LABEL), 'Debug: Focus Variables', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusWatchViewAction, FocusWatchViewAction.ID, FocusWatchViewAction.LABEL), 'Debug: Focus Watch', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusCallStackViewAction, FocusCallStackViewAction.ID, FocusCallStackViewAction.LABEL), 'Debug: Focus CallStack', debugCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBreakpointsViewAction, FocusBreakpointsViewAction.ID, FocusBreakpointsViewAction.LABEL), 'Debug: Focus Breakpoints', debugCategory);
|
||||
|
||||
|
||||
// Register Quick Open
|
||||
(<IQuickOpenRegistry>Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
'vs/workbench/parts/debug/browser/debugQuickOpen',
|
||||
'DebugQuickOpenHandler',
|
||||
DebugQuickOpenHandler,
|
||||
DebugQuickOpenHandler.ID,
|
||||
'debug ',
|
||||
'inLaunchConfigurationsPicker',
|
||||
nls.localize('debugCommands', "Debug Configuration")
|
||||
@@ -183,3 +197,31 @@ configurationRegistry.registerConfiguration({
|
||||
});
|
||||
|
||||
debugCommands.registerCommands();
|
||||
|
||||
// Register Debug Status
|
||||
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
|
||||
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(DebugStatus, StatusbarAlignment.LEFT, 30 /* Low Priority */));
|
||||
|
||||
// Touch Bar
|
||||
if (isMacintosh) {
|
||||
|
||||
const registerTouchBarEntry = (id: string, title: string, order, when: ContextKeyExpr, icon: string) => {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: {
|
||||
id, title, iconPath: URI.parse(require.toUrl(`vs/workbench/parts/debug/electron-browser/media/${icon}`)).fsPath
|
||||
},
|
||||
when,
|
||||
group: '9_debug',
|
||||
order
|
||||
});
|
||||
};
|
||||
|
||||
registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_NOT_IN_DEBUG_MODE, 'continue-tb.png');
|
||||
registerTouchBarEntry(ContinueAction.ID, ContinueAction.LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png');
|
||||
registerTouchBarEntry(PauseAction.ID, PauseAction.LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png');
|
||||
registerTouchBarEntry(StepOverAction.ID, StepOverAction.LABEL, 2, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepover-tb.png');
|
||||
registerTouchBarEntry(StepIntoAction.ID, StepIntoAction.LABEL, 3, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepinto-tb.png');
|
||||
registerTouchBarEntry(StepOutAction.ID, StepOutAction.LABEL, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png');
|
||||
registerTouchBarEntry(RestartAction.ID, RestartAction.LABEL, 5, CONTEXT_IN_DEBUG_MODE, 'restart-tb.png');
|
||||
registerTouchBarEntry(StopAction.ID, StopAction.LABEL, 6, CONTEXT_IN_DEBUG_MODE, 'stop-tb.png');
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import severity from 'vs/base/common/severity';
|
||||
@@ -15,53 +14,14 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDebugService, IConfig, IEnablement, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
|
||||
export function registerCommands(): void {
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: '_workbench.startDebug',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
handler(accessor: ServicesAccessor, configurationOrName: IConfig | string, folderUri?: uri) {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
if (!configurationOrName) {
|
||||
configurationOrName = debugService.getConfigurationManager().selectedName;
|
||||
}
|
||||
|
||||
if (!folderUri) {
|
||||
const selectedLaunch = debugService.getConfigurationManager().selectedLaunch;
|
||||
folderUri = selectedLaunch ? selectedLaunch.workspaceUri : undefined;
|
||||
}
|
||||
|
||||
if (typeof configurationOrName === 'string') {
|
||||
debugService.startDebugging(folderUri, configurationOrName);
|
||||
} else {
|
||||
debugService.createProcess(folderUri, configurationOrName);
|
||||
}
|
||||
},
|
||||
when: CONTEXT_NOT_IN_DEBUG_MODE,
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.customDebugRequest',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
handler(accessor: ServicesAccessor, request: string, requestArgs: any) {
|
||||
const process = accessor.get(IDebugService).getViewModel().focusedProcess;
|
||||
if (process) {
|
||||
return process.session.custom(request, requestArgs);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
primary: undefined
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'debug.logToDebugConsole',
|
||||
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
|
||||
@@ -204,11 +164,11 @@ export function registerCommands(): void {
|
||||
primary: undefined,
|
||||
handler: (accessor, workspaceUri: string) => {
|
||||
const manager = accessor.get(IDebugService).getConfigurationManager();
|
||||
if (!accessor.get(IWorkspaceContextService).hasWorkspace()) {
|
||||
if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
accessor.get(IMessageService).show(severity.Info, nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
const launch = manager.getLaunches().filter(l => l.workspaceUri.toString() === workspaceUri).pop() || manager.selectedLaunch;
|
||||
const launch = manager.getLaunches().filter(l => l.workspace.uri.toString() === workspaceUri).pop() || manager.selectedLaunch;
|
||||
|
||||
return launch.openConfigFile(false).done(editor => {
|
||||
if (editor) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import { first } from 'vs/base/common/arrays';
|
||||
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { IModel, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
|
||||
@@ -21,14 +20,15 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import * as extensionsRegistry from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
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 } from 'vs/platform/workspace/common/workspace';
|
||||
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';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, DEBUG_SCHEME, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug';
|
||||
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';
|
||||
@@ -83,10 +83,6 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.adapterExecutableCommand', "If specified VS Code will call this command to determine the executable path of the debug adapter and the arguments to pass."),
|
||||
type: 'string'
|
||||
},
|
||||
startSessionCommand: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.startSessionCommand', "If specified VS Code will call this command for the \"debug\" or \"run\" actions targeted for this extension."),
|
||||
type: 'string'
|
||||
},
|
||||
configurationSnippets: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.configurationSnippets', "Snippets for adding new configurations in \'launch.json\'."),
|
||||
type: 'array'
|
||||
@@ -217,7 +213,7 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
private _selectedLaunch: ILaunch;
|
||||
private toDispose: IDisposable[];
|
||||
private _onDidSelectConfigurationName = new Emitter<void>();
|
||||
private _providers: Map<number, IDebugConfigurationProvider>;
|
||||
private providers: IDebugConfigurationProvider[];
|
||||
|
||||
constructor(
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@@ -230,66 +226,53 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService
|
||||
) {
|
||||
this._providers = new Map<number, IDebugConfigurationProvider>();
|
||||
this.providers = [];
|
||||
this.adapters = [];
|
||||
this.toDispose = [];
|
||||
this.registerListeners(lifecycleService);
|
||||
this.initLaunches();
|
||||
const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
|
||||
const filtered = this.launches.filter(l => l.workspaceUri.toString() === previousSelectedRoot);
|
||||
const launchToSelect = filtered.length ? filtered[0] : this.launches.length ? this.launches[0] : undefined;
|
||||
this.selectConfiguration(launchToSelect, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
|
||||
const filtered = this.launches.filter(l => l.workspace.uri.toString() === previousSelectedRoot);
|
||||
this.selectConfiguration(filtered.length ? filtered[0] : undefined, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
|
||||
}
|
||||
|
||||
public registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void {
|
||||
if (!debugConfigurationProvider) {
|
||||
return;
|
||||
}
|
||||
this._providers.set(handle, debugConfigurationProvider);
|
||||
|
||||
debugConfigurationProvider.handle = handle;
|
||||
this.providers = this.providers.filter(p => p.handle !== handle);
|
||||
this.providers.push(debugConfigurationProvider);
|
||||
const adapter = this.getAdapter(debugConfigurationProvider.type);
|
||||
if (adapter) {
|
||||
// Check if the provider contributes provideDebugConfigurations method
|
||||
if (adapter && debugConfigurationProvider.provideDebugConfigurations) {
|
||||
adapter.hasConfigurationProvider = true;
|
||||
}
|
||||
}
|
||||
|
||||
public unregisterDebugConfigurationProvider(handle: number): boolean {
|
||||
return this._providers.delete(handle);
|
||||
public unregisterDebugConfigurationProvider(handle: number): void {
|
||||
this.providers = this.providers.filter(p => p.handle !== handle);
|
||||
}
|
||||
|
||||
public resolveDebugConfiguration(folderUri: uri | undefined, debugConfiguration: any): TPromise<any> {
|
||||
|
||||
// collect all candidates
|
||||
const providers: IDebugConfigurationProvider[] = [];
|
||||
this._providers.forEach(provider => {
|
||||
if (provider.type === debugConfiguration.type && provider.resolveDebugConfiguration) {
|
||||
providers.push(provider);
|
||||
}
|
||||
});
|
||||
|
||||
public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): TPromise<IConfig> {
|
||||
// pipe the config through the promises sequentially
|
||||
return providers.reduce((promise, provider) => {
|
||||
return this.providers.filter(p => p.type === type && p.resolveDebugConfiguration).reduce((promise, provider) => {
|
||||
return promise.then(config => {
|
||||
return provider.resolveDebugConfiguration(folderUri, config);
|
||||
if (config) {
|
||||
return provider.resolveDebugConfiguration(folderUri, config);
|
||||
} else {
|
||||
return Promise.resolve(config);
|
||||
}
|
||||
});
|
||||
}, TPromise.as(debugConfiguration));
|
||||
}
|
||||
|
||||
public provideDebugConfigurations(folderUri: uri | undefined, type: string): TPromise<any[]> {
|
||||
|
||||
// collect all candidates
|
||||
const configs: TPromise<any[]>[] = [];
|
||||
this._providers.forEach(provider => {
|
||||
if (provider.type === type && provider.provideDebugConfigurations) {
|
||||
configs.push(provider.provideDebugConfigurations(folderUri));
|
||||
}
|
||||
});
|
||||
|
||||
// combine all configs into one array
|
||||
return TPromise.join(configs).then(results => {
|
||||
return [].concat.apply([], results);
|
||||
});
|
||||
return TPromise.join(this.providers.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations(folderUri)))
|
||||
.then(results => results.reduce((first, second) => first.concat(second), []));
|
||||
}
|
||||
|
||||
private registerListeners(lifecycleService: ILifecycleService): void {
|
||||
@@ -336,18 +319,21 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
});
|
||||
});
|
||||
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => {
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => {
|
||||
this.initLaunches();
|
||||
const toSelect = this.selectedLaunch && this.selectedLaunch.getConfigurationNames().length ? this.selectedLaunch : first(this.launches, l => !!l.getConfigurationNames().length, this.launches.length ? this.launches[0] : undefined);
|
||||
this.selectConfiguration(toSelect);
|
||||
this.selectConfiguration();
|
||||
}));
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('launch')) {
|
||||
this.selectConfiguration();
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(lifecycleService.onShutdown(this.store, this));
|
||||
}
|
||||
|
||||
private initLaunches(): void {
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
this.launches = workspace ? workspace.roots.map(root => this.instantiationService.createInstance(Launch, this, root)) : [];
|
||||
this.launches = this.contextService.getWorkspace().folders.map(folder => this.instantiationService.createInstance(Launch, this, folder));
|
||||
if (this.launches.indexOf(this._selectedLaunch) === -1) {
|
||||
this._selectedLaunch = undefined;
|
||||
}
|
||||
@@ -369,10 +355,14 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
return this._onDidSelectConfigurationName.event;
|
||||
}
|
||||
|
||||
public selectConfiguration(launch: ILaunch, name?: string, debugStarted?: boolean): void {
|
||||
public selectConfiguration(launch?: ILaunch, name?: string, debugStarted?: boolean): void {
|
||||
const previousLaunch = this._selectedLaunch;
|
||||
const previousName = this._selectedName;
|
||||
|
||||
if (!launch) {
|
||||
launch = this.selectedLaunch && this.selectedLaunch.getConfigurationNames().length ? this.selectedLaunch : first(this.launches, l => !!l.getConfigurationNames().length, this.launches.length ? this.launches[0] : undefined);
|
||||
}
|
||||
|
||||
this._selectedLaunch = launch;
|
||||
const names = launch ? launch.getConfigurationNames() : [];
|
||||
if (name && names.indexOf(name) >= 0) {
|
||||
@@ -388,15 +378,15 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
}
|
||||
|
||||
public canSetBreakpointsIn(model: IModel): boolean {
|
||||
if (model.uri.scheme !== Schemas.file && model.uri.scheme !== DEBUG_SCHEME) {
|
||||
const modeId = model ? model.getLanguageIdentifier().language : null;
|
||||
if (!modeId || modeId === 'json') {
|
||||
// do not allow breakpoints in our settings files
|
||||
return false;
|
||||
}
|
||||
if (this.configurationService.getConfiguration<IDebugConfiguration>('debug').allowBreakpointsEverywhere) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const modeId = model ? model.getLanguageIdentifier().language : null;
|
||||
|
||||
return this.breakpointModeIdsSet.has(modeId);
|
||||
}
|
||||
|
||||
@@ -435,22 +425,10 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
});
|
||||
}
|
||||
|
||||
public getStartSessionCommand(type?: string): TPromise<{ command: string, type: string }> {
|
||||
return this.guessAdapter(type).then(adapter => {
|
||||
if (adapter) {
|
||||
return {
|
||||
command: adapter.startSessionCommand,
|
||||
type: adapter.type
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private store(): void {
|
||||
this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE);
|
||||
if (this._selectedLaunch) {
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspaceUri.toString(), StorageScope.WORKSPACE);
|
||||
this.storageService.store(DEBUG_SELECTED_ROOT, this._selectedLaunch.workspace.uri.toString(), StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,21 +439,20 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
|
||||
class Launch implements ILaunch {
|
||||
|
||||
public name: string;
|
||||
|
||||
constructor(
|
||||
private configurationManager: ConfigurationManager,
|
||||
public workspaceUri: uri,
|
||||
public workspace: IWorkspaceFolder,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
|
||||
@IExtensionService private extensionService: IExtensionService
|
||||
) {
|
||||
this.name = paths.basename(this.workspaceUri.fsPath);
|
||||
// noop
|
||||
}
|
||||
|
||||
public getCompound(name: string): ICompound {
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspaceUri });
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
if (!config || !config.compounds) {
|
||||
return null;
|
||||
}
|
||||
@@ -484,8 +461,8 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public getConfigurationNames(): string[] {
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspaceUri });
|
||||
if (!config || !config.configurations) {
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
if (!config || !config.configurations || !Array.isArray(config.configurations)) {
|
||||
return [];
|
||||
} else {
|
||||
const names = config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name);
|
||||
@@ -501,7 +478,7 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public getConfiguration(name: string): IConfig {
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspaceUri });
|
||||
const config = this.configurationService.getConfiguration<IGlobalConfig>('launch', { resource: this.workspace.uri });
|
||||
if (!config || !config.configurations) {
|
||||
return null;
|
||||
}
|
||||
@@ -510,7 +487,7 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public resolveConfiguration(config: IConfig): TPromise<IConfig> {
|
||||
const result = objects.deepClone(config) as IConfig;
|
||||
const result = objects.clone(config) as IConfig;
|
||||
// Set operating system specific properties #1873
|
||||
const setOSProperties = (flag: boolean, osConfig: IEnvConfig) => {
|
||||
if (flag && osConfig) {
|
||||
@@ -525,7 +502,7 @@ class Launch implements ILaunch {
|
||||
|
||||
// massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths.
|
||||
Object.keys(result).forEach(key => {
|
||||
result[key] = this.configurationResolverService.resolveAny(this.workspaceUri, result[key]);
|
||||
result[key] = this.configurationResolverService.resolveAny(this.workspace, result[key]);
|
||||
});
|
||||
|
||||
const adapter = this.configurationManager.getAdapter(result.type);
|
||||
@@ -533,61 +510,63 @@ class Launch implements ILaunch {
|
||||
}
|
||||
|
||||
public get uri(): uri {
|
||||
return uri.file(paths.join(this.workspaceUri.fsPath, '/.vscode/launch.json'));
|
||||
return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') });
|
||||
}
|
||||
|
||||
public openConfigFile(sideBySide: boolean, type?: string): TPromise<IEditor> {
|
||||
const resource = this.uri;
|
||||
let configFileCreated = false;
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => {
|
||||
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.workspaceUri, adapter.type).then(initialConfigs => {
|
||||
return adapter.getInitialConfigurationContent(this.workspaceUri, initialConfigs);
|
||||
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 };
|
||||
});
|
||||
} 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 };
|
||||
});
|
||||
});
|
||||
}).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 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;
|
||||
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));
|
||||
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));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ 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 { first } from 'vs/base/common/arrays';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
@@ -146,7 +147,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => {
|
||||
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel());
|
||||
@@ -158,7 +160,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = { x: e.event.posx + 1, y: e.event.posy };
|
||||
const anchor = { x: e.event.posx, y: e.event.posy };
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp.lineNumber === lineNumber && bp.uri.toString() === uri.toString());
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
@@ -182,8 +184,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
let showBreakpointHintAtLineNumber = -1;
|
||||
if (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
if (!e.target.detail) {
|
||||
// is not after last line
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
showBreakpointHintAtLineNumber = e.target.position.lineNumber;
|
||||
}
|
||||
}
|
||||
@@ -365,7 +367,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
// First call stack frame that is available is the frame where exception has been thrown
|
||||
const exceptionSf = first(callStack, sf => sf.source && sf.source.available, undefined);
|
||||
if (!exceptionSf) {
|
||||
if (!exceptionSf || exceptionSf !== focusedSf) {
|
||||
this.closeExceptionWidget();
|
||||
return;
|
||||
}
|
||||
@@ -411,6 +413,9 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
|
||||
public addLaunchConfiguration(): TPromise<any> {
|
||||
/* __GDPR__
|
||||
"debug/addLaunchConfiguration" : {}
|
||||
*/
|
||||
this.telemetryService.publicLog('debug/addLaunchConfiguration');
|
||||
let configurationsArrayPosition: Position;
|
||||
const model = this.editor.getModel();
|
||||
|
||||
@@ -254,7 +254,8 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
this.valueContainer.hidden = false;
|
||||
renderExpressionValue(expression, this.valueContainer, {
|
||||
showChanged: false,
|
||||
preserveWhitespace: true
|
||||
preserveWhitespace: true,
|
||||
colorize: true
|
||||
});
|
||||
this.valueContainer.title = '';
|
||||
this.editor.layoutContentWidget(this);
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import uri from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { first, distinct } from 'vs/base/common/arrays';
|
||||
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
@@ -33,7 +34,7 @@ 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';
|
||||
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, OutputNameValueElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
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';
|
||||
@@ -46,10 +47,12 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
|
||||
import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
|
||||
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';
|
||||
|
||||
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
|
||||
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
|
||||
@@ -72,9 +75,8 @@ export class DebugService implements debug.IDebugService {
|
||||
private _onDidCustomEvent: Emitter<debug.DebugEvent>;
|
||||
private model: Model;
|
||||
private viewModel: ViewModel;
|
||||
private allSessionIds: Set<string>;
|
||||
private allProcesses: Map<string, debug.IProcess>;
|
||||
private configurationManager: ConfigurationManager;
|
||||
private customTelemetryService: ITelemetryService;
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private toDisposeOnSessionEnd: Map<string, lifecycle.IDisposable[]>;
|
||||
private inDebugMode: IContextKey<boolean>;
|
||||
@@ -82,6 +84,7 @@ export class DebugService implements debug.IDebugService {
|
||||
private debugState: IContextKey<string>;
|
||||
private breakpointsToSendOnResourceSaved: Set<string>;
|
||||
private launchJsonChanged: boolean;
|
||||
private previousState: debug.State;
|
||||
|
||||
constructor(
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@@ -114,7 +117,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this._onDidEndProcess = new Emitter<debug.IProcess>();
|
||||
this._onDidCustomEvent = new Emitter<debug.DebugEvent>();
|
||||
this.sessionStates = new Map<string, debug.State>();
|
||||
this.allSessionIds = new Set<string>();
|
||||
this.allProcesses = new Map<string, debug.IProcess>();
|
||||
|
||||
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
|
||||
this.toDispose.push(this.configurationManager);
|
||||
@@ -140,49 +143,46 @@ export class DebugService implements debug.IDebugService {
|
||||
private onBroadcast(broadcast: IBroadcast): void {
|
||||
|
||||
// attach: PH is ready to be attached to
|
||||
const process = this.model.getProcesses().filter(p => p.getId() === broadcast.payload.debugId).pop();
|
||||
const session = process ? <RawDebugSession>process.session : null;
|
||||
if (!this.allSessionIds.has(broadcast.payload.debugId)) {
|
||||
const process = this.allProcesses.get(broadcast.payload.debugId);
|
||||
if (!process) {
|
||||
// Ignore attach events for sessions that never existed (wrong vscode windows)
|
||||
return;
|
||||
}
|
||||
const session = <RawDebugSession>process.session;
|
||||
|
||||
if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
|
||||
if (session) {
|
||||
this.onSessionEnd(session);
|
||||
}
|
||||
|
||||
const config = this.configurationManager.selectedLaunch.getConfiguration(this.configurationManager.selectedName);
|
||||
this.configurationManager.selectedLaunch.resolveConfiguration(config).done(resolvedConfig => {
|
||||
resolvedConfig.request = 'attach';
|
||||
resolvedConfig.port = broadcast.payload.port;
|
||||
this.doCreateProcess(this.configurationManager.selectedLaunch.workspaceUri, resolvedConfig, broadcast.payload.debugId);
|
||||
}, errors.onUnexpectedError);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (session && broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
|
||||
this.onSessionEnd(session);
|
||||
|
||||
process.configuration.request = 'attach';
|
||||
process.configuration.port = broadcast.payload.port;
|
||||
this.doCreateProcess(process.session.root, process.configuration, process.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// from this point on we require an active session
|
||||
if (!session) {
|
||||
if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
|
||||
this.onSessionEnd(session);
|
||||
return;
|
||||
}
|
||||
|
||||
// an extension logged output, show it inside the REPL
|
||||
if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
|
||||
let extensionOutput: ILogEntry = broadcast.payload.logEntry;
|
||||
let extensionOutput: IRemoteConsoleLog = broadcast.payload.logEntry;
|
||||
let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;
|
||||
|
||||
let args: any[] = [];
|
||||
try {
|
||||
let parsed = JSON.parse(extensionOutput.arguments);
|
||||
args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
|
||||
} catch (error) {
|
||||
args.push(extensionOutput.arguments);
|
||||
const { args, stack } = parse(extensionOutput);
|
||||
let source: debug.IReplElementSource;
|
||||
if (stack) {
|
||||
const frame = getFirstFrame(stack);
|
||||
if (frame) {
|
||||
source = {
|
||||
column: frame.column,
|
||||
lineNumber: frame.line,
|
||||
source: process.getSource({
|
||||
name: resources.basenameOrAuthority(frame.uri),
|
||||
path: frame.uri.fsPath
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// add output for each argument logged
|
||||
@@ -205,12 +205,12 @@ export class DebugService implements debug.IDebugService {
|
||||
|
||||
// flush any existing simple values logged
|
||||
if (simpleVals.length) {
|
||||
this.logToRepl(simpleVals.join(' '), sev);
|
||||
this.logToRepl(simpleVals.join(' '), sev, source);
|
||||
simpleVals = [];
|
||||
}
|
||||
|
||||
// show object
|
||||
this.logToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
|
||||
this.logToRepl(new RawObjectReplElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source);
|
||||
}
|
||||
|
||||
// string: watch out for % replacement directive
|
||||
@@ -240,14 +240,14 @@ export class DebugService implements debug.IDebugService {
|
||||
// flush simple values
|
||||
// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
|
||||
if (simpleVals.length) {
|
||||
this.logToRepl(simpleVals.join(' ') + '\n', sev);
|
||||
this.logToRepl(simpleVals.join(' ') + '\n', sev, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
|
||||
const callStack = thread.getCallStack();
|
||||
if (!callStack.length || this.viewModel.focusedStackFrame) {
|
||||
if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ export class DebugService implements debug.IDebugService {
|
||||
aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
|
||||
}
|
||||
|
||||
return stackFrameToFocus.openInEditor(this.editorService);
|
||||
return stackFrameToFocus.openInEditor(this.editorService, true);
|
||||
}
|
||||
|
||||
private registerSessionListeners(process: Process, session: RawDebugSession): void {
|
||||
@@ -290,23 +290,8 @@ export class DebugService implements debug.IDebugService {
|
||||
|
||||
this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Stopped);
|
||||
const threadId = event.body.threadId;
|
||||
|
||||
session.threads().then(response => {
|
||||
if (!response || !response.body || !response.body.threads) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rawThread = response.body.threads.filter(t => t.id === threadId).pop();
|
||||
this.model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
thread: rawThread,
|
||||
threadId,
|
||||
stoppedDetails: event.body,
|
||||
allThreadsStopped: event.body.allThreadsStopped
|
||||
});
|
||||
|
||||
const thread = process && process.getThread(threadId);
|
||||
this.fetchThreads(session, event.body).done(() => {
|
||||
const thread = process && process.getThread(event.body.threadId);
|
||||
if (thread) {
|
||||
// Call fetch call stack twice, the first only return the top stack frame.
|
||||
// Second retrieves the rest of the call stack. For performance reasons #25605
|
||||
@@ -354,33 +339,69 @@ export class DebugService implements debug.IDebugService {
|
||||
if (event.body.category === 'telemetry') {
|
||||
// only log telemetry events from debug adapter if the adapter provided the telemetry key
|
||||
// and the user opted in telemetry
|
||||
if (this.customTelemetryService && this.telemetryService.isOptedIn) {
|
||||
this.customTelemetryService.publicLog(event.body.output, event.body.data);
|
||||
if (session.customTelemetryService && this.telemetryService.isOptedIn) {
|
||||
// __GDPR__TODO__ We're sending events in the name of the debug adapter and we can not ensure that those are declared correctly.
|
||||
session.customTelemetryService.publicLog(event.body.output, event.body.data);
|
||||
}
|
||||
} else if (event.body.variablesReference) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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 => {
|
||||
// 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);
|
||||
this.logToRepl(child, outputSeverity, source);
|
||||
});
|
||||
});
|
||||
} else if (typeof event.body.output === 'string') {
|
||||
this.logToRepl(event.body.output, outputSeverity);
|
||||
this.logToRepl(event.body.output, outputSeverity, source);
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
|
||||
const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
|
||||
const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
|
||||
if (breakpoint) {
|
||||
if (!breakpoint.column) {
|
||||
event.body.breakpoint.column = undefined;
|
||||
const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
|
||||
|
||||
if (event.body.reason === 'new' && event.body.breakpoint.source) {
|
||||
const source = process.getSource(event.body.breakpoint.source);
|
||||
const bps = this.model.addBreakpoints(source.uri, [{
|
||||
column: event.body.breakpoint.column,
|
||||
enabled: true,
|
||||
lineNumber: event.body.breakpoint.line,
|
||||
}], false);
|
||||
if (bps.length === 1) {
|
||||
this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint });
|
||||
}
|
||||
}
|
||||
|
||||
if (event.body.reason === 'removed') {
|
||||
if (breakpoint) {
|
||||
this.model.removeBreakpoints([breakpoint]);
|
||||
}
|
||||
if (functionBreakpoint) {
|
||||
this.model.removeFunctionBreakpoints(functionBreakpoint.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (breakpoint) {
|
||||
if (!breakpoint.column) {
|
||||
event.body.breakpoint.column = undefined;
|
||||
}
|
||||
this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
|
||||
}
|
||||
this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
|
||||
} else {
|
||||
const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
|
||||
if (functionBreakpoint) {
|
||||
this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
|
||||
}
|
||||
@@ -389,12 +410,11 @@ 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
|
||||
const process = this.viewModel.focusedProcess;
|
||||
if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
|
||||
process && this.contextService.hasWorkspace() && process.configuration.noDebug) {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
|
||||
process && this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && process.configuration.noDebug) {
|
||||
this.broadcastService.broadcast({
|
||||
channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
|
||||
payload: [process.session.root.fsPath]
|
||||
payload: [process.session.root.uri.fsPath]
|
||||
});
|
||||
}
|
||||
if (session && session.getId() === event.sessionId) {
|
||||
@@ -407,15 +427,17 @@ export class DebugService implements debug.IDebugService {
|
||||
}));
|
||||
}
|
||||
|
||||
private fetchThreads(session: RawDebugSession): TPromise<any> {
|
||||
private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> {
|
||||
return session.threads().then(response => {
|
||||
if (response && response.body && response.body.threads) {
|
||||
response.body.threads.forEach(thread =>
|
||||
response.body.threads.forEach(thread => {
|
||||
this.model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threadId: thread.id,
|
||||
thread
|
||||
}));
|
||||
thread,
|
||||
stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -424,7 +446,7 @@ export class DebugService implements debug.IDebugService {
|
||||
let result: Breakpoint[];
|
||||
try {
|
||||
result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
|
||||
return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition);
|
||||
return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.adapterData);
|
||||
});
|
||||
} catch (e) { }
|
||||
|
||||
@@ -506,11 +528,14 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
|
||||
const state = this.state;
|
||||
const stateLabel = debug.State[state];
|
||||
if (stateLabel) {
|
||||
this.debugState.set(stateLabel.toLowerCase());
|
||||
if (this.previousState !== state) {
|
||||
const stateLabel = debug.State[state];
|
||||
if (stateLabel) {
|
||||
this.debugState.set(stateLabel.toLowerCase());
|
||||
}
|
||||
this.previousState = state;
|
||||
this._onDidChangeState.fire(state);
|
||||
}
|
||||
this._onDidChangeState.fire(state);
|
||||
}
|
||||
|
||||
public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
|
||||
@@ -520,7 +545,7 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
if (!stackFrame) {
|
||||
const threads = process ? process.getAllThreads() : null;
|
||||
const callStack = threads && threads.length ? threads[0].getCallStack() : null;
|
||||
const callStack = threads && threads.length === 1 ? threads[0].getCallStack() : null;
|
||||
stackFrame = callStack && callStack.length ? callStack[0] : null;
|
||||
}
|
||||
|
||||
@@ -582,6 +607,9 @@ 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.
|
||||
@@ -592,12 +620,12 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.removeReplExpressions();
|
||||
}
|
||||
|
||||
public logToRepl(value: string | debug.IExpression, sev = severity.Info): void {
|
||||
public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void {
|
||||
if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) {
|
||||
// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
|
||||
this.model.removeReplExpressions();
|
||||
} else {
|
||||
this.model.appendToRepl(value, sev);
|
||||
this.model.appendToRepl(value, sev, source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,17 +645,22 @@ export class DebugService implements debug.IDebugService {
|
||||
this.model.removeWatchExpressions(id);
|
||||
}
|
||||
|
||||
public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
|
||||
public evaluateWatchExpressions(): TPromise<void> {
|
||||
return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
|
||||
}
|
||||
|
||||
public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
|
||||
|
||||
// make sure to save all files and that the configuration is up to date
|
||||
return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() =>
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() =>
|
||||
this.extensionService.onReady().then(() => {
|
||||
if (this.model.getProcesses().length === 0) {
|
||||
this.removeReplExpressions();
|
||||
this.allProcesses.clear();
|
||||
}
|
||||
this.launchJsonChanged = false;
|
||||
const manager = this.getConfigurationManager();
|
||||
const launch = root ? manager.getLaunches().filter(l => l.workspaceUri.toString() === root.toString()).pop() : undefined;
|
||||
const launch = root ? manager.getLaunches().filter(l => l.workspace.uri.toString() === root.uri.toString()).pop() : undefined;
|
||||
|
||||
let config: debug.IConfig, compound: debug.ICompound;
|
||||
if (!configOrName) {
|
||||
@@ -656,63 +689,45 @@ export class DebugService implements debug.IDebugService {
|
||||
return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName)));
|
||||
}
|
||||
|
||||
return manager.getStartSessionCommand(config ? config.type : undefined).then<any>(commandAndType => {
|
||||
if (noDebug && config) {
|
||||
config.noDebug = true;
|
||||
// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
|
||||
// Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config.
|
||||
let type: string;
|
||||
if (config) {
|
||||
type = config.type;
|
||||
} else {
|
||||
// a no-folder workspace has no launch.config
|
||||
config = <debug.IConfig>{};
|
||||
}
|
||||
if (noDebug) {
|
||||
config.noDebug = true;
|
||||
}
|
||||
|
||||
const sessionId = generateUuid();
|
||||
this.updateStateAndEmit(sessionId, debug.State.Initializing);
|
||||
const wrapUpState = () => {
|
||||
if (this.sessionStates.get(sessionId) === debug.State.Initializing) {
|
||||
this.updateStateAndEmit(sessionId, debug.State.Inactive);
|
||||
}
|
||||
};
|
||||
|
||||
// deprecated code: use DebugConfigurationProvider instead of startSessionCommand
|
||||
if (commandAndType && commandAndType.command) {
|
||||
const defaultConfig = noDebug ? { noDebug: true } : {};
|
||||
return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => {
|
||||
if (launch) {
|
||||
if (result && result.status === 'initialConfiguration') {
|
||||
return launch.openConfigFile(false, commandAndType.type);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (result && result.status === 'saveConfiguration') {
|
||||
return this.fileService.updateContent(launch.uri, result.content).then(() => launch.openConfigFile(false));
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
// end of deprecation
|
||||
|
||||
if (!config && commandAndType.type) {
|
||||
config = {
|
||||
type: commandAndType.type,
|
||||
request: 'launch'
|
||||
};
|
||||
}
|
||||
|
||||
if (config) {
|
||||
return this.configurationManager.resolveDebugConfiguration(launch ? launch.workspaceUri : undefined, config).then(config => {
|
||||
|
||||
// TODO@AW: handle the 'initialConfiguration' and 'saveConfiguration' cases from above!
|
||||
return this.createProcess(root, config);
|
||||
});
|
||||
}
|
||||
if (launch && commandAndType) {
|
||||
return launch.openConfigFile(false, commandAndType.type);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return <any>launch.openConfigFile(false, type); // cast to ignore weird compile error
|
||||
})
|
||||
).then(() => wrapUpState(), err => {
|
||||
wrapUpState();
|
||||
return <any>TPromise.wrapError(err);
|
||||
});
|
||||
})
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
public findProcessByUUID(uuid: string): debug.IProcess | null {
|
||||
const processes = this.getModel().getProcesses();
|
||||
const result = processes.filter(process => process.getId() === uuid);
|
||||
if (result.length > 0) {
|
||||
return result[0]; // there can only be one
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public createProcess(root: uri, config: debug.IConfig): TPromise<debug.IProcess> {
|
||||
private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
|
||||
return this.textFileService.saveAll().then(() =>
|
||||
(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
|
||||
if (!resolvedConfig) {
|
||||
@@ -720,18 +735,31 @@ export class DebugService implements debug.IDebugService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this.configurationManager.getAdapter(resolvedConfig.type)) {
|
||||
const message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
|
||||
nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
|
||||
if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
|
||||
let message: string;
|
||||
if (config.request !== 'attach' && config.request !== 'launch') {
|
||||
message = config.request ? nls.localize('debugRequestNotSupported', "Attribute `{0}` has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
|
||||
: nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request');
|
||||
|
||||
} else {
|
||||
message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
|
||||
nls.localize('debugTypeMissing', "Missing property `type` for the chosen launch configuration.");
|
||||
}
|
||||
|
||||
return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
|
||||
}
|
||||
|
||||
return this.runPreLaunchTask(resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
|
||||
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) => {
|
||||
const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
|
||||
const successExitCode = taskSummary && taskSummary.exitCode === 0;
|
||||
const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
|
||||
if (successExitCode || (errorCount === 0 && !failureExitCode)) {
|
||||
return this.doCreateProcess(root, resolvedConfig);
|
||||
return this.doCreateProcess(root, resolvedConfig, sessionId);
|
||||
}
|
||||
|
||||
this.messageService.show(severity.Error, {
|
||||
@@ -739,10 +767,7 @@ export class DebugService implements debug.IDebugService {
|
||||
errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
|
||||
nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode),
|
||||
actions: [
|
||||
new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
|
||||
this.messageService.hideAll();
|
||||
return this.doCreateProcess(root, resolvedConfig);
|
||||
}),
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
|
||||
CloseAction
|
||||
]
|
||||
@@ -752,6 +777,7 @@ export class DebugService implements debug.IDebugService {
|
||||
this.messageService.show(err.severity, {
|
||||
message: err.message,
|
||||
actions: [
|
||||
debugAnywayAction,
|
||||
this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
|
||||
this.taskService.configureAction(),
|
||||
CloseAction
|
||||
@@ -759,7 +785,7 @@ export class DebugService implements debug.IDebugService {
|
||||
});
|
||||
});
|
||||
}, err => {
|
||||
if (!this.contextService.hasWorkspace()) {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
|
||||
return undefined;
|
||||
}
|
||||
@@ -774,10 +800,9 @@ export class DebugService implements debug.IDebugService {
|
||||
);
|
||||
}
|
||||
|
||||
private doCreateProcess(root: uri, configuration: debug.IConfig, sessionId = generateUuid()): TPromise<debug.IProcess> {
|
||||
private doCreateProcess(root: IWorkspaceFolder, configuration: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
|
||||
configuration.__sessionId = sessionId;
|
||||
this.allSessionIds.add(sessionId);
|
||||
this.updateStateAndEmit(sessionId, debug.State.Initializing);
|
||||
this.inDebugMode.set(true);
|
||||
|
||||
return this.telemetryService.getTelemetryInfo().then(info => {
|
||||
const telemetryInfo: { [key: string]: string } = Object.create(null);
|
||||
@@ -788,9 +813,9 @@ export class DebugService implements debug.IDebugService {
|
||||
const adapter = this.configurationManager.getAdapter(configuration.type);
|
||||
const { aiKey, type } = adapter;
|
||||
const publisher = adapter.extensionDescription.publisher;
|
||||
this.customTelemetryService = null;
|
||||
let client: TelemetryClient;
|
||||
|
||||
let customTelemetryService: TelemetryService;
|
||||
if (aiKey) {
|
||||
client = new TelemetryClient(
|
||||
uri.parse(require.toUrl('bootstrap')).fsPath,
|
||||
@@ -809,11 +834,12 @@ export class DebugService implements debug.IDebugService {
|
||||
const channel = client.getChannel('telemetryAppender');
|
||||
const appender = new TelemetryAppenderClient(channel);
|
||||
|
||||
this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
|
||||
customTelemetryService = new TelemetryService({ appender }, this.configurationService);
|
||||
}
|
||||
|
||||
const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root);
|
||||
const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, customTelemetryService, root);
|
||||
const process = this.model.addProcess(configuration, session);
|
||||
this.allProcesses.set(process.getId(), process);
|
||||
|
||||
this.toDisposeOnSessionEnd.set(session.getId(), []);
|
||||
if (client) {
|
||||
@@ -829,7 +855,8 @@ export class DebugService implements debug.IDebugService {
|
||||
columnsStartAt1: true,
|
||||
supportsVariableType: true, // #8858
|
||||
supportsVariablePaging: true, // #9537
|
||||
supportsRunInTerminalRequest: true // #10574
|
||||
supportsRunInTerminalRequest: true, // #10574
|
||||
locale: platform.locale
|
||||
}).then((result: DebugProtocol.InitializeResponse) => {
|
||||
this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
|
||||
return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
|
||||
@@ -845,20 +872,29 @@ export class DebugService implements debug.IDebugService {
|
||||
this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.hasWorkspace())) {
|
||||
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;
|
||||
this.viewletService.openViewlet(debug.VIEWLET_ID);
|
||||
}
|
||||
|
||||
this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
|
||||
this.inDebugMode.set(true);
|
||||
this.debugType.set(configuration.type);
|
||||
if (this.model.getProcesses().length > 1) {
|
||||
this.viewModel.setMultiProcessView(true);
|
||||
}
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Running);
|
||||
|
||||
/* __GDPR__
|
||||
"debugSessionStart" : {
|
||||
"type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"exceptionBreakpoints": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
|
||||
"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"extensionName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
|
||||
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"launchJsonExists": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
return this.telemetryService.publicLog('debugSessionStart', {
|
||||
type: configuration.type,
|
||||
breakpointCount: this.model.getBreakpoints().length,
|
||||
@@ -866,7 +902,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.hasWorkspace() && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root })
|
||||
launchJsonExists: this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root.uri })
|
||||
});
|
||||
}).then(() => process, (error: any) => {
|
||||
if (error instanceof Error && error.message === 'Canceled') {
|
||||
@@ -875,6 +911,12 @@ export class DebugService implements debug.IDebugService {
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : error;
|
||||
/* __GDPR__
|
||||
"debugMisconfiguration" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"error": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
|
||||
this.updateStateAndEmit(session.getId(), debug.State.Inactive);
|
||||
if (!session.disconnected) {
|
||||
@@ -887,6 +929,9 @@ export class DebugService implements debug.IDebugService {
|
||||
if (this.model.getReplElements().length > 0) {
|
||||
this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
if (this.model.getReplElements().length === 0) {
|
||||
this.inDebugMode.reset();
|
||||
}
|
||||
|
||||
const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
|
||||
const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction];
|
||||
@@ -896,30 +941,46 @@ export class DebugService implements debug.IDebugService {
|
||||
});
|
||||
}
|
||||
|
||||
private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
|
||||
private runPreLaunchTask(root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
|
||||
if (!taskName) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
// run a task before starting a debug session
|
||||
return this.taskService.getTask(taskName).then(task => {
|
||||
return this.taskService.getTask(root, taskName).then(task => {
|
||||
if (!task) {
|
||||
return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
|
||||
}
|
||||
|
||||
return this.taskService.getActiveTasks().then(tasks => {
|
||||
// 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 => {
|
||||
if (tasks.filter(t => t._id === task._id).length) {
|
||||
// task is already running - nothing to do.
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Active, () => taskStarted = true));
|
||||
const taskPromise = this.taskService.run(task);
|
||||
if (task.isBackground) {
|
||||
return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
|
||||
return new TPromise((c, e) => this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null))));
|
||||
}
|
||||
|
||||
return taskPromise;
|
||||
});
|
||||
|
||||
return new TPromise((c, e) => {
|
||||
promise.then(result => {
|
||||
taskStarted = true;
|
||||
c(result);
|
||||
}, error => e(error));
|
||||
|
||||
setTimeout(() => {
|
||||
if (!taskStarted) {
|
||||
e({ severity: severity.Error, message: nls.localize('taskNotTracked', "The preLaunchTask '{0}' cannot be tracked.", taskName) });
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -938,7 +999,7 @@ export class DebugService implements debug.IDebugService {
|
||||
if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
|
||||
return this.broadcastService.broadcast({
|
||||
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
|
||||
payload: [process.session.root.fsPath]
|
||||
payload: [process.session.root.uri.fsPath]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -954,7 +1015,7 @@ export class DebugService implements debug.IDebugService {
|
||||
config.noDebug = process.configuration.noDebug;
|
||||
}
|
||||
config.__restart = restartData;
|
||||
this.createProcess(process.session.root, config).then(() => c(null), err => e(err));
|
||||
this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
|
||||
}, 300);
|
||||
});
|
||||
}).then(() => {
|
||||
@@ -986,6 +1047,15 @@ export class DebugService implements debug.IDebugService {
|
||||
private onSessionEnd(session: RawDebugSession): void {
|
||||
const bpsExist = this.model.getBreakpoints().length > 0;
|
||||
const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
|
||||
/* __GDPR__
|
||||
"debugSessionStop" : {
|
||||
"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"success": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugSessionStop', {
|
||||
type: process && process.configuration.type,
|
||||
success: session.emittedStopped || !bpsExist,
|
||||
@@ -996,6 +1066,7 @@ export class DebugService implements debug.IDebugService {
|
||||
|
||||
this.model.removeProcess(session.getId());
|
||||
if (process && process.state !== debug.ProcessState.INACTIVE) {
|
||||
process.inactive = true;
|
||||
this._onDidEndProcess.fire(process);
|
||||
}
|
||||
|
||||
@@ -1059,7 +1130,17 @@ export class DebugService implements debug.IDebugService {
|
||||
const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString());
|
||||
|
||||
const source = process.sources.get(modelUri.toString());
|
||||
const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) };
|
||||
let rawSource: DebugProtocol.Source;
|
||||
if (source) {
|
||||
rawSource = source.raw;
|
||||
} else {
|
||||
const data = Source.getEncodedDebugData(modelUri);
|
||||
rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference };
|
||||
}
|
||||
|
||||
if (breakpointsToSend.length && !rawSource.adapterData) {
|
||||
rawSource.adapterData = breakpointsToSend[0].adapterData;
|
||||
}
|
||||
|
||||
return session.setBreakpoints({
|
||||
source: rawSource,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
@@ -22,7 +23,7 @@ import { DefaultController, DefaultDragAndDrop, ClickBehavior } from 'vs/base/pa
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
@@ -48,6 +49,7 @@ export interface IRenderValueOptions {
|
||||
showChanged?: boolean;
|
||||
maxValueLength?: number;
|
||||
showHover?: boolean;
|
||||
colorize?: boolean;
|
||||
}
|
||||
|
||||
function replaceWhitespace(value: string): string {
|
||||
@@ -66,12 +68,18 @@ export function renderExpressionValue(expressionOrValue: debug.IExpression | str
|
||||
if (value !== Expression.DEFAULT_VALUE) {
|
||||
dom.addClass(container, 'error');
|
||||
}
|
||||
} 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.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) {
|
||||
@@ -104,7 +112,8 @@ export function renderVariable(tree: ITree, variable: Variable, data: IVariableT
|
||||
showChanged,
|
||||
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
|
||||
preserveWhitespace: false,
|
||||
showHover: true
|
||||
showHover: true,
|
||||
colorize: true
|
||||
});
|
||||
} else {
|
||||
data.value.textContent = '';
|
||||
@@ -152,7 +161,10 @@ function renderRenameBox(debugService: debug.IDebugService, contextViewService:
|
||||
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), errors.onUnexpectedError);
|
||||
.done(() => {
|
||||
tree.refresh(element, false);
|
||||
debugService.evaluateWatchExpressions();
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +197,7 @@ function getSourceName(source: Source, contextService: IWorkspaceContextService,
|
||||
return source.name;
|
||||
}
|
||||
|
||||
return paths.basename(source.uri.fsPath);
|
||||
return resources.basenameOrAuthority(source.uri);
|
||||
}
|
||||
|
||||
export class BaseDebugController extends DefaultController {
|
||||
@@ -206,7 +218,7 @@ export class BaseDebugController extends DefaultController {
|
||||
this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService);
|
||||
}
|
||||
|
||||
public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent): boolean {
|
||||
public onContextMenu(tree: ITree, element: debug.IEnablement, event: ContextMenuEvent, focusElement = true): boolean {
|
||||
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
|
||||
return false;
|
||||
}
|
||||
@@ -214,10 +226,12 @@ export class BaseDebugController extends DefaultController {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
tree.setFocus(element);
|
||||
if (focusElement) {
|
||||
tree.setFocus(element);
|
||||
}
|
||||
|
||||
if (this.actionProvider.hasSecondaryActions(tree, element)) {
|
||||
const anchor = { x: event.posx + 1, y: event.posy };
|
||||
const anchor = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => {
|
||||
@@ -268,6 +282,9 @@ export class CallStackController extends BaseDebugController {
|
||||
|
||||
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.
|
||||
@@ -499,7 +516,8 @@ export class CallStackRenderer implements IRenderer {
|
||||
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'));
|
||||
data.lineNumber = dom.append(data.file, $('span.line-number'));
|
||||
const wrapper = dom.append(data.file, $('span.line-number-wrapper'));
|
||||
data.lineNumber = dom.append(wrapper, $('span.line-number'));
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -520,7 +538,7 @@ export class CallStackRenderer implements IRenderer {
|
||||
|
||||
private renderProcess(process: debug.IProcess, data: IProcessTemplateData): void {
|
||||
data.process.title = nls.localize({ key: 'process', comment: ['Process is a noun'] }, "Process");
|
||||
data.name.textContent = process.getName(this.contextService.hasMultiFolderWorkspace());
|
||||
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")
|
||||
@@ -938,7 +956,8 @@ export class WatchExpressionsRenderer implements IRenderer {
|
||||
showChanged: true,
|
||||
maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET,
|
||||
preserveWhitespace: false,
|
||||
showHover: true
|
||||
showHover: true,
|
||||
colorize: true
|
||||
});
|
||||
data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value;
|
||||
}
|
||||
@@ -1049,11 +1068,12 @@ export class BreakpointsActionProvider implements IActionProvider {
|
||||
}
|
||||
|
||||
public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
|
||||
const actions: IAction[] = [];
|
||||
|
||||
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
|
||||
actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
|
||||
if (element instanceof ExceptionBreakpoint) {
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
const actions: IAction[] = [];
|
||||
actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL));
|
||||
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
|
||||
actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL));
|
||||
actions.push(new Separator());
|
||||
@@ -1223,10 +1243,10 @@ export class BreakpointsRenderer implements IRenderer {
|
||||
if (breakpoint.column) {
|
||||
data.lineNumber.textContent += `:${breakpoint.column}`;
|
||||
}
|
||||
data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService, this.environmentService);
|
||||
data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.contextService, this.environmentService);
|
||||
data.checkbox.checked = breakpoint.enabled;
|
||||
|
||||
const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing;
|
||||
const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped;
|
||||
if (debugActive && !breakpoint.verified) {
|
||||
tree.addTraits('disabled', [breakpoint]);
|
||||
if (breakpoint.message) {
|
||||
@@ -1250,7 +1270,7 @@ export class BreakpointsAccessibilityProvider implements IAccessibilityProvider
|
||||
|
||||
public getAriaLabel(tree: ITree, element: any): string {
|
||||
if (element instanceof Breakpoint) {
|
||||
return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (<Breakpoint>element).lineNumber, getPathLabel(paths.basename((<Breakpoint>element).uri.fsPath), this.contextService), this.contextService);
|
||||
return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (<Breakpoint>element).lineNumber, getPathLabel(resources.basenameOrAuthority((<Breakpoint>element).uri), this.contextService), this.contextService);
|
||||
}
|
||||
if (element instanceof FunctionBreakpoint) {
|
||||
return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (<FunctionBreakpoint>element).name);
|
||||
@@ -1280,6 +1300,10 @@ export class BreakpointsController extends BaseDebugController {
|
||||
}
|
||||
|
||||
public openBreakpointSource(breakpoint: Breakpoint, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
|
||||
if (breakpoint.uri.scheme === debug.DEBUG_SCHEME && this.debugService.state === debug.State.Inactive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sideBySide = (event && (event.ctrlKey || event.metaKey));
|
||||
const selection = breakpoint.endLineNumber ? {
|
||||
startLineNumber: breakpoint.lineNumber,
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
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';
|
||||
@@ -15,9 +15,8 @@ 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 { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views';
|
||||
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';
|
||||
@@ -42,15 +41,15 @@ function renderViewTree(container: HTMLElement): HTMLElement {
|
||||
const $ = builder.$;
|
||||
const twistiePixels = 20;
|
||||
|
||||
export class VariablesView extends CollapsibleView {
|
||||
export class VariablesView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'variablesview.memento';
|
||||
private onFocusStackFrameScheduler: RunOnceScheduler;
|
||||
private variablesFocusedContext: IContextKey<boolean>;
|
||||
private settings: any;
|
||||
private expandedElements: any[];
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@@ -61,36 +60,39 @@ export class VariablesView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), sizing: ViewSizing.Flexible, ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
|
||||
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;
|
||||
if (stackFrame) {
|
||||
return stackFrame.getScopes().then(scopes => {
|
||||
if (scopes.length > 0 && !scopes[0].expensive) {
|
||||
return this.tree.expand(scopes[0]);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
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 renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
@@ -106,17 +108,17 @@ export class VariablesView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.variablesFocusedContext]));
|
||||
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.toolbar.setActions(prepareActions([collapseAction]))();
|
||||
|
||||
this.toDispose.push(viewModel.onDidFocusStackFrame(sf => {
|
||||
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) {
|
||||
@@ -125,11 +127,11 @@ export class VariablesView extends CollapsibleView {
|
||||
this.onFocusStackFrameScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
this.disposables.push(this.debugService.onDidChangeState(state => {
|
||||
collapseAction.enabled = state === State.Running || state === State.Stopped;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Variable)) {
|
||||
return;
|
||||
}
|
||||
@@ -146,12 +148,12 @@ export class VariablesView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[VariablesView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[VariablesView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class WatchExpressionsView extends CollapsibleView {
|
||||
export class WatchExpressionsView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'watchexpressionsview.memento';
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
@@ -160,7 +162,6 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@@ -170,13 +171,13 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof Expression) {
|
||||
this.expand();
|
||||
this.setExpanded(true);
|
||||
}
|
||||
}));
|
||||
this.watchExpressionsFocusedContext = CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(contextKeyService);
|
||||
@@ -188,13 +189,6 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
}, 50);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-watch');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
@@ -212,24 +206,24 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.watchExpressionsFocusedContext]));
|
||||
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.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
|
||||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
this.toReveal = we;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Expression)) {
|
||||
return;
|
||||
}
|
||||
@@ -246,12 +240,12 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[WatchExpressionsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[WatchExpressionsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class CallStackView extends CollapsibleView {
|
||||
export class CallStackView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'callstackview.memento';
|
||||
private pauseMessage: builder.Builder;
|
||||
@@ -260,7 +254,6 @@ export class CallStackView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@@ -270,7 +263,7 @@ export class CallStackView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
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
|
||||
@@ -301,14 +294,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}, 50);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const title = $('div.debug-call-stack-title').appendTo(container);
|
||||
$('span.title').text(this.options.name).appendTo(title);
|
||||
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);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
@@ -328,10 +319,10 @@ export class CallStackView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree));
|
||||
|
||||
this.toDispose.push(this.tree.addListener('selection', event => {
|
||||
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) {
|
||||
@@ -342,12 +333,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeCallStack(() => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeCallStack(() => {
|
||||
if (!this.onCallStackChangeScheduler.isScheduled()) {
|
||||
this.onCallStackChangeScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
|
||||
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
|
||||
@@ -386,12 +377,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[CallStackView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[CallStackView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class BreakpointsView extends CollapsibleView {
|
||||
export class BreakpointsView extends ViewsViewletPanel {
|
||||
|
||||
private static MAX_VISIBLE_FILES = 9;
|
||||
private static MEMENTO = 'breakopintsview.memento';
|
||||
@@ -399,7 +390,6 @@ export class BreakpointsView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@@ -409,23 +399,15 @@ export class BreakpointsView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, {
|
||||
super({
|
||||
...(options as IViewOptions),
|
||||
ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section"),
|
||||
sizing: ViewSizing.Fixed,
|
||||
initialBodySize: BreakpointsView.getExpandedBodySize(debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length)
|
||||
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.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
@@ -457,7 +439,7 @@ export class BreakpointsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
if (first.uri.toString() !== second.uri.toString()) {
|
||||
return paths.basename(first.uri.fsPath).localeCompare(paths.basename(second.uri.fsPath));
|
||||
return resources.basenameOrAuthority(first.uri).localeCompare(resources.basenameOrAuthority(second.uri));
|
||||
}
|
||||
if (first.lineNumber === second.lineNumber) {
|
||||
return first.column - second.column;
|
||||
@@ -472,10 +454,10 @@ export class BreakpointsView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.breakpointsFocusedContext]));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree, [this.breakpointsFocusedContext]));
|
||||
|
||||
this.toDispose.push(this.tree.addListener('selection', event => {
|
||||
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) {
|
||||
@@ -488,7 +470,7 @@ export class BreakpointsView extends CollapsibleView {
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => {
|
||||
if (!fbp || !(fbp instanceof FunctionBreakpoint)) {
|
||||
return;
|
||||
}
|
||||
@@ -513,21 +495,20 @@ export class BreakpointsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const model = this.debugService.getModel();
|
||||
this.setBodySize(BreakpointsView.getExpandedBodySize(
|
||||
model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length));
|
||||
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize();
|
||||
if (this.tree) {
|
||||
this.tree.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private static getExpandedBodySize(length: number): number {
|
||||
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.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@@ -6,7 +6,6 @@
|
||||
import nls = require('vs/nls');
|
||||
import cp = require('child_process');
|
||||
import net = require('net');
|
||||
import uri from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import platform = require('vs/base/common/platform');
|
||||
import objects = require('vs/base/common/objects');
|
||||
@@ -23,6 +22,7 @@ import debug = require('vs/workbench/parts/debug/common/debug');
|
||||
import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter';
|
||||
import { V8Protocol } from 'vs/workbench/parts/debug/node/v8Protocol';
|
||||
import { IOutputService } from 'vs/workbench/parts/output/common/output';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { TerminalSupport } from 'vs/workbench/parts/debug/electron-browser/terminalSupport';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -70,8 +70,8 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
id: string,
|
||||
private debugServerPort: number,
|
||||
private adapter: Adapter,
|
||||
private customTelemetryService: ITelemetryService,
|
||||
public root: uri,
|
||||
public customTelemetryService: ITelemetryService,
|
||||
public root: IWorkspaceFolder,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IOutputService private outputService: IOutputService,
|
||||
@@ -164,8 +164,17 @@ export class RawDebugSession extends V8Protocol implements debug.ISession {
|
||||
const errorMessage = errorResponse ? errorResponse.message : '';
|
||||
const telemetryMessage = error ? debug.formatPII(error.format, true, error.variables) : errorMessage;
|
||||
if (error && error.sendTelemetry) {
|
||||
/* __GDPR__
|
||||
"debugProtocolErrorResponse" : {
|
||||
"error" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
if (this.customTelemetryService) {
|
||||
/* __GDPR__TODO__
|
||||
The message is sent in the name of the adapter but the adapter doesn't know about it.
|
||||
However, since adapters are an open-ended set, we can not declared the events statically either.
|
||||
*/
|
||||
this.customTelemetryService.publicLog('debugProtocolErrorResponse', { error: telemetryMessage });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export class Repl extends Panel implements IPrivateReplService {
|
||||
|
||||
modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: Position, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
provideCompletionItems: (model: IReadOnlyModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
|
||||
const word = this.replInput.getModel().getWordAtPosition(position);
|
||||
const overwriteBefore = word ? word.word.length : 0;
|
||||
const text = this.replInput.getModel().getLineContent(position.lineNumber);
|
||||
|
||||
@@ -6,15 +6,17 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ITree, IAccessibilityProvider, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Model, OutputNameValueElement, Expression, OutputElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
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 { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
@@ -38,10 +40,10 @@ export class ReplExpressionsDataSource implements IDataSource {
|
||||
if (element instanceof Model) {
|
||||
return TPromise.as(element.getReplElements());
|
||||
}
|
||||
if (element instanceof OutputNameValueElement) {
|
||||
if (element instanceof RawObjectReplElement) {
|
||||
return TPromise.as(element.getChildren());
|
||||
}
|
||||
if (element instanceof OutputElement) {
|
||||
if (element instanceof SimpleReplElement) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
@@ -60,12 +62,15 @@ interface IExpressionTemplateData {
|
||||
annotation: HTMLElement;
|
||||
}
|
||||
|
||||
interface IValueOutputTemplateData {
|
||||
interface ISimpleReplElementTemplateData {
|
||||
container: HTMLElement;
|
||||
value: HTMLElement;
|
||||
source: HTMLElement;
|
||||
getReplElementSource(): IReplElementSource;
|
||||
toDispose: lifecycle.IDisposable[];
|
||||
}
|
||||
|
||||
interface IKeyValueOutputTemplateData {
|
||||
interface IRawObjectReplTemplateData {
|
||||
container: HTMLElement;
|
||||
expression: HTMLElement;
|
||||
name: HTMLElement;
|
||||
@@ -76,9 +81,9 @@ interface IKeyValueOutputTemplateData {
|
||||
export class ReplExpressionsRenderer implements IRenderer {
|
||||
|
||||
private static VARIABLE_TEMPLATE_ID = 'variable';
|
||||
private static EXPRESSION_TEMPLATE_ID = 'inputOutputPair';
|
||||
private static VALUE_OUTPUT_TEMPLATE_ID = 'outputValue';
|
||||
private static NAME_VALUE_OUTPUT_TEMPLATE_ID = 'outputNameValue';
|
||||
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 LINE_HEIGHT_PX = 18;
|
||||
|
||||
@@ -139,12 +144,12 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
if (element instanceof Expression) {
|
||||
return ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID;
|
||||
}
|
||||
if (element instanceof OutputElement || (element instanceof Variable && !element.name)) {
|
||||
// Variable with no name is a top level variable which should be rendered like an output element #17404
|
||||
return ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID;
|
||||
if (element instanceof SimpleReplElement || (element instanceof Variable && !element.name)) {
|
||||
// Variable with no name is a top level variable which should be rendered like a repl element #17404
|
||||
return ReplExpressionsRenderer.SIMPLE_REPL_ELEMENT_TEMPLATE_ID;
|
||||
}
|
||||
if (element instanceof OutputNameValueElement) {
|
||||
return ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID;
|
||||
if (element instanceof RawObjectReplElement) {
|
||||
return ReplExpressionsRenderer.RAW_OBJECT_REPL_ELEMENT_TEMPLATE_ID;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -171,19 +176,34 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) {
|
||||
let data: IValueOutputTemplateData = Object.create(null);
|
||||
if (templateId === ReplExpressionsRenderer.SIMPLE_REPL_ELEMENT_TEMPLATE_ID) {
|
||||
let data: ISimpleReplElementTemplateData = Object.create(null);
|
||||
dom.addClass(container, 'output');
|
||||
let expression = dom.append(container, $('.output.expression'));
|
||||
let expression = dom.append(container, $('.output.expression.value-and-source'));
|
||||
|
||||
data.container = container;
|
||||
data.value = dom.append(expression, $('span.value'));
|
||||
data.source = dom.append(expression, $('.source'));
|
||||
data.toDispose = [];
|
||||
data.toDispose.push(dom.addDisposableListener(data.source, 'click', e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const source = data.getReplElementSource();
|
||||
if (source) {
|
||||
source.source.openInEditor(this.editorService, {
|
||||
startLineNumber: source.lineNumber,
|
||||
startColumn: source.column,
|
||||
endLineNumber: source.lineNumber,
|
||||
endColumn: source.column
|
||||
}).done(undefined, errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
if (templateId === ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID) {
|
||||
let data: IKeyValueOutputTemplateData = Object.create(null);
|
||||
if (templateId === ReplExpressionsRenderer.RAW_OBJECT_REPL_ELEMENT_TEMPLATE_ID) {
|
||||
let data: IRawObjectReplTemplateData = Object.create(null);
|
||||
dom.addClass(container, 'output');
|
||||
|
||||
data.container = container;
|
||||
@@ -201,10 +221,10 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
renderVariable(tree, element, templateData, false);
|
||||
} else if (templateId === ReplExpressionsRenderer.EXPRESSION_TEMPLATE_ID) {
|
||||
this.renderExpression(tree, element, templateData);
|
||||
} else if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) {
|
||||
this.renderOutputValue(element, templateData);
|
||||
} else if (templateId === ReplExpressionsRenderer.NAME_VALUE_OUTPUT_TEMPLATE_ID) {
|
||||
this.renderOutputNameValue(tree, element, templateData);
|
||||
} else if (templateId === ReplExpressionsRenderer.SIMPLE_REPL_ELEMENT_TEMPLATE_ID) {
|
||||
this.renderSimpleReplElement(element, templateData);
|
||||
} else if (templateId === ReplExpressionsRenderer.RAW_OBJECT_REPL_ELEMENT_TEMPLATE_ID) {
|
||||
this.renderRawObjectReplElement(tree, element, templateData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +232,8 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
templateData.input.textContent = expression.name;
|
||||
renderExpressionValue(expression, templateData.value, {
|
||||
preserveWhitespace: !expression.hasChildren,
|
||||
showHover: false
|
||||
showHover: false,
|
||||
colorize: true
|
||||
});
|
||||
if (expression.hasChildren) {
|
||||
templateData.annotation.className = 'annotation octicon octicon-info';
|
||||
@@ -220,12 +241,13 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private renderOutputValue(output: OutputElement, templateData: IValueOutputTemplateData): void {
|
||||
private renderSimpleReplElement(element: SimpleReplElement, templateData: ISimpleReplElementTemplateData): void {
|
||||
|
||||
// value
|
||||
dom.clearNode(templateData.value);
|
||||
templateData.value.className = '';
|
||||
let result = this.handleANSIOutput(output.value);
|
||||
// Reset classes to clear ansi decorations since templates are reused
|
||||
templateData.value.className = 'value';
|
||||
let result = this.handleANSIOutput(element.value);
|
||||
if (typeof result === 'string') {
|
||||
renderExpressionValue(result, templateData.value, {
|
||||
preserveWhitespace: true,
|
||||
@@ -235,27 +257,30 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
templateData.value.appendChild(result);
|
||||
}
|
||||
|
||||
dom.addClass(templateData.value, (output.severity === severity.Warning) ? 'warn' : (output.severity === severity.Error) ? 'error' : 'info');
|
||||
dom.addClass(templateData.value, (element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : 'info');
|
||||
templateData.source.textContent = element.sourceData ? `${element.sourceData.source.name}:${element.sourceData.lineNumber}` : '';
|
||||
templateData.source.title = element.sourceData ? element.sourceData.source.uri.toString() : '';
|
||||
templateData.getReplElementSource = () => element.sourceData;
|
||||
}
|
||||
|
||||
private renderOutputNameValue(tree: ITree, output: OutputNameValueElement, templateData: IKeyValueOutputTemplateData): void {
|
||||
private renderRawObjectReplElement(tree: ITree, element: RawObjectReplElement, templateData: IRawObjectReplTemplateData): void {
|
||||
// key
|
||||
if (output.name) {
|
||||
templateData.name.textContent = `${output.name}:`;
|
||||
if (element.name) {
|
||||
templateData.name.textContent = `${element.name}:`;
|
||||
} else {
|
||||
templateData.name.textContent = '';
|
||||
}
|
||||
|
||||
// value
|
||||
renderExpressionValue(output.value, templateData.value, {
|
||||
renderExpressionValue(element.value, templateData.value, {
|
||||
preserveWhitespace: true,
|
||||
showHover: false
|
||||
});
|
||||
|
||||
// annotation if any
|
||||
if (output.annotation) {
|
||||
if (element.annotation) {
|
||||
templateData.annotation.className = 'annotation octicon octicon-info';
|
||||
templateData.annotation.title = output.annotation;
|
||||
templateData.annotation.title = element.annotation;
|
||||
} else {
|
||||
templateData.annotation.className = '';
|
||||
templateData.annotation.title = '';
|
||||
@@ -352,7 +377,9 @@ export class ReplExpressionsRenderer implements IRenderer {
|
||||
}
|
||||
|
||||
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
|
||||
// noop
|
||||
if (templateData.toDispose) {
|
||||
lifecycle.dispose(templateData.toDispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,11 +392,11 @@ export class ReplExpressionsAccessibilityProvider implements IAccessibilityProvi
|
||||
if (element instanceof Expression) {
|
||||
return nls.localize('replExpressionAriaLabel', "Expression {0} has value {1}, read eval print loop, debug", (<Expression>element).name, (<Expression>element).value);
|
||||
}
|
||||
if (element instanceof OutputElement) {
|
||||
return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", (<OutputElement>element).value);
|
||||
if (element instanceof SimpleReplElement) {
|
||||
return nls.localize('replValueOutputAriaLabel', "{0}, read eval print loop, debug", (<SimpleReplElement>element).value);
|
||||
}
|
||||
if (element instanceof OutputNameValueElement) {
|
||||
return nls.localize('replKeyValueOutputAriaLabel', "Output variable {0} has value {1}, read eval print loop, debug", (<OutputNameValueElement>element).name, (<OutputNameValueElement>element).value);
|
||||
if (element instanceof RawObjectReplElement) {
|
||||
return nls.localize('replRawObjectAriaLabel', "Repl variable {0} has value {1}, read eval print loop, debug", (<RawObjectReplElement>element).name, (<RawObjectReplElement>element).value);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -416,7 +443,7 @@ export class ReplExpressionsController extends BaseDebugController {
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
|
||||
const mouseEvent = <IMouseEvent>eventish;
|
||||
// input and output are one element in the tree => we only expand if the user clicked on the output.
|
||||
if ((element.reference > 0 || (element instanceof OutputNameValueElement && element.hasChildren)) && mouseEvent.target.className.indexOf('input expression') === -1) {
|
||||
if ((element.reference > 0 || (element instanceof RawObjectReplElement && element.hasChildren)) && mouseEvent.target.className.indexOf('input expression') === -1) {
|
||||
super.onLeftClick(tree, element, eventish, origin);
|
||||
tree.clearFocus();
|
||||
tree.deselect(element);
|
||||
@@ -431,4 +458,8 @@ export class ReplExpressionsController extends BaseDebugController {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean {
|
||||
return super.onContextMenu(tree, element, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { registerColor, contrastBorder } from 'vs/platform/theme/common/colorReg
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme';
|
||||
import { addClass, removeClass } from 'vs/base/browser/dom';
|
||||
|
||||
@@ -49,7 +49,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.debugService.onDidChangeState(state => this.updateStyles()));
|
||||
this.toUnbind.push(this.contextService.onDidChangeWorkspaceRoots(state => this.updateStyles()));
|
||||
this.toUnbind.push(this.contextService.onDidChangeWorkbenchState(state => this.updateStyles()));
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
@@ -75,7 +75,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
|
||||
// Not debugging
|
||||
if (!this.isDebugging()) {
|
||||
if (this.contextService.hasWorkspace()) {
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||
return normalColor;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
}
|
||||
|
||||
private isDebugging(): boolean {
|
||||
if (this.debugService.state === State.Inactive) {
|
||||
if (this.debugService.state === State.Inactive || this.debugService.state === State.Initializing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||