Merge from vscode cbeff45f80213db0ddda2183170281ed97ed3b12 (#8670)

* Merge from vscode cbeff45f80213db0ddda2183170281ed97ed3b12

* fix null strict checks
This commit is contained in:
Anthony Dresser
2019-12-13 00:50:37 -08:00
committed by GitHub
parent 67abc2f690
commit 642920504a
136 changed files with 2918 additions and 1729 deletions

View File

@@ -641,7 +641,7 @@ registerThemingParticipant((theme, collector) => {
.monaco-workbench .codicon-debug-breakpoint-function,
.monaco-workbench .codicon-debug-breakpoint-data,
.monaco-workbench .codicon-debug-breakpoint-unsupported,
.monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']),
.monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']),
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after {
color: ${debugIconBreakpointColor} !important;
@@ -662,7 +662,7 @@ registerThemingParticipant((theme, collector) => {
if (debugIconBreakpointUnverifiedColor) {
collector.addRule(`
.monaco-workbench .codicon[class*='-unverified'] {
color: ${debugIconBreakpointUnverifiedColor} !important;
color: ${debugIconBreakpointUnverifiedColor};
}
`);
}
@@ -670,7 +670,8 @@ registerThemingParticipant((theme, collector) => {
const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground);
if (debugIconBreakpointCurrentStackframeForegroundColor) {
collector.addRule(`
.monaco-workbench .codicon-debug-stackframe {
.monaco-workbench .codicon-debug-stackframe,
.monaco-editor .debug-top-stack-frame-column::before {
color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important;
}
`);

View File

@@ -119,7 +119,6 @@ class CallStackEditorContribution implements IEditorContribution {
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
isWholeLine: true,
inlineClassName: 'debug-remove-token-colors',
className: 'debug-top-stack-frame-line',
stickiness
};
@@ -130,7 +129,6 @@ class CallStackEditorContribution implements IEditorContribution {
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
isWholeLine: true,
inlineClassName: 'debug-remove-token-colors',
className: 'debug-focused-stack-frame-line',
stickiness
};

View File

@@ -84,7 +84,7 @@ Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(Viewlet
DebugViewlet,
VIEWLET_ID,
nls.localize('debugAndRun', "Debug and Run"),
'codicon-debug',
'codicon-debug-alt',
13 // {{SQL CARBON EDIT}}
));

View File

@@ -11,7 +11,6 @@ import * as errors from 'vs/base/common/errors';
import severity from 'vs/base/common/severity';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -22,18 +21,15 @@ import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expres
import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions';
import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager';
import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { parse, getFirstFrame } from 'vs/base/common/console';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IAction } from 'vs/base/common/actions';
@@ -42,12 +38,13 @@ import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX } from 'vs/workbench/contrib/debug/common/debug';
import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { isErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { TaskRunResult, DebugTaskRunner } from 'vs/workbench/contrib/debug/browser/debugTaskRunner';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
@@ -55,23 +52,6 @@ const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
function once(match: (e: TaskEvent) => boolean, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (match(e)) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
const enum TaskRunResult {
Failure,
Success
}
export class DebugService implements IDebugService {
_serviceBrand: undefined;
@@ -81,6 +61,7 @@ export class DebugService implements IDebugService {
private readonly _onDidEndSession: Emitter<IDebugSession>;
private model: DebugModel;
private viewModel: ViewModel;
private taskRunner: DebugTaskRunner;
private configurationManager: ConfigurationManager;
private toDispose: IDisposable[];
private debugType: IContextKey<string>;
@@ -91,6 +72,7 @@ export class DebugService implements IDebugService {
private initializing = false;
private previousState: State | undefined;
private initCancellationToken: CancellationTokenSource | undefined;
private activity: IDisposable | undefined;
constructor(
@IStorageService private readonly storageService: IStorageService,
@@ -107,11 +89,10 @@ export class DebugService implements IDebugService {
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IMarkerService private readonly markerService: IMarkerService,
@ITaskService private readonly taskService: ITaskService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
@IActivityService private readonly activityService: IActivityService
) {
this.toDispose = [];
@@ -136,6 +117,7 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.model);
this.viewModel = new ViewModel(contextKeyService);
this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner);
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.lifecycleService.onShutdown(this.dispose, this);
@@ -176,6 +158,16 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.configurationManager.onDidSelectConfiguration(() => {
this.debugUx.set(!!(this.state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
}));
this.toDispose.push(Event.any(this.onDidNewSession, this.onDidEndSession)(() => {
const numberOfSessions = this.model.getSessions().length;
if (numberOfSessions === 0) {
if (this.activity) {
this.activity.dispose();
}
} else {
this.activity = this.activityService.showActivity(VIEWLET_ID, new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)));
}
}));
}
getModel(): IDebugModel {
@@ -299,7 +291,7 @@ export class DebugService implements IDebugService {
"Compound must have \"configurations\" attribute set in order to start multiple configurations."));
}
if (compound.preLaunchTask) {
const taskResult = await this.runTaskAndCheckErrors(launch?.workspace || this.contextService.getWorkspace(), compound.preLaunchTask);
const taskResult = await this.taskRunner.runTaskAndCheckErrors(launch?.workspace || this.contextService.getWorkspace(), compound.preLaunchTask, this.showError);
if (taskResult === TaskRunResult.Failure) {
this.endInitializingState();
return false;
@@ -411,7 +403,7 @@ export class DebugService implements IDebugService {
}
const workspace = launch ? launch.workspace : this.contextService.getWorkspace();
const taskResult = await this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask);
const taskResult = await this.taskRunner.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask, this.showError);
if (taskResult === TaskRunResult.Success) {
return this.doCreateSession(launch?.workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options);
}
@@ -548,7 +540,7 @@ export class DebugService implements IDebugService {
if (session.configuration.postDebugTask) {
try {
await this.runTask(session.root, session.configuration.postDebugTask);
await this.taskRunner.runTask(session.root, session.configuration.postDebugTask);
} catch (err) {
this.notificationService.error(err);
}
@@ -587,8 +579,8 @@ export class DebugService implements IDebugService {
return Promise.resolve(TaskRunResult.Success);
}
await this.runTask(session.root, session.configuration.postDebugTask);
return this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask);
await this.taskRunner.runTask(session.root, session.configuration.postDebugTask);
return this.taskRunner.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask, this.showError);
};
const extensionDebugSession = getExtensionHostDebugSession(session);
@@ -715,129 +707,6 @@ export class DebugService implements IDebugService {
return undefined;
}
//---- task management
private async runTaskAndCheckErrors(root: IWorkspaceFolder | IWorkspace | undefined, taskId: string | TaskIdentifier | undefined): Promise<TaskRunResult> {
try {
const taskSummary = await this.runTask(root, taskId);
const errorCount = taskId ? this.markerService.getStatistics().errors : 0;
const successExitCode = taskSummary && taskSummary.exitCode === 0;
const failureExitCode = taskSummary && taskSummary.exitCode !== 0;
const onTaskErrors = this.configurationService.getValue<IDebugConfiguration>('debug').onTaskErrors;
if (successExitCode || onTaskErrors === 'debugAnyway' || (errorCount === 0 && !failureExitCode)) {
return TaskRunResult.Success;
}
if (onTaskErrors === 'showErrors') {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
}
const taskLabel = typeof taskId === 'string' ? taskId : taskId ? taskId.name : '';
const message = errorCount > 1
? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel)
: errorCount === 1
? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel)
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0);
const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
checkbox: {
label: nls.localize('remember', "Remember my choice in user settings"),
},
cancelId: 2
});
if (result.choice === 2) {
return Promise.resolve(TaskRunResult.Failure);
}
const debugAnyway = result.choice === 0;
if (result.checkboxChecked) {
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
}
if (debugAnyway) {
return TaskRunResult.Success;
}
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
} catch (err) {
await this.showError(err.message, [this.taskService.configureAction()]);
return TaskRunResult.Failure;
}
}
private async runTask(root: IWorkspace | IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
if (!taskId) {
return Promise.resolve(null);
}
if (!root) {
return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type)));
}
// run a task before starting a debug session
const task = await this.taskService.getTask(root, taskId);
if (!task) {
const errorMessage = typeof taskId === 'string'
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
return Promise.reject(createErrorWithActions(errorMessage));
}
// 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 inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
// When a task isBackground it will go inactive when it is safe to launch.
// But when a background task is terminated by the user, it will also fire an inactive event.
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
return (e.kind === TaskEventKind.Inactive
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
&& e.taskId === task._id;
}, this.taskService.onDidStateChange)(e => {
taskStarted = true;
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
}));
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
if (tasks.filter(t => t._id === task._id).length) {
// Check that the task isn't busy and if it is, wait for it
const busyTasks = await this.taskService.getBusyTasks();
if (busyTasks.filter(t => t._id === task._id).length) {
taskStarted = true;
return inactivePromise;
}
// task is already running and isn't busy - nothing to do.
return Promise.resolve(null);
}
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
taskStarted = true;
});
const taskPromise = this.taskService.run(task);
if (task.configurationProperties.isBackground) {
return inactivePromise;
}
return taskPromise.then(withUndefinedAsNull);
});
return new Promise((c, e) => {
promise.then(result => {
taskStarted = true;
c(result);
}, error => e(error));
setTimeout(() => {
if (!taskStarted) {
const errorMessage = typeof taskId === 'string'
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
e({ severity: severity.Error, message: errorMessage });
}
}, 10000);
});
}
//---- focus management
async focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise<void> {

View File

@@ -691,6 +691,11 @@ export class DebugSession implements IDebugSession {
}
}
initializeForTest(raw: RawDebugSession): void {
this.raw = raw;
this.registerListeners();
}
//---- private
private registerListeners(): void {

View File

@@ -0,0 +1,169 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { Event } from 'vs/base/common/event';
import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IAction } from 'vs/base/common/actions';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
function once(match: (e: TaskEvent) => boolean, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (match(e)) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
export const enum TaskRunResult {
Failure,
Success
}
export class DebugTaskRunner {
constructor(
@ITaskService private readonly taskService: ITaskService,
@IMarkerService private readonly markerService: IMarkerService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IPanelService private readonly panelService: IPanelService,
@IDialogService private readonly dialogService: IDialogService,
) { }
async runTaskAndCheckErrors(root: IWorkspaceFolder | IWorkspace | undefined, taskId: string | TaskIdentifier | undefined, onError: (msg: string, actions: IAction[]) => Promise<void>): Promise<TaskRunResult> {
try {
const taskSummary = await this.runTask(root, taskId);
const errorCount = taskId ? this.markerService.getStatistics().errors : 0;
const successExitCode = taskSummary && taskSummary.exitCode === 0;
const failureExitCode = taskSummary && taskSummary.exitCode !== 0;
const onTaskErrors = this.configurationService.getValue<IDebugConfiguration>('debug').onTaskErrors;
if (successExitCode || onTaskErrors === 'debugAnyway' || (errorCount === 0 && !failureExitCode)) {
return TaskRunResult.Success;
}
if (onTaskErrors === 'showErrors') {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
}
const taskLabel = typeof taskId === 'string' ? taskId : taskId ? taskId.name : '';
const message = errorCount > 1
? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel)
: errorCount === 1
? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel)
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0);
const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
checkbox: {
label: nls.localize('remember', "Remember my choice in user settings"),
},
cancelId: 2
});
if (result.choice === 2) {
return Promise.resolve(TaskRunResult.Failure);
}
const debugAnyway = result.choice === 0;
if (result.checkboxChecked) {
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
}
if (debugAnyway) {
return TaskRunResult.Success;
}
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
} catch (err) {
await onError(err.message, [this.taskService.configureAction()]);
return TaskRunResult.Failure;
}
}
async runTask(root: IWorkspace | IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
if (!taskId) {
return Promise.resolve(null);
}
if (!root) {
return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type)));
}
// run a task before starting a debug session
const task = await this.taskService.getTask(root, taskId);
if (!task) {
const errorMessage = typeof taskId === 'string'
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
return Promise.reject(createErrorWithActions(errorMessage));
}
// 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 inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
// When a task isBackground it will go inactive when it is safe to launch.
// But when a background task is terminated by the user, it will also fire an inactive event.
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
return (e.kind === TaskEventKind.Inactive
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
&& e.taskId === task._id;
}, this.taskService.onDidStateChange)(e => {
taskStarted = true;
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
}));
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
if (tasks.filter(t => t._id === task._id).length) {
// Check that the task isn't busy and if it is, wait for it
const busyTasks = await this.taskService.getBusyTasks();
if (busyTasks.filter(t => t._id === task._id).length) {
taskStarted = true;
return inactivePromise;
}
// task is already running and isn't busy - nothing to do.
return Promise.resolve(null);
}
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
taskStarted = true;
});
const taskPromise = this.taskService.run(task);
if (task.configurationProperties.isBackground) {
return inactivePromise;
}
return taskPromise.then(withUndefinedAsNull);
});
return new Promise((c, e) => {
promise.then(result => {
taskStarted = true;
c(result);
}, error => e(error));
setTimeout(() => {
if (!taskStarted) {
const errorMessage = typeof taskId === 'string'
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
e({ severity: severity.Error, message: errorMessage });
}
}, 10000);
});
}
}

