mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-11 05:16:34 +00:00
ATL-1206: Reuse selected board for new sketches.
Closes #95. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
fa9334eb7a
commit
c86d82d273
@ -158,6 +158,7 @@ import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
|
|||||||
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||||
import { DebugEditorModel } from './theia/debug/debug-editor-model';
|
import { DebugEditorModel } from './theia/debug/debug-editor-model';
|
||||||
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
|
import { DebugEditorModelFactory } from '@theia/debug/lib/browser/editor/debug-editor-model';
|
||||||
|
import { StorageWrapper } from './storage-wrapper';
|
||||||
|
|
||||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||||
|
|
||||||
@ -435,4 +436,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
|||||||
bind(SettingsDialogProps).toConstantValue({
|
bind(SettingsDialogProps).toConstantValue({
|
||||||
title: 'Preferences'
|
title: 'Preferences'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bind(StorageWrapper).toSelf().inSingletonScope();
|
||||||
|
bind(CommandContribution).toService(StorageWrapper);
|
||||||
});
|
});
|
||||||
|
@ -281,6 +281,39 @@ export namespace BoardsConfig {
|
|||||||
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
|
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setConfig(config: Config | undefined, urlToAttachTo: URL): URL {
|
||||||
|
const copy = new URL(urlToAttachTo.toString());
|
||||||
|
if (!config) {
|
||||||
|
copy.searchParams.delete('boards-config');
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedBoard = config.selectedBoard ? { name: config.selectedBoard.name, fqbn: config.selectedBoard.fqbn } : undefined;
|
||||||
|
const selectedPort = config.selectedPort ? { protocol: config.selectedPort.protocol, address: config.selectedPort.address } : undefined;
|
||||||
|
const jsonConfig = JSON.stringify({ selectedBoard, selectedPort });
|
||||||
|
copy.searchParams.set('boards-config', encodeURIComponent(jsonConfig));
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConfig(url: URL): Config | undefined {
|
||||||
|
const encoded = url.searchParams.get('boards-config');
|
||||||
|
if (!encoded) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const raw = decodeURIComponent(encoded);
|
||||||
|
const candidate = JSON.parse(raw);
|
||||||
|
if (typeof candidate === 'object') {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
console.warn(`Expected candidate to be an object. It was ${typeof candidate}. URL was: ${url}`);
|
||||||
|
return undefined;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Could not get board config from URL: ${url}.`, e);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { injectable, inject } from 'inversify';
|
import { injectable, inject } from 'inversify';
|
||||||
import { Emitter } from '@theia/core/lib/common/event';
|
import { Emitter } from '@theia/core/lib/common/event';
|
||||||
import { ILogger } from '@theia/core/lib/common/logger';
|
import { ILogger } from '@theia/core/lib/common/logger';
|
||||||
|
import { CommandService } from '@theia/core/lib/common/command';
|
||||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||||
import { StorageService } from '@theia/core/lib/browser/storage-service';
|
|
||||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||||
import { RecursiveRequired } from '../../common/types';
|
import { RecursiveRequired } from '../../common/types';
|
||||||
import {
|
import {
|
||||||
@ -16,8 +16,8 @@ import {
|
|||||||
import { BoardsConfig } from './boards-config';
|
import { BoardsConfig } from './boards-config';
|
||||||
import { naturalCompare } from '../../common/utils';
|
import { naturalCompare } from '../../common/utils';
|
||||||
import { NotificationCenter } from '../notification-center';
|
import { NotificationCenter } from '../notification-center';
|
||||||
import { CommandService } from '@theia/core';
|
|
||||||
import { ArduinoCommands } from '../arduino-commands';
|
import { ArduinoCommands } from '../arduino-commands';
|
||||||
|
import { StorageWrapper } from '../storage-wrapper';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||||
@ -28,8 +28,6 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
@inject(MessageService)
|
@inject(MessageService)
|
||||||
protected messageService: MessageService;
|
protected messageService: MessageService;
|
||||||
|
|
||||||
@inject(StorageService)
|
|
||||||
protected storageService: StorageService;
|
|
||||||
|
|
||||||
@inject(BoardsService)
|
@inject(BoardsService)
|
||||||
protected boardsService: BoardsService;
|
protected boardsService: BoardsService;
|
||||||
@ -349,7 +347,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const key = this.getLastSelectedBoardOnPortKey(port);
|
const key = this.getLastSelectedBoardOnPortKey(port);
|
||||||
return this.storageService.getData<Board>(key);
|
return this.getData<Board>(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async saveState(): Promise<void> {
|
protected async saveState(): Promise<void> {
|
||||||
@ -360,11 +358,11 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
const { selectedBoard, selectedPort } = this.boardsConfig;
|
const { selectedBoard, selectedPort } = this.boardsConfig;
|
||||||
if (selectedBoard && selectedPort) {
|
if (selectedBoard && selectedPort) {
|
||||||
const key = this.getLastSelectedBoardOnPortKey(selectedPort);
|
const key = this.getLastSelectedBoardOnPortKey(selectedPort);
|
||||||
await this.storageService.setData(key, selectedBoard);
|
await this.setData(key, selectedBoard);
|
||||||
}
|
}
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.storageService.setData('latest-valid-boards-config', this.latestValidBoardsConfig),
|
this.setData('latest-valid-boards-config', this.latestValidBoardsConfig),
|
||||||
this.storageService.setData('latest-boards-config', this.latestBoardsConfig)
|
this.setData('latest-boards-config', this.latestBoardsConfig)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +372,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async loadState(): Promise<void> {
|
protected async loadState(): Promise<void> {
|
||||||
const storedLatestValidBoardsConfig = await this.storageService.getData<RecursiveRequired<BoardsConfig.Config>>('latest-valid-boards-config');
|
const storedLatestValidBoardsConfig = await this.getData<RecursiveRequired<BoardsConfig.Config>>('latest-valid-boards-config');
|
||||||
if (storedLatestValidBoardsConfig) {
|
if (storedLatestValidBoardsConfig) {
|
||||||
this.latestValidBoardsConfig = storedLatestValidBoardsConfig;
|
this.latestValidBoardsConfig = storedLatestValidBoardsConfig;
|
||||||
if (this.canUploadTo(this.latestValidBoardsConfig)) {
|
if (this.canUploadTo(this.latestValidBoardsConfig)) {
|
||||||
@ -382,13 +380,25 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we could not restore the latest valid config, try to restore something, the board at least.
|
// If we could not restore the latest valid config, try to restore something, the board at least.
|
||||||
const storedLatestBoardsConfig = await this.storageService.getData<BoardsConfig.Config | undefined>('latest-boards-config');
|
let storedLatestBoardsConfig = await this.getData<BoardsConfig.Config | undefined>('latest-boards-config');
|
||||||
|
// Try to get from the URL if it was not persisted.
|
||||||
|
if (!storedLatestBoardsConfig) {
|
||||||
|
storedLatestBoardsConfig = BoardsConfig.Config.getConfig(new URL(window.location.href));
|
||||||
|
}
|
||||||
if (storedLatestBoardsConfig) {
|
if (storedLatestBoardsConfig) {
|
||||||
this.latestBoardsConfig = storedLatestBoardsConfig;
|
this.latestBoardsConfig = storedLatestBoardsConfig;
|
||||||
this.boardsConfig = this.latestBoardsConfig;
|
this.boardsConfig = this.latestBoardsConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setData<T>(key: string, value: T): Promise<void> {
|
||||||
|
return this.commandService.executeCommand(StorageWrapper.Commands.SET_DATA.id, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getData<T>(key: string): Promise<T | undefined> {
|
||||||
|
return this.commandService.executeCommand<T>(StorageWrapper.Commands.GET_DATA.id, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
33
arduino-ide-extension/src/browser/storage-wrapper.ts
Normal file
33
arduino-ide-extension/src/browser/storage-wrapper.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { injectable, inject } from 'inversify';
|
||||||
|
import { StorageService } from '@theia/core/lib/browser/storage-service';
|
||||||
|
import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a workaround to break cycles in the dependency injection. Provides commands for `setData` and `getData`.
|
||||||
|
*/
|
||||||
|
@injectable()
|
||||||
|
export class StorageWrapper implements CommandContribution {
|
||||||
|
|
||||||
|
@inject(StorageService)
|
||||||
|
protected storageService: StorageService;
|
||||||
|
|
||||||
|
registerCommands(commands: CommandRegistry): void {
|
||||||
|
commands.registerCommand(StorageWrapper.Commands.GET_DATA, {
|
||||||
|
execute: (key: string, defaultValue?: any) => this.storageService.getData(key, defaultValue)
|
||||||
|
});
|
||||||
|
commands.registerCommand(StorageWrapper.Commands.SET_DATA, {
|
||||||
|
execute: (key: string, value: any) => this.storageService.setData(key, value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export namespace StorageWrapper {
|
||||||
|
export namespace Commands {
|
||||||
|
export const SET_DATA: Command = {
|
||||||
|
id: 'arduino-store-wrapper-set'
|
||||||
|
};
|
||||||
|
export const GET_DATA: Command = {
|
||||||
|
id: 'arduino-store-wrapper-get'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/
|
|||||||
import { ConfigService } from '../../../common/protocol/config-service';
|
import { ConfigService } from '../../../common/protocol/config-service';
|
||||||
import { SketchesService, Sketch, SketchContainer } from '../../../common/protocol/sketches-service';
|
import { SketchesService, Sketch, SketchContainer } from '../../../common/protocol/sketches-service';
|
||||||
import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
|
import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver';
|
||||||
|
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||||
|
import { BoardsConfig } from '../../boards/boards-config';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class WorkspaceService extends TheiaWorkspaceService {
|
export class WorkspaceService extends TheiaWorkspaceService {
|
||||||
@ -33,6 +35,9 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
|||||||
@inject(FrontendApplicationStateService)
|
@inject(FrontendApplicationStateService)
|
||||||
protected readonly appStateService: FrontendApplicationStateService;
|
protected readonly appStateService: FrontendApplicationStateService;
|
||||||
|
|
||||||
|
@inject(BoardsServiceProvider)
|
||||||
|
protected readonly boardsServiceProvider: BoardsServiceProvider;
|
||||||
|
|
||||||
private application: FrontendApplication;
|
private application: FrontendApplication;
|
||||||
private workspaceUri?: Promise<string | undefined>;
|
private workspaceUri?: Promise<string | undefined>;
|
||||||
private version?: string;
|
private version?: string;
|
||||||
@ -80,6 +85,13 @@ export class WorkspaceService extends TheiaWorkspaceService {
|
|||||||
return this.workspaceUri;
|
return this.workspaceUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected openNewWindow(workspacePath: string): void {
|
||||||
|
const { boardsConfig } = this.boardsServiceProvider;
|
||||||
|
const url = BoardsConfig.Config.setConfig(boardsConfig, new URL(window.location.href)); // Set the current boards config for the new browser window.
|
||||||
|
url.hash = workspacePath;
|
||||||
|
this.windowService.openNewWindow(url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
private async isValid(uri: string): Promise<boolean> {
|
private async isValid(uri: string): Promise<boolean> {
|
||||||
const exists = await this.fileService.exists(new URI(uri));
|
const exists = await this.fileService.exists(new URI(uri));
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
|
@ -11,7 +11,8 @@ export class ElectronMainWindowServiceImpl extends TheiaElectronMainWindowServic
|
|||||||
|
|
||||||
openNewWindow(url: string, { external }: NewWindowOptions): undefined {
|
openNewWindow(url: string, { external }: NewWindowOptions): undefined {
|
||||||
if (!external) {
|
if (!external) {
|
||||||
const existing = this.app.windows.find(window => window.webContents.getURL() === url);
|
const sanitizedUrl = this.sanitize(url);
|
||||||
|
const existing = this.app.windows.find(window => this.sanitize(window.webContents.getURL()) === sanitizedUrl);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existing.focus();
|
existing.focus();
|
||||||
return;
|
return;
|
||||||
@ -20,5 +21,14 @@ export class ElectronMainWindowServiceImpl extends TheiaElectronMainWindowServic
|
|||||||
return super.openNewWindow(url, { external });
|
return super.openNewWindow(url, { external });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sanitize(url: string): string {
|
||||||
|
const copy = new URL(url);
|
||||||
|
const searchParams: string[] = [];
|
||||||
|
copy.searchParams.forEach((_, key) => searchParams.push(key));
|
||||||
|
for (const param of searchParams) {
|
||||||
|
copy.searchParams.delete(param);
|
||||||
|
}
|
||||||
|
return copy.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user