Skip to content

Commit c86d82d

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-1206: Reuse selected board for new sketches.
Closes #95. Signed-off-by: Akos Kitta <[email protected]>
1 parent fa9334e commit c86d82d

File tree

6 files changed

+113
-11
lines changed

6 files changed

+113
-11
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
158158
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
159159
import { DebugEditorModel } from './theia/debug/debug-editor-model';
160160
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
161+
import { StorageWrapper } from './storage-wrapper';
161162

162163
const ElementQueries = require('css-element-queries/src/ElementQueries');
163164

@@ -435,4 +436,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
435436
bind(SettingsDialogProps).toConstantValue({
436437
title: 'Preferences'
437438
});
439+
440+
bind(StorageWrapper).toSelf().inSingletonScope();
441+
bind(CommandContribution).toService(StorageWrapper);
438442
});

Diff for: arduino-ide-extension/src/browser/boards/boards-config.tsx

+33
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,39 @@ export namespace BoardsConfig {
281281
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
282282
}
283283

284+
export function setConfig(config: Config | undefined, urlToAttachTo: URL): URL {
285+
const copy = new URL(urlToAttachTo.toString());
286+
if (!config) {
287+
copy.searchParams.delete('boards-config');
288+
return copy;
289+
}
290+
291+
const selectedBoard = config.selectedBoard ? { name: config.selectedBoard.name, fqbn: config.selectedBoard.fqbn } : undefined;
292+
const selectedPort = config.selectedPort ? { protocol: config.selectedPort.protocol, address: config.selectedPort.address } : undefined;
293+
const jsonConfig = JSON.stringify({ selectedBoard, selectedPort });
294+
copy.searchParams.set('boards-config', encodeURIComponent(jsonConfig));
295+
return copy;
296+
}
297+
298+
export function getConfig(url: URL): Config | undefined {
299+
const encoded = url.searchParams.get('boards-config');
300+
if (!encoded) {
301+
return undefined;
302+
}
303+
try {
304+
const raw = decodeURIComponent(encoded);
305+
const candidate = JSON.parse(raw);
306+
if (typeof candidate === 'object') {
307+
return candidate;
308+
}
309+
console.warn(`Expected candidate to be an object. It was ${typeof candidate}. URL was: ${url}`);
310+
return undefined;
311+
} catch (e) {
312+
console.log(`Could not get board config from URL: ${url}.`, e);
313+
return undefined;
314+
}
315+
}
316+
284317
}
285318

286319
}

Diff for: arduino-ide-extension/src/browser/boards/boards-service-provider.ts

+20-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { injectable, inject } from 'inversify';
22
import { Emitter } from '@theia/core/lib/common/event';
33
import { ILogger } from '@theia/core/lib/common/logger';
4+
import { CommandService } from '@theia/core/lib/common/command';
45
import { MessageService } from '@theia/core/lib/common/message-service';
5-
import { StorageService } from '@theia/core/lib/browser/storage-service';
66
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
77
import { RecursiveRequired } from '../../common/types';
88
import {
@@ -16,8 +16,8 @@ import {
1616
import { BoardsConfig } from './boards-config';
1717
import { naturalCompare } from '../../common/utils';
1818
import { NotificationCenter } from '../notification-center';
19-
import { CommandService } from '@theia/core';
2019
import { ArduinoCommands } from '../arduino-commands';
20+
import { StorageWrapper } from '../storage-wrapper';
2121

2222
@injectable()
2323
export class BoardsServiceProvider implements FrontendApplicationContribution {
@@ -28,8 +28,6 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
2828
@inject(MessageService)
2929
protected messageService: MessageService;
3030

31-
@inject(StorageService)
32-
protected storageService: StorageService;
3331

3432
@inject(BoardsService)
3533
protected boardsService: BoardsService;
@@ -349,7 +347,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
349347
return undefined;
350348
}
351349
const key = this.getLastSelectedBoardOnPortKey(port);
352-
return this.storageService.getData<Board>(key);
350+
return this.getData<Board>(key);
353351
}
354352

355353
protected async saveState(): Promise<void> {
@@ -360,11 +358,11 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
360358
const { selectedBoard, selectedPort } = this.boardsConfig;
361359
if (selectedBoard && selectedPort) {
362360
const key = this.getLastSelectedBoardOnPortKey(selectedPort);
363-
await this.storageService.setData(key, selectedBoard);
361+
await this.setData(key, selectedBoard);
364362
}
365363
await Promise.all([
366-
this.storageService.setData('latest-valid-boards-config', this.latestValidBoardsConfig),
367-
this.storageService.setData('latest-boards-config', this.latestBoardsConfig)
364+
this.setData('latest-valid-boards-config', this.latestValidBoardsConfig),
365+
this.setData('latest-boards-config', this.latestBoardsConfig)
368366
]);
369367
}
370368

@@ -374,21 +372,33 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
374372
}
375373

