mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-02 23:05:15 +01:00
Merge pull request #60016 from Microsoft/isidorn/multiRepl
Multiple repls per debug session
This commit is contained in:
@@ -205,7 +205,10 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
|
||||
public $appendDebugConsole(value: string): Thenable<void> {
|
||||
// Use warning as severity to get the orange color for messages coming from the debug extension
|
||||
this.debugService.logToRepl(value, severity.Warning);
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
if (session) {
|
||||
session.appendToRepl(value, severity.Warning);
|
||||
}
|
||||
return TPromise.wrap<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
@@ -239,9 +239,7 @@ export class RestartAction extends AbstractDebugAction {
|
||||
return this.startAction.run();
|
||||
}
|
||||
|
||||
if (this.debugService.getModel().getSessions().length <= 1) {
|
||||
this.debugService.removeReplExpressions();
|
||||
}
|
||||
session.removeReplExpressions();
|
||||
return this.debugService.restartSession(session);
|
||||
}
|
||||
|
||||
@@ -692,8 +690,11 @@ export class ClearReplAction extends AbstractDebugAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
this.debugService.removeReplExpressions();
|
||||
aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared"));
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
if (session) {
|
||||
session.removeReplExpressions();
|
||||
aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared"));
|
||||
}
|
||||
|
||||
// focus back to repl
|
||||
return this.panelService.openPanel(REPL_ID, true);
|
||||
@@ -706,7 +707,6 @@ export class ToggleReplAction extends TogglePanelAction {
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@IPartService partService: IPartService,
|
||||
@IPanelService panelService: IPanelService
|
||||
) {
|
||||
@@ -716,12 +716,6 @@ export class ToggleReplAction extends TogglePanelAction {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeReplElements(() => {
|
||||
if (!this.isReplVisible()) {
|
||||
this.class = 'debug-action toggle-repl notification';
|
||||
this.tooltip = nls.localize('unreadOutput', "New Output in Debug Console");
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.panelService.onDidPanelOpen(panel => {
|
||||
if (panel.getId() === REPL_ID) {
|
||||
this.class = 'debug-action toggle-repl';
|
||||
@@ -730,11 +724,6 @@ export class ToggleReplAction extends TogglePanelAction {
|
||||
}));
|
||||
}
|
||||
|
||||
private isReplVisible(): boolean {
|
||||
const panel = this.panelService.getActivePanel();
|
||||
return panel && panel.getId() === REPL_ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
@@ -159,7 +159,9 @@ class SelectionToReplAction extends EditorAction {
|
||||
const panelService = accessor.get(IPanelService);
|
||||
|
||||
const text = editor.getModel().getValueInRange(editor.getSelection());
|
||||
return debugService.addReplExpression(text)
|
||||
const viewModel = debugService.getViewModel();
|
||||
const session = viewModel.focusedSession;
|
||||
return session.addReplExpression(viewModel.focusedStackFrame, text)
|
||||
.then(() => panelService.openPanel(REPL_ID, true))
|
||||
.then(_ => void 0);
|
||||
}
|
||||
|
||||
@@ -152,9 +152,21 @@ export interface IDebugSession extends ITreeElement {
|
||||
setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig }): void;
|
||||
rawUpdate(data: IRawModelUpdate): void;
|
||||
|
||||
getThread(threadId: number): IThread;
|
||||
getAllThreads(): ReadonlyArray<IThread>;
|
||||
clearThreads(removeThreads: boolean, reference?: number): void;
|
||||
|
||||
getReplElements(): ReadonlyArray<IReplElement>;
|
||||
|
||||
removeReplExpressions(): void;
|
||||
addReplExpression(stackFrame: IStackFrame, name: string): TPromise<void>;
|
||||
appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void;
|
||||
logToRepl(sev: severity, args: any[], frame?: { uri: uri, line: number, column: number });
|
||||
|
||||
// session events
|
||||
readonly onDidEndAdapter: Event<AdapterEndEvent>;
|
||||
readonly onDidChangeState: Event<void>;
|
||||
readonly onDidChangeReplElements: Event<void>;
|
||||
|
||||
// DA capabilities
|
||||
readonly capabilities: DebugProtocol.Capabilities;
|
||||
@@ -200,10 +212,6 @@ export interface IDebugSession extends ITreeElement {
|
||||
setVariable(variablesReference: number, name: string, value: string): TPromise<DebugProtocol.SetVariableResponse>;
|
||||
loadSource(resource: uri): TPromise<DebugProtocol.SourceResponse>;
|
||||
getLoadedSources(): TPromise<Source[]>;
|
||||
|
||||
getThread(threadId: number): IThread;
|
||||
getAllThreads(): ReadonlyArray<IThread>;
|
||||
clearThreads(removeThreads: boolean, reference?: number): void;
|
||||
}
|
||||
|
||||
export interface IThread extends ITreeElement {
|
||||
@@ -374,12 +382,10 @@ export interface IDebugModel extends ITreeElement {
|
||||
getFunctionBreakpoints(): ReadonlyArray<IFunctionBreakpoint>;
|
||||
getExceptionBreakpoints(): ReadonlyArray<IExceptionBreakpoint>;
|
||||
getWatchExpressions(): ReadonlyArray<IExpression>;
|
||||
getReplElements(): ReadonlyArray<IReplElement>;
|
||||
|
||||
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent>;
|
||||
onDidChangeCallStack: Event<void>;
|
||||
onDidChangeWatchExpressions: Event<IExpression>;
|
||||
onDidChangeReplElements: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -721,21 +727,6 @@ export interface IDebugService {
|
||||
*/
|
||||
sendAllBreakpoints(session?: IDebugSession): TPromise<any>;
|
||||
|
||||
/**
|
||||
* Adds a new expression to the repl.
|
||||
*/
|
||||
addReplExpression(name: string): TPromise<void>;
|
||||
|
||||
/**
|
||||
* Removes all repl expressions.
|
||||
*/
|
||||
removeReplExpressions(): void;
|
||||
|
||||
/**
|
||||
* Appends the passed string to the debug repl.
|
||||
*/
|
||||
logToRepl(value: string | IExpression, sev?: severity, source?: IReplElementSource): void;
|
||||
|
||||
/**
|
||||
* Adds a new watch expression and evaluates it against the debug adapter.
|
||||
*/
|
||||
|
||||
@@ -25,8 +25,6 @@ import { sep } from 'vs/base/common/paths';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
const MAX_REPL_LENGTH = 10000;
|
||||
|
||||
export abstract class AbstractReplElement implements IReplElement {
|
||||
private static ID_COUNTER = 0;
|
||||
|
||||
@@ -728,13 +726,11 @@ export class DebugModel implements IDebugModel {
|
||||
|
||||
private sessions: IDebugSession[];
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private replElements: IReplElement[];
|
||||
private schedulers = new Map<string, RunOnceScheduler>();
|
||||
private breakpointsSessionId: string;
|
||||
private readonly _onDidChangeBreakpoints: Emitter<IBreakpointsChangeEvent>;
|
||||
private readonly _onDidChangeCallStack: Emitter<void>;
|
||||
private readonly _onDidChangeWatchExpressions: Emitter<IExpression>;
|
||||
private readonly _onDidChangeREPLElements: Emitter<void>;
|
||||
|
||||
constructor(
|
||||
private breakpoints: Breakpoint[],
|
||||
@@ -745,12 +741,10 @@ export class DebugModel implements IDebugModel {
|
||||
private textFileService: ITextFileService
|
||||
) {
|
||||
this.sessions = [];
|
||||
this.replElements = [];
|
||||
this.toDispose = [];
|
||||
this._onDidChangeBreakpoints = new Emitter<IBreakpointsChangeEvent>();
|
||||
this._onDidChangeCallStack = new Emitter<void>();
|
||||
this._onDidChangeWatchExpressions = new Emitter<IExpression>();
|
||||
this._onDidChangeREPLElements = new Emitter<void>();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -782,10 +776,6 @@ export class DebugModel implements IDebugModel {
|
||||
return this._onDidChangeWatchExpressions.event;
|
||||
}
|
||||
|
||||
public get onDidChangeReplElements(): Event<void> {
|
||||
return this._onDidChangeREPLElements.event;
|
||||
}
|
||||
|
||||
public rawUpdate(data: IRawModelUpdate): void {
|
||||
let session = this.sessions.filter(p => p.getId() === data.sessionId).pop();
|
||||
if (session) {
|
||||
@@ -1015,53 +1005,6 @@ export class DebugModel implements IDebugModel {
|
||||
this._onDidChangeBreakpoints.fire({ removed: removed });
|
||||
}
|
||||
|
||||
public getReplElements(): IReplElement[] {
|
||||
return this.replElements;
|
||||
}
|
||||
|
||||
public addReplExpression(session: IDebugSession, stackFrame: IStackFrame, name: string): TPromise<void> {
|
||||
const expression = new Expression(name);
|
||||
this.addReplElements([expression]);
|
||||
return expression.evaluate(session, stackFrame, 'repl')
|
||||
.then(() => this._onDidChangeREPLElements.fire());
|
||||
}
|
||||
|
||||
public appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {
|
||||
if (typeof data === 'string') {
|
||||
const previousElement = this.replElements.length && (this.replElements[this.replElements.length - 1] as SimpleReplElement);
|
||||
|
||||
const toAdd = data.split('\n').map((line, index) => new SimpleReplElement(line, severity, index === 0 ? source : undefined));
|
||||
if (previousElement && previousElement.value === '') {
|
||||
// remove potential empty lines between different repl types
|
||||
this.replElements.pop();
|
||||
} else if (previousElement instanceof SimpleReplElement && severity === previousElement.severity && toAdd.length && toAdd[0].sourceData === previousElement.sourceData) {
|
||||
previousElement.value += toAdd.shift().value;
|
||||
}
|
||||
this.addReplElements(toAdd);
|
||||
} else {
|
||||
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
|
||||
(<any>data).severity = severity;
|
||||
(<any>data).sourceData = source;
|
||||
this.addReplElements([data]);
|
||||
}
|
||||
|
||||
this._onDidChangeREPLElements.fire();
|
||||
}
|
||||
|
||||
private addReplElements(newElements: IReplElement[]): void {
|
||||
this.replElements.push(...newElements);
|
||||
if (this.replElements.length > MAX_REPL_LENGTH) {
|
||||
this.replElements.splice(0, this.replElements.length - MAX_REPL_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
public removeReplExpressions(): void {
|
||||
if (this.replElements.length > 0) {
|
||||
this.replElements = [];
|
||||
this._onDidChangeREPLElements.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public getWatchExpressions(): Expression[] {
|
||||
return this.watchExpressions;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { isUndefinedOrNull, isObject } from 'vs/base/common/types';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const MAX_REPL_LENGTH = 10000;
|
||||
|
||||
export class ReplModel {
|
||||
private replElements: IReplElement[] = [];
|
||||
|
||||
constructor(private session: IDebugSession) { }
|
||||
|
||||
public getReplElements(): ReadonlyArray<IReplElement> {
|
||||
return this.replElements;
|
||||
}
|
||||
|
||||
public addReplExpression(stackFrame: IStackFrame, name: string): TPromise<void> {
|
||||
const expression = new Expression(name);
|
||||
this.addReplElements([expression]);
|
||||
return expression.evaluate(this.session, stackFrame, 'repl');
|
||||
}
|
||||
|
||||
public appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void {
|
||||
const clearAnsiSequence = '\u001b[2J';
|
||||
if (typeof data === 'string' && data.indexOf(clearAnsiSequence) >= 0) {
|
||||
// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
|
||||
this.removeReplExpressions();
|
||||
this.appendToRepl(nls.localize('consoleCleared', "Console was cleared"), severity.Ignore);
|
||||
data = data.substr(data.lastIndexOf(clearAnsiSequence) + clearAnsiSequence.length);
|
||||
}
|
||||
|
||||
if (typeof data === 'string') {
|
||||
const previousElement = this.replElements.length && (this.replElements[this.replElements.length - 1] as SimpleReplElement);
|
||||
|
||||
const toAdd = data.split('\n').map((line, index) => new SimpleReplElement(line, sev, index === 0 ? source : undefined));
|
||||
if (previousElement && previousElement.value === '') {
|
||||
// remove potential empty lines between different repl types
|
||||
this.replElements.pop();
|
||||
} else if (previousElement instanceof SimpleReplElement && sev === previousElement.severity && toAdd.length && toAdd[0].sourceData === previousElement.sourceData) {
|
||||
previousElement.value += toAdd.shift().value;
|
||||
}
|
||||
this.addReplElements(toAdd);
|
||||
} else {
|
||||
// TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression
|
||||
(<any>data).severity = sev;
|
||||
(<any>data).sourceData = source;
|
||||
this.addReplElements([data]);
|
||||
}
|
||||
}
|
||||
|
||||
private addReplElements(newElements: IReplElement[]): void {
|
||||
this.replElements.push(...newElements);
|
||||
if (this.replElements.length > MAX_REPL_LENGTH) {
|
||||
this.replElements.splice(0, this.replElements.length - MAX_REPL_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
public logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) {
|
||||
|
||||
let source: IReplElementSource;
|
||||
if (frame) {
|
||||
source = {
|
||||
column: frame.column,
|
||||
lineNumber: frame.line,
|
||||
source: this.session.getSource({
|
||||
name: basenameOrAuthority(frame.uri),
|
||||
path: frame.uri.fsPath
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// add output for each argument logged
|
||||
let simpleVals: any[] = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
let a = args[i];
|
||||
|
||||
// undefined gets printed as 'undefined'
|
||||
if (typeof a === 'undefined') {
|
||||
simpleVals.push('undefined');
|
||||
}
|
||||
|
||||
// null gets printed as 'null'
|
||||
else if (a === null) {
|
||||
simpleVals.push('null');
|
||||
}
|
||||
|
||||
// objects & arrays are special because we want to inspect them in the REPL
|
||||
else if (isObject(a) || Array.isArray(a)) {
|
||||
|
||||
// flush any existing simple values logged
|
||||
if (simpleVals.length) {
|
||||
this.appendToRepl(simpleVals.join(' '), sev, source);
|
||||
simpleVals = [];
|
||||
}
|
||||
|
||||
// show object
|
||||
this.appendToRepl(new RawObjectReplElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source);
|
||||
}
|
||||
|
||||
// string: watch out for % replacement directive
|
||||
// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
|
||||
else if (typeof a === 'string') {
|
||||
let buf = '';
|
||||
|
||||
for (let j = 0, len = a.length; j < len; j++) {
|
||||
if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd' || a[j + 1] === 'O')) {
|
||||
i++; // read over substitution
|
||||
buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
|
||||
j++; // read over directive
|
||||
} else {
|
||||
buf += a[j];
|
||||
}
|
||||
}
|
||||
|
||||
simpleVals.push(buf);
|
||||
}
|
||||
|
||||
// number or boolean is joined together
|
||||
else {
|
||||
simpleVals.push(a);
|
||||
}
|
||||
}
|
||||
|
||||
// flush simple values
|
||||
// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
|
||||
if (simpleVals.length) {
|
||||
this.appendToRepl(simpleVals.join(' ') + '\n', sev, source);
|
||||
}
|
||||
}
|
||||
|
||||
removeReplExpressions(): void {
|
||||
if (this.replElements.length > 0) {
|
||||
this.replElements = [];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { first, distinct } from 'vs/base/common/arrays';
|
||||
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -21,7 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
|
||||
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';
|
||||
import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
|
||||
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
|
||||
@@ -46,7 +44,7 @@ import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { deepClone, equals } from 'vs/base/common/objects';
|
||||
import { DebugSession } from 'vs/workbench/parts/debug/electron-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, IReplElementSource, IEnablement, IBreakpoint, IBreakpointData, IExpression, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug';
|
||||
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 } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
@@ -158,7 +156,14 @@ export class DebugService implements IDebugService {
|
||||
|
||||
case EXTENSION_LOG_BROADCAST_CHANNEL:
|
||||
// extension logged output -> show it in REPL
|
||||
this.addToRepl(session, broadcast.payload.logEntry);
|
||||
const extensionOutput = <IRemoteConsoleLog>broadcast.payload.logEntry;
|
||||
const sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;
|
||||
const { args, stack } = parse(extensionOutput);
|
||||
let frame = undefined;
|
||||
if (stack) {
|
||||
frame = getFirstFrame(stack);
|
||||
}
|
||||
session.logToRepl(sev, args, frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -266,9 +271,7 @@ export class DebugService implements IDebugService {
|
||||
this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() =>
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
|
||||
// If it is the very first start debugging we need to clear the repl and our sessions map
|
||||
if (this.model.getSessions().length === 0) {
|
||||
this.removeReplExpressions();
|
||||
this.allSessions.clear();
|
||||
}
|
||||
|
||||
@@ -468,7 +471,7 @@ export class DebugService implements IDebugService {
|
||||
}
|
||||
|
||||
// Show the repl if some error got logged there #5870
|
||||
if (this.model.getReplElements().length > 0) {
|
||||
if (session && session.getReplElements().length > 0) {
|
||||
this.panelService.openPanel(REPL_ID, false);
|
||||
}
|
||||
|
||||
@@ -784,109 +787,6 @@ export class DebugService implements IDebugService {
|
||||
this.viewModel.setFocus(stackFrame, thread, session, explicit);
|
||||
}
|
||||
|
||||
//---- REPL
|
||||
|
||||
addReplExpression(name: string): TPromise<void> {
|
||||
return this.model.addReplExpression(this.viewModel.focusedSession, this.viewModel.focusedStackFrame, name)
|
||||
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
|
||||
.then(() => this.focusStackFrame(this.viewModel.focusedStackFrame, this.viewModel.focusedThread, this.viewModel.focusedSession));
|
||||
}
|
||||
|
||||
removeReplExpressions(): void {
|
||||
this.model.removeReplExpressions();
|
||||
}
|
||||
|
||||
private addToRepl(session: IDebugSession, extensionOutput: IRemoteConsoleLog) {
|
||||
|
||||
let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;
|
||||
|
||||
const { args, stack } = parse(extensionOutput);
|
||||
let source: IReplElementSource;
|
||||
if (stack) {
|
||||
const frame = getFirstFrame(stack);
|
||||
if (frame) {
|
||||
source = {
|
||||
column: frame.column,
|
||||
lineNumber: frame.line,
|
||||
source: session.getSource({
|
||||
name: resources.basenameOrAuthority(frame.uri),
|
||||
path: frame.uri.fsPath
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// add output for each argument logged
|
||||
let simpleVals: any[] = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
let a = args[i];
|
||||
|
||||
// undefined gets printed as 'undefined'
|
||||
if (typeof a === 'undefined') {
|
||||
simpleVals.push('undefined');
|
||||
}
|
||||
|
||||
// null gets printed as 'null'
|
||||
else if (a === null) {
|
||||
simpleVals.push('null');
|
||||
}
|
||||
|
||||
// objects & arrays are special because we want to inspect them in the REPL
|
||||
else if (isObject(a) || Array.isArray(a)) {
|
||||
|
||||
// flush any existing simple values logged
|
||||
if (simpleVals.length) {
|
||||
this.logToRepl(simpleVals.join(' '), sev, source);
|
||||
simpleVals = [];
|
||||
}
|
||||
|
||||
// show object
|
||||
this.logToRepl(new RawObjectReplElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source);
|
||||
}
|
||||
|
||||
// string: watch out for % replacement directive
|
||||
// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
|
||||
else if (typeof a === 'string') {
|
||||
let buf = '';
|
||||
|
||||
for (let j = 0, len = a.length; j < len; j++) {
|
||||
if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd' || a[j + 1] === 'O')) {
|
||||
i++; // read over substitution
|
||||
buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
|
||||
j++; // read over directive
|
||||
} else {
|
||||
buf += a[j];
|
||||
}
|
||||
}
|
||||
|
||||
simpleVals.push(buf);
|
||||
}
|
||||
|
||||
// number or boolean is joined together
|
||||
else {
|
||||
simpleVals.push(a);
|
||||
}
|
||||
}
|
||||
|
||||
// flush simple values
|
||||
// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
|
||||
if (simpleVals.length) {
|
||||
this.logToRepl(simpleVals.join(' ') + '\n', sev, source);
|
||||
}
|
||||
}
|
||||
|
||||
logToRepl(value: string | IExpression, sev = severity.Info, source?: IReplElementSource): void {
|
||||
const clearAnsiSequence = '\u001b[2J';
|
||||
if (typeof value === 'string' && value.indexOf(clearAnsiSequence) >= 0) {
|
||||
// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
|
||||
this.model.removeReplExpressions();
|
||||
this.model.appendToRepl(nls.localize('consoleCleared', "Console was cleared"), severity.Ignore);
|
||||
value = value.substr(value.lastIndexOf(clearAnsiSequence) + clearAnsiSequence.length);
|
||||
}
|
||||
|
||||
this.model.appendToRepl(value, sev, source);
|
||||
}
|
||||
|
||||
//---- watches
|
||||
|
||||
addWatchExpression(name: string): void {
|
||||
|
||||
@@ -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, VIEWLET_ID, IDebugConfiguration } 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, IReplElement, IStackFrame, IExpression, IReplElementSource } 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';
|
||||
@@ -31,9 +31,9 @@ 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';
|
||||
import { ReplModel } from 'vs/workbench/parts/debug/common/replModel';
|
||||
|
||||
export class DebugSession implements IDebugSession {
|
||||
|
||||
private id: string;
|
||||
private raw: RawDebugSession;
|
||||
|
||||
@@ -41,6 +41,7 @@ export class DebugSession implements IDebugSession {
|
||||
private threads = new Map<number, Thread>();
|
||||
private rawListeners: IDisposable[] = [];
|
||||
private fetchThreadsScheduler: RunOnceScheduler;
|
||||
private repl: ReplModel;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
private readonly _onDidEndAdapter = new Emitter<AdapterEndEvent>();
|
||||
@@ -48,6 +49,7 @@ export class DebugSession implements IDebugSession {
|
||||
private readonly _onDidLoadedSource = new Emitter<LoadedSourceEvent>();
|
||||
private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();
|
||||
|
||||
private readonly _onDidChangeREPLElements = new Emitter<void>();
|
||||
|
||||
constructor(
|
||||
private _configuration: { resolved: IConfig, unresolved: IConfig },
|
||||
@@ -62,6 +64,7 @@ export class DebugSession implements IDebugSession {
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
this.id = generateUuid();
|
||||
this.repl = new ReplModel(this);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
@@ -109,6 +112,10 @@ export class DebugSession implements IDebugSession {
|
||||
return this._onDidEndAdapter.event;
|
||||
}
|
||||
|
||||
get onDidChangeReplElements(): Event<void> {
|
||||
return this._onDidChangeREPLElements.event;
|
||||
}
|
||||
|
||||
//---- DAP events
|
||||
|
||||
get onDidCustomEvent(): Event<DebugProtocol.Event> {
|
||||
@@ -119,7 +126,6 @@ export class DebugSession implements IDebugSession {
|
||||
return this._onDidLoadedSource.event;
|
||||
}
|
||||
|
||||
|
||||
//---- DAP requests
|
||||
|
||||
/**
|
||||
@@ -673,11 +679,11 @@ export class DebugSession implements IDebugSession {
|
||||
return Promise.all(waitFor).then(() => children.forEach(child => {
|
||||
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
|
||||
child.name = null;
|
||||
this.debugService.logToRepl(child, outputSeverity, source);
|
||||
this.appendToRepl(child, outputSeverity, source);
|
||||
}));
|
||||
}));
|
||||
} else if (typeof event.body.output === 'string') {
|
||||
Promise.all(waitFor).then(() => this.debugService.logToRepl(event.body.output, outputSeverity, source));
|
||||
Promise.all(waitFor).then(() => this.appendToRepl(event.body.output, outputSeverity, source));
|
||||
}
|
||||
Promise.all(outputPromises).then(() => outputPromises = []);
|
||||
}));
|
||||
@@ -775,4 +781,33 @@ export class DebugSession implements IDebugSession {
|
||||
private getUriKey(uri: URI): string {
|
||||
return platform.isLinux ? uri.toString() : uri.toString().toLowerCase();
|
||||
}
|
||||
|
||||
// REPL
|
||||
|
||||
getReplElements(): ReadonlyArray<IReplElement> {
|
||||
return this.repl.getReplElements();
|
||||
}
|
||||
|
||||
removeReplExpressions(): void {
|
||||
this.repl.removeReplExpressions();
|
||||
this._onDidChangeREPLElements.fire();
|
||||
}
|
||||
|
||||
addReplExpression(stackFrame: IStackFrame, name: string): TPromise<void> {
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
return this.repl.addReplExpression(stackFrame, name)
|
||||
.then(() => this._onDidChangeREPLElements.fire())
|
||||
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
|
||||
.then(() => this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession));
|
||||
}
|
||||
|
||||
appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void {
|
||||
this.repl.appendToRepl(data, severity, source);
|
||||
this._onDidChangeREPLElements.fire();
|
||||
}
|
||||
|
||||
logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) {
|
||||
this.repl.logToRepl(sev, args, frame);
|
||||
this._onDidChangeREPLElements.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IAction, IActionItem } from 'vs/base/common/actions';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
@@ -29,7 +29,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer';
|
||||
import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { ClearReplAction, FocusSessionAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { Panel } from 'vs/workbench/browser/panel';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -37,7 +37,7 @@ import { clipboard } from 'electron';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
@@ -48,6 +48,10 @@ import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions';
|
||||
import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { transparent, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { FocusSessionActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -58,6 +62,7 @@ const replTreeOptions: ITreeOptions = {
|
||||
|
||||
const HISTORY_STORAGE_KEY = 'debug.repl.history';
|
||||
const IPrivateReplService = createDecorator<IPrivateReplService>('privateReplService');
|
||||
const DECORATION_KEY = 'replinputdecoration';
|
||||
|
||||
export interface IPrivateReplService {
|
||||
_serviceBrand: any;
|
||||
@@ -82,7 +87,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
private replInput: CodeEditorWidget;
|
||||
private replInputContainer: HTMLElement;
|
||||
private refreshTimeoutHandle: any;
|
||||
private actions: IAction[];
|
||||
private dimension: dom.Dimension;
|
||||
private replInputHeight: number;
|
||||
private model: ITextModel;
|
||||
@@ -96,20 +100,38 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
@IPanelService private panelService: IPanelService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IModelService private modelService: IModelService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
) {
|
||||
super(REPL_ID, telemetryService, themeService);
|
||||
|
||||
this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT;
|
||||
this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50);
|
||||
this.registerListeners();
|
||||
codeEditorService.registerDecorationType(DECORATION_KEY, {});
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.debugService.getModel().onDidChangeReplElements(() => {
|
||||
this.refreshReplElements(this.debugService.getModel().getReplElements().length === 0);
|
||||
let replElementsChangeListener: IDisposable;
|
||||
this._register(this.debugService.getViewModel().onDidFocusSession(session => {
|
||||
if (replElementsChangeListener) {
|
||||
replElementsChangeListener.dispose();
|
||||
}
|
||||
if (session) {
|
||||
replElementsChangeListener = session.onDidChangeReplElements(() => {
|
||||
this.refreshReplElements(session.getReplElements().length === 0);
|
||||
});
|
||||
|
||||
if (this.tree && this.isVisible()) {
|
||||
this.tree.setInput(session);
|
||||
}
|
||||
}
|
||||
this.replInput.updateOptions({ readOnly: !session });
|
||||
this.updateInputDecoration();
|
||||
}));
|
||||
this._register(this.panelService.onDidPanelOpen(panel => this.refreshReplElements(true)));
|
||||
this._register(this.debugService.onDidNewSession(() => this.updateTitleArea()));
|
||||
this._register(this.themeService.onThemeChange(() => this.updateInputDecoration()));
|
||||
}
|
||||
|
||||
private refreshReplElements(noDelay: boolean): void {
|
||||
@@ -148,8 +170,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
accessibilityProvider: new ReplExpressionsAccessibilityProvider(),
|
||||
controller
|
||||
}, replTreeOptions);
|
||||
|
||||
await this.tree.setInput(this.debugService.getModel());
|
||||
}
|
||||
|
||||
public setVisible(visible: boolean): Promise<void> {
|
||||
@@ -158,6 +178,11 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
} else {
|
||||
this.model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:replinput`), true);
|
||||
this.replInput.setModel(this.model);
|
||||
this.updateInputDecoration();
|
||||
const focusedSession = this.debugService.getViewModel().focusedSession;
|
||||
if (focusedSession && this.tree.getInput() !== focusedSession) {
|
||||
this.tree.setInput(focusedSession);
|
||||
}
|
||||
}
|
||||
|
||||
return super.setVisible(visible);
|
||||
@@ -173,7 +198,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, scopedContextKeyService], [IPrivateReplService, this]));
|
||||
this.replInput = scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, getSimpleEditorOptions(), getSimpleCodeEditorWidgetOptions());
|
||||
const options = getSimpleEditorOptions();
|
||||
options.readOnly = true;
|
||||
this.replInput = scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, options, getSimpleCodeEditorWidgetOptions());
|
||||
|
||||
modes.CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, {
|
||||
triggerCharacters: ['.'],
|
||||
@@ -235,12 +262,16 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
}
|
||||
|
||||
public acceptReplInput(): void {
|
||||
this.debugService.addReplExpression(this.replInput.getValue());
|
||||
this.history.add(this.replInput.getValue());
|
||||
this.replInput.setValue('');
|
||||
// Trigger a layout to shrink a potential multi line input
|
||||
this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT;
|
||||
this.layout(this.dimension);
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
const session = viewModel.focusedSession;
|
||||
if (session) {
|
||||
session.addReplExpression(viewModel.focusedStackFrame, this.replInput.getValue());
|
||||
this.history.add(this.replInput.getValue());
|
||||
this.replInput.setValue('');
|
||||
// Trigger a layout to shrink a potential multi line input
|
||||
this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT;
|
||||
this.layout(this.dimension);
|
||||
}
|
||||
}
|
||||
|
||||
public getVisibleContent(): string {
|
||||
@@ -286,16 +317,24 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
this.replInput.focus();
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
if (!this.actions) {
|
||||
this.actions = [
|
||||
this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL)
|
||||
];
|
||||
|
||||
this.actions.forEach(a => this._register(a));
|
||||
getActionItem(action: IAction): IActionItem {
|
||||
if (action.id === FocusSessionAction.ID) {
|
||||
return this.focusSessionActionItem;
|
||||
}
|
||||
|
||||
return this.actions;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
const result: IAction[] = [];
|
||||
if (this.debugService.getModel().getSessions().length > 1) {
|
||||
result.push(this.focusSessionAction);
|
||||
}
|
||||
result.push(this.clearReplAction);
|
||||
|
||||
result.forEach(a => this._register(a));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
@@ -307,6 +346,47 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
}
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get focusSessionAction(): FocusSessionAction {
|
||||
return this.instantiationService.createInstance(FocusSessionAction, FocusSessionAction.ID, FocusSessionAction.LABEL);
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get clearReplAction(): ClearReplAction {
|
||||
return this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL);
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get focusSessionActionItem(): FocusSessionActionItem {
|
||||
return this.instantiationService.createInstance(FocusSessionActionItem, this.focusSessionAction);
|
||||
}
|
||||
|
||||
private updateInputDecoration(): void {
|
||||
if (!this.replInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decorations: IDecorationOptions[] = [];
|
||||
if (!this.debugService.getViewModel().focusedSession) {
|
||||
decorations.push({
|
||||
range: {
|
||||
startLineNumber: 0,
|
||||
endLineNumber: 0,
|
||||
startColumn: 0,
|
||||
endColumn: 1
|
||||
},
|
||||
renderOptions: {
|
||||
after: {
|
||||
contentText: nls.localize('startDebugFirst', "Please start a debug session to evaluate"),
|
||||
color: transparent(editorForeground, 0.4)(this.themeService.getTheme()).toString()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.replInput.setDecorations(DECORATION_KEY, decorations);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.replInput.dispose();
|
||||
super.dispose();
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { DebugModel, RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { RawObjectReplElement, Expression, SimpleReplElement, Variable } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/browser/baseDebugView';
|
||||
import { ClearReplAction, ReplCollapseAllAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions';
|
||||
@@ -24,6 +24,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector';
|
||||
import { handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSession';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -34,11 +35,11 @@ export class ReplExpressionsDataSource implements IDataSource {
|
||||
}
|
||||
|
||||
public hasChildren(tree: ITree, element: any): boolean {
|
||||
return element instanceof DebugModel || (<IExpressionContainer>element).hasChildren;
|
||||
return element instanceof DebugSession || (<IExpressionContainer>element).hasChildren;
|
||||
}
|
||||
|
||||
public getChildren(tree: ITree, element: any): TPromise<any> {
|
||||
if (element instanceof DebugModel) {
|
||||
if (element instanceof DebugSession) {
|
||||
return Promise.resolve(element.getReplElements());
|
||||
}
|
||||
if (element instanceof RawObjectReplElement) {
|
||||
|
||||
@@ -8,9 +8,10 @@ import { Event } from 'vs/base/common/event';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, ActualBreakpoints, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, ActualBreakpoints, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export class MockDebugService implements IDebugService {
|
||||
|
||||
@@ -117,7 +118,7 @@ export class MockDebugService implements IDebugService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public logToRepl(value: string): void { }
|
||||
public logToRepl(session: IDebugSession, value: string): void { }
|
||||
|
||||
public sourceIsNotAvailable(uri: uri): void { }
|
||||
|
||||
@@ -127,6 +128,21 @@ export class MockDebugService implements IDebugService {
|
||||
}
|
||||
|
||||
export class MockSession implements IDebugSession {
|
||||
getReplElements(): ReadonlyArray<IReplElement> {
|
||||
return [];
|
||||
}
|
||||
|
||||
removeReplExpressions(): void { }
|
||||
get onDidChangeReplElements(): Event<void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
addReplExpression(stackFrame: IStackFrame, name: string): TPromise<void> {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
appendToRepl(data: string | IExpression, severity: Severity, source?: IReplElementSource): void { }
|
||||
logToRepl(sev: Severity, args: any[], frame?: { uri: uri; line: number; column: number; }) { }
|
||||
|
||||
configuration: IConfig = { type: 'mock', request: 'launch' };
|
||||
unresolvedConfiguration: IConfig = { type: 'mock', request: 'launch' };
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as sinon from 'sinon';
|
||||
import { MockRawSession } from 'vs/workbench/parts/debug/test/common/mockDebug';
|
||||
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
|
||||
import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSession';
|
||||
import { ReplModel } from 'vs/workbench/parts/debug/common/replModel';
|
||||
|
||||
suite('Debug - Model', () => {
|
||||
let model: DebugModel;
|
||||
@@ -132,8 +133,6 @@ suite('Debug - Model', () => {
|
||||
});
|
||||
|
||||
test('threads multiple wtih allThreadsStopped', () => {
|
||||
const sessionStub = sinon.spy(rawSession, 'stackTrace');
|
||||
|
||||
const threadId1 = 1;
|
||||
const threadName1 = 'firstThread';
|
||||
const threadId2 = 2;
|
||||
@@ -193,22 +192,16 @@ suite('Debug - Model', () => {
|
||||
// and results in a request for the callstack in the debug adapter
|
||||
thread1.fetchCallStack().then(() => {
|
||||
assert.notEqual(thread1.getCallStack().length, 0);
|
||||
assert.equal(thread2.getCallStack().length, 0);
|
||||
assert.equal(sessionStub.callCount, 1);
|
||||
});
|
||||
|
||||
thread2.fetchCallStack().then(() => {
|
||||
assert.notEqual(thread1.getCallStack().length, 0);
|
||||
assert.notEqual(thread2.getCallStack().length, 0);
|
||||
assert.equal(sessionStub.callCount, 2);
|
||||
});
|
||||
|
||||
// calling multiple times getCallStack doesn't result in multiple calls
|
||||
// to the debug adapter
|
||||
thread1.fetchCallStack().then(() => {
|
||||
return thread2.fetchCallStack();
|
||||
}).then(() => {
|
||||
assert.equal(sessionStub.callCount, 4);
|
||||
});
|
||||
|
||||
// clearing the callstack results in the callstack not being available
|
||||
@@ -347,26 +340,27 @@ 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, undefined, undefined, undefined);
|
||||
assert.equal(session.getReplElements().length, 0);
|
||||
model.addSession(session);
|
||||
|
||||
session['raw'] = <any>rawSession;
|
||||
const thread = new Thread(session, 'mockthread', 1);
|
||||
const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
|
||||
model.addReplExpression(session, stackFrame, 'myVariable').then();
|
||||
model.addReplExpression(session, stackFrame, 'myVariable').then();
|
||||
model.addReplExpression(session, stackFrame, 'myVariable').then();
|
||||
const replModel = new ReplModel(session);
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
|
||||
assert.equal(model.getReplElements().length, 3);
|
||||
model.getReplElements().forEach(re => {
|
||||
assert.equal(replModel.getReplElements().length, 3);
|
||||
replModel.getReplElements().forEach(re => {
|
||||
assert.equal((<Expression>re).available, false);
|
||||
assert.equal((<Expression>re).name, 'myVariable');
|
||||
assert.equal((<Expression>re).reference, 0);
|
||||
});
|
||||
|
||||
model.removeReplExpressions();
|
||||
assert.equal(model.getReplElements().length, 0);
|
||||
replModel.removeReplExpressions();
|
||||
assert.equal(replModel.getReplElements().length, 0);
|
||||
});
|
||||
|
||||
test('stack frame get specific source name', () => {
|
||||
@@ -401,12 +395,14 @@ suite('Debug - Model', () => {
|
||||
// Repl output
|
||||
|
||||
test('repl output', () => {
|
||||
model.appendToRepl('first line\n', severity.Error);
|
||||
model.appendToRepl('second line', severity.Error);
|
||||
model.appendToRepl('third line', severity.Warning);
|
||||
model.appendToRepl('fourth line', severity.Error);
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
const repl = new ReplModel(session);
|
||||
repl.appendToRepl('first line\n', severity.Error);
|
||||
repl.appendToRepl('second line', severity.Error);
|
||||
repl.appendToRepl('third line', severity.Warning);
|
||||
repl.appendToRepl('fourth line', severity.Error);
|
||||
|
||||
let elements = <SimpleReplElement[]>model.getReplElements();
|
||||
let elements = <SimpleReplElement[]>repl.getReplElements();
|
||||
assert.equal(elements.length, 4);
|
||||
assert.equal(elements[0].value, 'first line');
|
||||
assert.equal(elements[0].severity, severity.Error);
|
||||
@@ -417,19 +413,19 @@ suite('Debug - Model', () => {
|
||||
assert.equal(elements[3].value, 'fourth line');
|
||||
assert.equal(elements[3].severity, severity.Error);
|
||||
|
||||
model.appendToRepl('1', severity.Warning);
|
||||
elements = <SimpleReplElement[]>model.getReplElements();
|
||||
repl.appendToRepl('1', severity.Warning);
|
||||
elements = <SimpleReplElement[]>repl.getReplElements();
|
||||
assert.equal(elements.length, 5);
|
||||
assert.equal(elements[4].value, '1');
|
||||
assert.equal(elements[4].severity, severity.Warning);
|
||||
|
||||
const keyValueObject = { 'key1': 2, 'key2': 'value' };
|
||||
model.appendToRepl(new RawObjectReplElement('fake', keyValueObject), null);
|
||||
const element = <RawObjectReplElement>model.getReplElements()[5];
|
||||
repl.appendToRepl(new RawObjectReplElement('fake', keyValueObject), null);
|
||||
const element = <RawObjectReplElement>repl.getReplElements()[5];
|
||||
assert.equal(element.value, 'Object');
|
||||
assert.deepEqual(element.valueObj, keyValueObject);
|
||||
|
||||
model.removeReplExpressions();
|
||||
assert.equal(model.getReplElements().length, 0);
|
||||
repl.removeReplExpressions();
|
||||
assert.equal(repl.getReplElements().length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user