mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-25 02:58:33 +00:00
697 lines
22 KiB
TypeScript
697 lines
22 KiB
TypeScript
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
|
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
|
import { StorageService } from '@theia/core/lib/browser/storage-service';
|
|
import {
|
|
Command,
|
|
CommandContribution,
|
|
CommandRegistry,
|
|
CommandService,
|
|
} from '@theia/core/lib/common/command';
|
|
import type { Disposable } from '@theia/core/lib/common/disposable';
|
|
import { Emitter } from '@theia/core/lib/common/event';
|
|
import { ILogger } from '@theia/core/lib/common/logger';
|
|
import { MessageService } from '@theia/core/lib/common/message-service';
|
|
import { nls } from '@theia/core/lib/common/nls';
|
|
import { deepClone } from '@theia/core/lib/common/objects';
|
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
import type { Mutable } from '@theia/core/lib/common/types';
|
|
import { inject, injectable, optional } from '@theia/core/shared/inversify';
|
|
import {
|
|
OutputChannel,
|
|
OutputChannelManager,
|
|
} from '@theia/output/lib/browser/output-channel';
|
|
import {
|
|
BoardIdentifier,
|
|
BoardUserField,
|
|
BoardWithPackage,
|
|
BoardsConfig,
|
|
BoardsConfigChangeEvent,
|
|
BoardsPackage,
|
|
BoardsService,
|
|
DetectedPorts,
|
|
Port,
|
|
PortIdentifier,
|
|
boardIdentifierEquals,
|
|
emptyBoardsConfig,
|
|
isBoardIdentifier,
|
|
isBoardIdentifierChangeEvent,
|
|
isPortIdentifier,
|
|
isPortIdentifierChangeEvent,
|
|
portIdentifierEquals,
|
|
sanitizeFqbn,
|
|
serializePlatformIdentifier,
|
|
} from '../../common/protocol';
|
|
import {
|
|
BoardList,
|
|
BoardListHistory,
|
|
EditBoardsConfigActionParams,
|
|
SelectBoardsConfigActionParams,
|
|
createBoardList,
|
|
isBoardListHistory,
|
|
} from '../../common/protocol/board-list';
|
|
import type { Defined } from '../../common/types';
|
|
import type {
|
|
StartupTask,
|
|
StartupTaskProvider,
|
|
} from '../../electron-common/startup-task';
|
|
import { NotificationCenter } from '../notification-center';
|
|
|
|
const boardListHistoryStorageKey = 'arduino-ide:boardListHistory';
|
|
const selectedPortStorageKey = 'arduino-ide:selectedPort';
|
|
const selectedBoardStorageKey = 'arduino-ide:selectedBoard';
|
|
|
|
type UpdateBoardsConfigReason =
|
|
/**
|
|
* Restore previous state at IDE startup.
|
|
*/
|
|
| 'restore'
|
|
/**
|
|
* The board and the optional port were changed from the dialog.
|
|
*/
|
|
| 'dialog'
|
|
/**
|
|
* The board and the port were updated from the board select toolbar.
|
|
*/
|
|
| 'toolbar'
|
|
/**
|
|
* The board and port configuration was inherited from another window.
|
|
*/
|
|
| 'inherit';
|
|
|
|
interface RefreshBoardListParams {
|
|
detectedPorts?: DetectedPorts;
|
|
boardsConfig?: BoardsConfig;
|
|
boardListHistory?: BoardListHistory;
|
|
}
|
|
|
|
export type UpdateBoardsConfigParams =
|
|
| BoardIdentifier
|
|
/**
|
|
* `'unset-board'` is special case when a non installed board is selected (no FQBN), the platform is installed,
|
|
* but there is no way to determine the FQBN from the previous partial data, and IDE2 unsets the board.
|
|
*/
|
|
| 'unset-board'
|
|
| PortIdentifier
|
|
| (Readonly<Defined<BoardsConfig>> &
|
|
Readonly<{ reason?: UpdateBoardsConfigReason }>);
|
|
|
|
type HistoryDidNotChange = undefined;
|
|
type HistoryDidDelete = Readonly<{ [portKey: string]: undefined }>;
|
|
type HistoryDidUpdate = Readonly<{ [portKey: string]: BoardIdentifier }>;
|
|
type BoardListHistoryUpdateResult =
|
|
| HistoryDidNotChange
|
|
| HistoryDidDelete
|
|
| HistoryDidUpdate;
|
|
|
|
type BoardToSelect = BoardIdentifier | undefined | 'ignore-board';
|
|
type PortToSelect = PortIdentifier | undefined | 'ignore-port';
|
|
|
|
function sanitizeBoardToSelectFQBN(board: BoardToSelect): BoardToSelect {
|
|
if (isBoardIdentifier(board)) {
|
|
return sanitizeBoardIdentifierFQBN(board);
|
|
}
|
|
return board;
|
|
}
|
|
function sanitizeBoardIdentifierFQBN(board: BoardIdentifier): BoardIdentifier {
|
|
if (board.fqbn) {
|
|
const copy: Mutable<BoardIdentifier> = deepClone(board);
|
|
copy.fqbn = sanitizeFqbn(board.fqbn);
|
|
return copy;
|
|
}
|
|
return board;
|
|
}
|
|
|
|
interface UpdateBoardListHistoryParams {
|
|
readonly portToSelect: PortToSelect;
|
|
readonly boardToSelect: BoardToSelect;
|
|
}
|
|
|
|
interface UpdateBoardsDataParams {
|
|
readonly boardToSelect: BoardToSelect;
|
|
readonly reason?: UpdateBoardsConfigReason;
|
|
}
|
|
|
|
export interface SelectBoardsConfigAction {
|
|
(params: SelectBoardsConfigActionParams): void;
|
|
}
|
|
|
|
export interface EditBoardsConfigAction {
|
|
(params?: EditBoardsConfigActionParams): void;
|
|
}
|
|
|
|
export interface BoardListUIActions {
|
|
/**
|
|
* Sets the frontend's port and board configuration according to the params.
|
|
*/
|
|
readonly select: SelectBoardsConfigAction;
|
|
/**
|
|
* Opens up the boards config dialog with the port and (optional) board to select in the dialog.
|
|
* Calling this function does not immediately change the frontend's port and board config, but
|
|
* preselects items in the dialog.
|
|
*/
|
|
readonly edit: EditBoardsConfigAction;
|
|
}
|
|
export type BoardListUI = BoardList & BoardListUIActions;
|
|
|
|
export type BoardsConfigChangeEventUI = BoardsConfigChangeEvent &
|
|
Readonly<{ reason?: UpdateBoardsConfigReason }>;
|
|
|
|
@injectable()
|
|
export class BoardListDumper implements Disposable {
|
|
@inject(OutputChannelManager)
|
|
private readonly outputChannelManager: OutputChannelManager;
|
|
|
|
private outputChannel: OutputChannel | undefined;
|
|
|
|
dump(boardList: BoardList): void {
|
|
if (!this.outputChannel) {
|
|
this.outputChannel = this.outputChannelManager.getChannel(
|
|
'Developer (Arduino)'
|
|
);
|
|
}
|
|
this.outputChannel.show({ preserveFocus: true });
|
|
this.outputChannel.append(boardList.toString() + '\n');
|
|
}
|
|
|
|
dispose(): void {
|
|
this.outputChannel?.dispose();
|
|
}
|
|
}
|
|
|
|
@injectable()
|
|
export class BoardsServiceProvider
|
|
implements
|
|
FrontendApplicationContribution,
|
|
StartupTaskProvider,
|
|
CommandContribution
|
|
{
|
|
@inject(ILogger)
|
|
private readonly logger: ILogger;
|
|
@inject(MessageService)
|
|
private readonly messageService: MessageService;
|
|
@inject(BoardsService)
|
|
private readonly boardsService: BoardsService;
|
|
@inject(CommandService)
|
|
private readonly commandService: CommandService;
|
|
@inject(StorageService)
|
|
private readonly storageService: StorageService;
|
|
@inject(NotificationCenter)
|
|
private readonly notificationCenter: NotificationCenter;
|
|
@inject(FrontendApplicationStateService)
|
|
private readonly appStateService: FrontendApplicationStateService;
|
|
@optional()
|
|
@inject(BoardListDumper)
|
|
private readonly boardListDumper?: BoardListDumper;
|
|
|
|
private _boardsConfig = emptyBoardsConfig();
|
|
private _detectedPorts: DetectedPorts = {};
|
|
private _boardList = this.createBoardListUI(createBoardList({}));
|
|
private _boardListHistory: Mutable<BoardListHistory> = {};
|
|
private _ready = new Deferred<void>();
|
|
|
|
private readonly boardsConfigDidChangeEmitter =
|
|
new Emitter<BoardsConfigChangeEventUI>();
|
|
readonly onBoardsConfigDidChange = this.boardsConfigDidChangeEmitter.event;
|
|
|
|
private readonly boardListDidChangeEmitter = new Emitter<BoardListUI>();
|
|
/**
|
|
* Emits an event on board config (port or board) change, and when the discovery (`board list --watch`) detected any changes.
|
|
*/
|
|
readonly onBoardListDidChange = this.boardListDidChangeEmitter.event;
|
|
|
|
onStart(): void {
|
|
this.notificationCenter.onDetectedPortsDidChange(({ detectedPorts }) =>
|
|
this.refreshBoardList({ detectedPorts })
|
|
);
|
|
this.notificationCenter.onPlatformDidInstall((event) =>
|
|
this.maybeUpdateSelectedBoard(event)
|
|
);
|
|
this.appStateService
|
|
.reachedState('ready')
|
|
.then(async () => {
|
|
const [detectedPorts, storedState] = await Promise.all([
|
|
this.boardsService.getDetectedPorts(),
|
|
this.restoreState(),
|
|
]);
|
|
const { selectedBoard, selectedPort, boardListHistory } = storedState;
|
|
const options: RefreshBoardListParams = {
|
|
boardListHistory,
|
|
detectedPorts,
|
|
};
|
|
// If either the port or the board is set, restore it. Otherwise, do not restore nothing.
|
|
// It might override the inherited boards config from the other window on File > New Sketch
|
|
if (selectedBoard || selectedPort) {
|
|
options.boardsConfig = { selectedBoard, selectedPort };
|
|
}
|
|
this.refreshBoardList(options);
|
|
this._ready.resolve();
|
|
})
|
|
.finally(() => this._ready.resolve());
|
|
}
|
|
|
|
onStop(): void {
|
|
this.boardListDumper?.dispose();
|
|
}
|
|
|
|
registerCommands(registry: CommandRegistry): void {
|
|
registry.registerCommand(USE_INHERITED_CONFIG, {
|
|
execute: (
|
|
boardsConfig: BoardsConfig,
|
|
boardListHistory: BoardListHistory
|
|
) => {
|
|
if (boardListHistory) {
|
|
this._boardListHistory = boardListHistory;
|
|
}
|
|
this.update({ boardsConfig }, 'inherit');
|
|
},
|
|
});
|
|
if (this.boardListDumper) {
|
|
registry.registerCommand(DUMP_BOARD_LIST, {
|
|
execute: () => this.boardListDumper?.dump(this._boardList),
|
|
});
|
|
}
|
|
registry.registerCommand(CLEAR_BOARD_LIST_HISTORY, {
|
|
execute: () => {
|
|
this.refreshBoardList({ boardListHistory: {} });
|
|
this.setData(boardListHistoryStorageKey, undefined);
|
|
},
|
|
});
|
|
registry.registerCommand(CLEAR_BOARDS_CONFIG, {
|
|
execute: () => {
|
|
this.refreshBoardList({ boardsConfig: emptyBoardsConfig() });
|
|
Promise.all([
|
|
this.setData(selectedPortStorageKey, undefined),
|
|
this.setData(selectedBoardStorageKey, undefined),
|
|
]);
|
|
},
|
|
});
|
|
}
|
|
|
|
tasks(): StartupTask[] {
|
|
return [
|
|
{
|
|
command: USE_INHERITED_CONFIG.id,
|
|
args: [this._boardsConfig, this._boardListHistory],
|
|
},
|
|
];
|
|
}
|
|
|
|
private refreshBoardList(params?: RefreshBoardListParams): void {
|
|
if (params?.detectedPorts) {
|
|
this._detectedPorts = params.detectedPorts;
|
|
}
|
|
if (params?.boardsConfig) {
|
|
this._boardsConfig = params.boardsConfig;
|
|
}
|
|
if (params?.boardListHistory) {
|
|
this._boardListHistory = params.boardListHistory;
|
|
}
|
|
const boardList = createBoardList(
|
|
this._detectedPorts,
|
|
this._boardsConfig,
|
|
this._boardListHistory
|
|
);
|
|
this._boardList = this.createBoardListUI(boardList);
|
|
this.boardListDidChangeEmitter.fire(this._boardList);
|
|
}
|
|
|
|
private createBoardListUI(boardList: BoardList): BoardListUI {
|
|
return Object.assign(boardList, {
|
|
select: this.onBoardsConfigSelect.bind(this),
|
|
edit: this.onBoardsConfigEdit.bind(this),
|
|
});
|
|
}
|
|
|
|
private onBoardsConfigSelect(params: SelectBoardsConfigActionParams): void {
|
|
this.updateConfig({ ...params, reason: 'toolbar' });
|
|
}
|
|
|
|
private async onBoardsConfigEdit(
|
|
params?: EditBoardsConfigActionParams
|
|
): Promise<void> {
|
|
const boardsConfig = await this.commandService.executeCommand<
|
|
BoardsConfig | undefined
|
|
>('arduino-open-boards-dialog', params);
|
|
if (boardsConfig) {
|
|
this.update({ boardsConfig }, 'dialog');
|
|
}
|
|
}
|
|
|
|
private update(
|
|
params: RefreshBoardListParams,
|
|
reason?: UpdateBoardsConfigReason
|
|
): void {
|
|
const { boardsConfig } = params;
|
|
if (!boardsConfig) {
|
|
return;
|
|
}
|
|
const { selectedBoard, selectedPort } = boardsConfig;
|
|
if (selectedBoard && selectedPort) {
|
|
this.updateConfig({
|
|
selectedBoard,
|
|
selectedPort,
|
|
reason,
|
|
});
|
|
} else if (selectedBoard) {
|
|
this.updateConfig(selectedBoard);
|
|
} else if (selectedPort) {
|
|
this.updateConfig(selectedPort);
|
|
}
|
|
}
|
|
|
|
updateConfig(params: UpdateBoardsConfigParams): boolean {
|
|
const previousSelectedBoard = this._boardsConfig.selectedBoard;
|
|
const previousSelectedPort = this._boardsConfig.selectedPort;
|
|
const boardToSelect = this.getBoardToSelect(params);
|
|
const portToSelect = this.getPortToSelect(params);
|
|
const reason = this.getUpdateReason(params);
|
|
|
|
const boardDidChange =
|
|
boardToSelect !== 'ignore-board' &&
|
|
!boardIdentifierEquals(boardToSelect, previousSelectedBoard);
|
|
const portDidChange =
|
|
portToSelect !== 'ignore-port' &&
|
|
!portIdentifierEquals(portToSelect, previousSelectedPort);
|
|
const boardDidChangeEvent = boardDidChange
|
|
? // The change event must always contain any custom board options. Hence the board to select is not sanitized.
|
|
{ selectedBoard: boardToSelect, previousSelectedBoard }
|
|
: undefined;
|
|
const portDidChangeEvent = portDidChange
|
|
? { selectedPort: portToSelect, previousSelectedPort }
|
|
: undefined;
|
|
|
|
let event: BoardsConfigChangeEvent | undefined = boardDidChangeEvent;
|
|
if (portDidChangeEvent) {
|
|
if (event) {
|
|
event = {
|
|
...event,
|
|
...portDidChangeEvent,
|
|
};
|
|
} else {
|
|
event = portDidChangeEvent;
|
|
}
|
|
}
|
|
if (!event) {
|
|
return false;
|
|
}
|
|
|
|
// unlike for the board change event, every persistent state must not contain custom board config options in the FQBN
|
|
const sanitizedBoardToSelect = sanitizeBoardToSelectFQBN(boardToSelect);
|
|
|
|
this.maybeUpdateBoardListHistory({
|
|
portToSelect,
|
|
boardToSelect: sanitizedBoardToSelect,
|
|
});
|
|
this.maybeUpdateBoardsData({
|
|
boardToSelect: sanitizedBoardToSelect,
|
|
reason,
|
|
});
|
|
|
|
if (isBoardIdentifierChangeEvent(event)) {
|
|
this._boardsConfig.selectedBoard = event.selectedBoard
|
|
? sanitizeBoardIdentifierFQBN(event.selectedBoard)
|
|
: event.selectedBoard;
|
|
}
|
|
if (isPortIdentifierChangeEvent(event)) {
|
|
this._boardsConfig.selectedPort = event.selectedPort;
|
|
}
|
|
|
|
if (reason) {
|
|
event = Object.assign(event, { reason });
|
|
}
|
|
|
|
this.boardsConfigDidChangeEmitter.fire(event);
|
|
this.refreshBoardList();
|
|
this.saveState();
|
|
return true;
|
|
}
|
|
|
|
private getBoardToSelect(params: UpdateBoardsConfigParams): BoardToSelect {
|
|
if (isPortIdentifier(params)) {
|
|
return 'ignore-board';
|
|
}
|
|
if (params === 'unset-board') {
|
|
return undefined;
|
|
}
|
|
return isBoardIdentifier(params) ? params : params.selectedBoard;
|
|
}
|
|
|
|
private getPortToSelect(
|
|
params: UpdateBoardsConfigParams
|
|
): Exclude<PortToSelect, undefined> {
|
|
if (isBoardIdentifier(params) || params === 'unset-board') {
|
|
return 'ignore-port';
|
|
}
|
|
return isPortIdentifier(params) ? params : params.selectedPort;
|
|
}
|
|
|
|
private getUpdateReason(
|
|
params: UpdateBoardsConfigParams
|
|
): UpdateBoardsConfigReason | undefined {
|
|
if (
|
|
isBoardIdentifier(params) ||
|
|
isPortIdentifier(params) ||
|
|
params === 'unset-board'
|
|
) {
|
|
return undefined;
|
|
}
|
|
return params.reason;
|
|
}
|
|
|
|
get ready(): Promise<void> {
|
|
return this._ready.promise;
|
|
}
|
|
|
|
get boardsConfig(): BoardsConfig {
|
|
return this._boardsConfig;
|
|
}
|
|
|
|
get boardList(): BoardListUI {
|
|
return this._boardList;
|
|
}
|
|
|
|
get detectedPorts(): DetectedPorts {
|
|
return this._detectedPorts;
|
|
}
|
|
|
|
async searchBoards({
|
|
query,
|
|
}: {
|
|
query?: string;
|
|
cores?: string[];
|
|
}): Promise<BoardWithPackage[]> {
|
|
const boards = await this.boardsService.searchBoards({ query });
|
|
return boards;
|
|
}
|
|
|
|
async selectedBoardUserFields(): Promise<BoardUserField[]> {
|
|
if (!this._boardsConfig.selectedBoard) {
|
|
return [];
|
|
}
|
|
const fqbn = this._boardsConfig.selectedBoard.fqbn;
|
|
if (!fqbn) {
|
|
return [];
|
|
}
|
|
// Protocol must be set to `default` when uploading without a port selected:
|
|
// https://arduino.github.io/arduino-cli/dev/platform-specification/#sketch-upload-configuration
|
|
const protocol = this._boardsConfig.selectedPort?.protocol || 'default';
|
|
return await this.boardsService.getBoardUserFields({ fqbn, protocol });
|
|
}
|
|
|
|
private async maybeUpdateSelectedBoard(platformDidInstallEvent: {
|
|
item: BoardsPackage;
|
|
}): Promise<void> {
|
|
const { selectedBoard } = this._boardsConfig;
|
|
if (
|
|
selectedBoard &&
|
|
!selectedBoard.fqbn &&
|
|
BoardWithPackage.is(selectedBoard)
|
|
) {
|
|
const selectedBoardPlatformId = serializePlatformIdentifier(
|
|
selectedBoard.packageId
|
|
);
|
|
if (selectedBoardPlatformId === platformDidInstallEvent.item.id) {
|
|
const installedSelectedBoard = platformDidInstallEvent.item.boards.find(
|
|
(board) => board.name === selectedBoard.name
|
|
);
|
|
// if the board can be found by its name after the install event select it. otherwise unselect it
|
|
// historical hint: https://github.com/arduino/arduino-ide/blob/144df893d0dafec64a26565cf912a98f32572da9/arduino-ide-extension/src/browser/boards/boards-service-provider.ts#L289-L320
|
|
this.updateConfig(
|
|
installedSelectedBoard ? installedSelectedBoard : 'unset-board'
|
|
);
|
|
if (!installedSelectedBoard) {
|
|
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
|
|
const answer = await this.messageService.warn(
|
|
nls.localize(
|
|
'arduino/board/couldNotFindPreviouslySelected',
|
|
"Could not find previously selected board '{0}' in installed platform '{1}'. Please manually reselect the board you want to use. Do you want to reselect it now?",
|
|
selectedBoard.name,
|
|
platformDidInstallEvent.item.name
|
|
),
|
|
nls.localize('arduino/board/reselectLater', 'Reselect later'),
|
|
yes
|
|
);
|
|
if (answer === yes) {
|
|
this.onBoardsConfigEdit({
|
|
query: selectedBoard.name,
|
|
portToSelect: this._boardsConfig.selectedPort,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private maybeUpdateBoardListHistory(
|
|
params: UpdateBoardListHistoryParams
|
|
): BoardListHistoryUpdateResult {
|
|
const { portToSelect, boardToSelect } = params;
|
|
const selectedPort = isPortIdentifier(portToSelect)
|
|
? portToSelect
|
|
: portToSelect === 'ignore-port'
|
|
? this._boardsConfig.selectedPort
|
|
: undefined;
|
|
const selectedBoard = isBoardIdentifier(boardToSelect)
|
|
? boardToSelect
|
|
: boardToSelect === 'ignore-board'
|
|
? this._boardsConfig.selectedBoard
|
|
: undefined;
|
|
if (selectedBoard && selectedPort) {
|
|
const match = this.boardList.items.find(
|
|
(item) =>
|
|
portIdentifierEquals(item.port, selectedPort) &&
|
|
item.board &&
|
|
boardIdentifierEquals(item.board, selectedBoard)
|
|
);
|
|
const portKey = Port.keyOf(selectedPort);
|
|
if (match) {
|
|
// When board `B` is detected on port `P` and saving `B` on `P`, remove the entry instead!
|
|
delete this._boardListHistory[portKey];
|
|
} else {
|
|
this._boardListHistory[portKey] = selectedBoard;
|
|
}
|
|
if (match) {
|
|
return { [portKey]: undefined };
|
|
}
|
|
return { [portKey]: selectedBoard };
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
private maybeUpdateBoardsData(params: UpdateBoardsDataParams): void {
|
|
const { boardToSelect, reason } = params;
|
|
if (
|
|
boardToSelect &&
|
|
boardToSelect !== 'ignore-board' &&
|
|
boardToSelect.fqbn &&
|
|
(reason === 'toolbar' || reason === 'inherit')
|
|
) {
|
|
const [, , , ...rest] = boardToSelect.fqbn.split(':');
|
|
if (rest.length) {
|
|
// https://github.com/arduino/arduino-ide/pull/2113
|
|
// TODO: save update data store if reason is toolbar and the FQBN has options
|
|
}
|
|
}
|
|
}
|
|
|
|
private async saveState(): Promise<void> {
|
|
const { selectedBoard, selectedPort } = this.boardsConfig;
|
|
await Promise.all([
|
|
this.setData(
|
|
selectedBoardStorageKey,
|
|
selectedBoard ? JSON.stringify(selectedBoard) : undefined
|
|
),
|
|
this.setData(
|
|
selectedPortStorageKey,
|
|
selectedPort ? JSON.stringify(selectedPort) : undefined
|
|
),
|
|
this.setData(
|
|
boardListHistoryStorageKey,
|
|
JSON.stringify(this._boardListHistory)
|
|
),
|
|
]);
|
|
}
|
|
|
|
private async restoreState(): Promise<
|
|
Readonly<BoardsConfig> & { boardListHistory: BoardListHistory | undefined }
|
|
> {
|
|
const [maybeSelectedBoard, maybeSelectedPort, maybeBoardHistory] =
|
|
await Promise.all([
|
|
this.getData<string>(selectedBoardStorageKey),
|
|
this.getData<string>(selectedPortStorageKey),
|
|
this.getData<string>(boardListHistoryStorageKey),
|
|
]);
|
|
const selectedBoard = this.tryParse(maybeSelectedBoard, isBoardIdentifier);
|
|
const selectedPort = this.tryParse(maybeSelectedPort, isPortIdentifier);
|
|
const boardListHistory = this.tryParse(
|
|
maybeBoardHistory,
|
|
isBoardListHistory
|
|
);
|
|
return { selectedBoard, selectedPort, boardListHistory };
|
|
}
|
|
|
|
private tryParse<T>(
|
|
raw: string | undefined,
|
|
typeGuard: (object: unknown) => object is T
|
|
): T | undefined {
|
|
if (!raw) {
|
|
return undefined;
|
|
}
|
|
try {
|
|
const object = JSON.parse(raw);
|
|
if (typeGuard(object)) {
|
|
return object;
|
|
}
|
|
} catch {
|
|
this.logger.error(`Failed to parse raw: '${raw}'`);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
private setData<T>(key: string, value: T): Promise<void> {
|
|
return this.storageService.setData(key, value);
|
|
}
|
|
|
|
private getData<T>(key: string): Promise<T | undefined> {
|
|
return this.storageService.getData(key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* It should be neither visible nor called from outside.
|
|
*
|
|
* This service creates a startup task with the current board config and
|
|
* passes the task to the electron-main process so that the new window
|
|
* can inherit the boards config state of this service.
|
|
*
|
|
* Note that the state is always set, but new windows might ignore it.
|
|
* For example, the new window already has a valid boards config persisted to the local storage.
|
|
*/
|
|
const USE_INHERITED_CONFIG: Command = {
|
|
id: 'arduino-use-inherited-boards-config',
|
|
};
|
|
|
|
const DUMP_BOARD_LIST: Command = {
|
|
id: 'arduino-dump-board-list',
|
|
label: nls.localize('arduino/developer/dumpBoardList', 'Dump the Board List'),
|
|
category: 'Developer (Arduino)',
|
|
};
|
|
|
|
const CLEAR_BOARD_LIST_HISTORY: Command = {
|
|
id: 'arduino-clear-board-list-history',
|
|
label: nls.localize(
|
|
'arduino/developer/clearBoardList',
|
|
'Clear the Board List History'
|
|
),
|
|
category: 'Developer (Arduino)',
|
|
};
|
|
|
|
const CLEAR_BOARDS_CONFIG: Command = {
|
|
id: 'arduino-clear-boards-config',
|
|
label: nls.localize(
|
|
'arduino/developer/clearBoardsConfig',
|
|
'Clear the Board and Port Selection'
|
|
),
|
|
category: 'Developer (Arduino)',
|
|
};
|