Skip to content

Commit

Permalink
Plugin API for debug
Browse files Browse the repository at this point in the history
Signed-off-by: Anatoliy Bazko <abazko@redhat.com>
  • Loading branch information
tolusha committed Dec 22, 2018
1 parent a9e4f97 commit c20b224
Show file tree
Hide file tree
Showing 53 changed files with 2,253 additions and 378 deletions.
21 changes: 21 additions & 0 deletions packages/core/src/common/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

export function deepClone<T>(obj: T): T {
if (!obj || typeof obj !== 'object') {
return obj;
}
if (obj instanceof RegExp) {
return obj;
}
// tslint:disable-next-line:no-any
const result: any = Array.isArray(obj) ? [] : {};
Object.keys(obj).forEach((key: string) => {
// tslint:disable-next-line:no-any
const prop = (<any>obj)[key];
if (prop && typeof prop === 'object') {
result[key] = deepClone(prop);
} else {
result[key] = prop;
}
});
return result;
}

export function deepFreeze<T>(obj: T): T {
if (!obj || typeof obj !== 'object') {
return obj;
Expand Down
57 changes: 41 additions & 16 deletions packages/core/src/common/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { injectable, inject, named } from 'inversify';
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-types';
import URI from '../common/uri';
import { ContributionProvider } from './contribution-provider';
import { Event } from './event';
import { Event, Emitter } from './event';
import { Disposable } from './disposable';
import { MaybePromise } from './types';
import { CancellationToken } from './cancellation';
Expand Down Expand Up @@ -117,26 +117,52 @@ export class DefaultResourceProvider {

}

export class MutableResource implements Resource {
private contents: string;

constructor(readonly uri: URI, contents: string, readonly dispose: () => void) {
this.contents = contents;
}

async readContents(): Promise<string> {
return this.contents;
}

async saveContents(contents: string): Promise<void> {
this.contents = contents;
this.fireDidChangeContents();
}

protected readonly onDidChangeContentsEmitter = new Emitter<void>();
onDidChangeContents = this.onDidChangeContentsEmitter.event;
protected fireDidChangeContents(): void {
this.onDidChangeContentsEmitter.fire(undefined);
}
}

@injectable()
export class InMemoryResources implements ResourceResolver {

private resources = new Map<string, Resource>();
private resources = new Map<string, MutableResource>();

add(uri: URI, contents: string): Resource {
const stringUri = uri.toString();
if (this.resources.has(stringUri)) {
throw new Error(`Cannot add already existing in-memory resource '${stringUri}'`);
const resourceUri = uri.toString();
if (this.resources.has(resourceUri)) {
throw new Error(`Cannot add already existing in-memory resource '${resourceUri}'`);
}
const resource: Resource = {
uri,
async readContents(): Promise<string> {
return contents;
},
dispose: () => {
this.resources.delete(stringUri);
}
};
this.resources.set(stringUri, resource);

const resource = new MutableResource(uri, contents, () => this.resources.delete(resourceUri));
this.resources.set(resourceUri, resource);
return resource;
}

update(uri: URI, contents: string): Resource {
const resourceUri = uri.toString();
const resource = this.resources.get(resourceUri);
if (!resource) {
throw new Error(`Cannot update non-existed in-memory resource '${resourceUri}'`);
}
resource.saveContents(contents);
return resource;
}

Expand All @@ -146,5 +172,4 @@ export class InMemoryResources implements ResourceResolver {
}
return this.resources.get(uri.toString())!;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { ContainerModule } from 'inversify';
import { NodeDebugAdapterContribution, Node2DebugAdapterContribution } from './node-debug-adapter-contribution';
import { DebugAdapterContribution } from '@theia/debug/lib/node/debug-model';
import { DebugAdapterContribution } from '@theia/debug/lib/common/debug-model';

export default new ContainerModule(bind => {
bind(DebugAdapterContribution).to(NodeDebugAdapterContribution).inSingletonScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export const LEGACY_PORT_DEFAULT = 5858;

@injectable()
export class NodeDebugAdapterContribution extends AbstractVSCodeDebugAdapterContribution {

constructor() {
super(
'node',
Expand Down Expand Up @@ -94,12 +93,10 @@ export class NodeDebugAdapterContribution extends AbstractVSCodeDebugAdapterCont

@injectable()
export class Node2DebugAdapterContribution extends AbstractVSCodeDebugAdapterContribution {

constructor() {
super(
'node2',
path.join(__dirname, '../../download/node-debug2/extension')
);
}

}
8 changes: 7 additions & 1 deletion packages/debug/src/browser/breakpoint/breakpoint-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
return marker && marker.data;
}

getBreakpoints(uri: URI): SourceBreakpoint[] {
getBreakpoints(uri?: URI): SourceBreakpoint[] {
return this.findMarkers({ uri }).map(marker => marker.data);
}

Expand All @@ -64,6 +64,12 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
}
}

deleteBreakpoint(uri: URI, line: number, column?: number): void {
const breakpoints = this.getBreakpoints(uri);
const newBreakpoints = breakpoints.filter(({ raw }) => raw.line !== line);
this.setBreakpoints(uri, newBreakpoints);
}

enableAllBreakpoints(enabled: boolean): void {
for (const uriString of this.getUris()) {
let didChange = false;
Expand Down
25 changes: 25 additions & 0 deletions packages/debug/src/browser/console/debug-console-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export class DebugConsoleSession extends ConsoleSession {
readonly id = 'debug';
protected items: ConsoleItem[] = [];

// content buffer for [append](#append) method
protected uncompletedItemContent: string | undefined;

@inject(DebugSessionManager)
protected readonly manager: DebugSessionManager;

Expand Down Expand Up @@ -125,6 +128,28 @@ export class DebugConsoleSession extends ConsoleSession {
this.fireDidChange();
}

append(value: string): void {
if (!value) {
return;
}

const lastItem = this.items.slice(-1)[0];
if (lastItem instanceof AnsiConsoleItem && lastItem.content === this.uncompletedItemContent) {
this.items.pop();
this.uncompletedItemContent += value;
} else {
this.uncompletedItemContent = value;
}

this.items.push(new AnsiConsoleItem(this.uncompletedItemContent, MessageType.Info));
this.fireDidChange();
}

appendLine(value: string): void {
this.items.push(new AnsiConsoleItem(value, MessageType.Info));
this.fireDidChange();
}

protected async logOutput(session: DebugSession, event: DebugProtocol.OutputEvent): Promise<void> {
const body = event.body;
const { category, variablesReference } = body;
Expand Down
2 changes: 1 addition & 1 deletion packages/debug/src/browser/debug-configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import { QuickPickService, StorageService } from '@theia/core/lib/browser';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { DebugService } from '../common/debug-service';
import { DebugConfiguration } from '../common/debug-configuration';
import { DebugConfigurationModel } from './debug-configuration-model';
import { DebugSessionOptions } from './debug-session-options';
import { DebugService } from '../common/debug-service';

@injectable()
export class DebugConfigurationManager {
Expand Down
125 changes: 10 additions & 115 deletions packages/debug/src/browser/debug-frontend-application-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@

import { AbstractViewContribution, ApplicationShell, KeybindingRegistry } from '@theia/core/lib/browser';
import { injectable, inject } from 'inversify';
import { JsonSchemaStore } from '@theia/core/lib/browser/json-schema-store';
import { ThemeService } from '@theia/core/lib/browser/theming';
import { InMemoryResources, MenuModelRegistry, CommandRegistry, MAIN_MENU_BAR, Command } from '@theia/core/lib/common';
import { DebugService } from '../common/debug-service';
import { MenuModelRegistry, CommandRegistry, MAIN_MENU_BAR, Command } from '@theia/core/lib/common';
import { DebugViewLocation } from '../common/debug-configuration';
import { IJSONSchema } from '@theia/core/lib/common/json-schema';
import { EditorKeybindingContexts } from '@theia/editor/lib/browser';
import URI from '@theia/core/lib/common/uri';
import { DebugSessionManager } from './debug-session-manager';
import { DebugWidget } from './view/debug-widget';
import { BreakpointManager } from './breakpoint/breakpoint-manager';
Expand All @@ -42,6 +38,8 @@ import { DebugKeybindingContexts } from './debug-keybinding-contexts';
import { DebugEditorModel } from './editor/debug-editor-model';
import { DebugEditorService } from './editor/debug-editor-service';
import { DebugConsoleContribution } from './console/debug-console-contribution';
import { DebugService } from '../common/debug-service';
import { DebugSchemaUpdater } from './debug-schema-updater';

export namespace DebugMenus {
export const DEBUG = [...MAIN_MENU_BAR, '6_debug'];
Expand Down Expand Up @@ -267,9 +265,8 @@ ThemeService.get().onThemeChange(() => updateTheme());
@injectable()
export class DebugFrontendApplicationContribution extends AbstractViewContribution<DebugWidget> {

@inject(JsonSchemaStore) protected readonly jsonSchemaStore: JsonSchemaStore;
@inject(InMemoryResources) protected readonly inmemoryResources: InMemoryResources;
@inject(DebugService) protected readonly debugService: DebugService;
@inject(DebugService)
protected readonly debug: DebugService;

@inject(DebugSessionManager)
protected readonly manager: DebugSessionManager;
Expand All @@ -292,6 +289,9 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
@inject(DebugConsoleContribution)
protected readonly console: DebugConsoleContribution;

@inject(DebugSchemaUpdater)
protected readonly schemaUpdater: DebugSchemaUpdater;

constructor() {
super({
widgetId: DebugWidget.ID,
Expand Down Expand Up @@ -336,43 +336,8 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
this.openSession(session);
}
});
this.debugService.debugTypes().then(async types => {
const launchSchemaUrl = new URI('vscode://debug/launch.json');
const attributePromises = types.map(type => this.debugService.getSchemaAttributes(type));
const schema: IJSONSchema = {
...launchSchema
};
const items = (<IJSONSchema>launchSchema!.properties!['configurations'].items);
for (const attributes of await Promise.all(attributePromises)) {
for (const attribute of attributes) {
attribute.properties = {
'debugViewLocation': {
enum: ['default', 'left', 'right', 'bottom'],
default: 'default',
description: 'Controls the location of the debug view.'
},
'openDebug': {
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'],
default: 'openOnSessionStart',
description: 'Controls when the debug view should open.'
},
'internalConsoleOptions': {
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
default: 'openOnFirstSessionStart',
description: 'Controls when the internal debug console should open.'
},
...attribute.properties
};
items.oneOf!.push(attribute);
}
}
items.defaultSnippets!.push(...await this.debugService.getConfigurationSnippets());
this.inmemoryResources.add(launchSchemaUrl, JSON.stringify(schema));
this.jsonSchemaStore.registerSchema({
fileMatch: ['launch.json'],
url: launchSchemaUrl.toString()
});
});

this.schemaUpdater.update();
this.configurations.load();
await this.breakpointManager.load();
}
Expand Down Expand Up @@ -843,73 +808,3 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
}

}

// debug general schema
const defaultCompound = { name: 'Compound', configurations: [] };

const launchSchemaId = 'vscode://schemas/launch';
const launchSchema: IJSONSchema = {
id: launchSchemaId,
type: 'object',
title: 'Launch',
required: [],
default: { version: '0.2.0', configurations: [], compounds: [] },
properties: {
version: {
type: 'string',
description: 'Version of this file format.',
default: '0.2.0'
},
configurations: {
type: 'array',
description: 'List of configurations. Add new configurations or edit existing ones by using IntelliSense.',
items: {
defaultSnippets: [],
'type': 'object',
oneOf: []
}
},
compounds: {
type: 'array',
description: 'List of compounds. Each compound references multiple configurations which will get launched together.',
items: {
type: 'object',
required: ['name', 'configurations'],
properties: {
name: {
type: 'string',
description: 'Name of compound. Appears in the launch configuration drop down menu.'
},
configurations: {
type: 'array',
default: [],
items: {
oneOf: [{
enum: [],
description: 'Please use unique configuration names.'
}, {
type: 'object',
required: ['name'],
properties: {
name: {
enum: [],
description: 'Name of compound. Appears in the launch configuration drop down menu.'
},
folder: {
enum: [],
description: 'Name of folder in which the compound is located.'
}
}
}]
},
description: 'Names of configurations that will be started as part of this compound.'
}
},
default: defaultCompound
},
default: [
defaultCompound
]
}
}
};
Loading

0 comments on commit c20b224

Please sign in to comment.