feat: disable debug widget if unsupported by board

Remove the 'Add configuration...' select option from the debug widget.

Closes #14

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2024-01-26 16:48:21 +01:00 committed by Akos Kitta
parent 0e7b0c9486
commit 763fde036c
5 changed files with 115 additions and 39 deletions

View File

@ -180,7 +180,7 @@ import { TabBarRenderer } from './theia/core/tab-bars';
import { EditorCommandContribution } from './theia/editor/editor-command'; import { EditorCommandContribution } from './theia/editor/editor-command';
import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator'; import { NavigatorTabBarDecorator as TheiaNavigatorTabBarDecorator } from '@theia/navigator/lib/browser/navigator-tab-bar-decorator';
import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator'; import { NavigatorTabBarDecorator } from './theia/navigator/navigator-tab-bar-decorator';
import { Debug } from './contributions/debug'; import { Debug, DebugDisabledStatusMessageSource } from './contributions/debug';
import { Sketchbook } from './contributions/sketchbook'; import { Sketchbook } from './contributions/sketchbook';
import { DebugFrontendApplicationContribution } from './theia/debug/debug-frontend-application-contribution'; import { DebugFrontendApplicationContribution } from './theia/debug/debug-frontend-application-contribution';
import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution'; import { DebugFrontendApplicationContribution as TheiaDebugFrontendApplicationContribution } from '@theia/debug/lib/browser/debug-frontend-application-contribution';
@ -365,7 +365,8 @@ import { AutoSelectProgrammer } from './contributions/auto-select-programmer';
import { HostedPluginSupport } from './hosted/hosted-plugin-support'; import { HostedPluginSupport } from './hosted/hosted-plugin-support';
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager'; import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { DebugSessionManager } from './theia/debug/debug-session-manager'; import { DebugSessionManager } from './theia/debug/debug-session-manager';
import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget'; import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
import { DebugWidget } from './theia/debug/debug-widget';
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model'; import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget'; import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
import { DebugConfigurationWidget } from './theia/debug/debug-configuration-widget'; import { DebugConfigurationWidget } from './theia/debug/debug-configuration-widget';
@ -771,6 +772,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bindContributionProvider(bind, StartupTaskProvider); bindContributionProvider(bind, StartupTaskProvider);
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
bind(DebugDisabledStatusMessageSource).toService(Debug);
// Disabled the quick-pick customization from Theia when multiple formatters are available. // Disabled the quick-pick customization from Theia when multiple formatters are available.
// Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors. // Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors.
bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope(); bind(MonacoFormattingConflictsContribution).toSelf().inSingletonScope();
@ -874,7 +877,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Customized debug widget with its customized config <select> to update it programmatically. // Customized debug widget with its customized config <select> to update it programmatically.
bind(WidgetFactory) bind(WidgetFactory)
.toDynamicValue(({ container }) => ({ .toDynamicValue(({ container }) => ({
id: DebugWidget.ID, id: TheiaDebugWidget.ID,
createWidget: () => { createWidget: () => {
const child = new Container({ defaultScope: 'Singleton' }); const child = new Container({ defaultScope: 'Singleton' });
child.parent = container; child.parent = container;

View File

@ -64,8 +64,26 @@ interface StartDebugParams {
} }
type StartDebugResult = boolean; type StartDebugResult = boolean;
export const DebugDisabledStatusMessageSource = Symbol(
'DebugDisabledStatusMessageSource'
);
export interface DebugDisabledStatusMessageSource {
/**
* `undefined` if debugging is enabled (for the currently selected board + programmer + config options).
* Otherwise, it's the human readable message why it's disabled.
*/
get message(): string | undefined;
/**
* Emits an event when {@link message} changes.
*/
get onDidChangeMessage(): Event<string | undefined>;
}
@injectable() @injectable()
export class Debug extends SketchContribution { export class Debug
extends SketchContribution
implements DebugDisabledStatusMessageSource
{
@inject(HostedPluginSupport) @inject(HostedPluginSupport)
private readonly hostedPluginSupport: HostedPluginSupport; private readonly hostedPluginSupport: HostedPluginSupport;
@inject(NotificationCenter) @inject(NotificationCenter)
@ -83,10 +101,10 @@ export class Debug extends SketchContribution {
* If `undefined`, debugging is enabled. Otherwise, the human-readable reason why it's disabled. * If `undefined`, debugging is enabled. Otherwise, the human-readable reason why it's disabled.
*/ */
private _message?: string = noBoardSelected; // Initial pessimism. private _message?: string = noBoardSelected; // Initial pessimism.
private didChangeMessageEmitter = new Emitter<string | undefined>(); private readonly didChangeMessageEmitter = new Emitter<string | undefined>();
private onDidChangeMessage = this.didChangeMessageEmitter.event; readonly onDidChangeMessage = this.didChangeMessageEmitter.event;
private get message(): string | undefined { get message(): string | undefined {
return this._message; return this._message;
} }
private set message(message: string | undefined) { private set message(message: string | undefined) {
@ -349,6 +367,8 @@ export namespace Debug {
} }
/** /**
* Resolves with the FQBN to use for the `debug --info --programmer p --fqbn $FQBN` command. Otherwise, rejects.
*
* (non-API) * (non-API)
*/ */
export async function isDebugEnabled( export async function isDebugEnabled(

View File

@ -1,36 +1,13 @@
/* TODO: remove after https://github.com/eclipse-theia/theia/pull/9256/ */ /* Naive way of hiding the debug widget when the debug functionality is disabled https://github.com/arduino/arduino-ide/issues/14 */
.theia-debug-container .debug-toolbar.hidden,
/* To fix colors in Theia. */ .theia-debug-container .theia-session-container.hidden {
.theia-debug-hover-title.number, visibility: hidden;
.theia-debug-console-variable.number {
color: var(--theia-variable-number-variable-color);
}
.theia-debug-hover-title.boolean,
.theia-debug-console-variable.boolean {
color: var(--theia-variable-boolean-variable-color);
}
.theia-debug-hover-title.string,
.theia-debug-console-variable.string {
color: var(--theia-variable-string-variable-color);
} }
/* To unset the default debug hover dimension. */ .theia-debug-container .status-message {
.theia-debug-hover { font-family: "Open Sans";
min-width: unset; font-style: normal;
min-height: unset; font-size: 12px;
width: unset;
height: unset;
}
/* To adjust the left padding in the hover title. */ padding: 10px;
.theia-debug-hover-title {
padding-left: 5px;
}
/* Use the default Theia dimensions only iff the expression is complex (`!!expression.hasChildren~) */
.theia-debug-hover.complex-value {
min-width: 324px;
min-height: 324px;
width: 324px;
height: 324px;
} }

View File

@ -1,3 +1,4 @@
import { SelectOption } from '@theia/core/lib/browser/widgets/select-component';
import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { nls } from '@theia/core/lib/common/nls'; import { nls } from '@theia/core/lib/common/nls';
import { injectable } from '@theia/core/shared/inversify'; import { injectable } from '@theia/core/shared/inversify';
@ -51,6 +52,24 @@ class DebugConfigurationSelect extends TheiaDebugConfigurationSelect {
); );
} }
protected override renderOptions(): SelectOption[] {
const options = super.renderOptions();
const addConfiguration = options[options.length - 1];
const separator = options[options.length - 2];
// Remove "Add configuration..." and the preceding separator options.
// They're expected to be the last two items.
if (
addConfiguration.value ===
TheiaDebugConfigurationSelect.ADD_CONFIGURATION &&
separator.separator
) {
options.splice(options.length - 2, 2);
return options;
}
// Something is unexpected with the select options.
return options;
}
override componentWillUnmount(): void { override componentWillUnmount(): void {
this.toDisposeOnUnmount.dispose(); this.toDisposeOnUnmount.dispose();
} }

View File

@ -0,0 +1,57 @@
import {
codicon,
PanelLayout,
Widget,
} from '@theia/core/lib/browser/widgets/widget';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { DebugWidget as TheiaDebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
import { DebugDisabledStatusMessageSource } from '../../contributions/debug';
@injectable()
export class DebugWidget extends TheiaDebugWidget {
@inject(DebugDisabledStatusMessageSource)
private readonly debugStatusMessageSource: DebugDisabledStatusMessageSource;
private readonly statusMessageWidget = new Widget();
private readonly messageNode = document.createElement('div');
@postConstruct()
protected override init(): void {
super.init();
this.messageNode.classList.add('status-message', 'noselect');
this.statusMessageWidget.node.appendChild(this.messageNode);
this.updateState();
this.toDisposeOnDetach.pushAll([
this.debugStatusMessageSource.onDidChangeMessage((message) =>
this.updateState(message)
),
this.statusMessageWidget,
]);
}
private updateState(message = this.debugStatusMessageSource.message): void {
requestAnimationFrame(() => {
this.messageNode.textContent = message ?? '';
const enabled = !message;
updateVisibility(enabled, this.toolbar, this.sessionWidget);
if (this.layout instanceof PanelLayout) {
if (enabled) {
this.layout.removeWidget(this.statusMessageWidget);
} else {
this.layout.insertWidget(0, this.statusMessageWidget);
}
}
this.title.iconClass = enabled ? codicon('debug-alt') : 'fa fa-ban'; // TODO: find a better icon?
});
}
}
function updateVisibility(visible: boolean, ...widgets: Widget[]): void {
widgets.forEach((widget) =>
visible ? widget.removeClass('hidden') : widget.addClass('hidden')
);
}