diff --git a/packages/core/src/browser/icons/add-inverse.svg b/packages/core/src/browser/icons/add-inverse.svg
new file mode 100644
index 0000000000000..7d3fd77ffd6d3
--- /dev/null
+++ b/packages/core/src/browser/icons/add-inverse.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/core/src/browser/icons/add.svg b/packages/core/src/browser/icons/add.svg
new file mode 100644
index 0000000000000..2b679663e8037
--- /dev/null
+++ b/packages/core/src/browser/icons/add.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/debug/src/browser/style/remove-all-inverse.svg b/packages/core/src/browser/icons/remove-all-inverse.svg
similarity index 100%
rename from packages/debug/src/browser/style/remove-all-inverse.svg
rename to packages/core/src/browser/icons/remove-all-inverse.svg
diff --git a/packages/debug/src/browser/style/remove-all.svg b/packages/core/src/browser/icons/remove-all.svg
similarity index 100%
rename from packages/debug/src/browser/style/remove-all.svg
rename to packages/core/src/browser/icons/remove-all.svg
diff --git a/packages/core/src/browser/shell/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar.tsx
index 006908129c3a1..5d7ef808e86f5 100644
--- a/packages/core/src/browser/shell/tab-bar-toolbar.tsx
+++ b/packages/core/src/browser/shell/tab-bar-toolbar.tsx
@@ -128,8 +128,9 @@ export class TabBarToolbar extends ReactWidget {
if (iconClass) {
classNames.push(iconClass);
}
+ const tooltip = item.tooltip || (command && command.label);
return
-
{innerText}
+
{innerText}
;
}
diff --git a/packages/core/src/browser/source-tree/source-tree.ts b/packages/core/src/browser/source-tree/source-tree.ts
index 915402fe5fe14..19ecec1f9a25b 100644
--- a/packages/core/src/browser/source-tree/source-tree.ts
+++ b/packages/core/src/browser/source-tree/source-tree.ts
@@ -48,6 +48,12 @@ export class SourceTree extends TreeImpl {
const updated = existing && Object.assign(existing, { element, parent });
if (CompositeTreeElement.hasElements(element)) {
if (updated) {
+ if (!ExpandableTreeNode.is(updated)) {
+ Object.assign(updated, { expanded: false });
+ }
+ if (!CompositeTreeNode.is(updated)) {
+ Object.assign(updated, { children: [] });
+ }
return updated;
}
return {
@@ -65,6 +71,12 @@ export class SourceTree extends TreeImpl {
delete updated.children;
}
if (updated) {
+ if (ExpandableTreeNode.is(updated)) {
+ delete updated.expanded;
+ }
+ if (CompositeTreeNode.is(updated)) {
+ delete updated.children;
+ }
return updated;
}
return {
diff --git a/packages/core/src/browser/source-tree/tree-source.ts b/packages/core/src/browser/source-tree/tree-source.ts
index cf7b3d1bf7780..9ac5898e5cbda 100644
--- a/packages/core/src/browser/source-tree/tree-source.ts
+++ b/packages/core/src/browser/source-tree/tree-source.ts
@@ -17,7 +17,7 @@
// tslint:disable:no-any
import { ReactNode } from 'react';
-import { injectable } from 'inversify';
+import { injectable, unmanaged } from 'inversify';
import { Emitter, Event } from '../../common/event';
import { MaybePromise } from '../../common/types';
import { Disposable, DisposableCollection } from '../../common/disposable';
@@ -57,7 +57,7 @@ export abstract class TreeSource implements Disposable {
readonly id: string | undefined;
readonly placeholder: string | undefined;
- constructor(options: TreeSourceOptions = {}) {
+ constructor(@unmanaged() options: TreeSourceOptions = {}) {
this.id = options.id;
this.placeholder = options.placeholder;
}
diff --git a/packages/core/src/browser/style/icons.css b/packages/core/src/browser/style/icons.css
index c1d033277eea0..3a7733c66b877 100644
--- a/packages/core/src/browser/style/icons.css
+++ b/packages/core/src/browser/style/icons.css
@@ -37,3 +37,15 @@
height: 16px;
background: var(--theia-icon-open-json) no-repeat;
}
+
+.theia-collapse-all-icon {
+ background: var(--theia-icon-collapse-all) center center no-repeat;
+}
+
+.theia-remove-all-icon {
+ background: var(--theia-icon-remove-all) center center no-repeat;
+}
+
+.theia-add-icon {
+ background: var(--theia-icon-add) center center no-repeat;
+}
diff --git a/packages/core/src/browser/style/variables-bright.useable.css b/packages/core/src/browser/style/variables-bright.useable.css
index 653ac802d21b5..90e12b945ed84 100644
--- a/packages/core/src/browser/style/variables-bright.useable.css
+++ b/packages/core/src/browser/style/variables-bright.useable.css
@@ -181,6 +181,8 @@ is not optimized for dense, information rich UIs.
--theia-icon-whole-word: url(../icons/whole-word.svg);
--theia-icon-refresh: url(../icons/Refresh.svg);
--theia-icon-collapse-all: url(../icons/CollapseAll.svg);
+ --theia-icon-remove-all: url(../icons/remove-all.svg);
+ --theia-icon-add: url(../icons/add.svg);
--theia-icon-clear: url(../icons/clear-search-results.svg);
--theia-icon-replace: url(../icons/replace.svg);
--theia-icon-replace-all: url(../icons/replace-all.svg);
diff --git a/packages/core/src/browser/style/variables-dark.useable.css b/packages/core/src/browser/style/variables-dark.useable.css
index 9f975a8c48b69..7e5fd0786e5a2 100644
--- a/packages/core/src/browser/style/variables-dark.useable.css
+++ b/packages/core/src/browser/style/variables-dark.useable.css
@@ -181,6 +181,8 @@ is not optimized for dense, information rich UIs.
--theia-icon-whole-word: url(../icons/whole-word-dark.svg);
--theia-icon-refresh: url(../icons/Refresh_inverse.svg);
--theia-icon-collapse-all: url(../icons/CollapseAll_inverse.svg);
+ --theia-icon-remove-all: url(../icons/remove-all-inverse.svg);
+ --theia-icon-add: url(../icons/add-inverse.svg);
--theia-icon-clear: url(../icons/clear-search-results-dark.svg);
--theia-icon-replace: url(../icons/replace-inverse.svg);
--theia-icon-replace-all: url(../icons/replace-all-inverse.svg);
diff --git a/packages/debug/src/browser/console/debug-console-items.tsx b/packages/debug/src/browser/console/debug-console-items.tsx
index ee05afbe78285..c836fd98a503a 100644
--- a/packages/debug/src/browser/console/debug-console-items.tsx
+++ b/packages/debug/src/browser/console/debug-console-items.tsx
@@ -21,18 +21,24 @@ import { SingleTextInputDialog } from '@theia/core/lib/browser';
import { ConsoleItem, CompositeConsoleItem } from '@theia/console/lib/browser/console-session';
import { DebugSession } from '../debug-session';
+export type DebugSessionProvider = () => DebugSession | undefined;
+
export class ExpressionContainer implements CompositeConsoleItem {
private static readonly BASE_CHUNK_SIZE = 100;
- protected readonly session: DebugSession | undefined;
+ protected readonly sessionProvider: DebugSessionProvider;
+ protected get session(): DebugSession | undefined {
+ return this.sessionProvider();
+ }
+
protected variablesReference: number;
protected namedVariables: number | undefined;
protected indexedVariables: number | undefined;
protected readonly startOfVariables: number;
constructor(options: ExpressionContainer.Options) {
- this.session = options.session;
+ this.sessionProvider = options.session;
this.variablesReference = options.variablesReference || 0;
this.namedVariables = options.namedVariables;
this.indexedVariables = options.indexedVariables;
@@ -72,9 +78,10 @@ export class ExpressionContainer implements CompositeConsoleItem {
for (let i = 0; i < numberOfChunks; i++) {
const start = this.startOfVariables + i * chunkSize;
const count = Math.min(chunkSize, this.indexedVariables - i * chunkSize);
- const { session, variablesReference } = this;
+ const { variablesReference } = this;
result.push(new DebugVirtualVariable({
- session, variablesReference,
+ session: this.sessionProvider,
+ variablesReference,
namedVariables: 0,
indexedVariables: count,
startOfVariables: start,
@@ -98,7 +105,7 @@ export class ExpressionContainer implements CompositeConsoleItem {
const names = new Set();
for (const variable of variables) {
if (!names.has(variable.name)) {
- result.push(new DebugVariable(this.session, variable, this));
+ result.push(new DebugVariable(this.sessionProvider, variable, this));
names.add(variable.name);
}
}
@@ -114,7 +121,7 @@ export class ExpressionContainer implements CompositeConsoleItem {
}
export namespace ExpressionContainer {
export interface Options {
- session: DebugSession | undefined,
+ session: DebugSessionProvider,
variablesReference?: number
namedVariables?: number
indexedVariables?: number
@@ -128,7 +135,7 @@ export class DebugVariable extends ExpressionContainer {
static stringRegex = /^(['"]).*\1$/;
constructor(
- protected readonly session: DebugSession | undefined,
+ session: DebugSessionProvider,
protected readonly variable: DebugProtocol.Variable,
protected readonly parent: ExpressionContainer
) {
@@ -262,6 +269,10 @@ export class ExpressionItem extends ExpressionContainer {
get value(): string {
return this._value;
}
+ protected _type: string | undefined;
+ get type(): string | undefined {
+ return this._type;
+ }
protected _available = false;
get available(): boolean {
@@ -269,12 +280,16 @@ export class ExpressionItem extends ExpressionContainer {
}
constructor(
- protected readonly expression: string,
- protected readonly session: DebugSession | undefined
+ protected _expression: string,
+ session: DebugSessionProvider
) {
super({ session });
}
+ get expression(): string {
+ return this._expression;
+ }
+
render(): React.ReactNode {
const valueClassNames: string[] = [];
if (!this._available) {
@@ -282,32 +297,42 @@ export class ExpressionItem extends ExpressionContainer {
valueClassNames.push('theia-debug-console-unavailable');
}
return
-
{this.expression}
+
{this._expression}
{this._value}
;
}
async evaluate(context: string = 'repl'): Promise {
- if (this.session) {
+ const session = this.session;
+ if (session) {
try {
- const { expression } = this;
- const body = await this.session.evaluate(expression, context);
- if (body) {
- this._value = body.result;
- this._available = true;
- this.variablesReference = body.variablesReference;
- this.namedVariables = body.namedVariables;
- this.indexedVariables = body.indexedVariables;
- this.elements = undefined;
- }
+ const body = await session.evaluate(this._expression, context);
+ this.setResult(body);
} catch (err) {
- this._value = err.message;
- this._available = false;
+ this.setResult(undefined, err.message);
}
} else {
- this._value = 'Please start a debug session to evaluate';
+ this.setResult(undefined, 'Please start a debug session to evaluate');
+ }
+ }
+
+ protected setResult(body?: DebugProtocol.EvaluateResponse['body'], error: string = ExpressionItem.notAvailable): void {
+ if (body) {
+ this._value = body.result;
+ this._type = body.type;
+ this._available = true;
+ this.variablesReference = body.variablesReference;
+ this.namedVariables = body.namedVariables;
+ this.indexedVariables = body.indexedVariables;
+ } else {
+ this._value = error;
+ this._type = undefined;
this._available = false;
+ this.variablesReference = 0;
+ this.namedVariables = undefined;
+ this.indexedVariables = undefined;
}
+ this.elements = undefined;
}
}
@@ -316,7 +341,7 @@ export class DebugScope extends ExpressionContainer {
constructor(
protected readonly raw: DebugProtocol.Scope,
- protected readonly session: DebugSession
+ session: DebugSessionProvider
) {
super({
session,
diff --git a/packages/debug/src/browser/console/debug-console-session.ts b/packages/debug/src/browser/console/debug-console-session.ts
index d3975f677c0b8..f549ea41988be 100644
--- a/packages/debug/src/browser/console/debug-console-session.ts
+++ b/packages/debug/src/browser/console/debug-console-session.ts
@@ -118,7 +118,7 @@ export class DebugConsoleSession extends ConsoleSession {
}
async execute(value: string): Promise {
- const expression = new ExpressionItem(value, this.manager.currentSession);
+ const expression = new ExpressionItem(value, () => this.manager.currentSession);
this.items.push(expression);
await expression.evaluate();
this.fireDidChange();
@@ -160,7 +160,7 @@ export class DebugConsoleSession extends ConsoleSession {
}
const severity = category === 'stderr' ? MessageType.Error : event.body.category === 'console' ? MessageType.Warning : MessageType.Info;
if (variablesReference) {
- const items = await new ExpressionContainer({ session, variablesReference }).getElements();
+ const items = await new ExpressionContainer({ session: () => session, variablesReference }).getElements();
this.items.push(...items);
} else if (typeof body.output === 'string') {
for (const line of body.output.split('\n')) {
diff --git a/packages/debug/src/browser/debug-frontend-application-contribution.ts b/packages/debug/src/browser/debug-frontend-application-contribution.ts
index e8700528d7a7c..a9cd3e7263027 100644
--- a/packages/debug/src/browser/debug-frontend-application-contribution.ts
+++ b/packages/debug/src/browser/debug-frontend-application-contribution.ts
@@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
-import { AbstractViewContribution, ApplicationShell, KeybindingRegistry, Widget } from '@theia/core/lib/browser';
+import { AbstractViewContribution, ApplicationShell, KeybindingRegistry, Widget, CompositeTreeNode } from '@theia/core/lib/browser';
import { injectable, inject } from 'inversify';
import { ThemeService } from '@theia/core/lib/browser/theming';
import { MenuModelRegistry, CommandRegistry, MAIN_MENU_BAR, Command, Emitter, Mutable } from '@theia/core/lib/common';
@@ -42,6 +42,9 @@ import { DebugService } from '../common/debug-service';
import { DebugSchemaUpdater } from './debug-schema-updater';
import { DebugPreferences } from './debug-preferences';
import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { DebugWatchWidget } from './view/debug-watch-widget';
+import { DebugWatchExpression } from './view/debug-watch-expression';
+import { DebugWatchManager } from './debug-watch-manager';
export namespace DebugMenus {
export const DEBUG = [...MAIN_MENU_BAR, '6_debug'];
@@ -214,6 +217,42 @@ export namespace DebugCommands {
category: DEBUG_CATEGORY,
label: 'Copy As Expression',
};
+ export const WATCH_VARIABLE: Command = {
+ id: 'debug.variable.watch',
+ category: DEBUG_CATEGORY,
+ label: 'Add to Watch',
+ };
+
+ export const ADD_WATCH_EXPRESSION: Command = {
+ id: 'debug.watch.addExpression',
+ category: DEBUG_CATEGORY,
+ label: 'Add Watch Expression'
+ };
+ export const EDIT_WATCH_EXPRESSION: Command = {
+ id: 'debug.watch.editExpression',
+ category: DEBUG_CATEGORY,
+ label: 'Edit Watch Expression'
+ };
+ export const COPY_WATCH_EXPRESSION_VALUE: Command = {
+ id: 'debug.watch.copyExpressionValue',
+ category: DEBUG_CATEGORY,
+ label: 'Copy Watch Expression Value'
+ };
+ export const REMOVE_WATCH_EXPRESSION: Command = {
+ id: 'debug.watch.removeExpression',
+ category: DEBUG_CATEGORY,
+ label: 'Remove Watch Expression'
+ };
+ export const COLLAPSE_ALL_WATCH_EXPRESSIONS: Command = {
+ id: 'debug.watch.collapseAllExpressions',
+ category: DEBUG_CATEGORY,
+ label: 'Collapse All Watch Expressions'
+ };
+ export const REMOVE_ALL_WATCH_EXPRESSIONS: Command = {
+ id: 'debug.watch.removeAllExpressions',
+ category: DEBUG_CATEGORY,
+ label: 'Remove All Watch Expresssions'
+ };
}
export namespace DebugThreadContextCommands {
export const STEP_OVER = {
@@ -354,6 +393,9 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
@inject(DebugPreferences)
protected readonly preference: DebugPreferences;
+ @inject(DebugWatchManager)
+ protected readonly watchManager: DebugWatchManager;
+
constructor() {
super({
widgetId: DebugWidget.ID,
@@ -404,11 +446,13 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
this.schemaUpdater.update();
this.configurations.load();
await this.breakpointManager.load();
+ await this.watchManager.load();
}
onStop(): void {
this.configurations.save();
this.breakpointManager.save();
+ this.watchManager.save();
}
registerMenus(menus: MenuModelRegistry): void {
@@ -479,11 +523,23 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
DebugCommands.COPY_CALL_STACK
);
- registerMenuActions(DebugVariablesWidget.CONTEXT_MENU,
+ registerMenuActions(DebugVariablesWidget.EDIT_MENU,
DebugCommands.SET_VARIABLE_VALUE,
DebugCommands.COPY_VAIRABLE_VALUE,
DebugCommands.COPY_VAIRABLE_AS_EXPRESSION
);
+ registerMenuActions(DebugVariablesWidget.WATCH_MENU,
+ DebugCommands.WATCH_VARIABLE
+ );
+
+ registerMenuActions(DebugWatchWidget.EDIT_MENU,
+ { ...DebugCommands.EDIT_WATCH_EXPRESSION, label: 'Edit Expression' },
+ { ...DebugCommands.COPY_WATCH_EXPRESSION_VALUE, label: 'Copy Value' }
+ );
+ registerMenuActions(DebugWatchWidget.REMOVE_MENU,
+ { ...DebugCommands.REMOVE_WATCH_EXPRESSION, label: 'Remove Expression' },
+ { ...DebugCommands.REMOVE_ALL_WATCH_EXPRESSIONS, label: 'Remove All Expressions' }
+ );
registerMenuActions(DebugBreakpointsWidget.EDIT_MENU,
DebugCommands.EDIT_BREAKPOINT,
@@ -751,6 +807,16 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isEnabled: () => !!this.selectedVariable && this.selectedVariable.supportCopyAsExpression,
isVisible: () => !!this.selectedVariable && this.selectedVariable.supportCopyAsExpression
});
+ registry.registerCommand(DebugCommands.WATCH_VARIABLE, {
+ execute: () => {
+ const { selectedVariable, watch } = this;
+ if (selectedVariable && watch) {
+ watch.viewModel.addWatchExpression(selectedVariable.name);
+ }
+ },
+ isEnabled: () => !!this.selectedVariable && !!this.watch,
+ isVisible: () => !!this.selectedVariable && !!this.watch,
+ });
registry.registerCommand(DebugEditorContextCommands.ADD_BREAKPOINT, {
execute: () => this.editors.toggleBreakpoint(),
@@ -814,6 +880,68 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
registry.registerCommand(DebugBreakpointWidgetCommands.CLOSE, {
execute: () => this.editors.closeBreakpoint()
});
+
+ registry.registerCommand(DebugCommands.ADD_WATCH_EXPRESSION, {
+ execute: widget => {
+ if (widget instanceof Widget) {
+ if (widget instanceof DebugWatchWidget) {
+ widget.viewModel.addWatchExpression();
+ }
+ } else if (this.watch) {
+ this.watch.viewModel.addWatchExpression();
+ }
+ },
+ isEnabled: widget => widget instanceof Widget ? widget instanceof DebugWatchWidget : !!this.watch,
+ isVisible: widget => widget instanceof Widget ? widget instanceof DebugWatchWidget : !!this.watch
+ });
+ registry.registerCommand(DebugCommands.EDIT_WATCH_EXPRESSION, {
+ execute: () => {
+ const { watchExpression } = this;
+ if (watchExpression) {
+ watchExpression.open();
+ }
+ },
+ isEnabled: () => !!this.watchExpression,
+ isVisible: () => !!this.watchExpression
+ });
+ registry.registerCommand(DebugCommands.COPY_WATCH_EXPRESSION_VALUE, {
+ execute: () => this.watchExpression && this.watchExpression.copyValue(),
+ isEnabled: () => !!this.watchExpression && this.watchExpression.supportCopyValue,
+ isVisible: () => !!this.watchExpression && this.watchExpression.supportCopyValue
+ });
+ registry.registerCommand(DebugCommands.COLLAPSE_ALL_WATCH_EXPRESSIONS, {
+ execute: widget => {
+ if (widget instanceof DebugWatchWidget) {
+ const root = widget.model.root;
+ widget.model.collapseAll(CompositeTreeNode.is(root) ? root : undefined);
+ }
+ },
+ isEnabled: widget => widget instanceof DebugWatchWidget,
+ isVisible: widget => widget instanceof DebugWatchWidget
+ });
+ registry.registerCommand(DebugCommands.REMOVE_WATCH_EXPRESSION, {
+ execute: () => {
+ const { watch, watchExpression } = this;
+ if (watch && watchExpression) {
+ watch.viewModel.removeWatchExpression(watchExpression);
+ }
+ },
+ isEnabled: () => !!this.watchExpression,
+ isVisible: () => !!this.watchExpression
+ });
+ registry.registerCommand(DebugCommands.REMOVE_ALL_WATCH_EXPRESSIONS, {
+ execute: widget => {
+ if (widget instanceof Widget) {
+ if (widget instanceof DebugWatchWidget) {
+ widget.viewModel.removeWatchExpressions();
+ }
+ } else if (this.watch) {
+ this.watch.viewModel.removeWatchExpressions();
+ }
+ },
+ isEnabled: widget => widget instanceof Widget ? widget instanceof DebugWatchWidget : !!this.watch,
+ isVisible: widget => widget instanceof Widget ? widget instanceof DebugWatchWidget : !!this.watch
+ });
}
registerKeybindings(keybindings: KeybindingRegistry): void {
@@ -902,9 +1030,30 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
toolbar.registerItem({
id: DebugCommands.REMOVE_ALL_BREAKPOINTS.id,
command: DebugCommands.REMOVE_ALL_BREAKPOINTS.id,
- icon: 'fa breakpoints-remove-all',
+ icon: 'theia-remove-all-icon',
+ priority: 1
+ });
+
+ toolbar.registerItem({
+ id: DebugCommands.ADD_WATCH_EXPRESSION.id,
+ command: DebugCommands.ADD_WATCH_EXPRESSION.id,
+ icon: 'theia-add-icon',
+ tooltip: 'Add Expression'
+ });
+ toolbar.registerItem({
+ id: DebugCommands.COLLAPSE_ALL_WATCH_EXPRESSIONS.id,
+ command: DebugCommands.COLLAPSE_ALL_WATCH_EXPRESSIONS.id,
+ icon: 'theia-collapse-all-icon',
+ tooltip: 'Collapse All',
priority: 1
});
+ toolbar.registerItem({
+ id: DebugCommands.REMOVE_ALL_WATCH_EXPRESSIONS.id,
+ command: DebugCommands.REMOVE_ALL_WATCH_EXPRESSIONS.id,
+ icon: 'theia-remove-all-icon',
+ tooltip: 'Remove All Expressions',
+ priority: 2
+ });
}
protected readonly sessionWidgets = new Map();
@@ -1014,4 +1163,13 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
return variables && variables.selectedElement instanceof DebugVariable && variables.selectedElement || undefined;
}
+ get watch(): DebugWatchWidget | undefined {
+ const { currentWidget } = this.shell;
+ return currentWidget instanceof DebugWatchWidget && currentWidget || undefined;
+ }
+ get watchExpression(): DebugWatchExpression | undefined {
+ const { watch } = this;
+ return watch && watch.selectedElement instanceof DebugWatchExpression && watch.selectedElement || undefined;
+ }
+
}
diff --git a/packages/debug/src/browser/debug-frontend-module.ts b/packages/debug/src/browser/debug-frontend-module.ts
index bc62cefce199c..794426c694180 100644
--- a/packages/debug/src/browser/debug-frontend-module.ts
+++ b/packages/debug/src/browser/debug-frontend-module.ts
@@ -51,6 +51,7 @@ import { bindLaunchPreferences } from './preferences/launch-preferences';
import { DebugPrefixConfiguration } from './debug-prefix-configuration';
import { CommandContribution } from '@theia/core/lib/common/command';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { DebugWatchManager } from './debug-watch-manager';
export default new ContainerModule((bind: interfaces.Bind) => {
bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) =>
@@ -100,4 +101,6 @@ export default new ContainerModule((bind: interfaces.Bind) => {
bindDebugPreferences(bind);
bindLaunchPreferences(bind);
+
+ bind(DebugWatchManager).toSelf().inSingletonScope();
});
diff --git a/packages/debug/src/browser/debug-session-connection.ts b/packages/debug/src/browser/debug-session-connection.ts
index 001325011d556..8f8c364c58047 100644
--- a/packages/debug/src/browser/debug-session-connection.ts
+++ b/packages/debug/src/browser/debug-session-connection.ts
@@ -180,7 +180,21 @@ export class DebugSessionConnection implements Disposable {
arguments: args
};
+ const onDispose = this.toDispose.push(Disposable.create(() => {
+ const pendingRequest = this.pendingRequests.get(request.seq);
+ if (pendingRequest) {
+ pendingRequest({
+ type: 'response',
+ request_seq: request.seq,
+ command: request.command,
+ seq: 0,
+ success: false,
+ message: 'debug session is closed'
+ });
+ }
+ }));
this.pendingRequests.set(request.seq, (response: K) => {
+ onDispose.dispose();
if (!response.success) {
result.reject(response);
} else {
diff --git a/packages/debug/src/browser/debug-watch-manager.ts b/packages/debug/src/browser/debug-watch-manager.ts
new file mode 100644
index 0000000000000..ff11ce244714e
--- /dev/null
+++ b/packages/debug/src/browser/debug-watch-manager.ts
@@ -0,0 +1,93 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { injectable, inject } from 'inversify';
+import { Emitter } from '@theia/core/lib/common/event';
+import { StorageService } from '@theia/core/lib/browser/storage-service';
+
+@injectable()
+export class DebugWatchManager {
+
+ @inject(StorageService)
+ protected readonly storage: StorageService;
+
+ protected readonly onDidChangeEmitter = new Emitter();
+ readonly onDidChange = this.onDidChangeEmitter.event;
+
+ protected idSequence = 0;
+ protected readonly _watchExpressions = new Map();
+
+ get watchExpressions(): IterableIterator<[number, string]> {
+ return this._watchExpressions.entries();
+ }
+
+ addWatchExpression(expression: string): number {
+ const id = this.idSequence++;
+ this._watchExpressions.set(id, expression);
+ this.onDidChangeEmitter.fire(undefined);
+ return id;
+ }
+
+ removeWatchExpression(id: number): boolean {
+ if (!this._watchExpressions.has(id)) {
+ return false;
+ }
+ this._watchExpressions.delete(id);
+ this.onDidChangeEmitter.fire(undefined);
+ return true;
+ }
+
+ removeWatchExpressions(): void {
+ if (this._watchExpressions.size) {
+ this.idSequence = 0;
+ this._watchExpressions.clear();
+ this.onDidChangeEmitter.fire(undefined);
+ }
+ }
+
+ async load(): Promise {
+ const data = await this.storage.getData(this.storageKey, {
+ expressions: []
+ });
+ this.restoreState(data);
+ }
+
+ save(): void {
+ const data = this.storeState();
+ this.storage.setData(this.storageKey, data);
+ }
+
+ protected get storageKey(): string {
+ return 'debug:watch';
+ }
+
+ protected storeState(): DebugWatchData {
+ return {
+ expressions: [...this._watchExpressions.values()]
+ };
+ }
+
+ protected restoreState(state: DebugWatchData): void {
+ for (const expression of state.expressions) {
+ this.addWatchExpression(expression);
+ }
+ }
+
+}
+
+export interface DebugWatchData {
+ readonly expressions: string[];
+}
diff --git a/packages/debug/src/browser/editor/debug-hover-source.tsx b/packages/debug/src/browser/editor/debug-hover-source.tsx
index eeb69badb61f3..b86ce847cf24c 100644
--- a/packages/debug/src/browser/editor/debug-hover-source.tsx
+++ b/packages/debug/src/browser/editor/debug-hover-source.tsx
@@ -60,7 +60,7 @@ export class DebugHoverSource extends TreeSource {
return undefined;
}
if (currentSession.capabilities.supportsEvaluateForHovers) {
- const item = new ExpressionItem(expression, currentSession);
+ const item = new ExpressionItem(expression, () => currentSession);
await item.evaluate('hover');
return item.available && item || undefined;
}
diff --git a/packages/debug/src/browser/model/debug-stack-frame.tsx b/packages/debug/src/browser/model/debug-stack-frame.tsx
index e98c4e1cb6810..5a9611bda3174 100644
--- a/packages/debug/src/browser/model/debug-stack-frame.tsx
+++ b/packages/debug/src/browser/model/debug-stack-frame.tsx
@@ -84,12 +84,16 @@ export class DebugStackFrame extends DebugStackFrameData implements TreeElement
return this.scopes || (this.scopes = this.doGetScopes());
}
protected async doGetScopes(): Promise {
+ let response;
try {
- const response = await this.session.sendRequest('scopes', this.toArgs());
- return response.body.scopes.map(raw => new DebugScope(raw, this.session));
+ response = await this.session.sendRequest('scopes', this.toArgs());
} catch (e) {
+ // no-op: ignore debug adapter errors
+ }
+ if (!response) {
return [];
}
+ return response.body.scopes.map(raw => new DebugScope(raw, () => this.session));
}
protected toArgs(arg?: T): { frameId: number } & T {
diff --git a/packages/debug/src/browser/style/debug-bright.useable.css b/packages/debug/src/browser/style/debug-bright.useable.css
index ad603fbe020f2..5b1f2aa740cdf 100644
--- a/packages/debug/src/browser/style/debug-bright.useable.css
+++ b/packages/debug/src/browser/style/debug-bright.useable.css
@@ -9,6 +9,5 @@
--theia-debug-step-out: url('step-out.svg');
--theia-debug-configure: url('configure.svg');
--theia-debug-repl: url('repl.svg');
- --breakpoints-remove-all-url: url('remove-all.svg');
--breakpoints-activate-url: url('breakpoints-activate.svg');
}
diff --git a/packages/debug/src/browser/style/debug-dark.useable.css b/packages/debug/src/browser/style/debug-dark.useable.css
index 056b2b3595023..b33b4a5274d78 100644
--- a/packages/debug/src/browser/style/debug-dark.useable.css
+++ b/packages/debug/src/browser/style/debug-dark.useable.css
@@ -9,6 +9,5 @@
--theia-debug-step-out: url('step-out-inverse.svg');
--theia-debug-configure: url('configure-inverse.svg');
--theia-debug-repl: url('repl-inverse.svg');
- --breakpoints-remove-all-url: url('remove-all-inverse.svg');
--breakpoints-activate-url: url('breakpoints-activate-inverse.svg');
}
diff --git a/packages/debug/src/browser/style/index.css b/packages/debug/src/browser/style/index.css
index b63dd49fbb75a..56d52040e8867 100644
--- a/packages/debug/src/browser/style/index.css
+++ b/packages/debug/src/browser/style/index.css
@@ -307,9 +307,6 @@
background: var(--theia-debug-step-out) center center no-repeat;
}
-.breakpoints-remove-all {
- background: var(--breakpoints-remove-all-url) center center no-repeat;
-}
.breakpoints-activate {
background: var(--breakpoints-activate-url) center center no-repeat;
}
diff --git a/packages/debug/src/browser/view/debug-session-widget.ts b/packages/debug/src/browser/view/debug-session-widget.ts
index a1856db39606d..b7e611c2ede30 100644
--- a/packages/debug/src/browser/view/debug-session-widget.ts
+++ b/packages/debug/src/browser/view/debug-session-widget.ts
@@ -24,6 +24,7 @@ import { DebugBreakpointsWidget } from './debug-breakpoints-widget';
import { DebugVariablesWidget } from './debug-variables-widget';
import { DebugToolBar } from './debug-toolbar-widget';
import { DebugViewModel, DebugViewOptions } from './debug-view-model';
+import { DebugWatchWidget } from './debug-watch-widget';
export const DebugSessionWidgetFactory = Symbol('DebugSessionWidgetFactory');
export type DebugSessionWidgetFactory = (options: DebugViewOptions) => DebugSessionWidget;
@@ -40,6 +41,7 @@ export class DebugSessionWidget extends BaseWidget implements StatefulWidget, Ap
child.bind(DebugThreadsWidget).toDynamicValue(({ container }) => DebugThreadsWidget.createWidget(container));
child.bind(DebugStackFramesWidget).toDynamicValue(({ container }) => DebugStackFramesWidget.createWidget(container));
child.bind(DebugVariablesWidget).toDynamicValue(({ container }) => DebugVariablesWidget.createWidget(container));
+ child.bind(DebugWatchWidget).toDynamicValue(({ container }) => DebugWatchWidget.createWidget(container));
child.bind(DebugBreakpointsWidget).toDynamicValue(({ container }) => DebugBreakpointsWidget.createWidget(container));
child.bind(DebugSessionWidget).toSelf();
return child;
@@ -68,6 +70,9 @@ export class DebugSessionWidget extends BaseWidget implements StatefulWidget, Ap
@inject(DebugVariablesWidget)
protected readonly variables: DebugVariablesWidget;
+ @inject(DebugWatchWidget)
+ protected readonly watch: DebugWatchWidget;
+
@inject(DebugBreakpointsWidget)
protected readonly breakpoints: DebugBreakpointsWidget;
@@ -75,16 +80,18 @@ export class DebugSessionWidget extends BaseWidget implements StatefulWidget, Ap
protected init(): void {
this.id = 'debug:session:' + this.model.id;
this.title.label = this.model.label;
+ this.title.caption = this.model.label;
this.title.closable = true;
- this.title.iconClass = 'fa debug-tab-icon';
+ this.title.iconClass = 'debug-tab-icon';
this.addClass('theia-session-container');
this.viewContainer = this.viewContainerFactory({
- id: 'debug:view-container'
+ id: 'debug:view-container:' + this.model.id
});
this.viewContainer.addWidget(this.threads, { weight: 30 });
this.viewContainer.addWidget(this.stackFrames, { weight: 20 });
this.viewContainer.addWidget(this.variables, { weight: 10 });
+ this.viewContainer.addWidget(this.watch, { weight: 10 });
this.viewContainer.addWidget(this.breakpoints, { weight: 10 });
this.toDispose.pushAll([
diff --git a/packages/debug/src/browser/view/debug-variables-widget.ts b/packages/debug/src/browser/view/debug-variables-widget.ts
index 11a11cc15bd5a..5ada69010a664 100644
--- a/packages/debug/src/browser/view/debug-variables-widget.ts
+++ b/packages/debug/src/browser/view/debug-variables-widget.ts
@@ -24,6 +24,8 @@ import { DebugViewModel } from './debug-view-model';
export class DebugVariablesWidget extends SourceTreeWidget {
static CONTEXT_MENU: MenuPath = ['debug-variables-context-menu'];
+ static EDIT_MENU: MenuPath = [...DebugVariablesWidget.CONTEXT_MENU, 'a_edit'];
+ static WATCH_MENU: MenuPath = [...DebugVariablesWidget.CONTEXT_MENU, 'b_watch'];
static createContainer(parent: interfaces.Container): Container {
const child = SourceTreeWidget.createContainer(parent, {
contextMenuPath: DebugVariablesWidget.CONTEXT_MENU,
diff --git a/packages/debug/src/browser/view/debug-view-model.ts b/packages/debug/src/browser/view/debug-view-model.ts
index 338fe5d8bae91..8c097dbd228c3 100644
--- a/packages/debug/src/browser/view/debug-view-model.ts
+++ b/packages/debug/src/browser/view/debug-view-model.ts
@@ -14,6 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
+import debounce from 'p-debounce';
import { injectable, inject, postConstruct } from 'inversify';
import { Disposable, DisposableCollection, Event, Emitter } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
@@ -22,6 +23,8 @@ import { DebugSessionManager } from '../debug-session-manager';
import { DebugThread } from '../model/debug-thread';
import { DebugStackFrame } from '../model/debug-stack-frame';
import { DebugBreakpoint } from '../model/debug-breakpoint';
+import { DebugWatchExpression } from './debug-watch-expression';
+import { DebugWatchManager } from '../debug-watch-manager';
export const DebugViewOptions = Symbol('DebugViewOptions');
export interface DebugViewOptions {
@@ -34,6 +37,7 @@ export class DebugViewModel implements Disposable {
protected readonly onDidChangeEmitter = new Emitter();
readonly onDidChange: Event = this.onDidChangeEmitter.event;
protected fireDidChange(): void {
+ this.refreshWatchExpressions();
this.onDidChangeEmitter.fire(undefined);
}
@@ -43,9 +47,18 @@ export class DebugViewModel implements Disposable {
this.onDidChangeBreakpointsEmitter.fire(uri);
}
+ protected readonly _watchExpressions = new Map();
+
+ protected readonly onDidChangeWatchExpressionsEmitter = new Emitter();
+ readonly onDidChangeWatchExpressions = this.onDidChangeWatchExpressionsEmitter.event;
+ protected fireDidChangeWatchExpressions(): void {
+ this.onDidChangeWatchExpressionsEmitter.fire(undefined);
+ }
+
protected readonly toDispose = new DisposableCollection(
this.onDidChangeEmitter,
- this.onDidChangeBreakpointsEmitter
+ this.onDidChangeBreakpointsEmitter,
+ this.onDidChangeWatchExpressionsEmitter
);
@inject(DebugViewOptions)
@@ -54,6 +67,9 @@ export class DebugViewModel implements Disposable {
@inject(DebugSessionManager)
protected readonly manager: DebugSessionManager;
+ @inject(DebugWatchManager)
+ protected readonly watch: DebugWatchManager;
+
protected readonly _sessions = new Set();
get sessions(): IterableIterator {
return this._sessions.values();
@@ -109,6 +125,8 @@ export class DebugViewModel implements Disposable {
this.fireDidChangeBreakpoints(uri);
}
}));
+ this.updateWatchExpressions();
+ this.toDispose.push(this.watch.onDidChange(() => this.updateWatchExpressions()));
}
dispose(): void {
@@ -166,4 +184,69 @@ export class DebugViewModel implements Disposable {
this.fireDidChange();
}
+ get watchExpressions(): IterableIterator {
+ return this._watchExpressions.values();
+ }
+
+ async addWatchExpression(expression: string = ''): Promise {
+ const watchExpression = new DebugWatchExpression({
+ id: Number.MAX_SAFE_INTEGER,
+ expression,
+ session: () => this.currentSession,
+ onDidChange: () => { /* no-op */ }
+ });
+ await watchExpression.open();
+ if (!watchExpression.expression) {
+ return undefined;
+ }
+ const id = this.watch.addWatchExpression(watchExpression.expression);
+ return this._watchExpressions.get(id);
+ }
+
+ removeWatchExpressions(): void {
+ this.watch.removeWatchExpressions();
+ }
+
+ removeWatchExpression(expression: DebugWatchExpression): void {
+ this.watch.removeWatchExpression(expression.id);
+ }
+
+ protected updateWatchExpressions(): void {
+ let added = false;
+ const toRemove = new Set(this._watchExpressions.keys());
+ for (const [id, expression] of this.watch.watchExpressions) {
+ toRemove.delete(id);
+ if (!this._watchExpressions.has(id)) {
+ added = true;
+ const watchExpression = new DebugWatchExpression({
+ id,
+ expression,
+ session: () => this.currentSession,
+ onDidChange: () => this.fireDidChangeWatchExpressions()
+ });
+ this._watchExpressions.set(id, watchExpression);
+ watchExpression.evaluate();
+ }
+ }
+ for (const id of toRemove) {
+ this._watchExpressions.delete(id);
+ }
+ if (added || toRemove.size) {
+ this.fireDidChangeWatchExpressions();
+ }
+ }
+
+ protected refreshWatchExpressionsQueue = Promise.resolve();
+ protected refreshWatchExpressions = debounce(() => {
+ this.refreshWatchExpressionsQueue = this.refreshWatchExpressionsQueue.then(async () => {
+ try {
+ for (const watchExpression of this.watchExpressions) {
+ await watchExpression.evaluate();
+ }
+ } catch (e) {
+ console.error('Failed to refresh watch expressions: ', e);
+ }
+ });
+ }, 50);
+
}
diff --git a/packages/debug/src/browser/view/debug-watch-expression.tsx b/packages/debug/src/browser/view/debug-watch-expression.tsx
new file mode 100644
index 0000000000000..3e1d8f142a6ba
--- /dev/null
+++ b/packages/debug/src/browser/view/debug-watch-expression.tsx
@@ -0,0 +1,78 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import * as React from 'react';
+import { SingleTextInputDialog } from '@theia/core/lib/browser/dialogs';
+import { ExpressionItem, DebugSessionProvider } from '../console/debug-console-items';
+import { DebugProtocol } from 'vscode-debugprotocol';
+
+export class DebugWatchExpression extends ExpressionItem {
+
+ readonly id: number;
+
+ constructor(protected readonly options: {
+ id: number,
+ expression: string,
+ session: DebugSessionProvider,
+ onDidChange: () => void
+ }) {
+ super(options.expression, options.session);
+ this.id = options.id;
+ }
+
+ async evaluate(): Promise {
+ await super.evaluate('watch');
+ }
+
+ protected setResult(body?: DebugProtocol.EvaluateResponse['body']): void {
+ // overriden to ignore error
+ super.setResult(body);
+ this.options.onDidChange();
+ }
+
+ render(): React.ReactNode {
+ return
+ {this._expression}:
+ {this._value}
+
;
+ }
+
+ async open(): Promise {
+ const input = new SingleTextInputDialog({
+ title: 'Edit Watch Expression',
+ initialValue: this.expression
+ });
+ const newValue = await input.open();
+ if (newValue !== undefined) {
+ this._expression = newValue;
+ await this.evaluate();
+ }
+ }
+
+ get supportCopyValue(): boolean {
+ return !!this.valueRef && document.queryCommandSupported('copy');
+ }
+ copyValue(): void {
+ const selection = document.getSelection();
+ if (this.valueRef && selection) {
+ selection.selectAllChildren(this.valueRef);
+ document.execCommand('copy');
+ }
+ }
+ protected valueRef: HTMLSpanElement | undefined;
+ protected setValueRef = (valueRef: HTMLSpanElement | null) => this.valueRef = valueRef || undefined;
+
+}
diff --git a/packages/debug/src/browser/view/debug-watch-source.ts b/packages/debug/src/browser/view/debug-watch-source.ts
new file mode 100644
index 0000000000000..fd7a67e3ac3bb
--- /dev/null
+++ b/packages/debug/src/browser/view/debug-watch-source.ts
@@ -0,0 +1,47 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { injectable, inject, postConstruct } from 'inversify';
+import { TreeSource } from '@theia/core/lib/browser/source-tree';
+import { DebugViewModel } from './debug-view-model';
+import { DebugWatchExpression } from './debug-watch-expression';
+import debounce = require('p-debounce');
+
+@injectable()
+export class DebugWatchSource extends TreeSource {
+
+ @inject(DebugViewModel)
+ protected readonly model: DebugViewModel;
+
+ constructor() {
+ super({
+ placeholder: 'No expressions'
+ });
+ }
+
+ @postConstruct()
+ protected init(): void {
+ this.refresh();
+ this.toDispose.push(this.model.onDidChangeWatchExpressions(() => this.refresh()));
+ }
+
+ protected readonly refresh = debounce(() => this.fireDidChange(), 100);
+
+ async getElements(): Promise> {
+ return this.model.watchExpressions[Symbol.iterator]();
+ }
+
+}
diff --git a/packages/debug/src/browser/view/debug-watch-widget.ts b/packages/debug/src/browser/view/debug-watch-widget.ts
new file mode 100644
index 0000000000000..a36487bb78b0f
--- /dev/null
+++ b/packages/debug/src/browser/view/debug-watch-widget.ts
@@ -0,0 +1,59 @@
+/********************************************************************************
+ * Copyright (C) 2019 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+import { injectable, inject, postConstruct, interfaces, Container } from 'inversify';
+import { MenuPath } from '@theia/core/lib/common';
+import { SourceTreeWidget } from '@theia/core/lib/browser/source-tree';
+import { DebugWatchSource } from './debug-watch-source';
+import { DebugViewModel } from './debug-view-model';
+
+@injectable()
+export class DebugWatchWidget extends SourceTreeWidget {
+
+ static CONTEXT_MENU: MenuPath = ['debug-watch-context-menu'];
+ static EDIT_MENU = [...DebugWatchWidget.CONTEXT_MENU, 'a_edit'];
+ static REMOVE_MENU = [...DebugWatchWidget.CONTEXT_MENU, 'b_remove'];
+ static createContainer(parent: interfaces.Container): Container {
+ const child = SourceTreeWidget.createContainer(parent, {
+ contextMenuPath: DebugWatchWidget.CONTEXT_MENU,
+ virtualized: false,
+ scrollIfActive: true
+ });
+ child.bind(DebugWatchSource).toSelf();
+ child.unbind(SourceTreeWidget);
+ child.bind(DebugWatchWidget).toSelf();
+ return child;
+ }
+ static createWidget(parent: interfaces.Container): DebugWatchWidget {
+ return DebugWatchWidget.createContainer(parent).get(DebugWatchWidget);
+ }
+
+ @inject(DebugViewModel)
+ readonly viewModel: DebugViewModel;
+
+ @inject(DebugWatchSource)
+ protected readonly variables: DebugWatchSource;
+
+ @postConstruct()
+ protected init(): void {
+ super.init();
+ this.id = 'debug:watch:' + this.viewModel.id;
+ this.title.label = 'Watch';
+ this.toDispose.push(this.variables);
+ this.source = this.variables;
+ }
+
+}
diff --git a/packages/markers/src/browser/problem/problem-contribution.ts b/packages/markers/src/browser/problem/problem-contribution.ts
index 89e8a2cd5c72c..c8067962899d8 100644
--- a/packages/markers/src/browser/problem/problem-contribution.ts
+++ b/packages/markers/src/browser/problem/problem-contribution.ts
@@ -40,7 +40,7 @@ export namespace ProblemsCommands {
};
export const COLLAPSE_ALL_TOOLBAR: Command = {
id: 'problems.collapse.all.toolbar',
- iconClass: 'collapse-all'
+ iconClass: 'theia-collapse-all-icon'
};
export const COPY: Command = {
id: 'problems.copy'
diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts
index 0edfb7cf173fa..0fb9b29df9990 100644
--- a/packages/navigator/src/browser/navigator-contribution.ts
+++ b/packages/navigator/src/browser/navigator-contribution.ts
@@ -48,7 +48,7 @@ export namespace FileNavigatorCommands {
id: 'navigator.collapse.all',
category: 'File',
label: 'Collapse Folders in Explorer',
- iconClass: 'collapse-all'
+ iconClass: 'theia-collapse-all-icon'
};
export const ADD_ROOT_FOLDER: Command = {
id: 'navigator.addRootFolder'
diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
index 742c1b784ba69..45a8a7dcb3851 100644
--- a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
+++ b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts
@@ -55,7 +55,7 @@ export namespace SearchInWorkspaceCommands {
id: 'search-in-workspace.collapse-all',
category: SEARCH_CATEGORY,
label: 'Collapse All',
- iconClass: 'collapse-all'
+ iconClass: 'theia-collapse-all-icon'
};
export const CLEAR_ALL: Command = {
id: 'search-in-workspace.clear-all',
diff --git a/packages/search-in-workspace/src/browser/styles/index.css b/packages/search-in-workspace/src/browser/styles/index.css
index 0162a42b5feac..13130d6946f43 100644
--- a/packages/search-in-workspace/src/browser/styles/index.css
+++ b/packages/search-in-workspace/src/browser/styles/index.css
@@ -68,10 +68,6 @@
background: var(--theia-icon-refresh) no-repeat;
}
-.p-TabBar-toolbar .item .collapse-all {
- background: var(--theia-icon-collapse-all) no-repeat;
-}
-
.p-TabBar-toolbar .item .clear-all {
background: var(--theia-icon-clear) no-repeat;
}