376374
protected async loadState(): Promise<void> {
377-
const storedLatestValidBoardsConfig = await this.storageService.getData<RecursiveRequired<BoardsConfig.Config>>('latest-valid-boards-config');
375+
const storedLatestValidBoardsConfig = await this.getData<RecursiveRequired<BoardsConfig.Config>>('latest-valid-boards-config');
378376
if (storedLatestValidBoardsConfig) {
379377
this.latestValidBoardsConfig = storedLatestValidBoardsConfig;
380378
if (this.canUploadTo(this.latestValidBoardsConfig)) {
381379
this.boardsConfig = this.latestValidBoardsConfig;
382380
}
383381
} else {
384382
// If we could not restore the latest valid config, try to restore something, the board at least.
385-
const storedLatestBoardsConfig = await this.storageService.getData<BoardsConfig.Config | undefined>('latest-boards-config');
383+
let storedLatestBoardsConfig = await this.getData<BoardsConfig.Config | undefined>('latest-boards-config');
384+
// Try to get from the URL if it was not persisted.
385+
if (!storedLatestBoardsConfig) {
386+
storedLatestBoardsConfig = BoardsConfig.Config.getConfig(new URL(window.location.href));
387+
}
386388
if (storedLatestBoardsConfig) {
387389
this.latestBoardsConfig = storedLatestBoardsConfig;
388390
this.boardsConfig = this.latestBoardsConfig;
389391
}
390392
}
391393
}
394+
395+
private setData<T>(key: string, value: T): Promise<void> {
396+
return this.commandService.executeCommand(StorageWrapper.Commands.SET_DATA.id, key, value);
397+
}
398+
399+
private getData<T>(key: string): Promise<T | undefined> {
400+
return this.commandService.executeCommand<T>(StorageWrapper.Commands.GET_DATA.id, key);
401+
}
392402
}
393403

394404
/**

Diff for: arduino-ide-extension/src/browser/storage-wrapper.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { injectable, inject } from 'inversify';
2+
import { StorageService } from '@theia/core/lib/browser/storage-service';
3+
import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
4+
5+
/**
6+
* This is a workaround to break cycles in the dependency injection. Provides commands for `setData` and `getData`.
7+
*/
8+
@injectable()
9+
export class StorageWrapper implements CommandContribution {
10+
11+
@inject(StorageService)
12+
protected storageService: StorageService;
13+
14+
registerCommands(commands: CommandRegistry): void {
15+
commands.registerCommand(StorageWrapper.Commands.GET_DATA, {
16+
execute: (key: string, defaultValue?: any) => this.storageService.getData(key, defaultValue)
17+
});
18+
commands.registerCommand(StorageWrapper.Commands.SET_DATA, {
19+
execute: (key: string, value: any) => this.storageService.setData(key, value)
20+
});
21+
}
22+
23+
}
24+
export namespace StorageWrapper {
25+
export namespace Commands {
26+
export const SET_DATA: Command = {
27+
id: 'arduino-store-wrapper-set'
28+
};
29+
export const GET_DATA: Command = {
30+
id: 'arduino-store-wrapper-get'
31+
};
32+
}
33+
}

Diff for: arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/
1111
import { ConfigService } from '../../../common/protocol/config-service';
1212
import { SketchesService, Sketch, SketchContainer } from '../../../common/protocol/sketches-service';
1313
import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
14+
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
15+
import { BoardsConfig } from '../../boards/boards-config';
1416

1517
@injectable()
1618
export class WorkspaceService extends TheiaWorkspaceService {
@@ -33,6 +35,9 @@ export class WorkspaceService extends TheiaWorkspaceService {
3335
@inject(FrontendApplicationStateService)
3436
protected readonly appStateService: FrontendApplicationStateService;
3537

38+
@inject(BoardsServiceProvider)
39+
protected readonly boardsServiceProvider: BoardsServiceProvider;
40+
3641
private application: FrontendApplication;
3742
private workspaceUri?: Promise<string | undefined>;
3843
private version?: string;
@@ -80,6 +85,13 @@ export class WorkspaceService extends TheiaWorkspaceService {
8085
return this.workspaceUri;
8186
}
8287

88+
protected openNewWindow(workspacePath: string): void {
89+
const { boardsConfig } = this.boardsServiceProvider;
90+
const url = BoardsConfig.Config.setConfig(boardsConfig, new URL(window.location.href)); // Set the current boards config for the new browser window.
91+
url.hash = workspacePath;
92+
this.windowService.openNewWindow(url.toString());
93+
}
94+
8395
private async isValid(uri: string): Promise<boolean> {
8496
const exists = await this.fileService.exists(new URI(uri));
8597
if (!exists) {

Diff for: arduino-ide-extension/src/electron-main/theia/electron-main-window-service.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export class ElectronMainWindowServiceImpl extends TheiaElectronMainWindowServic
1111

1212
openNewWindow(url: string, { external }: NewWindowOptions): undefined {
1313
if (!external) {
14-
const existing = this.app.windows.find(window => window.webContents.getURL() === url);
14+
const sanitizedUrl = this.sanitize(url);
15+
const existing = this.app.windows.find(window => this.sanitize(window.webContents.getURL()) === sanitizedUrl);
1516
if (existing) {
1617
existing.focus();
1718
return;
@@ -20,5 +21,14 @@ export class ElectronMainWindowServiceImpl extends TheiaElectronMainWindowServic
2021
return super.openNewWindow(url, { external });
2122
}
2223

24+
private sanitize(url: string): string {
25+
const copy = new URL(url);
26+
const searchParams: string[] = [];
27+
copy.searchParams.forEach((_, key) => searchParams.push(key));
28+
for (const param of searchParams) {
29+
copy.searchParams.delete(param);
30+
}
31+
return copy.toString();
32+
}
2333

2434
}

0 commit comments

Comments
 (0)