提交 f07f5d38 编写于 作者: A Andre Weinand

add first cut of proposed breakpoints API; fixes #23188

上级 699a12cb
......@@ -247,4 +247,84 @@ declare module 'vscode' {
*/
provideCodeActions2?(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | CodeAction)[]>;
}
export namespace debug {
/**
* List of breakpoints.
*
* @readonly
*/
export let breakpoints: Breakpoint[];
/**
* An event that is emitted when a breakpoint is added, removed, or changed.
*/
export const onDidChangeBreakpoints: Event<BreakpointsChangeEvent>;
}
/**
* An event describing a change to the set of [breakpoints](#debug.Breakpoint).
*/
export interface BreakpointsChangeEvent {
/**
* Added breakpoints.
*/
readonly added: Breakpoint[];
/**
* Removed breakpoints.
*/
readonly removed: Breakpoint[];
/**
* Changed breakpoints.
*/
readonly changed: Breakpoint[];
}
export interface Breakpoint {
/**
* Type of breakpoint.
*/
readonly type: 'source' | 'function';
/**
* Is breakpoint enabled.
*/
readonly enabled: boolean;
/**
* An optional expression for conditional breakpoints.
*/
readonly condition?: string;
/**
* An optional expression that controls how many hits of the breakpoint are ignored.
*/
readonly hitCondition?: string;
}
export interface SourceBreakpoint extends Breakpoint {
/**
* Breakpoint type 'source'.
*/
readonly type: 'source';
/**
* The source to which this breakpoint is attached.
*/
readonly source: Uri;
/**
* The line and character position of the breakpoint.
*/
readonly location: Position;
}
export interface FunctionBreakpoint extends Breakpoint {
/**
* Breakpoint type 'function'.
*/
readonly type: 'function';
/**
* The name of the function to which this breakpoint is attached.
*/
readonly functionName: string;
}
}
......@@ -6,10 +6,10 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import uri from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint } from 'vs/workbench/parts/debug/common/debug';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext, IBreakpointsDelta, ISourceBreakpointData, IFunctionBreakpointData } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import severity from 'vs/base/common/severity';
......@@ -18,6 +18,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
private _proxy: ExtHostDebugServiceShape;
private _toDispose: IDisposable[];
private _breakpointEventsActive: boolean;
constructor(
extHostContext: IExtHostContext,
......@@ -35,6 +36,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
this._proxy.$acceptDebugSessionActiveChanged(undefined);
}
}));
this._toDispose.push(debugService.onDidCustomEvent(event => {
if (event && event.sessionId) {
const process = this.debugService.getModel().getProcesses().filter(p => p.getId() === event.sessionId).pop();
......@@ -49,6 +51,75 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
this._toDispose = dispose(this._toDispose);
}
public $startBreakpointEvents(): TPromise<any> {
if (!this._breakpointEventsActive) {
this._breakpointEventsActive = true;
// set up a handler to send more
this._toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(e => {
if (e) {
const delta: IBreakpointsDelta = {};
if (e.added) {
delta.added = this.toWire(e.added);
}
if (e.removed) {
delta.removed = e.removed.map(x => x.getId());
}
if (e.changed) {
delta.changed = this.toWire(e.changed);
}
if (delta.added || delta.removed || delta.changed) {
this._proxy.$acceptBreakpointsDelta(delta);
}
}
}));
// send all breakpoints
const bps = this.debugService.getModel().getBreakpoints();
const fbps = this.debugService.getModel().getFunctionBreakpoints();
if (bps.length > 0 || fbps.length > 0) {
this._proxy.$acceptBreakpointsDelta({
added: this.toWire(bps).concat(this.toWire(fbps))
});
}
}
return TPromise.wrap<void>(undefined);
}
private toWire(bps: (IBreakpoint | IFunctionBreakpoint)[]): (ISourceBreakpointData | IFunctionBreakpointData)[] {
return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;
return <IFunctionBreakpointData>{
type: 'function',
id: bp.getId(),
enabled: bp.enabled,
functionName: fbp.name,
hitCondition: bp.hitCondition,
/* condition: bp.condition */
};
} else {
const sbp = <IBreakpoint>bp;
return <ISourceBreakpointData>{
type: 'source',
id: bp.getId(),
enabled: bp.enabled,
condition: sbp.condition,
hitCondition: bp.hitCondition,
sourceUriStr: sbp.uri.toString(),
location: {
line: sbp.lineNumber,
character: sbp.column
}
};
}
});
}
public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, handle: number): TPromise<void> {
const provider = <IDebugConfigurationProvider>{
......
......@@ -515,8 +515,8 @@ export function createApiFactory(
get activeDebugConsole() {
return extHostDebugService.activeDebugConsole;
},
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) {
return extHostDebugService.startDebugging(folder, nameOrConfig);
get breakpoints() {
return extHostDebugService.breakpoints;
},
onDidStartDebugSession(listener, thisArg?, disposables?) {
return extHostDebugService.onDidStartDebugSession(listener, thisArg, disposables);
......@@ -530,6 +530,12 @@ export function createApiFactory(
onDidReceiveDebugSessionCustomEvent(listener, thisArg?, disposables?) {
return extHostDebugService.onDidReceiveDebugSessionCustomEvent(listener, thisArg, disposables);
},
onDidChangeBreakpoints: proposedApiFunction(extension, (listener, thisArgs?, disposables?) => {
return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables);
}),
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) {
return extHostDebugService.startDebugging(folder, nameOrConfig);
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) {
return extHostDebugService.registerDebugConfigurationProvider(debugType, provider);
}
......
......@@ -419,6 +419,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$startDebugging(folder: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise<any>;
$appendDebugConsole(value: string): TPromise<any>;
$startBreakpointEvents(): TPromise<any>;
}
export interface MainThreadWindowShape extends IDisposable {
......@@ -623,6 +624,31 @@ export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
}
export interface IBreakpointData {
type: 'source' | 'function';
id: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
}
export interface ISourceBreakpointData extends IBreakpointData {
type: 'source';
sourceUriStr: string;
location: vscode.Position;
}
export interface IFunctionBreakpointData extends IBreakpointData {
type: 'function';
functionName: string;
}
export interface IBreakpointsDelta {
added?: (ISourceBreakpointData | IFunctionBreakpointData)[];
removed?: string[];
changed?: (ISourceBreakpointData | IFunctionBreakpointData)[];
}
export interface ExtHostDebugServiceShape {
$resolveDebugConfiguration(handle: number, folder: URI | undefined, debugConfiguration: any): TPromise<any>;
$provideDebugConfigurations(handle: number, folder: URI | undefined): TPromise<any[]>;
......@@ -630,6 +656,7 @@ export interface ExtHostDebugServiceShape {
$acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void;
$acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void;
$acceptBreakpointsDelta(delat: IBreakpointsDelta): void;
}
......
......@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext, IBreakpointsDelta, ISourceBreakpointData, IFunctionBreakpointData } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
......@@ -43,6 +43,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _activeDebugConsole: ExtHostDebugConsole;
get activeDebugConsole(): ExtHostDebugConsole { return this._activeDebugConsole; }
private _breakpoints: Map<string, vscode.Breakpoint>;
private _breakpointEventsActive: boolean;
private _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace) {
......@@ -58,7 +63,84 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._debugServiceProxy = mainContext.get(MainContext.MainThreadDebugService);
this._onDidChangeBreakpoints = new Emitter<vscode.BreakpointsChangeEvent>({
onFirstListenerAdd: () => {
this.startBreakpoints();
}
});
this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy);
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
}
private startBreakpoints() {
if (!this._breakpointEventsActive) {
this._breakpointEventsActive = true;
this._debugServiceProxy.$startBreakpointEvents();
}
}
get onDidChangeBreakpoints(): Event<vscode.BreakpointsChangeEvent> {
return this._onDidChangeBreakpoints.event;
}
get breakpoints(): vscode.Breakpoint[] {
this.startBreakpoints();
const result: vscode.Breakpoint[] = [];
this._breakpoints.forEach(bp => result.push(bp));
return result;
}
public $acceptBreakpointsDelta(delta: IBreakpointsDelta): void {
let a: vscode.Breakpoint[] = [];
let r: vscode.Breakpoint[] = [];
let c: vscode.Breakpoint[] = [];
if (delta.added) {
a = delta.added.map(bpd => {
const id = bpd.id;
this._breakpoints.set(id, this.fromWire(bpd));
return bpd;
});
}
if (delta.removed) {
r = delta.removed.map(id => {
const bp = this._breakpoints.get(id);
if (bp) {
this._breakpoints.delete(id);
}
return bp;
});
}
if (delta.changed) {
c = delta.changed.map(bpd => {
const id = bpd.id;
this._breakpoints.set(id, this.fromWire(bpd));
return bpd;
});
}
this._onDidChangeBreakpoints.fire(Object.freeze({
added: Object.freeze<vscode.Breakpoint[]>(a || []),
removed: Object.freeze<vscode.Breakpoint[]>(r || []),
changed: Object.freeze<vscode.Breakpoint[]>(c || [])
}));
}
private fromWire(bp: ISourceBreakpointData | IFunctionBreakpointData): vscode.Breakpoint {
delete bp.id;
if (bp.type === 'source') {
(<any>bp).source = URI.parse(bp.sourceUriStr);
delete bp.sourceUriStr;
}
return bp;
}
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
......@@ -87,7 +169,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return asWinJsPromise(token => handler.provideDebugConfigurations(this.getFolder(folderUri), token));
}
public $resolveDebugConfiguration(handle: number, folderUri: URI | undefined, debugConfiguration: vscode.DebugConfiguration): TPromise<vscode.DebugConfiguration> {
let handler = this._handlers.get(handle);
if (!handler) {
......
......@@ -299,12 +299,21 @@ export interface IModel extends ITreeElement {
getWatchExpressions(): IExpression[];
getReplElements(): IReplElement[];
onDidChangeBreakpoints: Event<void>;
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent>;
onDidChangeCallStack: Event<void>;
onDidChangeWatchExpressions: Event<IExpression>;
onDidChangeReplElements: Event<void>;
}
/**
* An event describing a change to the set of [breakpoints](#debug.Breakpoint).
*/
export interface IBreakpointsChangeEvent {
added?: (IBreakpoint | IFunctionBreakpoint)[];
removed?: (IBreakpoint | IFunctionBreakpoint)[];
changed?: (IBreakpoint | IFunctionBreakpoint)[];
}
// Debug enums
export enum State {
......
......@@ -20,7 +20,7 @@ import { ISuggestion } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import {
ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource,
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState
IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState, IBreakpointsChangeEvent
} from 'vs/workbench/parts/debug/common/debug';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
......@@ -739,7 +739,7 @@ export class Model implements IModel {
private toDispose: lifecycle.IDisposable[];
private replElements: IReplElement[];
private schedulers = new Map<string, RunOnceScheduler>();
private _onDidChangeBreakpoints: Emitter<void>;
private _onDidChangeBreakpoints: Emitter<IBreakpointsChangeEvent>;
private _onDidChangeCallStack: Emitter<void>;
private _onDidChangeWatchExpressions: Emitter<IExpression>;
private _onDidChangeREPLElements: Emitter<void>;
......@@ -754,7 +754,7 @@ export class Model implements IModel {
this.processes = [];
this.replElements = [];
this.toDispose = [];
this._onDidChangeBreakpoints = new Emitter<void>();
this._onDidChangeBreakpoints = new Emitter<IBreakpointsChangeEvent>();
this._onDidChangeCallStack = new Emitter<void>();
this._onDidChangeWatchExpressions = new Emitter<IExpression>();
this._onDidChangeREPLElements = new Emitter<void>();
......@@ -780,7 +780,7 @@ export class Model implements IModel {
this._onDidChangeCallStack.fire();
}
public get onDidChangeBreakpoints(): Event<void> {
public get onDidChangeBreakpoints(): Event<IBreakpointsChangeEvent> {
return this._onDidChangeBreakpoints.event;
}
......@@ -868,8 +868,9 @@ export class Model implements IModel {
this.breakpoints = this.breakpoints.concat(newBreakpoints);
this.breakpointsActivated = true;
this.sortAndDeDup();
if (fireEvent) {
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ added: newBreakpoints });
}
return newBreakpoints;
......@@ -877,10 +878,11 @@ export class Model implements IModel {
public removeBreakpoints(toRemove: IBreakpoint[]): void {
this.breakpoints = this.breakpoints.filter(bp => !toRemove.some(toRemove => toRemove.getId() === bp.getId()));
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ removed: toRemove });
}
public updateBreakpoints(data: { [id: string]: DebugProtocol.Breakpoint }): void {
const updated: IBreakpoint[] = [];
this.breakpoints.forEach(bp => {
const bpData = data[bp.getId()];
if (bpData) {
......@@ -892,10 +894,11 @@ export class Model implements IModel {
bp.idFromAdapter = bpData.id;
bp.message = bpData.message;
bp.adapterData = bpData.source ? bpData.source.adapterData : bp.adapterData;
updated.push(bp);
}
});
this.sortAndDeDup();
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: updated });
}
private sortAndDeDup(): void {
......@@ -913,36 +916,56 @@ export class Model implements IModel {
}
public setEnablement(element: IEnablement, enable: boolean): void {
const changed: (IBreakpoint | IFunctionBreakpoint)[] = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint)) {
changed.push(element);
}
element.enabled = enable;
if (element instanceof Breakpoint && !element.enabled) {
const breakpoint = <Breakpoint>element;
breakpoint.verified = false;
}
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public enableOrDisableAllBreakpoints(enable: boolean): void {
const changed: (IBreakpoint | IFunctionBreakpoint)[] = [];
this.breakpoints.forEach(bp => {
if (bp.enabled !== enable) {
changed.push(bp);
}
bp.enabled = enable;
if (!enable) {
bp.verified = false;
}
});
this.functionBreakpoints.forEach(fbp => fbp.enabled = enable);
this.functionBreakpoints.forEach(fbp => {
if (fbp.enabled !== enable) {
changed.push(fbp);
}
fbp.enabled = enable;
});
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public addFunctionBreakpoint(functionName: string): FunctionBreakpoint {
const newFunctionBreakpoint = new FunctionBreakpoint(functionName, true, null);
this.functionBreakpoints.push(newFunctionBreakpoint);
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ added: [newFunctionBreakpoint] });
return newFunctionBreakpoint;
}
public updateFunctionBreakpoints(data: { [id: string]: { name?: string, verified?: boolean; id?: number; hitCondition?: string } }): void {
const changed: IFunctionBreakpoint[] = [];
this.functionBreakpoints.forEach(fbp => {
const fbpData = data[fbp.getId()];
if (fbpData) {
......@@ -950,15 +973,25 @@ export class Model implements IModel {
fbp.verified = fbpData.verified;
fbp.idFromAdapter = fbpData.id;
fbp.hitCondition = fbpData.hitCondition;
changed.push(fbp);
}
});
this._onDidChangeBreakpoints.fire();
this._onDidChangeBreakpoints.fire({ changed: changed });
}
public removeFunctionBreakpoints(id?: string): void {
this.functionBreakpoints = id ? this.functionBreakpoints.filter(fbp => fbp.getId() !== id) : [];
this._onDidChangeBreakpoints.fire();
let removed: IFunctionBreakpoint[];
if (id) {
removed = this.functionBreakpoints.filter(fbp => fbp.getId() === id);
this.functionBreakpoints = this.functionBreakpoints.filter(fbp => fbp.getId() !== id);
} else {
removed = this.functionBreakpoints;
this.functionBreakpoints = [];
}
this._onDidChangeBreakpoints.fire({ removed: removed });
}
public getReplElements(): IReplElement[] {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册