Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 73b6dc4

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
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>
1 parent 42bf1a0 commit 73b6dc4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4697
-3041
lines changed

‎.eslintrc.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = {
1818
'electron-app/src-gen/*',
1919
'electron-app/gen-webpack*.js',
2020
'!electron-app/webpack.config.js',
21-
'plugins/*',
21+
'electron-app/plugins/*',
2222
'arduino-ide-extension/src/node/cli-protocol',
2323
'**/lib/*',
2424
],

‎arduino-ide-extension/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"deepmerge": "^4.2.2",
7070
"drivelist": "^9.2.4",
7171
"electron-updater": "^4.6.5",
72+
"fast-deep-equal": "^3.1.3",
7273
"fast-json-stable-stringify": "^2.1.0",
7374
"fast-safe-stringify": "^2.1.1",
7475
"filename-reserved-regex": "^2.0.0",
@@ -169,7 +170,7 @@
169170
],
170171
"arduino": {
171172
"arduino-cli": {
172-
"version": "0.34.0"
173+
"version": "0.35.0-rc.7"
173174
},
174175
"arduino-fwuploader": {
175176
"version": "2.4.1"

‎arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import '../../src/browser/style/index.css';
2-
import { ContainerModule } from '@theia/core/shared/inversify';
2+
import { Container,ContainerModule } from '@theia/core/shared/inversify';
33
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager';
44
import { CommandContribution } from '@theia/core/lib/common/command';
55
import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
@@ -361,6 +361,16 @@ import { TerminalFrontendContribution as TheiaTerminalFrontendContribution } fro
361361
import { SelectionService } from '@theia/core/lib/common/selection-service';
362362
import { CommandService } from '@theia/core/lib/common/command';
363363
import { CorePreferences } from '@theia/core/lib/browser/core-preferences';
364+
import { AutoSelectProgrammer } from './contributions/auto-select-programmer';
365+
import { HostedPluginSupport } from './hosted/hosted-plugin-support';
366+
import { DebugSessionManager as TheiaDebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
367+
import { DebugSessionManager } from './theia/debug/debug-session-manager';
368+
import { DebugWidget } from '@theia/debug/lib/browser/view/debug-widget';
369+
import { DebugViewModel } from '@theia/debug/lib/browser/view/debug-view-model';
370+
import { DebugSessionWidget } from '@theia/debug/lib/browser/view/debug-session-widget';
371+
import { DebugConfigurationWidget } from './theia/debug/debug-configuration-widget';
372+
import { DebugConfigurationWidget as TheiaDebugConfigurationWidget } from '@theia/debug/lib/browser/view/debug-configuration-widget';
373+
import { DebugToolBar } from '@theia/debug/lib/browser/view/debug-toolbar-widget';
364374

365375
// Hack to fix copy/cut/paste issue after electron version update in Theia.
366376
// https://github.com/eclipse-theia/theia/issues/12487
@@ -756,6 +766,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
756766
Contribution.configure(bind, CreateCloudCopy);
757767
Contribution.configure(bind, UpdateArduinoState);
758768
Contribution.configure(bind, BoardsDataMenuUpdater);
769+
Contribution.configure(bind, AutoSelectProgrammer);
759770

760771
bindContributionProvider(bind, StartupTaskProvider);
761772
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
@@ -857,6 +868,28 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
857868
// To be able to use a `launch.json` from outside of the workspace.
858869
bind(DebugConfigurationManager).toSelf().inSingletonScope();
859870
rebind(TheiaDebugConfigurationManager).toService(DebugConfigurationManager);
871+
// To update the currently selected debug config <select> option when starting a debug session.
872+
bind(DebugSessionManager).toSelf().inSingletonScope();
873+
rebind(TheiaDebugSessionManager).toService(DebugSessionManager);
874+
// Customized debug widget with its customized config <select> to update it programmatically.
875+
bind(WidgetFactory)
876+
.toDynamicValue(({ container }) => ({
877+
id: DebugWidget.ID,
878+
createWidget: () => {
879+
const child = new Container({ defaultScope: 'Singleton' });
880+
child.parent = container;
881+
child.bind(DebugViewModel).toSelf();
882+
child.bind(DebugToolBar).toSelf();
883+
child.bind(DebugSessionWidget).toSelf();
884+
child.bind(DebugConfigurationWidget).toSelf(); // with the patched select
885+
child // use the customized one in the Theia DI
886+
.bind(TheiaDebugConfigurationWidget)
887+
.toService(DebugConfigurationWidget);
888+
child.bind(DebugWidget).toSelf();
889+
return child.get(DebugWidget);
890+
},
891+
}))
892+
.inSingletonScope();
860893

861894
// To avoid duplicate tabs use deepEqual instead of string equal: https://github.com/eclipse-theia/theia/issues/11309
862895
bind(WidgetManager).toSelf().inSingletonScope();

‎arduino-ide-extension/src/browser/boards/boards-data-store.ts‎

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
1010
import { Emitter, Event } from '@theia/core/lib/common/event';
1111
import { ILogger } from '@theia/core/lib/common/logger';
1212
import { deepClone, deepFreeze } from '@theia/core/lib/common/objects';
13+
import type { Mutable } from '@theia/core/lib/common/types';
1314
import { inject, injectable, named } from '@theia/core/shared/inversify';
1415
import {
1516
BoardDetails,
1617
BoardsService,
1718
ConfigOption,
19+
ConfigValue,
1820
Programmer,
1921
isBoardIdentifierChangeEvent,
22+
isProgrammer,
2023
} from '../../common/protocol';
2124
import { notEmpty } from '../../common/utils';
2225
import type {
@@ -74,7 +77,7 @@ export class BoardsDataStore
7477
const storedData =
7578
await this.storageService.getData<BoardsDataStore.Data>(key);
7679
if (!storedData) {
77-
// if not previously value is available for the board, do not update the cache
80+
// if no previously value is available for the board, do not update the cache
7881
continue;
7982
}
8083
const details = await this.loadBoardDetails(fqbn);
@@ -88,6 +91,13 @@ export class BoardsDataStore
8891
this.fireChanged(...changes);
8992
}
9093
}),
94+
this.onDidChange((event) => {
95+
const selectedFqbn =
96+
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
97+
if (event.changes.find((change) => change.fqbn === selectedFqbn)) {
98+
this.updateSelectedBoardData(selectedFqbn);
99+
}
100+
}),
91101
]);
92102

