Skip to content

Commit 230ad4e

Browse files
author
Akos Kitta
committed
feat: new window inherits the custom board options
A new startup task ensures setting any custom board menu selection in a new sketch window. Closes #2271 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 503533d commit 230ad4e

File tree

6 files changed

+562
-52
lines changed

6 files changed

+562
-52
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
454454
// To be able to track, and update the menu based on the core settings (aka. board details) of the currently selected board.
455455
bind(BoardsDataStore).toSelf().inSingletonScope();
456456
bind(FrontendApplicationContribution).toService(BoardsDataStore);
457+
bind(CommandContribution).toService(BoardsDataStore);
458+
bind(StartupTaskProvider).toService(BoardsDataStore); // to inherit the boards config options, programmer, etc in a new window
459+
457460
// Logger for the Arduino daemon
458461
bind(ILogger)
459462
.toDynamicValue((ctx) => {

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

Lines changed: 143 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
2+
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
23
import { StorageService } from '@theia/core/lib/browser/storage-service';
4+
import type {
5+
Command,
6+
CommandContribution,
7+
CommandRegistry,
8+
} from '@theia/core/lib/common/command';
39
import { DisposableCollection } from '@theia/core/lib/common/disposable';
410
import { Emitter, Event } from '@theia/core/lib/common/event';
511
import { ILogger } from '@theia/core/lib/common/logger';
@@ -10,12 +16,23 @@ import {
1016
BoardsService,
1117
ConfigOption,
1218
Programmer,
19+
isBoardIdentifierChangeEvent,
1320
} from '../../common/protocol';
1421
import { notEmpty } from '../../common/utils';
22+
import type {
23+
StartupTask,
24+
StartupTaskProvider,
25+
} from '../../electron-common/startup-task';
1526
import { NotificationCenter } from '../notification-center';
27+
import { BoardsServiceProvider } from './boards-service-provider';
1628

1729
@injectable()
18-
export class BoardsDataStore implements FrontendApplicationContribution {
30+
export class BoardsDataStore
31+
implements
32+
FrontendApplicationContribution,
33+
StartupTaskProvider,
34+
CommandContribution
35+
{
1936
@inject(ILogger)
2037
@named('store')
2138
private readonly logger: ILogger;
@@ -28,44 +45,107 @@ export class BoardsDataStore implements FrontendApplicationContribution {
2845
// In other words, store the data (such as the board configs) per sketch, not per IDE2 installation. https://github.com/arduino/arduino-ide/issues/2240
2946
@inject(StorageService)
3047
private readonly storageService: StorageService;
48+
@inject(BoardsServiceProvider)
49+
private readonly boardsServiceProvider: BoardsServiceProvider;
50+
@inject(FrontendApplicationStateService)
51+
private readonly appStateService: FrontendApplicationStateService;
3152

32-
private readonly onChangedEmitter = new Emitter<string[]>();
33-
private readonly toDispose = new DisposableCollection(this.onChangedEmitter);
53+
private readonly onDidChangeEmitter =
54+
new Emitter<BoardsDataStoreChangeEvent>();
55+
private readonly toDispose = new DisposableCollection(
56+
this.onDidChangeEmitter
57+
);
58+
private _selectedBoardData: BoardsDataStoreChange | undefined;
3459

3560
onStart(): void {
36-
this.toDispose.push(
61+
this.toDispose.pushAll([
62+
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
63+
if (isBoardIdentifierChangeEvent(event)) {
64+
this.updateSelectedBoardData(event.selectedBoard?.fqbn);
65+
}
66+
}),
3767
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
38-
const dataDidChangePerFqbn: string[] = [];
68+
const changes: BoardsDataStoreChange[] = [];
3969
for (const fqbn of item.boards
4070
.map(({ fqbn }) => fqbn)
41-
.filter(notEmpty)
42-
.filter((fqbn) => !!fqbn)) {
71+
.filter(notEmpty)) {
4372
const key = this.getStorageKey(fqbn);
44-
let data = await this.storageService.getData<ConfigOption[]>(key);
45-
if (!data || !data.length) {
73+
const storedData =
74+
await this.storageService.getData<BoardsDataStore.Data>(key);
75+
if (!storedData && BoardsDataStore.Data.EMPTY !== storedData) {
4676
const details = await this.getBoardDetailsSafe(fqbn);
4777
if (details) {
48-
data = details.configOptions;
49-
if (data.length) {
50-
await this.storageService.setData(key, data);
51-
dataDidChangePerFqbn.push(fqbn);
52-
}
78+
const data = createDataStoreEntry(details);
79+
await this.storageService.setData(key, data);
80+
changes.push({ fqbn, data });
5381
}
5482
}
5583
}
56-
if (dataDidChangePerFqbn.length) {
57-
this.fireChanged(...dataDidChangePerFqbn);
84+
if (changes.length) {
85+
this.fireChanged(...changes);
5886
}
59-
})
87+
}),
88+
]);
89+
90+
Promise.all([
91+
this.boardsServiceProvider.ready,
92+
this.appStateService.reachedState('ready'),
93+
]).then(() =>
94+
this.updateSelectedBoardData(
95+
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn
96+
)
6097
);
6198
}
6299

100+
private async getSelectedBoardData(
101+
fqbn: string | undefined
102+
): Promise<BoardsDataStoreChange | undefined> {
103+
if (!fqbn) {
104+
return undefined;
105+
} else {
106+
const data = await this.getData(fqbn);
107+
if (data === BoardsDataStore.Data.EMPTY) {
108+
return undefined;
109+
}
110+
return { fqbn, data };
111+
}
112+
}
113+
114+
private async updateSelectedBoardData(
115+
fqbn: string | undefined
116+
): Promise<void> {
117+
this._selectedBoardData = await this.getSelectedBoardData(fqbn);
118+
}
119+
63120
onStop(): void {
64121
this.toDispose.dispose();
65122
}
66123

67-
get onChanged(): Event<string[]> {
68-
return this.onChangedEmitter.event;
124+
registerCommands(registry: CommandRegistry): void {
125+
registry.registerCommand(USE_INHERITED_DATA, {
126+
execute: async (arg: unknown) => {
127+
if (isBoardsDataStoreChange(arg)) {
128+
await this.setData(arg);
129+
this.fireChanged(arg);
130+
}
131+
},
132+
});
133+
}
134+
135+
tasks(): StartupTask[] {
136+
if (!this._selectedBoardData) {
137+
return [];
138+
}
139+
return [
140+
{
141+
command: USE_INHERITED_DATA.id,
142+
args: [this._selectedBoardData],
143+
},
144+
];
145+
}
146+
147+
get onDidChange(): Event<BoardsDataStoreChangeEvent> {
148+
return this.onDidChangeEmitter.event;
69149
}
70150

71151
async appendConfigToFqbn(
@@ -84,22 +164,19 @@ export class BoardsDataStore implements FrontendApplicationContribution {
84164
}
85165

86166
const key = this.getStorageKey(fqbn);
87-
let data = await this.storageService.getData<
167+
const storedData = await this.storageService.getData<
88168
BoardsDataStore.Data | undefined
89169
>(key, undefined);
90-
if (BoardsDataStore.Data.is(data)) {
91-
return data;
170+
if (BoardsDataStore.Data.is(storedData)) {
171+
return storedData;
92172
}
93173

94174
const boardDetails = await this.getBoardDetailsSafe(fqbn);
95175
if (!boardDetails) {
96176
return BoardsDataStore.Data.EMPTY;
97177
}
98178

99-
data = {
100-
configOptions: boardDetails.configOptions,
101-
programmers: boardDetails.programmers,
102-
};
179+
const data = createDataStoreEntry(boardDetails);
103180
await this.storageService.setData(key, data);
104181
return data;
105182
}
@@ -111,17 +188,15 @@ export class BoardsDataStore implements FrontendApplicationContribution {
111188
fqbn: string;
112189
selectedProgrammer: Programmer;
113190
}): Promise<boolean> {
114-
const data = deepClone(await this.getData(fqbn));
115-
const { programmers } = data;
191+
const storedData = deepClone(await this.getData(fqbn));
192+
const { programmers } = storedData;
116193
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
117194
return false;
118195
}
119196

120-
await this.setData({
121-
fqbn,
122-
data: { ...data, selectedProgrammer },
123-
});
124-
this.fireChanged(fqbn);
197+
const data = { ...storedData, selectedProgrammer };
198+
await this.setData({ fqbn, data });
199+
this.fireChanged({ fqbn, data });
125200
return true;
126201
}
127202

@@ -153,17 +228,12 @@ export class BoardsDataStore implements FrontendApplicationContribution {
153228
return false;
154229
}
155230
await this.setData({ fqbn, data });
156-
this.fireChanged(fqbn);
231+
this.fireChanged({ fqbn, data });
157232
return true;
158233
}
159234

160-
protected async setData({
161-
fqbn,
162-
data,
163-
}: {
164-
fqbn: string;
165-
data: BoardsDataStore.Data;
166-
}): Promise<void> {
235+
protected async setData(change: BoardsDataStoreChange): Promise<void> {
236+
const { fqbn, data } = change;
167237
const key = this.getStorageKey(fqbn);
168238
return this.storageService.setData(key, data);
169239
}
@@ -176,7 +246,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
176246
fqbn: string
177247
): Promise<BoardDetails | undefined> {
178248
try {
179-
const details = this.boardsService.getBoardDetails({ fqbn });
249+
const details = await this.boardsService.getBoardDetails({ fqbn });
180250
return details;
181251
} catch (err) {
182252
if (
@@ -197,8 +267,8 @@ export class BoardsDataStore implements FrontendApplicationContribution {
197267
}
198268
}
199269

200-
protected fireChanged(...fqbn: string[]): void {
201-
this.onChangedEmitter.fire(fqbn);
270+
protected fireChanged(...changes: BoardsDataStoreChange[]): void {
271+
this.onDidChangeEmitter.fire({ changes });
202272
}
203273
}
204274

@@ -224,3 +294,32 @@ export namespace BoardsDataStore {
224294
}
225295
}
226296
}
297+
298+
function createDataStoreEntry(details: BoardDetails): BoardsDataStore.Data {
299+
return {
300+
configOptions: details.configOptions.slice(),
301+
programmers: details.programmers.slice(),
302+
};
303+
}
304+
305+
export interface BoardsDataStoreChange {
306+
readonly fqbn: string;
307+
readonly data: BoardsDataStore.Data;
308+
}
309+
310+
function isBoardsDataStoreChange(arg: unknown): arg is BoardsDataStoreChange {
311+
return (
312+
typeof arg === 'object' &&
313+
arg !== null &&
314+
typeof (<BoardsDataStoreChange>arg).fqbn === 'string' &&
315+
BoardsDataStore.Data.is((<BoardsDataStoreChange>arg).data)
316+
);
317+
}
318+
319+
export interface BoardsDataStoreChangeEvent {
320+
readonly changes: readonly BoardsDataStoreChange[];
321+
}
322+
323+
const USE_INHERITED_DATA: Command = {
324+
id: 'arduino-use-inherited-boards-data',
325+
};

arduino-ide-extension/src/browser/contributions/boards-data-menu-updater.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class BoardsDataMenuUpdater extends Contribution {
3535
private readonly toDisposeOnBoardChange = new DisposableCollection();
3636

3737
override onStart(): void {
38-
this.boardsDataStore.onChanged(() =>
38+
this.boardsDataStore.onDidChange(() =>
3939
this.updateMenuActions(
4040
this.boardsServiceProvider.boardsConfig.selectedBoard
4141
)

arduino-ide-extension/src/browser/contributions/ino-language.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,21 @@ export class InoLanguage extends SketchContribution {
9090
this.notificationCenter.onPlatformDidInstall(() => forceRestart()),
9191
this.notificationCenter.onPlatformDidUninstall(() => forceRestart()),
9292
this.notificationCenter.onDidReinitialize(() => forceRestart()),
93-
this.boardDataStore.onChanged((dataChangePerFqbn) => {
93+
this.boardDataStore.onDidChange((event) => {
9494
if (this.languageServerFqbn) {
9595
const sanitizedFqbn = sanitizeFqbn(this.languageServerFqbn);
9696
if (!sanitizeFqbn) {
9797
throw new Error(
9898
`Failed to sanitize the FQBN of the running language server. FQBN with the board settings was: ${this.languageServerFqbn}`
9999
);
100100
}
101-
const matchingFqbn = dataChangePerFqbn.find(
102-
(fqbn) => sanitizedFqbn === fqbn
101+
const matchingChange = event.changes.find(
102+
(change) => change.fqbn === sanitizedFqbn
103103
);
104104
const { boardsConfig } = this.boardsServiceProvider;
105105
if (
106-
matchingFqbn &&
107-
boardsConfig.selectedBoard?.fqbn === matchingFqbn
106+
matchingChange &&
107+
boardsConfig.selectedBoard?.fqbn === matchingChange.fqbn
108108
) {
109109
start(boardsConfig.selectedBoard);
110110
}

arduino-ide-extension/src/browser/contributions/update-arduino-state.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@ export class UpdateArduinoState extends SketchContribution {
6565
this.updateCompileSummary(args[0]);
6666
}
6767
}),
68-
this.boardsDataStore.onChanged((fqbn) => {
68+
this.boardsDataStore.onDidChange((event) => {
6969
const selectedFqbn =
7070
this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
71-
if (selectedFqbn && fqbn.includes(selectedFqbn)) {
71+
if (
72+
selectedFqbn &&
73+
event.changes.find((change) => change.fqbn === selectedFqbn)
74+
) {
7275
this.updateBoardDetails(selectedFqbn);
7376
}
7477
}),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy