Skip to content

Commit 9f65b22

Browse files
author
Akos Kitta
committed
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`, Signed-off-by: Akos Kitta <[email protected]>
1 parent 503533d commit 9f65b22

18 files changed

+745
-115
lines changed

Diff for: .eslintrc.js

+1-1
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
],

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+4
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ 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 { SelectProgrammer } from './contributions/select-programmer';
365+
import { AutoSelectProgrammer } from './contributions/auto-select-programmer';
364366

365367
// Hack to fix copy/cut/paste issue after electron version update in Theia.
366368
// https://github.com/eclipse-theia/theia/issues/12487
@@ -753,6 +755,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
753755
Contribution.configure(bind, CreateCloudCopy);
754756
Contribution.configure(bind, UpdateArduinoState);
755757
Contribution.configure(bind, BoardsDataMenuUpdater);
758+
Contribution.configure(bind, SelectProgrammer);
759+
Contribution.configure(bind, AutoSelectProgrammer);
756760

757761
bindContributionProvider(bind, StartupTaskProvider);
758762
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window

Diff for: arduino-ide-extension/src/browser/boards/boards-data-store.ts

+26-14
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
44
import { Emitter, Event } from '@theia/core/lib/common/event';
55
import { ILogger } from '@theia/core/lib/common/logger';
66
import { deepClone } from '@theia/core/lib/common/objects';
7+
import type { Mutable } from '@theia/core/lib/common/types';
78
import { inject, injectable, named } from '@theia/core/shared/inversify';
89
import {
910
BoardDetails,
1011
BoardsService,
1112
ConfigOption,
13+
ConfigValue,
1214
Programmer,
15+
isProgrammer,
1316
} from '../../common/protocol';
1417
import { notEmpty } from '../../common/utils';
1518
import { NotificationCenter } from '../notification-center';
@@ -43,7 +46,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
4346
const key = this.getStorageKey(fqbn);
4447
let data = await this.storageService.getData<ConfigOption[]>(key);
4548
if (!data || !data.length) {
46-
const details = await this.getBoardDetailsSafe(fqbn);
49+
const details = await this.loadBoardDetails(fqbn);
4750
if (details) {
4851
data = details.configOptions;
4952
if (data.length) {
@@ -91,14 +94,15 @@ export class BoardsDataStore implements FrontendApplicationContribution {
9194
return data;
9295
}
9396

94-
const boardDetails = await this.getBoardDetailsSafe(fqbn);
97+
const boardDetails = await this.loadBoardDetails(fqbn);
9598
if (!boardDetails) {
9699
return BoardsDataStore.Data.EMPTY;
97100
}
98101

99102
data = {
100103
configOptions: boardDetails.configOptions,
101104
programmers: boardDetails.programmers,
105+
selectedProgrammer: boardDetails.programmers.find((p) => p.default),
102106
};
103107
await this.storageService.setData(key, data);
104108
return data;
@@ -142,11 +146,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
142146
}
143147
let updated = false;
144148
for (const value of configOption.values) {
145-
if (value.value === selectedValue) {
146-
(value as any).selected = true;
149+
const mutable: Mutable<ConfigValue> = value;
150+
if (mutable.value === selectedValue) {
151+
mutable.selected = true;
147152
updated = true;
148153
} else {
149-
(value as any).selected = false;
154+
mutable.selected = false;
150155
}
151156
}
152157
if (!updated) {
@@ -172,9 +177,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
172177
return `.arduinoIDE-configOptions-${fqbn}`;
173178
}
174179

175-
protected async getBoardDetailsSafe(
176-
fqbn: string
177-
): Promise<BoardDetails | undefined> {
180+
async loadBoardDetails(fqbn: string): Promise<BoardDetails | undefined> {
178181
try {
179182
const details = this.boardsService.getBoardDetails({ fqbn });
180183
return details;
@@ -213,14 +216,23 @@ export namespace BoardsDataStore {
213216
configOptions: [],
214217
programmers: [],
215218
};
216-
export function is(arg: any): arg is Data {
219+
export function is(arg: unknown): arg is Data {
217220
return (
218-
!!arg &&
219-
'configOptions' in arg &&
220-
Array.isArray(arg['configOptions']) &&
221-
'programmers' in arg &&
222-
Array.isArray(arg['programmers'])
221+
typeof arg === 'object' &&
222+
arg !== null &&
223+
Array.isArray((<Data>arg).configOptions) &&
224+
Array.isArray((<Data>arg).programmers) &&
225+
((<Data>arg).selectedProgrammer === undefined ||
226+
isProgrammer((<Data>arg).selectedProgrammer))
223227
);
224228
}
225229
}
226230
}
231+
232+
export function isEmptyData(data: BoardsDataStore.Data): boolean {
233+
return (
234+
Boolean(!data.configOptions.length) &&
235+
Boolean(!data.programmers.length) &&
236+
Boolean(!data.selectedProgrammer)
237+
);
238+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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 { BoardsDataStore, isEmptyData } from '../boards/boards-data-store';
9+
import { BoardsServiceProvider } from '../boards/boards-service-provider';
10+
import { Contribution } from './contribution';
11+
12+
/**
13+
* Before CLI 0.35.0-rc.3, there was no `programmer#default` property in the `board details` response.
14+
* This method does the programmer migration in the data store. If there is a programmer selected, it's a noop.
15+
* If no programmer is selected, it forcefully reloads the details from the CLI and updates it in the local storage.
16+
*/
17+
@injectable()
18+
export class AutoSelectProgrammer extends Contribution {
19+
@inject(BoardsServiceProvider)
20+
private readonly boardsServiceProvider: BoardsServiceProvider;
21+
@inject(BoardsDataStore)
22+
private readonly boardsDataStore: BoardsDataStore;
23+
24+
override onStart(): void {
25+
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
26+
if (isBoardIdentifierChangeEvent(event)) {
27+
this.ensureProgrammerIsSelected();
28+
}
29+
});
30+
}
31+
32+
override onReady(): void {
33+
this.boardsServiceProvider.ready.then(() =>
34+
this.ensureProgrammerIsSelected()
35+
);
36+
}
37+
38+
private async ensureProgrammerIsSelected(): Promise<boolean> {
39+
return ensureProgrammerIsSelected({
40+
fqbn: this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn,
41+
getData: (fqbn) => this.boardsDataStore.getData(fqbn),
42+
loadBoardDetails: (fqbn) => this.boardsDataStore.loadBoardDetails(fqbn),
43+
selectProgrammer: (arg) => this.boardsDataStore.selectProgrammer(arg),
44+
});
45+
}
46+
}
47+
48+
interface EnsureProgrammerIsSelectedParams {
49+
fqbn: string | undefined;
50+
getData: (fqbn: string | undefined) => MaybePromise<BoardsDataStore.Data>;
51+
loadBoardDetails: (fqbn: string) => MaybePromise<BoardDetails | undefined>;
52+
selectProgrammer(options: {
53+
fqbn: string;
54+
selectedProgrammer: Programmer;
55+
}): MaybePromise<boolean>;
56+
}
57+
58+
export async function ensureProgrammerIsSelected(
59+
params: EnsureProgrammerIsSelectedParams
60+
): Promise<boolean> {
61+
const { fqbn, getData, loadBoardDetails, selectProgrammer } = params;
62+
if (!fqbn) {
63+
return false;
64+
}
65+
console.debug(`Ensuring a programmer is selected for ${fqbn}...`);
66+
const data = await getData(fqbn);
67+
if (isEmptyData(data)) {
68+
// For example, the platform is not installed.
69+
console.debug(`Skipping. No boards data is available for ${fqbn}.`);
70+
return false;
71+
}
72+
if (data.selectedProgrammer) {
73+
console.debug(
74+
`A programmer is already selected for ${fqbn}: '${data.selectedProgrammer.id}'.`
75+
);
76+
return true;
77+
}
78+
let programmer = data.programmers.find((p) => p.default);
79+
if (programmer) {
80+
// select the programmer if the default info is available
81+
const result = await selectProgrammer({
82+
fqbn,
83+
selectedProgrammer: programmer,
84+
});
85+
if (result) {
86+
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
87+
return result;
88+
}
89+
}
90+
console.debug(`Reloading board details for ${fqbn}...`);
91+
const reloadedData = await loadBoardDetails(fqbn);
92+
if (!reloadedData) {
93+
console.debug(`Skipping. No board details found for ${fqbn}.`);
94+
return false;
95+
}
96+
if (!reloadedData.programmers.length) {
97+
console.debug(`Skipping. ${fqbn} does not have programmers.`);
98+
return false;
99+
}
100+
programmer = reloadedData.programmers.find((p) => p.default);
101+
if (!programmer) {
102+
console.debug(
103+
`Skipping. Could not find a default programmer for ${fqbn}. Programmers were: `
104+
);
105+
return false;
106+
}
107+
const result = await selectProgrammer({
108+
fqbn,
109+
selectedProgrammer: programmer,
110+
});
111+
if (result) {
112+
console.debug(`Selected '${programmer.id}' programmer for ${fqbn}.`);
113+
} else {
114+
console.debug(
115+
`Could not select '${programmer.id}' programmer for ${fqbn}.`
116+
);
117+
}
118+
return result;
119+
}

0 commit comments

Comments
 (0)