93103
Promise.all([
@@ -174,7 +184,7 @@ export class BoardsDataStore
174184
return storedData;
175185
}
176186

177-
const boardDetails = await this.getBoardDetailsSafe(fqbn);
187+
const boardDetails = await this.loadBoardDetails(fqbn);
178188
if (!boardDetails) {
179189
return BoardsDataStore.Data.EMPTY;
180190
}
@@ -220,11 +230,12 @@ export class BoardsDataStore
220230
}
221231
let updated = false;
222232
for (const value of configOption.values) {
223-
if (value.value === selectedValue) {
224-
(value as any).selected = true;
233+
const mutable: Mutable<ConfigValue> = value;
234+
if (mutable.value === selectedValue) {
235+
mutable.selected = true;
225236
updated = true;
226237
} else {
227-
(valueasany).selected = false;
238+
mutable.selected = false;
228239
}
229240
}
230241
if (!updated) {
@@ -245,9 +256,7 @@ export class BoardsDataStore
245256
return `.arduinoIDE-configOptions-${fqbn}`;
246257
}
247258

248-
protected async getBoardDetailsSafe(
249-
fqbn: string
250-
): Promise<BoardDetails | undefined> {
259+
async loadBoardDetails(fqbn: string): Promise<BoardDetails | undefined> {
251260
try {
252261
const details = await this.boardsService.getBoardDetails({ fqbn });
253262
return details;
@@ -280,21 +289,24 @@ export namespace BoardsDataStore {
280289
readonly configOptions: ConfigOption[];
281290
readonly programmers: Programmer[];
282291
readonly selectedProgrammer?: Programmer;
292+
readonly defaultProgrammerId?: string;
283293
}
284294
export namespace Data {
285295
export const EMPTY: Data = deepFreeze({
286296
configOptions: [],
287297
programmers: [],
288-
defaultProgrammerId: undefined,
289298
});
290299

291300
export function is(arg: unknown): arg is Data {
292301
return (
293-
!!arg &&
294-
'configOptions' in arg &&
295-
Array.isArray(arg['configOptions']) &&
296-
'programmers' in arg &&
297-
Array.isArray(arg['programmers'])
302+
typeof arg === 'object' &&
303+
arg !== null &&
304+
Array.isArray((<Data>arg).configOptions) &&
305+
Array.isArray((<Data>arg).programmers) &&
306+
((<Data>arg).selectedProgrammer === undefined ||
307+
isProgrammer((<Data>arg).selectedProgrammer)) &&
308+
((<Data>arg).defaultProgrammerId === undefined ||
309+
typeof (<Data>arg).defaultProgrammerId === 'string')
298310
);
299311
}
300312
}
@@ -304,7 +316,8 @@ export function isEmptyData(data: BoardsDataStore.Data): boolean {
304316
return (
305317
Boolean(!data.configOptions.length) &&
306318
Boolean(!data.programmers.length) &&
307-
Boolean(!data.selectedProgrammer)
319+
Boolean(!data.selectedProgrammer) &&
320+
Boolean(!data.defaultProgrammerId)
308321
);
309322
}
310323

@@ -324,16 +337,18 @@ export function findDefaultProgrammer(
324337
function createDataStoreEntry(details: BoardDetails): BoardsDataStore.Data {
325338
const configOptions = details.configOptions.slice();
326339
const programmers = details.programmers.slice();
340+
const { defaultProgrammerId } = details;
327341
const selectedProgrammer = findDefaultProgrammer(
328342
programmers,
329-
details.defaultProgrammerId
343+
defaultProgrammerId
330344
);
331-
return {
345+
constdata= {
332346
configOptions,
333347
programmers,
334-
defaultProgrammerId: details.defaultProgrammerId,
335-
selectedProgrammer,
348+
...(selectedProgrammer ? { selectedProgrammer } : {}),
349+
...(defaultProgrammerId ? { defaultProgrammerId } : {}),
336350
};
351+
return data;
337352
}
338353

339354
export interface BoardsDataStoreChange {
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import type { MaybePromise } from '@theia/core/lib/common/types';
2+
import { inject, injectable } from '@theia/core/shared/inversify';
3+
import {
4+
BoardDetails,
5+
Programmer,
6+
isBoardIdentifierChangeEvent,
7+
} from '../../common/protocol';
8+
import {
9+
BoardsDataStore,
10+
findDefaultProgrammer,
11+
isEmptyData,
12+
} from '../boards/boards-data-store';
13+
import { BoardsServiceProvider } from '../boards/boards-service-provider';
14+
import { Contribution } from './contribution';
15+
16+
/**
17+
* Before CLI 0.35.0-rc.3, there was no `programmer#default` property in the `board details` response.
18+
* This method does the programmer migration in the data store. If there is a programmer selected, it's a noop.
19+
* If no programmer is selected, it forcefully reloads the details from the CLI and updates it in the local storage.
20+
*/
21+
@injectable()
22+
export class AutoSelectProgrammer extends Contribution {
23+
@inject(BoardsServiceProvider)
24+
private readonly boardsServiceProvider: BoardsServiceProvider;
25+
@inject(BoardsDataStore)
26+
private readonly boardsDataStore: BoardsDataStore;
27+
28+
override onStart(): void {
29+
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
30+
if (isBoardIdentifierChangeEvent(event)) {
31+
this.ensureProgrammerIsSelected();
32+
}
33+
});
34+
}
35+
36+
override onReady(): void {
37+
this.boardsServiceProvider.ready.then(() =>
38+
this.ensureProgrammerIsSelected()
39+
);
40+
}
41+
42+
private async ensureProgrammerIsSelected(): Promise<boolean> {
43+
return ensureProgrammerIsSelected({
44+
fqbn: this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn,
45+
getData: (fqbn) => this.boardsDataStore.getData(fqbn),
46+
loadBoardDetails: (fqbn) => this.boardsDataStore.loadBoardDetails(fqbn),
47+
selectProgrammer: (arg) => this.boardsDataStore.selectProgrammer(arg),
48+
});
49+
}
50+
}
51+
52+
interface EnsureProgrammerIsSelectedParams {
53+
fqbn: string | undefined;
54+
getData: (fqbn: string | undefined) => MaybePromise<BoardsDataStore.Data>;
55+
loadBoardDetails: (fqbn: string) => MaybePromise<BoardDetails | undefined>;
56+
selectProgrammer(options: {
57+
fqbn: string;
58+
selectedProgrammer: Programmer;
59+
}): MaybePromise<boolean>;
60+
}
61+
62+
export async function ensureProgrammerIsSelected(
63+
params: EnsureProgrammerIsSelectedParams
64+
): Promise<boolean> {
65+
const { fqbn, getData, loadBoardDetails, selectProgrammer } = params;
66+
if (!fqbn) {
67+
return false;
68+
}
69+
console.debug(`Ensuring a programmer is selected for ${fqbn}...`);
70+
const data = await getData(fqbn);
71+
if (isEmptyData(data)) {
72+
// For example, the platform is not installed.
73+
console.debug(`Skipping. No boards data is available for ${fqbn}.`);
74+
return false;
75+
}
76+
if (data.selectedProgrammer) {
77+
console.debug(
78+
`A programmer is already selected for ${fqbn}: '${data.selectedProgrammer.id}'.`
79+
);
80+
return true;
81+
}
82+
let programmer = findDefaultProgrammer(data.programmers, data);
83+
if (programmer) {
84+
// select the programmer if the default info is available
85+
const result = await selectProgrammer({
86+
fqbn,
87+
selectedProgrammer: programmer,
88+
});
89+
if (result) {
90+
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
91+
return result;
92+
}
93+
}
94+
console.debug(`Reloading board details for ${fqbn}...`);
95+
const reloadedData = await loadBoardDetails(fqbn);
96+
if (!reloadedData) {
97+
console.debug(`Skipping. No board details found for ${fqbn}.`);
98+
return false;
99+
}
100+
if (!reloadedData.programmers.length) {
101+
console.debug(`Skipping. ${fqbn} does not have programmers.`);
102+
return false;
103+
}
104+
programmer = findDefaultProgrammer(reloadedData.programmers, reloadedData);
105+
if (!programmer) {
106+
console.debug(
107+
`Skipping. Could not find a default programmer for ${fqbn}. Programmers were: `
108+
);
109+
return false;
110+
}
111+
const result = await selectProgrammer({
112+
fqbn,
113+
selectedProgrammer: programmer,
114+
});
115+
if (result) {
116+
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
117+
} else {
118+
console.debug(
119+
`Could not select '${programmer.id}' programmer for ${fqbn}.`
120+
);
121+
}
122+
return result;
123+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /