Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463 (#7206)

* Merge from vscode 64980ea1f3f532c82bb6c28d27bba9ef2c5b4463

* fix config changes

* fix strictnull checks
This commit is contained in:
Anthony Dresser
2019-09-15 22:38:26 -07:00
committed by GitHub
parent fa6c52699e
commit ea0f9e6ce9
1226 changed files with 21541 additions and 17633 deletions

View File

@@ -5,6 +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';
/**
* Abstract implementation of the low level API for a debug adapter.
@@ -70,7 +71,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
}
}
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void {
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number {
const request: any = {
command: command
};
@@ -100,6 +101,8 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
// store callback for this request
this.pendingRequests.set(request.seq, clb);
}
return request.seq;
}
acceptMessage(message: DebugProtocol.ProtocolMessage): void {
@@ -136,25 +139,33 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
this.sendMessage(message);
}
protected cancelPending() {
const pending = this.pendingRequests;
this.pendingRequests.clear();
setTimeout(_ => {
pending.forEach((callback, request_seq) => {
const err: DebugProtocol.Response = {
type: 'response',
seq: 0,
request_seq,
success: false,
command: 'canceled',
message: 'canceled'
};
callback(err);
});
}, 1000);
protected async cancelPendingRequests(): Promise<void> {
if (this.pendingRequests.size === 0) {
return Promise.resolve();
}
const pending = new Map<number, (e: DebugProtocol.Response) => void>();
this.pendingRequests.forEach((value, key) => pending.set(key, value));
await timeout(500);
pending.forEach((callback, request_seq) => {
const err: DebugProtocol.Response = {
type: 'response',
seq: 0,
request_seq,
success: false,
command: 'canceled',
message: 'canceled'
};
callback(err);
this.pendingRequests.delete(request_seq);
});
}
getPendingRequestIds(): number[] {
return Array.from(this.pendingRequests.keys());
}
dispose(): void {
this.cancelPending();
// noop
}
}

View File

@@ -104,13 +104,13 @@ export interface IExpressionContainer extends ITreeElement {
readonly hasChildren: boolean;
getChildren(): Promise<IExpression[]>;
readonly reference?: number;
readonly value: string;
readonly type?: string;
valueChanged?: boolean;
}
export interface IExpression extends IReplElement, IExpressionContainer {
export interface IExpression extends IExpressionContainer {
name: string;
readonly value: string;
valueChanged?: boolean;
readonly type?: string;
}
export interface IDebugger {
@@ -157,6 +157,8 @@ export interface IDebugSession extends ITreeElement {
setSubId(subId: string | undefined): void;
setName(name: string): void;
readonly onDidChangeName: Event<string>;
getLabel(): string;
getSourceForUri(modelUri: uri): Source | undefined;
@@ -208,8 +210,8 @@ export interface IDebugSession extends ITreeElement {
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse>;
exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>;
scopes(frameId: number): Promise<DebugProtocol.ScopesResponse>;
variables(variablesReference: number, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse>;
scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse>;
variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse>;
evaluate(expression: string, frameId?: number, context?: string): Promise<DebugProtocol.EvaluateResponse>;
customRequest(request: string, args: any): Promise<DebugProtocol.Response>;
@@ -223,7 +225,7 @@ export interface IDebugSession extends ITreeElement {
pause(threadId: number): Promise<void>;
terminateThreads(threadIds: number[]): Promise<void>;
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number): Promise<CompletionItem[]>;
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]>;
setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse>;
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse>;
getLoadedSources(): Promise<Source[]>;
@@ -308,6 +310,7 @@ export interface IStackFrame extends ITreeElement {
restart(): Promise<any>;
toString(): string;
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Promise<ITextEditor | null>;
equals(other: IStackFrame): boolean;
}
export interface IEnablement extends ITreeElement {
@@ -337,7 +340,7 @@ export interface IBaseBreakpoint extends IEnablement {
readonly hitCondition?: string;
readonly logMessage?: string;
readonly verified: boolean;
readonly idFromAdapter: number | undefined;
getIdFromAdapter(sessionId: string): number | undefined;
}
export interface IBreakpoint extends IBaseBreakpoint {
@@ -452,6 +455,7 @@ export interface IDebugConfiguration {
wordWrap: boolean;
};
focusWindowOnBreak: boolean;
onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt';
}
export interface IGlobalConfig {
@@ -500,7 +504,7 @@ export interface IDebugAdapter extends IDisposable {
startSession(): Promise<void>;
sendMessage(message: DebugProtocol.ProtocolMessage): void;
sendResponse(response: DebugProtocol.Response): void;
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void;
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number;
stopSession(): Promise<void>;
}
@@ -682,7 +686,7 @@ export interface ILaunch {
export const IDebugService = createDecorator<IDebugService>(DEBUG_SERVICE_ID);
export interface IDebugService {
_serviceBrand: any;
_serviceBrand: undefined;
/**
* Gets the current debug state.
@@ -857,7 +861,7 @@ export const DEBUG_HELPER_SERVICE_ID = 'debugHelperService';
export const IDebugHelperService = createDecorator<IDebugHelperService>(DEBUG_HELPER_SERVICE_ID);
export interface IDebugHelperService {
_serviceBrand: any;
_serviceBrand: undefined;
createTelemetryService(configurationService: IConfigurationService, args: string[]): TelemetryService | undefined;
}

View File

@@ -10,13 +10,12 @@ import * as lifecycle from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
import { RunOnceScheduler } from 'vs/base/common/async';
import severity from 'vs/base/common/severity';
import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types';
import { isString, isUndefinedOrNull } from 'vs/base/common/types';
import { distinct, lastIndex } from 'vs/base/common/arrays';
import { Range, IRange } from 'vs/editor/common/core/range';
import {
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource,
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel,
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint
} from 'vs/workbench/contrib/debug/common/debug';
import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource';
import { commonSuffixLength } from 'vs/base/common/strings';
@@ -25,81 +24,20 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextEditor } from 'vs/workbench/common/editor';
export class SimpleReplElement implements IReplElement {
constructor(
private id: string,
public value: string,
public severity: severity,
public sourceData?: IReplElementSource,
) { }
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class RawObjectReplElement implements IExpression {
private static readonly MAX_CHILDREN = 1000; // upper bound of children per value
constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { }
getId(): string {
return this.id;
}
get value(): string {
if (this.valueObj === null) {
return 'null';
} else if (Array.isArray(this.valueObj)) {
return `Array[${this.valueObj.length}]`;
} else if (isObject(this.valueObj)) {
return 'Object';
} else if (isString(this.valueObj)) {
return `"${this.valueObj}"`;
}
return String(this.valueObj) || '';
}
get hasChildren(): boolean {
return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0);
}
getChildren(): Promise<IExpression[]> {
let result: IExpression[] = [];
if (Array.isArray(this.valueObj)) {
result = (<any[]>this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v));
} else if (isObject(this.valueObj)) {
result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key]));
}
return Promise.resolve(result);
}
toString(): string {
return `${this.name}\n${this.value}`;
}
}
export class ExpressionContainer implements IExpressionContainer {
public static allValues = new Map<string, string>();
// Use chunks to support variable paging #9537
private static readonly BASE_CHUNK_SIZE = 100;
public type: string | undefined;
public valueChanged = false;
private _value: string = '';
protected children?: Promise<IExpression[]>;
constructor(
protected session: IDebugSession | undefined,
protected threadId: number | undefined,
private _reference: number | undefined,
private id: string,
public namedVariables: number | undefined = 0,
@@ -148,7 +86,7 @@ export class ExpressionContainer implements IExpressionContainer {
for (let i = 0; i < numberOfChunks; i++) {
const start = (this.startOfVariables || 0) + i * chunkSize;
const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize);
children.push(new Variable(this.session, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start));
children.push(new Variable(this.session, this.threadId, this, this.reference, `[${start}..${start + count - 1}]`, '', '', undefined, count, { kind: 'virtual' }, undefined, true, start));
}
return children;
@@ -172,12 +110,12 @@ export class ExpressionContainer implements IExpressionContainer {
}
private fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> {
return this.session!.variables(this.reference || 0, filter, start, count).then(response => {
return this.session!.variables(this.reference || 0, this.threadId, filter, start, count).then(response => {
return response && response.body && response.body.variables
? distinct(response.body.variables.filter(v => !!v && isString(v.name)), (v: DebugProtocol.Variable) => v.name).map((v: DebugProtocol.Variable) =>
new Variable(this.session, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type))
new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type))
: [];
}, (e: Error) => [new Variable(this.session, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]);
}, (e: Error) => [new Variable(this.session, this.threadId, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]);
}
// The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked.
@@ -195,16 +133,46 @@ export class ExpressionContainer implements IExpressionContainer {
toString(): string {
return this.value;
}
async evaluateExpression(
expression: string,
session: IDebugSession | undefined,
stackFrame: IStackFrame | undefined,
context: string): Promise<boolean> {
if (!session || (!stackFrame && context !== 'repl')) {
this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE;
this.reference = 0;
return false;
}
this.session = session;
try {
const response = await session.evaluate(expression, stackFrame ? stackFrame.frameId : undefined, context);
if (response && response.body) {
this.value = response.body.result || '';
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type || this.type;
return true;
}
return false;
} catch (e) {
this.value = e.message || '';
this.reference = 0;
return false;
}
}
}
export class Expression extends ExpressionContainer implements IExpression {
static DEFAULT_VALUE = nls.localize('notAvailable', "not available");
public available: boolean;
public type: string | undefined;
constructor(public name: string, id = generateUuid()) {
super(undefined, 0, id);
super(undefined, undefined, 0, id);
this.available = false;
// name is not set if the expression is just being added
// in that case do not set default value to prevent flashing #14499
@@ -214,30 +182,7 @@ export class Expression extends ExpressionContainer implements IExpression {
}
async evaluate(session: IDebugSession | undefined, stackFrame: IStackFrame | undefined, context: string): Promise<void> {
if (!session || (!stackFrame && context !== 'repl')) {
this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE;
this.available = false;
this.reference = 0;
return Promise.resolve(undefined);
}
this.session = session;
try {
const response = await session.evaluate(this.name, stackFrame ? stackFrame.frameId : undefined, context);
this.available = !!(response && response.body);
if (response && response.body) {
this.value = response.body.result || '';
this.reference = response.body.variablesReference;
this.namedVariables = response.body.namedVariables;
this.indexedVariables = response.body.indexedVariables;
this.type = response.body.type || this.type;
}
} catch (e) {
this.value = e.message || '';
this.available = false;
this.reference = 0;
}
this.available = await this.evaluateExpression(this.name, session, stackFrame, context);
}
toString(): string {
@@ -252,6 +197,7 @@ export class Variable extends ExpressionContainer implements IExpression {
constructor(
session: IDebugSession | undefined,
threadId: number | undefined,
public parent: IExpressionContainer,
reference: number | undefined,
public name: string,
@@ -264,7 +210,7 @@ export class Variable extends ExpressionContainer implements IExpression {
public available = true,
startOfVariables = 0
) {
super(session, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables);
super(session, threadId, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables);
this.value = value || '';
}
@@ -304,7 +250,7 @@ export class Scope extends ExpressionContainer implements IScope {
indexedVariables?: number,
public range?: IRange
) {
super(stackFrame.thread.session, reference, `scope:${name}:${index}`, namedVariables, indexedVariables);
super(stackFrame.thread.session, stackFrame.thread.threadId, reference, `scope:${name}:${index}`, namedVariables, indexedVariables);
}
toString(): string {
@@ -332,7 +278,7 @@ export class StackFrame implements IStackFrame {
getScopes(): Promise<IScope[]> {
if (!this.scopes) {
this.scopes = this.thread.session.scopes(this.frameId).then(response => {
this.scopes = this.thread.session.scopes(this.frameId, this.thread.threadId).then(response => {
return response && response.body && response.body.scopes ?
response.body.scopes.map((rs, index) => new Scope(this, index, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables,
rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : undefined)) : [];
@@ -395,6 +341,10 @@ export class StackFrame implements IStackFrame {
return !this.source.available ? Promise.resolve(null) :
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
}
equals(other: IStackFrame): boolean {
return (this.name === other.name) && (other.thread === this.thread) && (other.source === this.source) && (Range.equalsRange(this.range, other.range));
}
}
export class Thread implements IThread {
@@ -578,13 +528,22 @@ export class BaseBreakpoint extends Enablement implements IBaseBreakpoint {
this.sessionId = sessionId;
}
get message(): string | undefined {
const data = this.getSessionData();
if (!data) {
return undefined;
}
return data.message;
}
get verified(): boolean {
const data = this.getSessionData();
return data ? data.verified : true;
}
get idFromAdapter(): number | undefined {
const data = this.getSessionData();
getIdFromAdapter(sessionId: string): number | undefined {
const data = this.sessionData.get(sessionId);
return data ? data.id : undefined;
}
@@ -637,15 +596,11 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
}
get message(): string | undefined {
const data = this.getSessionData();
if (!data) {
return undefined;
}
if (this.textFileService.isDirty(this.uri)) {
return nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.");
}
return data.message;
return super.message;
}
get adapterData(): any {
@@ -797,13 +752,13 @@ export class DebugModel implements IDebugModel {
private toDispose: lifecycle.IDisposable[];
private schedulers = new Map<string, RunOnceScheduler>();
private breakpointsSessionId: string | undefined;
private breakpointsActivated = true;
private readonly _onDidChangeBreakpoints: Emitter<IBreakpointsChangeEvent | undefined>;
private readonly _onDidChangeCallStack: Emitter<void>;
private readonly _onDidChangeWatchExpressions: Emitter<IExpression | undefined>;
constructor(
private breakpoints: Breakpoint[],
private breakpointsActivated: boolean,
private functionBreakpoints: FunctionBreakpoint[],
private exceptionBreakpoints: ExceptionBreakpoint[],
private dataBreakopints: DataBreakpoint[],
@@ -901,7 +856,16 @@ export class DebugModel implements IDebugModel {
if (!this.schedulers.has(thread.getId())) {
this.schedulers.set(thread.getId(), new RunOnceScheduler(() => {
thread.fetchCallStack(19).then(() => {
this._onDidChangeCallStack.fire();
const stale = thread.getStaleCallStack();
const current = thread.getCallStack();
let bottomOfCallStackChanged = stale.length !== current.length;
for (let i = 1; i < stale.length && !bottomOfCallStackChanged; i++) {
bottomOfCallStackChanged = !stale[i].equals(current[i]);
}
if (bottomOfCallStackChanged) {
this._onDidChangeCallStack.fire();
}
c();
});
}, 420));
@@ -1065,9 +1029,9 @@ export class DebugModel implements IDebugModel {
}
setEnablement(element: IEnablement, enable: boolean): void {
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof ExceptionBreakpoint) {
const changed: Array<IBreakpoint | IFunctionBreakpoint> = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint)) {
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof ExceptionBreakpoint || element instanceof DataBreakpoint) {
const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint> = [];
if (element.enabled !== enable && (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof DataBreakpoint)) {
changed.push(element);
}

View File

@@ -10,7 +10,7 @@ declare module DebugProtocol {
/** Base class of requests, responses, and events. */
export interface ProtocolMessage {
/** Sequence number. */
/** Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request. */
seq: number;
/** Message type.
Values: 'request', 'response', 'event', etc.
@@ -41,11 +41,20 @@ declare module DebugProtocol {
// type: 'response';
/** Sequence number of the corresponding request. */
request_seq: number;
/** Outcome of the request. */
/** Outcome of the request.
If true, the request was successful and the 'body' attribute may contain the result of the request.
If the value is false, the attribute 'message' contains the error in short form and the 'body' may contain additional information (see 'ErrorResponse.body.error').
*/
success: boolean;
/** The command requested. */
command: string;
/** Contains error message if success == false. */
/** Contains the raw error in short form if 'success' is false.
This raw error might be interpreted by the frontend and is not shown in the UI.
Some predefined values exist.
Values:
'cancelled': request was cancelled.
etc.
*/
message?: string;
/** Contains request result if success is true and optional error details if success is false. */
body?: any;
@@ -59,6 +68,30 @@ declare module DebugProtocol {
};
}
/** Cancel request; value of command field is 'cancel'.
The 'cancel' request is used by the frontend to indicate that it is no longer interested in the result produced by a specific request issued earlier.
This request has a hint characteristic: a debug adapter can only be expected to make a 'best effort' in honouring this request but there are no guarantees.
The 'cancel' request may return an error if it could not cancel an operation but a frontend should refrain from presenting this error to end users.
A frontend client should only call this request if the capability 'supportsCancelRequest' is true.
The request that got canceled still needs to send a response back.
This can either be a normal result ('success' attribute true) or an error response ('success' attribute false and the 'message' set to 'cancelled').
Returning partial results from a cancelled request is possible but please note that a frontend client has no generic way for detecting that a response is partial or not.
*/
export interface CancelRequest extends Request {
// command: 'cancel';
arguments?: CancelArguments;
}
/** Arguments for 'cancel' request. */
export interface CancelArguments {
/** The ID (attribute 'seq') of the request to cancel. */
requestId?: number;
}
/** Response to 'cancel' request. This is just an acknowledgement, so no body field is required. */
export interface CancelResponse extends Response {
}
/** Event message for 'initialized' event type.
This event indicates that the debug adapter is ready to accept configuration requests (e.g. SetBreakpointsRequest, SetExceptionBreakpointsRequest).
A debug adapter is expected to send this event when it is ready to accept configuration requests (but not before the 'initialize' request has finished).
@@ -455,6 +488,38 @@ declare module DebugProtocol {
export interface TerminateResponse extends Response {
}
/** BreakpointLocations request; value of command field is 'breakpointLocations'.
The 'breakpointLocations' request returns all possible locations for source breakpoints in a given range.
*/
export interface BreakpointLocationsRequest extends Request {
// command: 'breakpointLocations';
arguments?: BreakpointLocationsArguments;
}
/** Arguments for 'breakpointLocations' request. */
export interface BreakpointLocationsArguments {
/** The source location of the breakpoints; either 'source.path' or 'source.reference' must be specified. */
source: Source;
/** Start line of range to search possible breakpoint locations in. If only the line is specified, the request returns all possible locations in that line. */
line: number;
/** Optional start column of range to search possible breakpoint locations in. If no start column is given, the first column in the start line is assumed. */
column?: number;
/** Optional end line of range to search possible breakpoint locations in. If no end line is given, then the end line is assumed to be the start line. */
endLine?: number;
/** Optional end column of range to search possible breakpoint locations in. If no end column is given, then it is assumed to be in the last column of the end line. */
endColumn?: number;
}
/** Response to 'breakpointLocations' request.
Contains possible locations for source breakpoints.
*/
export interface BreakpointLocationsResponse extends Response {
body: {
/** Sorted set of possible breakpoint locations. */
breakpoints: BreakpointLocation[];
};
}
/** SetBreakpoints request; value of command field is 'setBreakpoints'.
Sets multiple breakpoints for a single source and clears all previous breakpoints in that source.
To clear all breakpoint for a source, specify an empty array.
@@ -1330,6 +1395,10 @@ declare module DebugProtocol {
supportsReadMemoryRequest?: boolean;
/** The debug adapter supports the 'disassemble' request. */
supportsDisassembleRequest?: boolean;
/** The debug adapter supports the 'cancel' request. */
supportsCancelRequest?: boolean;
/** The debug adapter supports the 'breakpointLocations' request. */
supportsBreakpointLocationsRequest?: boolean;
}
/** An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with. */
@@ -1577,6 +1646,18 @@ declare module DebugProtocol {
visibility?: string;
}
/** Properties of a breakpoint location returned from the 'breakpointLocations' request. */
export interface BreakpointLocation {
/** Start line of breakpoint location. */
line: number;
/** Optional start column of breakpoint location. */
column?: number;
/** Optional end line of breakpoint location if the location covers a range. */
endLine?: number;
/** Optional end column of breakpoint location if the location covers a range. */
endColumn?: number;
}
/** Properties of a breakpoint or logpoint passed to the setBreakpoints request. */
export interface SourceBreakpoint {
/** The source line of the breakpoint or logpoint. */

View File

@@ -137,6 +137,8 @@ export const launchSchema: IJSONSchema = {
id: launchSchemaId,
type: 'object',
title: nls.localize('app.launch.json.title', "Launch"),
allowsTrailingCommas: true,
allowComments: true,
required: [],
default: { version: '0.2.0', configurations: [], compounds: [] },
properties: {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { IConfig, IDebuggerContribution, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IConfig, IDebuggerContribution, IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { URI as uri } from 'vs/base/common/uri';
import { isAbsolute } from 'vs/base/common/path';
import { deepClone } from 'vs/base/common/objects';
@@ -42,6 +42,10 @@ export function formatPII(value: string, excludePII: boolean, args: { [key: stri
});
}
export function isSessionAttach(session: IDebugSession): boolean {
return !session.parentSession && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
}
export function isExtensionHostDebugging(config: IConfig) {
return config.type && equalsIgnoreCase(config.type === 'vslsShare' ? (<any>config).adapterProxy.configuration.type : config.type, 'extensionhost');
}

View File

@@ -6,7 +6,7 @@
import { Event, Emitter } from 'vs/base/common/event';
import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils';
export class ViewModel implements IViewModel {
@@ -71,7 +71,7 @@ export class ViewModel implements IViewModel {
this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false);
this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false);
this.jumpToCursorSupported.set(session ? !!session.capabilities.supportsGotoTargetsRequest : false);
const attach = !!session && !session.parentSession && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
const attach = !!session && isSessionAttach(session);
this.focusedSessionIsAttach.set(attach);
if (shouldEmitForSession) {

View File

@@ -6,15 +6,105 @@
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/contrib/debug/common/debugModel';
import { isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { ExpressionContainer } from 'vs/workbench/contrib/debug/common/debugModel';
import { isString, isUndefinedOrNull, isObject } from 'vs/base/common/types';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { endsWith } from 'vs/base/common/strings';
import { generateUuid } from 'vs/base/common/uuid';
const MAX_REPL_LENGTH = 10000;
let topReplElementCounter = 0;
export class SimpleReplElement implements IReplElement {
constructor(
private id: string,
public value: string,
public severity: severity,
public sourceData?: IReplElementSource,
) { }
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class RawObjectReplElement implements IExpression {
private static readonly MAX_CHILDREN = 1000; // upper bound of children per value
constructor(private id: string, public name: string, public valueObj: any, public sourceData?: IReplElementSource, public annotation?: string) { }
getId(): string {
return this.id;
}
get value(): string {
if (this.valueObj === null) {
return 'null';
} else if (Array.isArray(this.valueObj)) {
return `Array[${this.valueObj.length}]`;
} else if (isObject(this.valueObj)) {
return 'Object';
} else if (isString(this.valueObj)) {
return `"${this.valueObj}"`;
}
return String(this.valueObj) || '';
}
get hasChildren(): boolean {
return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0);
}
getChildren(): Promise<IExpression[]> {
let result: IExpression[] = [];
if (Array.isArray(this.valueObj)) {
result = (<any[]>this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((v, index) => new RawObjectReplElement(`${this.id}:${index}`, String(index), v));
} else if (isObject(this.valueObj)) {
result = Object.getOwnPropertyNames(this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN)
.map((key, index) => new RawObjectReplElement(`${this.id}:${index}`, key, this.valueObj[key]));
}
return Promise.resolve(result);
}
toString(): string {
return `${this.name}\n${this.value}`;
}
}
export class ReplEvaluationInput implements IReplElement {
private id: string;
constructor(public value: string) {
this.id = generateUuid();
}
toString(): string {
return this.value;
}
getId(): string {
return this.id;
}
}
export class ReplEvaluationResult extends ExpressionContainer implements IReplElement {
constructor() {
super(undefined, undefined, 0, generateUuid());
}
toString(): string {
return `${this.value}`;
}
}
export class ReplModel {
private replElements: IReplElement[] = [];
@@ -24,10 +114,11 @@ export class ReplModel {
return this.replElements;
}
addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
const expression = new Expression(name);
this.addReplElement(expression);
return expression.evaluate(this.session, stackFrame, 'repl');
async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
this.addReplElement(new ReplEvaluationInput(name));
const result = new ReplEvaluationResult();
await result.evaluateExpression(name, this.session, stackFrame, 'repl');
this.addReplElement(result);
}
appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void {