mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-08 17:24:01 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
834
src/vs/workbench/contrib/debug/common/debug.ts
Normal file
834
src/vs/workbench/contrib/debug/common/debug.ts
Normal file
@@ -0,0 +1,834 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
|
||||
import { IEditor } from 'vs/workbench/common/editor';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.debug';
|
||||
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID);
|
||||
|
||||
export const VARIABLES_VIEW_ID = 'workbench.debug.variablesView';
|
||||
export const WATCH_VIEW_ID = 'workbench.debug.watchExpressionsView';
|
||||
export const CALLSTACK_VIEW_ID = 'workbench.debug.callStackView';
|
||||
export const LOADED_SCRIPTS_VIEW_ID = 'workbench.debug.loadedScriptsView';
|
||||
export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView';
|
||||
export const REPL_ID = 'workbench.panel.repl';
|
||||
export const DEBUG_SERVICE_ID = 'debugService';
|
||||
export const CONTEXT_DEBUG_TYPE = new RawContextKey<string>('debugType', undefined);
|
||||
export const CONTEXT_DEBUG_CONFIGURATION_TYPE = new RawContextKey<string>('debugConfigurationType', undefined);
|
||||
export const CONTEXT_DEBUG_STATE = new RawContextKey<string>('debugState', 'inactive');
|
||||
export const CONTEXT_IN_DEBUG_MODE = new RawContextKey<boolean>('inDebugMode', false);
|
||||
export const CONTEXT_IN_DEBUG_REPL = new RawContextKey<boolean>('inDebugRepl', false);
|
||||
export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('breakpointWidgetVisible', false);
|
||||
export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey<boolean>('inBreakpointWidget', false);
|
||||
export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', true);
|
||||
export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', true);
|
||||
export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', true);
|
||||
export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey<boolean>('expressionSelected', false);
|
||||
export const CONTEXT_BREAKPOINT_SELECTED = new RawContextKey<boolean>('breakpointSelected', false);
|
||||
export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey<string>('callStackItemType', undefined);
|
||||
export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey<boolean>('loadedScriptsSupported', false);
|
||||
export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey<string>('loadedScriptsItemType', undefined);
|
||||
export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey<boolean>('focusedSessionIsAttach', false);
|
||||
export const CONTEXT_STEP_BACK_SUPPORTED = new RawContextKey<boolean>('stepBackSupported', false);
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const DEBUG_SCHEME = 'debug';
|
||||
export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {
|
||||
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
|
||||
default: 'openOnFirstSessionStart',
|
||||
description: nls.localize('internalConsoleOptions', "Controls when the internal debug console should open.")
|
||||
};
|
||||
|
||||
// raw
|
||||
|
||||
export interface IRawModelUpdate {
|
||||
sessionId: string;
|
||||
threads: DebugProtocol.Thread[];
|
||||
stoppedDetails?: IRawStoppedDetails;
|
||||
}
|
||||
|
||||
export interface IRawStoppedDetails {
|
||||
reason?: string;
|
||||
description?: string;
|
||||
threadId?: number;
|
||||
text?: string;
|
||||
totalFrames?: number;
|
||||
allThreadsStopped?: boolean;
|
||||
framesErrorMessage?: string;
|
||||
}
|
||||
|
||||
// model
|
||||
|
||||
export interface ITreeElement {
|
||||
getId(): string;
|
||||
}
|
||||
|
||||
export interface IReplElement extends ITreeElement {
|
||||
toString(): string;
|
||||
readonly sourceData?: IReplElementSource;
|
||||
}
|
||||
|
||||
export interface IReplElementSource {
|
||||
readonly source: Source;
|
||||
readonly lineNumber: number;
|
||||
readonly column: number;
|
||||
}
|
||||
|
||||
export interface IExpressionContainer extends ITreeElement {
|
||||
readonly hasChildren: boolean;
|
||||
getChildren(): Promise<IExpression[]>;
|
||||
}
|
||||
|
||||
export interface IExpression extends IReplElement, IExpressionContainer {
|
||||
name: string;
|
||||
readonly value: string;
|
||||
valueChanged?: boolean;
|
||||
readonly type?: string;
|
||||
}
|
||||
|
||||
export interface IDebugger {
|
||||
createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise<IDebugAdapter>;
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined>;
|
||||
getCustomTelemetryService(): Promise<TelemetryService | undefined>;
|
||||
}
|
||||
|
||||
export const enum State {
|
||||
Inactive,
|
||||
Initializing,
|
||||
Stopped,
|
||||
Running
|
||||
}
|
||||
|
||||
export function getStateLabel(state: State): string {
|
||||
switch (state) {
|
||||
case State.Initializing: return 'initializing';
|
||||
case State.Stopped: return 'stopped';
|
||||
case State.Running: return 'running';
|
||||
default: return 'inactive';
|
||||
}
|
||||
}
|
||||
|
||||
export class AdapterEndEvent {
|
||||
error?: Error;
|
||||
sessionLengthInSeconds: number;
|
||||
emittedStopped: boolean;
|
||||
}
|
||||
|
||||
export interface LoadedSourceEvent {
|
||||
reason: 'new' | 'changed' | 'removed';
|
||||
source: Source;
|
||||
}
|
||||
|
||||
export interface IDebugSession extends ITreeElement {
|
||||
|
||||
readonly configuration: IConfig;
|
||||
readonly unresolvedConfiguration: IConfig | undefined;
|
||||
readonly state: State;
|
||||
readonly root: IWorkspaceFolder;
|
||||
|
||||
getLabel(): string;
|
||||
|
||||
getSourceForUri(modelUri: uri): Source | undefined;
|
||||
getSource(raw?: DebugProtocol.Source): Source;
|
||||
|
||||
setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }): void;
|
||||
rawUpdate(data: IRawModelUpdate): void;
|
||||
|
||||
getThread(threadId: number): IThread | undefined;
|
||||
getAllThreads(): IThread[];
|
||||
clearThreads(removeThreads: boolean, reference?: number): void;
|
||||
|
||||
getReplElements(): IReplElement[];
|
||||
|
||||
removeReplExpressions(): void;
|
||||
addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<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;
|
||||
|
||||
// DAP events
|
||||
|
||||
readonly onDidLoadedSource: Event<LoadedSourceEvent>;
|
||||
readonly onDidCustomEvent: Event<DebugProtocol.Event>;
|
||||
|
||||
// Disconnects and clears state. Session can be initialized again for a new connection.
|
||||
shutdown(): void;
|
||||
|
||||
// DAP request
|
||||
|
||||
initialize(dbgr: IDebugger): Promise<void>;
|
||||
launchOrAttach(config: IConfig): Promise<void>;
|
||||
restart(): Promise<void>;
|
||||
terminate(restart?: boolean /* false */): Promise<void>;
|
||||
disconnect(restart?: boolean /* false */): Promise<void>;
|
||||
|
||||
sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise<void>;
|
||||
sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void>;
|
||||
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void>;
|
||||
|
||||
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>;
|
||||
evaluate(expression: string, frameId?: number, context?: string): Promise<DebugProtocol.EvaluateResponse>;
|
||||
customRequest(request: string, args: any): Promise<DebugProtocol.Response>;
|
||||
|
||||
restartFrame(frameId: number, threadId: number): Promise<void>;
|
||||
next(threadId: number): Promise<void>;
|
||||
stepIn(threadId: number): Promise<void>;
|
||||
stepOut(threadId: number): Promise<void>;
|
||||
stepBack(threadId: number): Promise<void>;
|
||||
continue(threadId: number): Promise<void>;
|
||||
reverseContinue(threadId: number): Promise<void>;
|
||||
pause(threadId: number): Promise<void>;
|
||||
terminateThreads(threadIds: number[]): Promise<void>;
|
||||
|
||||
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number): Promise<CompletionItem[]>;
|
||||
setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse>;
|
||||
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse>;
|
||||
getLoadedSources(): Promise<Source[]>;
|
||||
}
|
||||
|
||||
export interface IThread extends ITreeElement {
|
||||
|
||||
/**
|
||||
* Process the thread belongs to
|
||||
*/
|
||||
readonly session: IDebugSession;
|
||||
|
||||
/**
|
||||
* Id of the thread generated by the debug adapter backend.
|
||||
*/
|
||||
readonly threadId: number;
|
||||
|
||||
/**
|
||||
* Name of the thread.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Information about the current thread stop event. Undefined if thread is not stopped.
|
||||
*/
|
||||
readonly stoppedDetails: IRawStoppedDetails | undefined;
|
||||
|
||||
/**
|
||||
* Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise undefined.
|
||||
*/
|
||||
readonly exceptionInfo: Promise<IExceptionInfo | undefined>;
|
||||
|
||||
readonly stateLabel: string;
|
||||
|
||||
/**
|
||||
* Gets the callstack if it has already been received from the debug
|
||||
* adapter.
|
||||
*/
|
||||
getCallStack(): ReadonlyArray<IStackFrame>;
|
||||
|
||||
/**
|
||||
* Invalidates the callstack cache
|
||||
*/
|
||||
clearCallStack(): void;
|
||||
|
||||
/**
|
||||
* Indicates whether this thread is stopped. The callstack for stopped
|
||||
* threads can be retrieved from the debug adapter.
|
||||
*/
|
||||
readonly stopped: boolean;
|
||||
|
||||
next(): Promise<any>;
|
||||
stepIn(): Promise<any>;
|
||||
stepOut(): Promise<any>;
|
||||
stepBack(): Promise<any>;
|
||||
continue(): Promise<any>;
|
||||
pause(): Promise<any>;
|
||||
terminate(): Promise<any>;
|
||||
reverseContinue(): Promise<any>;
|
||||
}
|
||||
|
||||
export interface IScope extends IExpressionContainer {
|
||||
readonly name: string;
|
||||
readonly expensive: boolean;
|
||||
readonly range?: IRange;
|
||||
}
|
||||
|
||||
export interface IStackFrame extends ITreeElement {
|
||||
readonly thread: IThread;
|
||||
readonly name: string;
|
||||
readonly presentationHint: string | undefined;
|
||||
readonly frameId: number;
|
||||
readonly range: IRange;
|
||||
readonly source: Source;
|
||||
getScopes(): Promise<IScope[]>;
|
||||
getMostSpecificScopes(range: IRange): Promise<ReadonlyArray<IScope>>;
|
||||
getSpecificSourceName(): string;
|
||||
restart(): Promise<any>;
|
||||
toString(): string;
|
||||
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Promise<any>;
|
||||
}
|
||||
|
||||
export interface IEnablement extends ITreeElement {
|
||||
readonly enabled: boolean;
|
||||
}
|
||||
|
||||
export interface IBreakpointData {
|
||||
readonly id?: string;
|
||||
readonly lineNumber: number;
|
||||
readonly column?: number;
|
||||
readonly enabled?: boolean;
|
||||
readonly condition?: string;
|
||||
readonly logMessage?: string;
|
||||
readonly hitCondition?: string;
|
||||
}
|
||||
|
||||
export interface IBreakpointUpdateData {
|
||||
readonly condition?: string;
|
||||
readonly hitCondition?: string;
|
||||
readonly logMessage?: string;
|
||||
readonly lineNumber?: number;
|
||||
readonly column?: number;
|
||||
}
|
||||
|
||||
export interface IBaseBreakpoint extends IEnablement {
|
||||
readonly condition?: string;
|
||||
readonly hitCondition?: string;
|
||||
readonly logMessage?: string;
|
||||
readonly verified: boolean;
|
||||
readonly idFromAdapter: number | undefined;
|
||||
}
|
||||
|
||||
export interface IBreakpoint extends IBaseBreakpoint {
|
||||
readonly uri: uri;
|
||||
readonly lineNumber: number;
|
||||
readonly endLineNumber?: number;
|
||||
readonly column?: number;
|
||||
readonly endColumn?: number;
|
||||
readonly message?: string;
|
||||
readonly adapterData: any;
|
||||
}
|
||||
|
||||
export interface IFunctionBreakpoint extends IBaseBreakpoint {
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
export interface IExceptionBreakpoint extends IEnablement {
|
||||
readonly filter: string;
|
||||
readonly label: string;
|
||||
}
|
||||
|
||||
export interface IExceptionInfo {
|
||||
readonly id?: string;
|
||||
readonly description?: string;
|
||||
readonly breakMode: string | null;
|
||||
readonly details?: DebugProtocol.ExceptionDetails;
|
||||
}
|
||||
|
||||
// model interfaces
|
||||
|
||||
export interface IViewModel extends ITreeElement {
|
||||
/**
|
||||
* Returns the focused debug session or undefined if no session is stopped.
|
||||
*/
|
||||
readonly focusedSession: IDebugSession | undefined;
|
||||
|
||||
/**
|
||||
* Returns the focused thread or undefined if no thread is stopped.
|
||||
*/
|
||||
readonly focusedThread: IThread | undefined;
|
||||
|
||||
/**
|
||||
* Returns the focused stack frame or undefined if there are no stack frames.
|
||||
*/
|
||||
readonly focusedStackFrame: IStackFrame | undefined;
|
||||
|
||||
getSelectedExpression(): IExpression | undefined;
|
||||
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined;
|
||||
setSelectedExpression(expression: IExpression | undefined): void;
|
||||
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void;
|
||||
|
||||
isMultiSessionView(): boolean;
|
||||
|
||||
onDidFocusSession: Event<IDebugSession | undefined>;
|
||||
onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }>;
|
||||
onDidSelectExpression: Event<IExpression | undefined>;
|
||||
}
|
||||
|
||||
export interface IEvaluate {
|
||||
evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IDebugModel extends ITreeElement {
|
||||
getSessions(includeInactive?: boolean): IDebugSession[];
|
||||
getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number, enabledOnly?: boolean }): ReadonlyArray<IBreakpoint>;
|
||||
areBreakpointsActivated(): boolean;
|
||||
getFunctionBreakpoints(): ReadonlyArray<IFunctionBreakpoint>;
|
||||
getExceptionBreakpoints(): ReadonlyArray<IExceptionBreakpoint>;
|
||||
getWatchExpressions(): ReadonlyArray<IExpression & IEvaluate>;
|
||||
|
||||
onDidChangeBreakpoints: Event<IBreakpointsChangeEvent | undefined>;
|
||||
onDidChangeCallStack: Event<void>;
|
||||
onDidChangeWatchExpressions: Event<IExpression | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event describing a change to the set of [breakpoints](#debug.Breakpoint).
|
||||
*/
|
||||
export interface IBreakpointsChangeEvent {
|
||||
added?: Array<IBreakpoint | IFunctionBreakpoint>;
|
||||
removed?: Array<IBreakpoint | IFunctionBreakpoint>;
|
||||
changed?: Array<IBreakpoint | IFunctionBreakpoint>;
|
||||
sessionOnly?: boolean;
|
||||
}
|
||||
|
||||
// Debug configuration interfaces
|
||||
|
||||
export interface IDebugConfiguration {
|
||||
allowBreakpointsEverywhere: boolean;
|
||||
openDebug: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart' | 'openOnDebugBreak';
|
||||
openExplorerOnEnd: boolean;
|
||||
inlineValues: boolean;
|
||||
toolBarLocation: 'floating' | 'docked' | 'hidden';
|
||||
showInStatusBar: 'never' | 'always' | 'onFirstSessionStart';
|
||||
internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
extensionHostDebugAdapter: boolean;
|
||||
enableAllHovers: boolean;
|
||||
console: {
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
lineHeight: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IGlobalConfig {
|
||||
version: string;
|
||||
compounds: ICompound[];
|
||||
configurations: IConfig[];
|
||||
}
|
||||
|
||||
export interface IEnvConfig {
|
||||
internalConsoleOptions?: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
preLaunchTask?: string | TaskIdentifier;
|
||||
postDebugTask?: string | TaskIdentifier;
|
||||
debugServer?: number;
|
||||
noDebug?: boolean;
|
||||
}
|
||||
|
||||
export interface IConfig extends IEnvConfig {
|
||||
|
||||
// fundamental attributes
|
||||
type: string;
|
||||
request: string;
|
||||
name: string;
|
||||
|
||||
// platform specifics
|
||||
windows?: IEnvConfig;
|
||||
osx?: IEnvConfig;
|
||||
linux?: IEnvConfig;
|
||||
|
||||
// internals
|
||||
__sessionId?: string;
|
||||
__restart?: any;
|
||||
__autoAttach?: boolean;
|
||||
port?: number; // TODO
|
||||
}
|
||||
|
||||
export interface ICompound {
|
||||
name: string;
|
||||
configurations: (string | { name: string, folder: string })[];
|
||||
}
|
||||
|
||||
export interface IDebugAdapter extends IDisposable {
|
||||
readonly onError: Event<Error>;
|
||||
readonly onExit: Event<number | null>;
|
||||
onRequest(callback: (request: DebugProtocol.Request) => void);
|
||||
onEvent(callback: (event: DebugProtocol.Event) => void);
|
||||
startSession(): Promise<void>;
|
||||
sendMessage(message: DebugProtocol.ProtocolMessage): void;
|
||||
sendResponse(response: DebugProtocol.Response): void;
|
||||
sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timemout?: number): void;
|
||||
stopSession(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterFactory extends ITerminalLauncher {
|
||||
createDebugAdapter(session: IDebugSession): IDebugAdapter;
|
||||
substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterExecutableOptions {
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IDebugAdapterExecutable {
|
||||
readonly type: 'executable';
|
||||
readonly command: string;
|
||||
readonly args: string[];
|
||||
readonly options?: IDebugAdapterExecutableOptions;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterServer {
|
||||
readonly type: 'server';
|
||||
readonly port: number;
|
||||
readonly host?: string;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterImplementation {
|
||||
readonly type: 'implementation';
|
||||
readonly implementation: any;
|
||||
}
|
||||
|
||||
export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterImplementation;
|
||||
|
||||
export interface IPlatformSpecificAdapterContribution {
|
||||
program?: string;
|
||||
args?: string[];
|
||||
runtime?: string;
|
||||
runtimeArgs?: string[];
|
||||
}
|
||||
|
||||
export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution {
|
||||
type: string;
|
||||
label?: string;
|
||||
// debug adapter executable
|
||||
adapterExecutableCommand?: string;
|
||||
win?: IPlatformSpecificAdapterContribution;
|
||||
winx86?: IPlatformSpecificAdapterContribution;
|
||||
windows?: IPlatformSpecificAdapterContribution;
|
||||
osx?: IPlatformSpecificAdapterContribution;
|
||||
linux?: IPlatformSpecificAdapterContribution;
|
||||
|
||||
// internal
|
||||
aiKey?: string;
|
||||
|
||||
// supported languages
|
||||
languages?: string[];
|
||||
enableBreakpointsFor?: { languageIds: string[] };
|
||||
|
||||
// debug configuration support
|
||||
configurationAttributes?: any;
|
||||
initialConfigurations?: any[];
|
||||
configurationSnippets?: IJSONSchemaSnippet[];
|
||||
variables?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IDebugConfigurationProvider {
|
||||
readonly type: string;
|
||||
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise<IConfig | null | undefined>;
|
||||
provideDebugConfigurations?(folderUri: uri | undefined): Promise<IConfig[]>;
|
||||
debugAdapterExecutable?(folderUri: uri | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
|
||||
}
|
||||
|
||||
export interface IDebugAdapterDescriptorFactory {
|
||||
readonly type: string;
|
||||
createDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor>;
|
||||
}
|
||||
|
||||
export interface IDebugAdapterTrackerFactory {
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
export interface ITerminalLauncher {
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined>;
|
||||
}
|
||||
|
||||
export interface ITerminalSettings {
|
||||
external: {
|
||||
windowsExec: string,
|
||||
osxExec: string,
|
||||
linuxExec: string
|
||||
};
|
||||
integrated: {
|
||||
shell: {
|
||||
osx: string,
|
||||
windows: string,
|
||||
linux: string
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface IConfigurationManager {
|
||||
/**
|
||||
* Returns true if breakpoints can be set for a given editor model. Depends on mode.
|
||||
*/
|
||||
canSetBreakpointsIn(model: EditorIModel): boolean;
|
||||
|
||||
/**
|
||||
* Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace).
|
||||
*/
|
||||
readonly selectedConfiguration: {
|
||||
launch: ILaunch | undefined;
|
||||
name: string | undefined;
|
||||
};
|
||||
|
||||
selectConfiguration(launch: ILaunch | undefined, name?: string, debugStarted?: boolean): void;
|
||||
|
||||
getLaunches(): ReadonlyArray<ILaunch>;
|
||||
|
||||
getLaunch(workspaceUri: uri | undefined): ILaunch | undefined;
|
||||
|
||||
/**
|
||||
* Allows to register on change of selected debug configuration.
|
||||
*/
|
||||
onDidSelectConfiguration: Event<void>;
|
||||
|
||||
activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
|
||||
|
||||
needsToRunInExtHost(debugType: string): boolean;
|
||||
hasDebugConfigurationProvider(debugType: string): boolean;
|
||||
|
||||
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable;
|
||||
unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void;
|
||||
|
||||
registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable;
|
||||
unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void;
|
||||
|
||||
registerDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): IDisposable;
|
||||
unregisterDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): void;
|
||||
|
||||
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): Promise<any>;
|
||||
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined>;
|
||||
|
||||
registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable;
|
||||
createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined;
|
||||
|
||||
substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
|
||||
runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined>;
|
||||
}
|
||||
|
||||
export interface ILaunch {
|
||||
|
||||
/**
|
||||
* Resource pointing to the launch.json this object is wrapping.
|
||||
*/
|
||||
readonly uri: uri;
|
||||
|
||||
/**
|
||||
* Name of the launch.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Workspace of the launch. Can be undefined.
|
||||
*/
|
||||
readonly workspace: IWorkspaceFolder | undefined;
|
||||
|
||||
/**
|
||||
* Should this launch be shown in the debug dropdown.
|
||||
*/
|
||||
readonly hidden: boolean;
|
||||
|
||||
/**
|
||||
* Returns a configuration with the specified name.
|
||||
* Returns undefined if there is no configuration with the specified name.
|
||||
*/
|
||||
getConfiguration(name: string): IConfig | undefined;
|
||||
|
||||
/**
|
||||
* Returns a compound with the specified name.
|
||||
* Returns undefined if there is no compound with the specified name.
|
||||
*/
|
||||
getCompound(name: string): ICompound | undefined;
|
||||
|
||||
/**
|
||||
* Returns the names of all configurations and compounds.
|
||||
* Ignores configurations which are invalid.
|
||||
*/
|
||||
getConfigurationNames(includeCompounds?: boolean): string[];
|
||||
|
||||
/**
|
||||
* Opens the launch.json file. Creates if it does not exist.
|
||||
*/
|
||||
openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }>;
|
||||
}
|
||||
|
||||
// Debug service interfaces
|
||||
|
||||
export const IDebugService = createDecorator<IDebugService>(DEBUG_SERVICE_ID);
|
||||
|
||||
export interface IDebugService {
|
||||
_serviceBrand: any;
|
||||
|
||||
/**
|
||||
* Gets the current debug state.
|
||||
*/
|
||||
readonly state: State;
|
||||
|
||||
/**
|
||||
* Allows to register on debug state changes.
|
||||
*/
|
||||
onDidChangeState: Event<State>;
|
||||
|
||||
/**
|
||||
* Allows to register on new session events.
|
||||
*/
|
||||
onDidNewSession: Event<IDebugSession>;
|
||||
|
||||
/**
|
||||
* Allows to register on sessions about to be created (not yet fully initialised)
|
||||
*/
|
||||
onWillNewSession: Event<IDebugSession>;
|
||||
|
||||
/**
|
||||
* Allows to register on end session events.
|
||||
*/
|
||||
onDidEndSession: Event<IDebugSession>;
|
||||
|
||||
/**
|
||||
* Gets the current configuration manager.
|
||||
*/
|
||||
getConfigurationManager(): IConfigurationManager;
|
||||
|
||||
/**
|
||||
* Sets the focused stack frame and evaluates all expressions against the newly focused stack frame,
|
||||
*/
|
||||
focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): void;
|
||||
|
||||
/**
|
||||
* Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): Promise<IBreakpoint[]>;
|
||||
|
||||
/**
|
||||
* Updates the breakpoints.
|
||||
*/
|
||||
updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void;
|
||||
|
||||
/**
|
||||
* Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint.
|
||||
* Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets the global activated property for all breakpoints.
|
||||
* Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
setBreakpointsActivated(activated: boolean): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes all breakpoints. If id is passed only removes the breakpoint associated with that id.
|
||||
* Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
removeBreakpoints(id?: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Adds a new function breakpoint for the given name.
|
||||
*/
|
||||
addFunctionBreakpoint(name?: string, id?: string): void;
|
||||
|
||||
/**
|
||||
* Renames an already existing function breakpoint.
|
||||
* Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
renameFunctionBreakpoint(id: string, newFunctionName: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id.
|
||||
* Notifies debug adapter of breakpoint changes.
|
||||
*/
|
||||
removeFunctionBreakpoints(id?: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sends all breakpoints to the passed session.
|
||||
* If session is not passed, sends all breakpoints to each session.
|
||||
*/
|
||||
sendAllBreakpoints(session?: IDebugSession): Promise<any>;
|
||||
|
||||
/**
|
||||
* Adds a new watch expression and evaluates it against the debug adapter.
|
||||
*/
|
||||
addWatchExpression(name?: string): void;
|
||||
|
||||
/**
|
||||
* Renames a watch expression and evaluates it against the debug adapter.
|
||||
*/
|
||||
renameWatchExpression(id: string, newName: string): void;
|
||||
|
||||
/**
|
||||
* Moves a watch expression to a new possition. Used for reordering watch expressions.
|
||||
*/
|
||||
moveWatchExpression(id: string, position: number): void;
|
||||
|
||||
/**
|
||||
* Removes all watch expressions. If id is passed only removes the watch expression with the passed id.
|
||||
*/
|
||||
removeWatchExpressions(id?: string): void;
|
||||
|
||||
/**
|
||||
* Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown.
|
||||
* Also saves all files, manages if compounds are present in the configuration
|
||||
* and resolveds configurations via DebugConfigurationProviders.
|
||||
*
|
||||
* Returns true if the start debugging was successfull. For compound launches, all configurations have to start successfuly for it to return success.
|
||||
* On errors the startDebugging will throw an error, however some error and cancelations are handled and in that case will simply return false.
|
||||
*/
|
||||
startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug?: boolean, parentSession?: IDebugSession): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Restarts a session or creates a new one if there is no active session.
|
||||
*/
|
||||
restartSession(session: IDebugSession, restartData?: any): Promise<any>;
|
||||
|
||||
/**
|
||||
* Stops the session. If the session does not exist then stops all sessions.
|
||||
*/
|
||||
stopSession(session: IDebugSession | undefined): Promise<any>;
|
||||
|
||||
/**
|
||||
* Makes unavailable all sources with the passed uri. Source will appear as grayed out in callstack view.
|
||||
*/
|
||||
sourceIsNotAvailable(uri: uri): void;
|
||||
|
||||
/**
|
||||
* Gets the current debug model.
|
||||
*/
|
||||
getModel(): IDebugModel;
|
||||
|
||||
/**
|
||||
* Gets the current view model.
|
||||
*/
|
||||
getViewModel(): IViewModel;
|
||||
}
|
||||
|
||||
// Editor interfaces
|
||||
export const enum BreakpointWidgetContext {
|
||||
CONDITION = 0,
|
||||
HIT_COUNT = 1,
|
||||
LOG_MESSAGE = 2
|
||||
}
|
||||
|
||||
export interface IDebugEditorContribution extends IEditorContribution {
|
||||
showHover(range: Range, focus: boolean): Promise<void>;
|
||||
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void;
|
||||
closeBreakpointWidget(): void;
|
||||
addLaunchConfiguration(): Promise<any>;
|
||||
}
|
||||
146
src/vs/workbench/contrib/debug/common/debugContentProvider.ts
Normal file
146
src/vs/workbench/contrib/debug/common/debugContentProvider.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { DEBUG_SCHEME, IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
/**
|
||||
* Debug URI format
|
||||
*
|
||||
* a debug URI represents a Source object and the debug session where the Source comes from.
|
||||
*
|
||||
* debug:arbitrary_path?session=123e4567-e89b-12d3-a456-426655440000&ref=1016
|
||||
* \___/ \____________/ \__________________________________________/ \______/
|
||||
* | | | |
|
||||
* scheme source.path session id source.reference
|
||||
*
|
||||
* the arbitrary_path and the session id are encoded with 'encodeURIComponent'
|
||||
*
|
||||
*/
|
||||
export class DebugContentProvider implements IWorkbenchContribution, ITextModelContentProvider {
|
||||
|
||||
private static INSTANCE: DebugContentProvider;
|
||||
|
||||
private readonly pendingUpdates = new Map<string, CancellationTokenSource>();
|
||||
|
||||
constructor(
|
||||
@ITextModelService textModelResolverService: ITextModelService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService
|
||||
) {
|
||||
textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this);
|
||||
DebugContentProvider.INSTANCE = this;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.pendingUpdates.forEach(cancellationSource => cancellationSource.dispose());
|
||||
}
|
||||
|
||||
provideTextContent(resource: uri): Promise<ITextModel> | null {
|
||||
return this.createOrUpdateContentModel(resource, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the model content of the given resource.
|
||||
* If there is no model for the given resource, this method does nothing.
|
||||
*/
|
||||
static refreshDebugContent(resource: uri): void {
|
||||
if (DebugContentProvider.INSTANCE) {
|
||||
DebugContentProvider.INSTANCE.createOrUpdateContentModel(resource, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or reload the model content of the given resource.
|
||||
*/
|
||||
private createOrUpdateContentModel(resource: uri, createIfNotExists: boolean): Promise<ITextModel> | null {
|
||||
|
||||
const model = this.modelService.getModel(resource);
|
||||
if (!model && !createIfNotExists) {
|
||||
// nothing to do
|
||||
return null;
|
||||
}
|
||||
|
||||
let session: IDebugSession | undefined;
|
||||
|
||||
if (resource.query) {
|
||||
const data = Source.getEncodedDebugData(resource);
|
||||
session = this.debugService.getModel().getSessions().filter(p => p.getId() === data.sessionId).pop();
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
// fallback: use focused session
|
||||
session = this.debugService.getViewModel().focusedSession;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return Promise.reject(new Error(localize('unable', "Unable to resolve the resource without a debug session")));
|
||||
}
|
||||
const createErrModel = (errMsg?: string) => {
|
||||
this.debugService.sourceIsNotAvailable(resource);
|
||||
const languageSelection = this.modeService.create(MIME_TEXT);
|
||||
const message = errMsg
|
||||
? localize('canNotResolveSourceWithError', "Could not load source '{0}': {1}.", resource.path, errMsg)
|
||||
: localize('canNotResolveSource', "Could not load source '{0}'.", resource.path);
|
||||
return this.modelService.createModel(message, languageSelection, resource);
|
||||
};
|
||||
|
||||
return session.loadSource(resource).then(response => {
|
||||
|
||||
if (response && response.body) {
|
||||
|
||||
if (model) {
|
||||
|
||||
const newContent = response.body.content;
|
||||
|
||||
// cancel and dispose an existing update
|
||||
const cancellationSource = this.pendingUpdates.get(model.id);
|
||||
if (cancellationSource) {
|
||||
cancellationSource.cancel();
|
||||
}
|
||||
|
||||
// create and keep update token
|
||||
const myToken = new CancellationTokenSource();
|
||||
this.pendingUpdates.set(model.id, myToken);
|
||||
|
||||
// update text model
|
||||
return this.editorWorkerService.computeMoreMinimalEdits(model.uri, [{ text: newContent, range: model.getFullModelRange() }]).then(edits => {
|
||||
|
||||
// remove token
|
||||
this.pendingUpdates.delete(model.id);
|
||||
|
||||
if (!myToken.token.isCancellationRequested && edits && edits.length > 0) {
|
||||
// use the evil-edit as these models show in readonly-editor only
|
||||
model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
|
||||
}
|
||||
return model;
|
||||
});
|
||||
} else {
|
||||
// create text model
|
||||
const mime = response.body.mimeType || guessMimeTypes(resource.path)[0];
|
||||
const languageSelection = this.modeService.create(mime);
|
||||
return this.modelService.createModel(response.body.content, languageSelection, resource);
|
||||
}
|
||||
}
|
||||
|
||||
return createErrModel();
|
||||
|
||||
}, (err: DebugProtocol.ErrorResponse) => createErrModel(err.message));
|
||||
}
|
||||
}
|
||||
1106
src/vs/workbench/contrib/debug/common/debugModel.ts
Normal file
1106
src/vs/workbench/contrib/debug/common/debugModel.ts
Normal file
File diff suppressed because it is too large
Load Diff
1677
src/vs/workbench/contrib/debug/common/debugProtocol.d.ts
vendored
Normal file
1677
src/vs/workbench/contrib/debug/common/debugProtocol.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
199
src/vs/workbench/contrib/debug/common/debugSchemas.ts
Normal file
199
src/vs/workbench/contrib/debug/common/debugSchemas.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IDebuggerContribution, ICompound } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { inputsSchema } from 'vs/workbench/services/configurationResolver/common/configurationResolverSchema';
|
||||
|
||||
// debuggers extension point
|
||||
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IDebuggerContribution[]>({
|
||||
extensionPoint: 'debuggers',
|
||||
defaultExtensionKind: 'workspace',
|
||||
jsonSchema: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers', 'Contributes debug adapters.'),
|
||||
type: 'array',
|
||||
defaultSnippets: [{ body: [{ type: '', extensions: [] }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
defaultSnippets: [{ body: { type: '', program: '', runtime: '', enableBreakpointsFor: { languageIds: [''] } } }],
|
||||
properties: {
|
||||
type: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.type', "Unique identifier for this debug adapter."),
|
||||
type: 'string'
|
||||
},
|
||||
label: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.label', "Display name for this debug adapter."),
|
||||
type: 'string'
|
||||
},
|
||||
program: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.program', "Path to the debug adapter program. Path is either absolute or relative to the extension folder."),
|
||||
type: 'string'
|
||||
},
|
||||
args: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.args', "Optional arguments to pass to the adapter."),
|
||||
type: 'array'
|
||||
},
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.runtime', "Optional runtime in case the program attribute is not an executable but requires a runtime."),
|
||||
type: 'string'
|
||||
},
|
||||
runtimeArgs: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.runtimeArgs', "Optional runtime arguments."),
|
||||
type: 'array'
|
||||
},
|
||||
variables: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.variables', "Mapping from interactive variables (e.g ${action.pickProcess}) in `launch.json` to a command."),
|
||||
type: 'object'
|
||||
},
|
||||
initialConfigurations: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.initialConfigurations', "Configurations for generating the initial \'launch.json\'."),
|
||||
type: ['array', 'string'],
|
||||
},
|
||||
languages: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.languages', "List of languages for which the debug extension could be considered the \"default debugger\"."),
|
||||
type: 'array'
|
||||
},
|
||||
adapterExecutableCommand: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.adapterExecutableCommand', "If specified VS Code will call this command to determine the executable path of the debug adapter and the arguments to pass."),
|
||||
type: 'string'
|
||||
},
|
||||
configurationSnippets: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.configurationSnippets', "Snippets for adding new configurations in \'launch.json\'."),
|
||||
type: 'array'
|
||||
},
|
||||
configurationAttributes: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.configurationAttributes', "JSON schema configurations for validating \'launch.json\'."),
|
||||
type: 'object'
|
||||
},
|
||||
windows: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.windows', "Windows specific settings."),
|
||||
type: 'object',
|
||||
properties: {
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.windows.runtime', "Runtime used for Windows."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
osx: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx', "macOS specific settings."),
|
||||
type: 'object',
|
||||
properties: {
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.osx.runtime', "Runtime used for macOS."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
linux: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.linux', "Linux specific settings."),
|
||||
type: 'object',
|
||||
properties: {
|
||||
runtime: {
|
||||
description: nls.localize('vscode.extension.contributes.debuggers.linux.runtime', "Runtime used for Linux."),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export interface IRawBreakpointContribution {
|
||||
language: string;
|
||||
}
|
||||
|
||||
// breakpoints extension point #9037
|
||||
export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawBreakpointContribution[]>({
|
||||
extensionPoint: 'breakpoints',
|
||||
jsonSchema: {
|
||||
description: nls.localize('vscode.extension.contributes.breakpoints', 'Contributes breakpoints.'),
|
||||
type: 'array',
|
||||
defaultSnippets: [{ body: [{ language: '' }] }],
|
||||
items: {
|
||||
type: 'object',
|
||||
defaultSnippets: [{ body: { language: '' } }],
|
||||
properties: {
|
||||
language: {
|
||||
description: nls.localize('vscode.extension.contributes.breakpoints.language', "Allow breakpoints for this language."),
|
||||
type: 'string'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// debug general schema
|
||||
const defaultCompound: ICompound = { name: 'Compound', configurations: [] };
|
||||
export const launchSchema: IJSONSchema = {
|
||||
id: launchSchemaId,
|
||||
type: 'object',
|
||||
title: nls.localize('app.launch.json.title', "Launch"),
|
||||
required: [],
|
||||
default: { version: '0.2.0', configurations: [], compounds: [] },
|
||||
properties: {
|
||||
version: {
|
||||
type: 'string',
|
||||
description: nls.localize('app.launch.json.version', "Version of this file format."),
|
||||
default: '0.2.0'
|
||||
},
|
||||
configurations: {
|
||||
type: 'array',
|
||||
description: nls.localize('app.launch.json.configurations', "List of configurations. Add new configurations or edit existing ones by using IntelliSense."),
|
||||
items: {
|
||||
defaultSnippets: [],
|
||||
'type': 'object',
|
||||
oneOf: []
|
||||
}
|
||||
},
|
||||
compounds: {
|
||||
type: 'array',
|
||||
description: nls.localize('app.launch.json.compounds', "List of compounds. Each compound references multiple configurations which will get launched together."),
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['name', 'configurations'],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: nls.localize('app.launch.json.compound.name', "Name of compound. Appears in the launch configuration drop down menu.")
|
||||
},
|
||||
configurations: {
|
||||
type: 'array',
|
||||
default: [],
|
||||
items: {
|
||||
oneOf: [{
|
||||
enum: [],
|
||||
description: nls.localize('useUniqueNames', "Please use unique configuration names.")
|
||||
}, {
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
properties: {
|
||||
name: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.name', "Name of compound. Appears in the launch configuration drop down menu.")
|
||||
},
|
||||
folder: {
|
||||
enum: [],
|
||||
description: nls.localize('app.launch.json.compound.folder', "Name of folder in which the compound is located.")
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
description: nls.localize('app.launch.json.compounds.configurations', "Names of configurations that will be started as part of this compound.")
|
||||
}
|
||||
},
|
||||
default: defaultCompound
|
||||
},
|
||||
default: [
|
||||
defaultCompound
|
||||
]
|
||||
},
|
||||
inputs: inputsSchema.definitions!.inputs
|
||||
}
|
||||
};
|
||||
142
src/vs/workbench/contrib/debug/common/debugSource.ts
Normal file
142
src/vs/workbench/contrib/debug/common/debugSource.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { normalize, isAbsolute } from 'vs/base/common/path';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { DEBUG_SCHEME } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
|
||||
const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
|
||||
|
||||
/**
|
||||
* Debug URI format
|
||||
*
|
||||
* a debug URI represents a Source object and the debug session where the Source comes from.
|
||||
*
|
||||
* debug:arbitrary_path?session=123e4567-e89b-12d3-a456-426655440000&ref=1016
|
||||
* \___/ \____________/ \__________________________________________/ \______/
|
||||
* | | | |
|
||||
* scheme source.path session id source.reference
|
||||
*
|
||||
* the arbitrary_path and the session id are encoded with 'encodeURIComponent'
|
||||
*
|
||||
*/
|
||||
|
||||
export class Source {
|
||||
|
||||
public readonly uri: uri;
|
||||
public available: boolean;
|
||||
public raw: DebugProtocol.Source;
|
||||
|
||||
constructor(raw_: DebugProtocol.Source | undefined, sessionId: string) {
|
||||
let path: string;
|
||||
if (raw_) {
|
||||
this.raw = raw_;
|
||||
path = this.raw.path || this.raw.name || '';
|
||||
this.available = true;
|
||||
} else {
|
||||
this.raw = { name: UNKNOWN_SOURCE_LABEL };
|
||||
this.available = false;
|
||||
path = `${DEBUG_SCHEME}:${UNKNOWN_SOURCE_LABEL}`;
|
||||
}
|
||||
|
||||
if (typeof this.raw.sourceReference === 'number' && this.raw.sourceReference > 0) {
|
||||
this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}&ref=${this.raw.sourceReference}`);
|
||||
} else {
|
||||
if (isUri(path)) { // path looks like a uri
|
||||
this.uri = uri.parse(path);
|
||||
} else {
|
||||
// assume a filesystem path
|
||||
if (isAbsolute(path)) {
|
||||
this.uri = uri.file(path);
|
||||
} else {
|
||||
// path is relative: since VS Code cannot deal with this by itself
|
||||
// create a debug url that will result in a DAP 'source' request when the url is resolved.
|
||||
this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.raw.name || resources.basenameOrAuthority(this.uri);
|
||||
}
|
||||
|
||||
get origin() {
|
||||
return this.raw.origin;
|
||||
}
|
||||
|
||||
get presentationHint() {
|
||||
return this.raw.presentationHint;
|
||||
}
|
||||
|
||||
get reference() {
|
||||
return this.raw.sourceReference;
|
||||
}
|
||||
|
||||
get inMemory() {
|
||||
return this.uri.scheme === DEBUG_SCHEME;
|
||||
}
|
||||
|
||||
openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<any> {
|
||||
return !this.available ? Promise.resolve(null) : editorService.openEditor({
|
||||
resource: this.uri,
|
||||
description: this.origin,
|
||||
options: {
|
||||
preserveFocus,
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: pinned || (!preserveFocus && !this.inMemory)
|
||||
}
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
}
|
||||
|
||||
static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId?: string, sourceReference?: number } {
|
||||
let path: string;
|
||||
let sourceReference: number | undefined;
|
||||
let sessionId: string | undefined;
|
||||
|
||||
switch (modelUri.scheme) {
|
||||
case Schemas.file:
|
||||
path = normalize(modelUri.fsPath);
|
||||
break;
|
||||
case DEBUG_SCHEME:
|
||||
path = modelUri.path;
|
||||
if (modelUri.query) {
|
||||
const keyvalues = modelUri.query.split('&');
|
||||
for (let keyvalue of keyvalues) {
|
||||
const pair = keyvalue.split('=');
|
||||
if (pair.length === 2) {
|
||||
switch (pair[0]) {
|
||||
case 'session':
|
||||
sessionId = decodeURIComponent(pair[1]);
|
||||
break;
|
||||
case 'ref':
|
||||
sourceReference = parseInt(pair[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
path = modelUri.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
name: resources.basenameOrAuthority(modelUri),
|
||||
path,
|
||||
sourceReference,
|
||||
sessionId
|
||||
};
|
||||
}
|
||||
}
|
||||
214
src/vs/workbench/contrib/debug/common/debugUtils.ts
Normal file
214
src/vs/workbench/contrib/debug/common/debugUtils.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IConfig, IDebuggerContribution } 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';
|
||||
|
||||
const _formatPIIRegexp = /{([^}]+)}/g;
|
||||
|
||||
export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string }): string {
|
||||
return value.replace(_formatPIIRegexp, function (match, group) {
|
||||
if (excludePII && group.length > 0 && group[0] !== '_') {
|
||||
return match;
|
||||
}
|
||||
|
||||
return args && args.hasOwnProperty(group) ?
|
||||
args[group] :
|
||||
match;
|
||||
});
|
||||
}
|
||||
|
||||
export function isExtensionHostDebugging(config: IConfig) {
|
||||
return config.type && equalsIgnoreCase(config.type === 'vslsShare' ? (<any>config).adapterProxy.configuration.type : config.type, 'extensionhost');
|
||||
}
|
||||
|
||||
// only a debugger contributions with a label, program, or runtime attribute is considered a "defining" or "main" debugger contribution
|
||||
export function isDebuggerMainContribution(dbg: IDebuggerContribution) {
|
||||
return dbg.type && (dbg.label || dbg.program || dbg.runtime);
|
||||
}
|
||||
|
||||
export function getExactExpressionStartAndEnd(lineContent: string, looseStart: number, looseEnd: number): { start: number, end: number } {
|
||||
let matchingExpression: string | undefined = undefined;
|
||||
let startOffset = 0;
|
||||
|
||||
// Some example supported expressions: myVar.prop, a.b.c.d, myVar?.prop, myVar->prop, MyClass::StaticProp, *myVar
|
||||
// Match any character except a set of characters which often break interesting sub-expressions
|
||||
let expression: RegExp = /([^()\[\]{}<>\s+\-/%~#^;=|,`!]|\->)+/g;
|
||||
let result: RegExpExecArray | null = null;
|
||||
|
||||
// First find the full expression under the cursor
|
||||
while (result = expression.exec(lineContent)) {
|
||||
let start = result.index + 1;
|
||||
let end = start + result[0].length;
|
||||
|
||||
if (start <= looseStart && end >= looseEnd) {
|
||||
matchingExpression = result[0];
|
||||
startOffset = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are non-word characters after the cursor, we want to truncate the expression then.
|
||||
// For example in expression 'a.b.c.d', if the focus was under 'b', 'a.b' would be evaluated.
|
||||
if (matchingExpression) {
|
||||
let subExpression: RegExp = /\w+/g;
|
||||
let subExpressionResult: RegExpExecArray | null = null;
|
||||
while (subExpressionResult = subExpression.exec(matchingExpression)) {
|
||||
let subEnd = subExpressionResult.index + 1 + startOffset + subExpressionResult[0].length;
|
||||
if (subEnd >= looseEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (subExpressionResult) {
|
||||
matchingExpression = matchingExpression.substring(0, subExpression.lastIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return matchingExpression ?
|
||||
{ start: startOffset, end: startOffset + matchingExpression.length - 1 } :
|
||||
{ start: 0, end: 0 };
|
||||
}
|
||||
|
||||
// RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt
|
||||
const _schemePattern = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/;
|
||||
|
||||
export function isUri(s: string | undefined): boolean {
|
||||
// heuristics: a valid uri starts with a scheme and
|
||||
// the scheme has at least 2 characters so that it doesn't look like a drive letter.
|
||||
return !!(s && s.match(_schemePattern));
|
||||
}
|
||||
|
||||
function stringToUri(path: string): string {
|
||||
if (typeof path === 'string') {
|
||||
if (isUri(path)) {
|
||||
return <string><unknown>uri.parse(path);
|
||||
} else {
|
||||
// assume path
|
||||
if (isAbsolute(path)) {
|
||||
return <string><unknown>uri.file(path);
|
||||
} else {
|
||||
// leave relative path as is
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function uriToString(path: string): string {
|
||||
if (typeof path === 'object') {
|
||||
const u = uri.revive(path);
|
||||
if (u.scheme === 'file') {
|
||||
return u.fsPath;
|
||||
} else {
|
||||
return u.toString();
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// path hooks helpers
|
||||
|
||||
interface PathContainer {
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage {
|
||||
|
||||
const fixPath = toUri ? stringToUri : uriToString;
|
||||
|
||||
// since we modify Source.paths in the message in place, we need to make a copy of it (see #61129)
|
||||
const msg = deepClone(message);
|
||||
|
||||
convertPaths(msg, (toDA: boolean, source: PathContainer | undefined) => {
|
||||
if (toDA && source) {
|
||||
source.path = source.path ? fixPath(source.path) : undefined;
|
||||
}
|
||||
});
|
||||
return msg;
|
||||
}
|
||||
|
||||
export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage {
|
||||
|
||||
const fixPath = toUri ? stringToUri : uriToString;
|
||||
|
||||
// since we modify Source.paths in the message in place, we need to make a copy of it (see #61129)
|
||||
const msg = deepClone(message);
|
||||
|
||||
convertPaths(msg, (toDA: boolean, source: PathContainer | undefined) => {
|
||||
if (!toDA && source) {
|
||||
source.path = source.path ? fixPath(source.path) : undefined;
|
||||
}
|
||||
});
|
||||
return msg;
|
||||
}
|
||||
|
||||
function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: boolean, source: PathContainer | undefined) => void): void {
|
||||
|
||||
switch (msg.type) {
|
||||
case 'event':
|
||||
const event = <DebugProtocol.Event>msg;
|
||||
switch (event.event) {
|
||||
case 'output':
|
||||
fixSourcePath(false, (<DebugProtocol.OutputEvent>event).body.source);
|
||||
break;
|
||||
case 'loadedSource':
|
||||
fixSourcePath(false, (<DebugProtocol.LoadedSourceEvent>event).body.source);
|
||||
break;
|
||||
case 'breakpoint':
|
||||
fixSourcePath(false, (<DebugProtocol.BreakpointEvent>event).body.breakpoint.source);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'request':
|
||||
const request = <DebugProtocol.Request>msg;
|
||||
switch (request.command) {
|
||||
case 'setBreakpoints':
|
||||
fixSourcePath(true, (<DebugProtocol.SetBreakpointsArguments>request.arguments).source);
|
||||
break;
|
||||
case 'source':
|
||||
fixSourcePath(true, (<DebugProtocol.SourceArguments>request.arguments).source);
|
||||
break;
|
||||
case 'gotoTargets':
|
||||
fixSourcePath(true, (<DebugProtocol.GotoTargetsArguments>request.arguments).source);
|
||||
break;
|
||||
case 'launchVSCode':
|
||||
request.arguments.args.forEach(arg => fixSourcePath(false, arg));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'response':
|
||||
const response = <DebugProtocol.Response>msg;
|
||||
if (response.success) {
|
||||
switch (response.command) {
|
||||
case 'stackTrace':
|
||||
(<DebugProtocol.StackTraceResponse>response).body.stackFrames.forEach(frame => fixSourcePath(false, frame.source));
|
||||
break;
|
||||
case 'loadedSources':
|
||||
(<DebugProtocol.LoadedSourcesResponse>response).body.sources.forEach(source => fixSourcePath(false, source));
|
||||
break;
|
||||
case 'scopes':
|
||||
(<DebugProtocol.ScopesResponse>response).body.scopes.forEach(scope => fixSourcePath(false, scope.source));
|
||||
break;
|
||||
case 'setFunctionBreakpoints':
|
||||
(<DebugProtocol.SetFunctionBreakpointsResponse>response).body.breakpoints.forEach(bp => fixSourcePath(false, bp.source));
|
||||
break;
|
||||
case 'setBreakpoints':
|
||||
(<DebugProtocol.SetBreakpointsResponse>response).body.breakpoints.forEach(bp => fixSourcePath(false, bp.source));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
117
src/vs/workbench/contrib/debug/common/debugViewModel.ts
Normal file
117
src/vs/workbench/contrib/debug/common/debugViewModel.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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 } 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';
|
||||
|
||||
export class ViewModel implements IViewModel {
|
||||
|
||||
firstSessionStart = true;
|
||||
|
||||
private _focusedStackFrame: IStackFrame | undefined;
|
||||
private _focusedSession: IDebugSession | undefined;
|
||||
private _focusedThread: IThread | undefined;
|
||||
private selectedExpression: IExpression | undefined;
|
||||
private selectedFunctionBreakpoint: IFunctionBreakpoint | undefined;
|
||||
private readonly _onDidFocusSession: Emitter<IDebugSession | undefined>;
|
||||
private readonly _onDidFocusStackFrame: Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>;
|
||||
private readonly _onDidSelectExpression: Emitter<IExpression | undefined>;
|
||||
private multiSessionView: boolean;
|
||||
private expressionSelectedContextKey: IContextKey<boolean>;
|
||||
private breakpointSelectedContextKey: IContextKey<boolean>;
|
||||
private loadedScriptsSupportedContextKey: IContextKey<boolean>;
|
||||
private stepBackSupportedContextKey: IContextKey<boolean>;
|
||||
private focusedSessionIsAttach: IContextKey<boolean>;
|
||||
|
||||
constructor(contextKeyService: IContextKeyService) {
|
||||
this._onDidFocusSession = new Emitter<IDebugSession | undefined>();
|
||||
this._onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame, explicit: boolean }>();
|
||||
this._onDidSelectExpression = new Emitter<IExpression>();
|
||||
this.multiSessionView = false;
|
||||
this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService);
|
||||
this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService);
|
||||
this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService);
|
||||
this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService);
|
||||
this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return 'root';
|
||||
}
|
||||
|
||||
get focusedSession(): IDebugSession | undefined {
|
||||
return this._focusedSession;
|
||||
}
|
||||
|
||||
get focusedThread(): IThread | undefined {
|
||||
return this._focusedThread;
|
||||
}
|
||||
|
||||
get focusedStackFrame(): IStackFrame | undefined {
|
||||
return this._focusedStackFrame;
|
||||
}
|
||||
|
||||
setFocus(stackFrame: IStackFrame | undefined, thread: IThread | undefined, session: IDebugSession | undefined, explicit: boolean): void {
|
||||
const shouldEmitForStackFrame = this._focusedStackFrame !== stackFrame;
|
||||
const shouldEmitForSession = this._focusedSession !== session;
|
||||
|
||||
this._focusedStackFrame = stackFrame;
|
||||
this._focusedThread = thread;
|
||||
this._focusedSession = session;
|
||||
|
||||
this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false);
|
||||
this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false);
|
||||
const attach = !!session && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
|
||||
this.focusedSessionIsAttach.set(attach);
|
||||
|
||||
if (shouldEmitForSession) {
|
||||
this._onDidFocusSession.fire(session);
|
||||
}
|
||||
if (shouldEmitForStackFrame) {
|
||||
this._onDidFocusStackFrame.fire({ stackFrame, explicit });
|
||||
}
|
||||
}
|
||||
|
||||
get onDidFocusSession(): Event<IDebugSession | undefined> {
|
||||
return this._onDidFocusSession.event;
|
||||
}
|
||||
|
||||
get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }> {
|
||||
return this._onDidFocusStackFrame.event;
|
||||
}
|
||||
|
||||
getSelectedExpression(): IExpression | undefined {
|
||||
return this.selectedExpression;
|
||||
}
|
||||
|
||||
setSelectedExpression(expression: IExpression | undefined) {
|
||||
this.selectedExpression = expression;
|
||||
this.expressionSelectedContextKey.set(!!expression);
|
||||
this._onDidSelectExpression.fire(expression);
|
||||
}
|
||||
|
||||
get onDidSelectExpression(): Event<IExpression | undefined> {
|
||||
return this._onDidSelectExpression.event;
|
||||
}
|
||||
|
||||
getSelectedFunctionBreakpoint(): IFunctionBreakpoint | undefined {
|
||||
return this.selectedFunctionBreakpoint;
|
||||
}
|
||||
|
||||
setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint | undefined): void {
|
||||
this.selectedFunctionBreakpoint = functionBreakpoint;
|
||||
this.breakpointSelectedContextKey.set(!!functionBreakpoint);
|
||||
}
|
||||
|
||||
isMultiSessionView(): boolean {
|
||||
return this.multiSessionView;
|
||||
}
|
||||
|
||||
setMultiSessionView(isMultiSessionView: boolean): void {
|
||||
this.multiSessionView = isMultiSessionView;
|
||||
}
|
||||
}
|
||||
145
src/vs/workbench/contrib/debug/common/replModel.ts
Normal file
145
src/vs/workbench/contrib/debug/common/replModel.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { 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 { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const MAX_REPL_LENGTH = 10000;
|
||||
let topReplElementCounter = 0;
|
||||
|
||||
export class ReplModel {
|
||||
private replElements: IReplElement[] = [];
|
||||
|
||||
constructor(private session: IDebugSession) { }
|
||||
|
||||
getReplElements(): IReplElement[] {
|
||||
return this.replElements;
|
||||
}
|
||||
|
||||
addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void> {
|
||||
const expression = new Expression(name);
|
||||
this.addReplElements([expression]);
|
||||
return expression.evaluate(this.session, stackFrame, 'repl');
|
||||
}
|
||||
|
||||
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(`topReplElement:${topReplElementCounter++}`, 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);
|
||||
}
|
||||
}
|
||||
|
||||
logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) {
|
||||
|
||||
let source: IReplElementSource | undefined;
|
||||
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(`topReplElement:${topReplElementCounter++}`, (<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 = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user