mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-11 19:29:28 +00:00
ATL-750: Handle board name change after install.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
@@ -19,6 +19,7 @@ export class BoardsConfigDialogWidget extends ReactWidget {
|
||||
@inject(NotificationCenter)
|
||||
protected readonly notificationCenter: NotificationCenter;
|
||||
|
||||
protected readonly onFilterTextDidChangeEmitter = new Emitter<string>();
|
||||
protected readonly onBoardConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
|
||||
readonly onBoardConfigChanged = this.onBoardConfigChangedEmitter.event;
|
||||
|
||||
@@ -27,6 +28,14 @@ export class BoardsConfigDialogWidget extends ReactWidget {
|
||||
constructor() {
|
||||
super();
|
||||
this.id = 'select-board-dialog';
|
||||
this.toDispose.pushAll([
|
||||
this.onBoardConfigChangedEmitter,
|
||||
this.onFilterTextDidChangeEmitter
|
||||
]);
|
||||
}
|
||||
|
||||
search(query: string): void {
|
||||
this.onFilterTextDidChangeEmitter.fire(query);
|
||||
}
|
||||
|
||||
protected fireConfigChanged = (config: BoardsConfig.Config) => {
|
||||
@@ -43,7 +52,8 @@ export class BoardsConfigDialogWidget extends ReactWidget {
|
||||
boardsServiceProvider={this.boardsServiceClient}
|
||||
notificationCenter={this.notificationCenter}
|
||||
onConfigChange={this.fireConfigChanged}
|
||||
onFocusNodeSet={this.setFocusNode} />
|
||||
onFocusNodeSet={this.setFocusNode}
|
||||
onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { Message } from '@phosphor/messaging';
|
||||
import { AbstractDialog, DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
|
||||
import { BoardsService } from '../../common/protocol/boards-service';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
|
||||
import { BoardsService } from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
|
||||
|
||||
@injectable()
|
||||
export class BoardsConfigDialogProps extends DialogProps {
|
||||
@@ -42,6 +42,16 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass in an empty string if you want to reset the search term. Using `undefined` has no effect.
|
||||
*/
|
||||
async open(query: string | undefined = undefined): Promise<BoardsConfig.Config | undefined> {
|
||||
if (typeof query === 'string') {
|
||||
this.widget.search(query);
|
||||
}
|
||||
return super.open();
|
||||
}
|
||||
|
||||
protected createDescription(): HTMLElement {
|
||||
const head = document.createElement('div');
|
||||
head.classList.add('head');
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { Board, Port, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
import { Board, Port, AttachedBoardsChangeEvent, BoardWithPackage } from '../../common/protocol/boards-service';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { MaybePromise } from '@theia/core';
|
||||
import { BoardsServiceProvider } from './boards-service-provider';
|
||||
|
||||
export namespace BoardsConfig {
|
||||
|
||||
@@ -18,10 +19,11 @@ export namespace BoardsConfig {
|
||||
readonly notificationCenter: NotificationCenter;
|
||||
readonly onConfigChange: (config: Config) => void;
|
||||
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
|
||||
readonly onFilteredTextDidChangeEvent: Event<string>;
|
||||
}
|
||||
|
||||
export interface State extends Config {
|
||||
searchResults: Array<Board & { packageName: string }>;
|
||||
searchResults: Array<BoardWithPackage>;
|
||||
knownPorts: Port[];
|
||||
showAllPorts: boolean;
|
||||
query: string;
|
||||
@@ -91,7 +93,8 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
this.props.notificationCenter.onPlatformUninstalled(() => this.updateBoards(this.state.query)),
|
||||
this.props.notificationCenter.onIndexUpdated(() => this.updateBoards(this.state.query)),
|
||||
this.props.notificationCenter.onDaemonStarted(() => this.updateBoards(this.state.query)),
|
||||
this.props.notificationCenter.onDaemonStopped(() => this.setState({ searchResults: [] }))
|
||||
this.props.notificationCenter.onDaemonStopped(() => this.setState({ searchResults: [] })),
|
||||
this.props.onFilteredTextDidChangeEvent(query => this.setState({ query }, () => this.updateBoards(this.state.query)))
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -105,10 +108,9 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
}
|
||||
|
||||
protected updateBoards = (eventOrQuery: React.ChangeEvent<HTMLInputElement> | string = '') => {
|
||||
const query = (typeof eventOrQuery === 'string'
|
||||
const query = typeof eventOrQuery === 'string'
|
||||
? eventOrQuery
|
||||
: eventOrQuery.target.value.toLowerCase()
|
||||
).trim();
|
||||
: eventOrQuery.target.value.toLowerCase();
|
||||
this.setState({ query });
|
||||
this.queryBoards({ query }).then(searchResults => this.setState({ searchResults }));
|
||||
}
|
||||
@@ -124,7 +126,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
});
|
||||
}
|
||||
|
||||
protected queryBoards = (options: { query?: string } = {}): Promise<Array<Board & { packageName: string }>> => {
|
||||
protected queryBoards = (options: { query?: string } = {}): Promise<Array<BoardWithPackage>> => {
|
||||
return this.props.boardsServiceProvider.searchBoards(options);
|
||||
}
|
||||
|
||||
@@ -145,7 +147,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
this.setState({ selectedPort }, () => this.fireConfigChanged());
|
||||
}
|
||||
|
||||
protected selectBoard = (selectedBoard: Board & { packageName: string } | undefined) => {
|
||||
protected selectBoard = (selectedBoard: BoardWithPackage | undefined) => {
|
||||
this.setState({ selectedBoard }, () => this.fireConfigChanged());
|
||||
}
|
||||
|
||||
@@ -175,7 +177,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
}
|
||||
|
||||
protected renderBoards(): React.ReactNode {
|
||||
const { selectedBoard, searchResults } = this.state;
|
||||
const { selectedBoard, searchResults, query } = this.state;
|
||||
// Board names are not unique per core https://github.com/arduino/arduino-pro-ide/issues/262#issuecomment-661019560
|
||||
// It is tricky when the core is not yet installed, no FQBNs are available.
|
||||
const distinctBoards = new Map<string, Board.Detailed>();
|
||||
@@ -189,11 +191,18 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
|
||||
return <React.Fragment>
|
||||
<div className='search'>
|
||||
<input type='search' className='theia-input' placeholder='SEARCH BOARD' onChange={this.updateBoards} ref={this.focusNodeSet} />
|
||||
<input
|
||||
type='search'
|
||||
value={query}
|
||||
className='theia-input'
|
||||
placeholder='SEARCH BOARD'
|
||||
onChange={this.updateBoards}
|
||||
ref={this.focusNodeSet}
|
||||
/>
|
||||
<i className='fa fa-search'></i>
|
||||
</div>
|
||||
<div className='boards list'>
|
||||
{Array.from(distinctBoards.values()).map(board => <Item<Board & { packageName: string }>
|
||||
{Array.from(distinctBoards.values()).map(board => <Item<BoardWithPackage>
|
||||
key={`${board.name}-${board.packageName}`}
|
||||
item={board}
|
||||
label={board.name}
|
||||
|
||||
@@ -83,7 +83,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
|
||||
}
|
||||
const key = this.getStorageKey(fqbn, version);
|
||||
let data = await this.storageService.getData<BoardsDataStore.Data | undefined>(key, undefined);
|
||||
if (data) {
|
||||
if (BoardsDataStore.Data.is(data)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -202,5 +202,10 @@ export namespace BoardsDataStore {
|
||||
configOptions: [],
|
||||
programmers: []
|
||||
};
|
||||
export function is(arg: any): arg is Data {
|
||||
return !!arg
|
||||
&& 'configOptions' in arg && Array.isArray(arg['configOptions'])
|
||||
&& 'programmers' in arg && Array.isArray(arg['programmers'])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,18 @@ import {
|
||||
Board,
|
||||
BoardsService,
|
||||
BoardsPackage,
|
||||
AttachedBoardsChangeEvent
|
||||
AttachedBoardsChangeEvent,
|
||||
BoardWithPackage
|
||||
} from '../../common/protocol';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { naturalCompare } from '../../common/utils';
|
||||
import { compareAnything } from '../theia/monaco/comparers';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { CommandService } from '@theia/core';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
|
||||
interface BoardMatch {
|
||||
readonly board: Board & Readonly<{ packageName: string }>;
|
||||
readonly board: BoardWithPackage;
|
||||
readonly matches: monaco.filters.IMatch[] | undefined;
|
||||
}
|
||||
|
||||
@@ -37,6 +40,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
@inject(BoardsService)
|
||||
protected boardsService: BoardsService;
|
||||
|
||||
@inject(CommandService)
|
||||
protected commandService: CommandService;
|
||||
|
||||
@inject(NotificationCenter)
|
||||
protected notificationCenter: NotificationCenter;
|
||||
|
||||
@@ -107,6 +113,19 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
};
|
||||
return;
|
||||
}
|
||||
// The board name can change after install.
|
||||
// This logic handles it "gracefully" by unselecting the board, so that we can avoid no FQBN is set error.
|
||||
// https://github.com/arduino/arduino-cli/issues/620
|
||||
// https://github.com/arduino/arduino-pro-ide/issues/374
|
||||
if (BoardWithPackage.is(selectedBoard) && selectedBoard.packageId === event.item.id && !installedBoard) {
|
||||
this.messageService.warn(`Could not find previously selected board '${selectedBoard.name}' in installed platform '${event.item.name}'. Please manually reselect the board you want to use. Do you want to reselect it now?`, 'Reselect later', 'Yes').then(async answer => {
|
||||
if (answer === 'Yes') {
|
||||
this.commandService.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id, selectedBoard.name);
|
||||
}
|
||||
});
|
||||
this.boardsConfig = {}
|
||||
return;
|
||||
}
|
||||
// Trigger a board re-set. See: https://github.com/arduino/arduino-cli/issues/954
|
||||
// E.g: install `adafruit:avr`, then select `adafruit:avr:adafruit32u4` board, and finally install the required `arduino:avr`
|
||||
this.boardsConfig = this.boardsConfig;
|
||||
@@ -173,15 +192,15 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||
}
|
||||
}
|
||||
|
||||
async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise<Array<Board & { packageName: string }>> {
|
||||
async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise<Array<BoardWithPackage>> {
|
||||
const boards = await this.boardsService.allBoards({});
|
||||
const coresFilter = !!cores && cores.length
|
||||
? ((toFilter: { packageName: string }) => cores.some(core => core === toFilter.packageName))
|
||||
? ((toFilter: BoardWithPackage) => cores.some(core => core === toFilter.packageName || core === toFilter.packageId))
|
||||
: () => true;
|
||||
if (!query) {
|
||||
return boards.filter(coresFilter).sort(Board.compare);
|
||||
}
|
||||
const toMatch = ((toFilter: Board & { packageName: string }) => (({ board: toFilter, matches: monaco.filters.matchesFuzzy(query, toFilter.name, true) })));
|
||||
const toMatch = ((toFilter: BoardWithPackage) => (({ board: toFilter, matches: monaco.filters.matchesFuzzy(query, toFilter.name, true) })));
|
||||
const compareEntries = (left: BoardMatch, right: BoardMatch, lookFor: string) => {
|
||||
const leftMatches = left.matches || [];
|
||||
const rightMatches = right.matches || [];
|
||||
|
||||
Reference in New Issue
Block a user