提交 0d93c840 编写于 作者: I isidor

debug: clean up focus transations

removes tryToAutoFocusStackFrame
reacts on running state change with 200ms delay
properly passes focus between mutliple threads / sessions

fixes #57253
上级 12704343
......@@ -794,11 +794,6 @@ export interface IDebugService {
* Gets the current view model.
*/
getViewModel(): IViewModel;
/**
* Try to auto focus the top stack frame of the passed thread.
*/
tryToAutoFocusStackFrame(thread: IThread): TPromise<any>;
}
// Editor interfaces
......
......@@ -19,7 +19,6 @@ 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';
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel';
......@@ -49,6 +48,7 @@ import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSes
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, IReplElementSource, IEnablement, IBreakpoint, IBreakpointData, IExpression, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug';
import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils';
import { RunOnceScheduler } from 'vs/base/common/async';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
......@@ -101,7 +101,6 @@ export class DebugService implements IDebugService {
@INotificationService private notificationService: INotificationService,
@IDialogService private dialogService: IDialogService,
@IPartService private partService: IPartService,
@IWindowService private windowService: IWindowService,
@IBroadcastService private broadcastService: IBroadcastService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
......@@ -499,11 +498,15 @@ export class DebugService implements IDebugService {
}
private registerSessionListeners(session: IDebugSession): void {
this.toDispose.push(session.onDidChangeState(() => {
if (session.state === State.Running && this.viewModel.focusedSession && this.viewModel.focusedSession.getId() === session.getId()) {
const sessionRunningScheduler = new RunOnceScheduler(() => {
if (session.state === State.Running && this.viewModel.focusedSession === session) {
this.focusStackFrame(undefined);
}
}, 200);
this.toDispose.push(session.onDidChangeState(() => {
if (session.state === State.Running && this.viewModel.focusedSession === session) {
sessionRunningScheduler.schedule();
}
}));
this.toDispose.push(session.onDidEndAdapter(adapterExitEvent => {
......@@ -741,37 +744,14 @@ export class DebugService implements IDebugService {
//---- focus management
tryToAutoFocusStackFrame(thread: IThread): TPromise<any> {
const callStack = thread.getCallStack();
if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) {
return Promise.resolve(null);
}
// focus first stack frame from top that has source location if no other stack frame is focused
const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, undefined);
if (!stackFrameToFocus) {
return Promise.resolve(null);
}
this.focusStackFrame(stackFrameToFocus);
if (thread.stoppedDetails) {
if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') {
this.viewletService.openViewlet(VIEWLET_ID);
}
this.windowService.focusWindow();
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, true);
}
focusStackFrame(stackFrame: IStackFrame, thread?: IThread, session?: IDebugSession, explicit?: boolean): void {
if (!session) {
if (stackFrame || thread) {
session = stackFrame ? stackFrame.thread.session : thread.session;
} else {
const sessions = this.model.getSessions();
session = sessions.length ? sessions[0] : undefined;
const stoppedSession = sessions.filter(s => s.state === State.Stopped).shift();
session = stoppedSession || (sessions.length ? sessions[0] : undefined);
}
}
......@@ -780,17 +760,23 @@ export class DebugService implements IDebugService {
thread = stackFrame.thread;
} else {
const threads = session ? session.getAllThreads() : undefined;
thread = threads && threads.length ? threads[0] : undefined;
const stoppedThread = threads && threads.filter(t => t.stopped).shift();
thread = stoppedThread || (threads && threads.length ? threads[0] : undefined);
}
}
if (!stackFrame) {
if (thread) {
const callStack = thread.getCallStack();
stackFrame = callStack && callStack.length ? callStack[0] : null;
stackFrame = first(callStack, sf => sf.source && sf.source.available, undefined);
}
}
if (stackFrame) {
stackFrame.openInEditor(this.editorService, true).then(undefined, errors.onUnexpectedError);
aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber));
}
this.viewModel.setFocus(stackFrame, thread, session, explicit);
}
......
......@@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { CompletionItem, completionKindFromLegacyString } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, ActualBreakpoints, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug';
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, ActualBreakpoints, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration } from 'vs/workbench/parts/debug/common/debug';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { mixin } from 'vs/base/common/objects';
import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/parts/debug/common/debugModel';
......@@ -24,10 +24,13 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { generateUuid } from 'vs/base/common/uuid';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { Range } from 'vs/editor/common/core/range';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
export class DebugSession implements IDebugSession {
......@@ -54,6 +57,9 @@ export class DebugSession implements IDebugSession {
@IDebugService private debugService: IDebugService,
@ITelemetryService private telemetryService: ITelemetryService,
@IOutputService private outputService: IOutputService,
@IWindowService private windowService: IWindowService,
@IConfigurationService private configurationService: IConfigurationService,
@IViewletService private viewletService: IViewletService
) {
this.id = generateUuid();
}
......@@ -80,7 +86,7 @@ export class DebugSession implements IDebugSession {
get state(): State {
const focusedThread = this.debugService.getViewModel().focusedThread;
if (focusedThread && !!this.getThread(focusedThread.threadId)) {
if (focusedThread && focusedThread.session === this) {
return focusedThread.stopped ? State.Stopped : State.Running;
}
if (this.getAllThreads().some(t => t.stopped)) {
......@@ -590,7 +596,15 @@ export class DebugSession implements IDebugSession {
// 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
this.model.fetchCallStack(<Thread>thread).then(() => {
return !event.body.preserveFocusHint ? this.debugService.tryToAutoFocusStackFrame(thread) : undefined;
if (!event.body.preserveFocusHint && thread.getCallStack().length) {
this.debugService.focusStackFrame(undefined, thread);
if (thread.stoppedDetails) {
if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') {
this.viewletService.openViewlet(VIEWLET_ID);
}
this.windowService.focusWindow();
}
}
});
}
}).then(() => this._onDidChangeState.fire());
......
......@@ -109,7 +109,7 @@ suite('Debug - Model', () => {
test('threads simple', () => {
const threadId = 1;
const threadName = 'firstThread';
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
model.addSession(session);
assert.equal(model.getSessions().length, 1);
......@@ -141,7 +141,7 @@ suite('Debug - Model', () => {
const stoppedReason = 'breakpoint';
// Add the threads
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
model.addSession(session);
session['raw'] = <any>rawSession;
......@@ -234,7 +234,7 @@ suite('Debug - Model', () => {
const runningThreadId = 2;
const runningThreadName = 'runningThread';
const stoppedReason = 'breakpoint';
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
model.addSession(session);
session['raw'] = <any>rawSession;
......@@ -348,7 +348,7 @@ suite('Debug - Model', () => {
test('repl expressions', () => {
assert.equal(model.getReplElements().length, 0);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
model.addSession(session);
session['raw'] = <any>rawSession;
......@@ -370,7 +370,7 @@ suite('Debug - Model', () => {
});
test('stack frame get specific source name', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
model.addSession(session);
let firstStackFrame: StackFrame;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册