View File

@@ -7,8 +7,8 @@
cursor: pointer;
}
.codicon-debug-hint:not([class*='codicon-debug-breakpoint']) {
opacity: .4 !important;
.codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']) {
opacity: 0.4 !important;
}
.inline-breakpoint-widget.codicon {
@@ -18,7 +18,7 @@
.codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.codicon-debug-breakpoint.codicon-debug-stackframe::after {
content: "\eb8a";
content: '\eb8a';
position: absolute;
}
@@ -28,26 +28,38 @@
.monaco-editor .debug-breakpoint-placeholder::before,
.monaco-editor .debug-top-stack-frame-column::before {
content: " ";
content: ' ';
width: 0.9em;
display: inline-block;
vertical-align: text-bottom;
display: inline-flex;
vertical-align: middle;
margin-right: 2px;
margin-left: 2px;
margin-top: -1px; /* TODO @misolori: figure out a way to not use negative margin for alignment */
}
.monaco-editor .debug-top-stack-frame-column {
display: inline-flex;
vertical-align: middle;
}
.monaco-editor .debug-top-stack-frame-column::before {
content: '\eb8b';
font: normal normal normal 16px/1 codicon;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin-left: 0;
margin-right: 4px;
}
/* Do not show call stack decoration when we plan to show breakpoint and top stack frame in one decoration */
.monaco-editor .debug-breakpoint-placeholder ~ .debug-top-stack-frame-column::before {
width: 0em;
content: "";
content: '';
margin-right: 0px;
margin-left: 0px;
}
.monaco-editor .debug-top-stack-frame-column::before {
height: 1.3em;
}
.monaco-editor .inline-breakpoint-widget {
cursor: pointer;
}
@@ -104,7 +116,7 @@
}
.monaco-workbench .monaco-list-row .expression .name {
color: #9B46B0;
color: #9b46b0;
}
.monaco-workbench .monaco-list-row .expression .name.virtual {
@@ -120,61 +132,61 @@
}
.monaco-workbench .monaco-list-row .expression .error {
color: #E51400;
color: #e51400;
}
.monaco-workbench .monaco-list-row .expression .value.number {
color: #09885A;
color: #09885a;
}
.monaco-workbench .monaco-list-row .expression .value.boolean {
color: #0000FF;
color: #0000ff;
}
.monaco-workbench .monaco-list-row .expression .value.string {
color: #A31515;
color: #a31515;
}
.vs-dark .monaco-workbench > .monaco-list-row .expression .value {
.vs-dark .monaco-workbench > .monaco-list-row .expression .value {
color: rgba(204, 204, 204, 0.6);
}
.vs-dark .monaco-workbench .monaco-list-row .expression .error {
color: #F48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.number {
color: #B5CEA8;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.number {
color: #89d185;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #75bdfe;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.string {
.vs-dark .monaco-workbench .monaco-list-row .expression .error {
color: #f48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #4E94CE;
.vs-dark .monaco-workbench .monaco-list-row .expression .value.number {
color: #b5cea8;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.string {
color: #CE9178;
.hc-black .monaco-workbench .monaco-list-row .expression .value.number {
color: #89d185;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #75bdfe;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.string {
color: #f48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #4e94ce;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.string {
color: #ce9178;
}
.hc-black .monaco-workbench .monaco-list-row .expression .error {
color: #F48771;
color: #f48771;
}
/* Dark theme */
.vs-dark .monaco-workbench .monaco-list-row .expression .name {
color: #C586C0;
color: #c586c0;
}
/* High Contrast Theming */
@@ -182,7 +194,3 @@
.hc-black .monaco-workbench .monaco-list-row .expression .name {
color: inherit;
}
.hc-black .monaco-editor .debug-remove-token-colors {
color:black;
}

View File

@@ -80,7 +80,6 @@ export class RawDebugSession implements IDisposable {
public readonly customTelemetryService: ITelemetryService | undefined,
private readonly extensionHostDebugService: IExtensionHostDebugService,
private readonly openerService: IOpenerService
) {
this.debugAdapter = debugAdapter;
this._capabilities = Object.create(null);
@@ -598,13 +597,13 @@ export class RawDebugSession implements IDisposable {
}
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
return new Promise<R>((completeDispatch, errorDispatch) => {
return new Promise<DebugProtocol.Response>((completeDispatch, errorDispatch) => {
if (!this.debugAdapter) {
errorDispatch(new Error('no debug adapter found'));
return;
}
let cancelationListener: IDisposable;
const requestId = this.debugAdapter.sendRequest(command, args, (response: R) => {
const requestId = this.debugAdapter.sendRequest(command, args, (response: DebugProtocol.Response) => {
if (cancelationListener) {
cancelationListener.dispose();
}

View File

@@ -25,7 +25,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
const $ = dom.$;
export class StartView extends ViewPane {
static ID = 'workbench.debug.startView';

View File

@@ -5,7 +5,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug';
import { timeout } from 'vs/base/common/async';
import { timeout, Queue } from 'vs/base/common/async';
/**
* Abstract implementation of the low level API for a debug adapter.
@@ -18,6 +18,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
private requestCallback: ((request: DebugProtocol.Request) => void) | undefined;
private eventCallback: ((request: DebugProtocol.Event) => void) | undefined;
private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined;
private readonly queue = new Queue();
protected readonly _onError = new Emitter<Error>();
protected readonly _onExit = new Emitter<number | null>();
@@ -108,26 +109,33 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
this.messageCallback(message);
}
else {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
}
this.queue.queue(() => {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
}
// Artificially queueing protocol messages guarantees that any microtasks for
// previous message finish before next message is processed. This is essential
// to guarantee ordering when using promises anywhere along the call path.
return timeout(0);
});
}
}
@@ -164,6 +172,6 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
}
dispose(): void {
// noop
this.queue.dispose();
}
}

View File

@@ -548,7 +548,7 @@ export interface IDebugAdapterServer {
}
export interface IDebugAdapterInlineImpl extends IDisposable {
readonly onSendMessage: Event<DebugProtocol.Message>;
readonly onDidSendMessage: Event<DebugProtocol.Message>;
handleMessage(message: DebugProtocol.Message): void;
}

View File

@@ -8,12 +8,14 @@ import { URI as uri } from 'vs/base/common/uri';
import severity from 'vs/base/common/severity';
import { DebugModel, Expression, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
import * as sinon from 'sinon';
import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
import { MockRawSession, MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel, ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
import { IBreakpointUpdateData, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { NullOpenerService } from 'vs/platform/opener/common/opener';
import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession';
import { timeout } from 'vs/base/common/async';
function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
@@ -548,4 +550,26 @@ suite('Debug - Model', () => {
assert.equal(grandChild.getReplElements().length, 2);
assert.equal(child3.getReplElements().length, 1);
});
test('repl ordering', async () => {
const session = createMockSession(model);
model.addSession(session);
const adapter = new MockDebugAdapter();
const raw = new RawDebugSession(adapter, undefined!, undefined!, undefined!, undefined!, undefined!);
session.initializeForTest(raw);
await session.addReplExpression(undefined, 'before.1');
assert.equal(session.getReplElements().length, 3);
assert.equal((<ReplEvaluationInput>session.getReplElements()[0]).value, 'before.1');
assert.equal((<SimpleReplElement>session.getReplElements()[1]).value, 'before.1');
assert.equal((<ReplEvaluationResult>session.getReplElements()[2]).value, '=before.1');
await session.addReplExpression(undefined, 'after.2');
await timeout(0);
assert.equal(session.getReplElements().length, 6);
assert.equal((<ReplEvaluationInput>session.getReplElements()[3]).value, 'after.2');
assert.equal((<ReplEvaluationResult>session.getReplElements()[4]).value, '=after.2');
assert.equal((<SimpleReplElement>session.getReplElements()[5]).value, 'after.2');
});
});

View File

@@ -11,6 +11,7 @@ import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IS
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { CompletionItem } from 'vs/editor/common/modes';
import Severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
export class MockDebugService implements IDebugService {
@@ -464,3 +465,67 @@ export class MockRawSession {
public readonly onDidStop: Event<DebugProtocol.StoppedEvent> = null!;
}
export class MockDebugAdapter extends AbstractDebugAdapter {
private seq = 0;
startSession(): Promise<void> {
return Promise.resolve();
}
stopSession(): Promise<void> {
return Promise.resolve();
}
sendMessage(message: DebugProtocol.ProtocolMessage): void {
setTimeout(() => {
if (message.type === 'request') {
const request = message as DebugProtocol.Request;
switch (request.command) {
case 'evaluate':
this.evaluate(request, request.arguments);
return;
}
this.sendResponseBody(request, {});
return;
}
}, 0);
}
sendResponseBody(request: DebugProtocol.Request, body: any) {
const response: DebugProtocol.Response = {
seq: ++this.seq,
type: 'response',
request_seq: request.seq,
command: request.command,
success: true,
body
};
this.acceptMessage(response);
}
sendEventBody(event: string, body: any) {
const response: DebugProtocol.Event = {
seq: ++this.seq,
type: 'event',
event,
body
};
this.acceptMessage(response);
}
evaluate(request: DebugProtocol.Request, args: DebugProtocol.EvaluateArguments) {
if (args.expression.indexOf('before.') === 0) {
this.sendEventBody('output', { output: args.expression });
}
this.sendResponseBody(request, {
result: '=' + args.expression,
variablesReference: 0
});
if (args.expression.indexOf('after.') === 0) {
this.sendEventBody('output', { output: args.expression });
}
}
}