feat: use new debug -I -P CLI output

- Can pick a programmer if missing,
 - Can auto-select a programmer on app start,
 - Can edit the `launch.json`,
 - Adjust board discovery to new gRPC API. From now on, it's a client
 read stream, not a duplex.
 - Allow `.cxx` and `.cc` file extensions. (Closes #2265)
 - Drop `debuggingSupported` from `BoardDetails`.
 - Dedicated service endpoint for checking the debugger.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta
2023-11-08 12:04:44 +01:00
committed by Akos Kitta
parent 42bf1a0e99
commit 73b6dc4774
48 changed files with 4697 additions and 3041 deletions

View File

@@ -1,44 +1,44 @@
import debounce from 'p-debounce';
import { inject, injectable } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
import { Disposable } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { DebugConfigurationManager as TheiaDebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager';
import { DebugConfigurationModel as TheiaDebugConfigurationModel } from '@theia/debug/lib/browser/debug-configuration-model';
import { DebugConfiguration } from '@theia/debug/lib/common/debug-common';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import {
FileOperationError,
FileOperationResult,
} from '@theia/filesystem/lib/common/files';
import debounce from 'p-debounce';
import { SketchesService } from '../../../common/protocol';
import {
CurrentSketch,
SketchesServiceClientImpl,
} from '../../sketches-service-client-impl';
import { maybeUpdateReadOnlyState } from '../monaco/monaco-editor-provider';
import { DebugConfigurationModel } from './debug-configuration-model';
import {
FileOperationError,
FileOperationResult,
} from '@theia/filesystem/lib/common/files';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
@injectable()
export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
@inject(SketchesService)
protected readonly sketchesService: SketchesService;
private readonly sketchesService: SketchesService;
@inject(SketchesServiceClientImpl)
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
private readonly sketchesServiceClient: SketchesServiceClientImpl;
@inject(FrontendApplicationStateService)
protected readonly appStateService: FrontendApplicationStateService;
private readonly appStateService: FrontendApplicationStateService;
@inject(FileService)
protected readonly fileService: FileService;
private readonly fileService: FileService;
protected onTempContentDidChangeEmitter =
private onTempContentDidChangeEmitter =
new Emitter<TheiaDebugConfigurationModel.JsonContent>();
get onTempContentDidChange(): Event<TheiaDebugConfigurationModel.JsonContent> {
return this.onTempContentDidChangeEmitter.event;
}
protected override async doInit(): Promise<void> {
this.watchLaunchConfigEditor();
this.appStateService.reachedState('ready').then(async () => {
const tempContent = await this.getTempLaunchJsonContent();
if (!tempContent) {
@@ -75,6 +75,19 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
return super.doInit();
}
/**
* Sets a listener on current sketch change, and maybe updates the readonly state of the editor showing the debug configuration. aka the `launch.json`.
*/
private watchLaunchConfigEditor(): Disposable {
return this.sketchesServiceClient.onCurrentSketchDidChange(() => {
for (const widget of this.editorManager.all) {
maybeUpdateReadOnlyState(widget, (uri) =>
this.sketchesServiceClient.isReadOnly(uri)
);
}
});
}
protected override updateModels = debounce(async () => {
await this.appStateService.reachedState('ready');
const roots = await this.workspaceService.roots;
@@ -111,7 +124,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
this.updateCurrent();
}, 500);
protected async getTempLaunchJsonContent(): Promise<
private async getTempLaunchJsonContent(): Promise<
(TheiaDebugConfigurationModel.JsonContent & { uri: URI }) | URI | undefined
> {
const sketch = await this.sketchesServiceClient.currentSketch();

View File

@@ -0,0 +1,57 @@
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { nls } from '@theia/core/lib/common/nls';
import { injectable } from '@theia/core/shared/inversify';
import React from '@theia/core/shared/react';
import { DebugAction } from '@theia/debug/lib/browser/view/debug-action';
import { DebugConfigurationSelect as TheiaDebugConfigurationSelect } from '@theia/debug/lib/browser/view/debug-configuration-select';
import { DebugConfigurationWidget as TheiaDebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
/**
* Patched to programmatically update the debug config <select> in the widget.
*/
@injectable()
export class DebugConfigurationWidget extends TheiaDebugConfigurationWidget {
override render(): React.ReactNode {
return (
<React.Fragment>
<DebugAction
run={this.start}
label={nls.localizeByDefault('Start Debugging')}
iconClass="debug-start"
ref={this.setStepRef}
/>
{/* The customized select component that will refresh when the config manager did change */}
<DebugConfigurationSelect
manager={this.manager}
quickInputService={this.quickInputService}
isMultiRoot={this.workspaceService.isMultiRootWorkspaceOpened}
/>
<DebugAction
run={this.openConfiguration}
label={nls.localizeByDefault('Open {0}', '"launch.json"')}
iconClass="settings-gear"
/>
<DebugAction
run={this.openConsole}
label={nls.localizeByDefault('Debug Console')}
iconClass="terminal"
/>
</React.Fragment>
);
}
}
class DebugConfigurationSelect extends TheiaDebugConfigurationSelect {
private readonly toDisposeOnUnmount = new DisposableCollection();
override componentDidMount(): void {
super.componentDidMount();
this.toDisposeOnUnmount.push(
this['manager'].onDidChange(() => this.refreshDebugConfigurations())
);
}
override componentWillUnmount(): void {
this.toDisposeOnUnmount.dispose();
}
}

View File

@@ -0,0 +1,39 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { DebugSession } from '@theia/debug/lib/browser/debug-session';
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import deepEqual from 'fast-deep-equal';
@injectable()
export class DebugSessionManager extends TheiaDebugSessionManager {
@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService;
protected override doStart(
sessionId: string,
options: DebugConfigurationSessionOptions
): Promise<DebugSession> {
this.syncCurrentOptions(options);
return super.doStart(sessionId, options);
}
/**
* If the debug config manager knows about the currently started options, and it's not the currently selected one, select it.
*/
private syncCurrentOptions(options: DebugConfigurationSessionOptions): void {
const knownConfigOptions = this.debugConfigurationManager.find(
options.configuration,
options.workspaceFolderUri ??
this.workspaceService
.tryGetRoots()
.map((stat) => stat.resource.toString())[0]
);
if (
knownConfigOptions &&
!deepEqual(knownConfigOptions, this.debugConfigurationManager.current)
) {
this.debugConfigurationManager.current = knownConfigOptions;
}
}
}