Skip to content

Commit c024a8d

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-750: Handle board name change after install.
Signed-off-by: Akos Kitta <[email protected]>
1 parent 7696e2c commit c024a8d

26 files changed

+674
-79
lines changed

Diff for: .vscode/launch.json

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"args": [
3535
"--log-level=debug",
3636
"--hostname=localhost",
37+
"--no-cluster",
3738
"--remote-debugging-port=9222",
3839
"--no-app-auto-install",
3940
"--plugins=local-dir:plugins"
@@ -60,6 +61,7 @@
6061
"args": [
6162
"--hostname=0.0.0.0",
6263
"--port=3000",
64+
"--no-cluster",
6365
"--no-app-auto-install",
6466
"--plugins=local-dir:plugins"
6567
],

Diff for: arduino-ide-extension/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
],
121121
"arduino": {
122122
"cli": {
123-
"version": "20201112"
123+
"version": "20201201"
124124
}
125125
}
126126
}

Diff for: arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ export class ArduinoFrontendContribution implements FrontendApplicationContribut
272272
}
273273
});
274274
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
275-
execute: async () => {
276-
const boardsConfig = await this.boardsConfigDialog.open();
275+
execute: async (query?: string | undefined) => {
276+
const boardsConfig = await this.boardsConfigDialog.open(query);
277277
if (boardsConfig) {
278278
this.boardsServiceClientImpl.boardsConfig = boardsConfig;
279279
}

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class BoardsConfigDialogWidget extends ReactWidget {
1919
@inject(NotificationCenter)
2020
protected readonly notificationCenter: NotificationCenter;
2121

22+
protected readonly onFilterTextDidChangeEmitter = new Emitter<string>();
2223
protected readonly onBoardConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
2324
readonly onBoardConfigChanged = this.onBoardConfigChangedEmitter.event;
2425

@@ -27,6 +28,14 @@ export class BoardsConfigDialogWidget extends ReactWidget {
2728
constructor() {
2829
super();
2930
this.id = 'select-board-dialog';
31+
this.toDispose.pushAll([
32+
this.onBoardConfigChangedEmitter,
33+
this.onFilterTextDidChangeEmitter
34+
]);
35+
}
36+
37+
search(query: string): void {
38+
this.onFilterTextDidChangeEmitter.fire(query);
3039
}
3140

3241
protected fireConfigChanged = (config: BoardsConfig.Config) => {
@@ -43,7 +52,8 @@ export class BoardsConfigDialogWidget extends ReactWidget {
4352
boardsServiceProvider={this.boardsServiceClient}
4453
notificationCenter={this.notificationCenter}
4554
onConfigChange={this.fireConfigChanged}
46-
onFocusNodeSet={this.setFocusNode} />
55+
onFocusNodeSet={this.setFocusNode}
56+
onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event} />
4757
</div>;
4858
}
4959

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { injectable, inject, postConstruct } from 'inversify';
22
import { Message } from '@phosphor/messaging';
33
import { AbstractDialog, DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
4-
import { BoardsService } from '../../common/protocol/boards-service';
54
import { BoardsConfig } from './boards-config';
6-
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
5+
import { BoardsService } from '../../common/protocol/boards-service';
76
import { BoardsServiceProvider } from './boards-service-provider';
7+
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
88

99
@injectable()
1010
export class BoardsConfigDialogProps extends DialogProps {
@@ -42,6 +42,16 @@ export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
4242
}));
4343
}
4444

45+
/**
46+
* Pass in an empty string if you want to reset the search term. Using `undefined` has no effect.
47+
*/
48+
async open(query: string | undefined = undefined): Promise<BoardsConfig.Config | undefined> {
49+
if (typeof query === 'string') {
50+
this.widget.search(query);
51+
}
52+
return super.open();
53+
}
54+
4555
protected createDescription(): HTMLElement {
4656
const head = document.createElement('div');
4757
head.classList.add('head');

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

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as React from 'react';
2+
import { Event } from '@theia/core/lib/common/event';
23
import { notEmpty } from '@theia/core/lib/common/objects';
4+
import { MaybePromise } from '@theia/core/lib/common/types';
35
import { DisposableCollection } from '@theia/core/lib/common/disposable';
4-
import { Board, Port, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
5-
import { BoardsServiceProvider } from './boards-service-provider';
6+
import { Board, Port, AttachedBoardsChangeEvent, BoardWithPackage } from '../../common/protocol/boards-service';
67
import { NotificationCenter } from '../notification-center';
7-
import { MaybePromise } from '@theia/core';
8+
import { BoardsServiceProvider } from './boards-service-provider';
89

910
export namespace BoardsConfig {
1011

@@ -18,10 +19,11 @@ export namespace BoardsConfig {
1819
readonly notificationCenter: NotificationCenter;
1920
readonly onConfigChange: (config: Config) => void;
2021
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
22+
readonly onFilteredTextDidChangeEvent: Event<string>;
2123
}
2224

2325
export interface State extends Config {
24-
searchResults: Array<Board & { packageName: string }>;
26+
searchResults: Array<BoardWithPackage>;
2527
knownPorts: Port[];
2628
showAllPorts: boolean;
2729
query: string;
@@ -91,7 +93,8 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
9193
this.props.notificationCenter.onPlatformUninstalled(() => this.updateBoards(this.state.query)),
9294
this.props.notificationCenter.onIndexUpdated(() => this.updateBoards(this.state.query)),
9395
this.props.notificationCenter.onDaemonStarted(() => this.updateBoards(this.state.query)),
94-
this.props.notificationCenter.onDaemonStopped(() => this.setState({ searchResults: [] }))
96+
this.props.notificationCenter.onDaemonStopped(() => this.setState({ searchResults: [] })),
97+
this.props.onFilteredTextDidChangeEvent(query => this.setState({ query }, () => this.updateBoards(this.state.query)))
9598
]);
9699
}
97100

@@ -105,10 +108,9 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
105108
}
106109

107110
protected updateBoards = (eventOrQuery: React.ChangeEvent<HTMLInputElement> | string = '') => {
108-
const query = (typeof eventOrQuery === 'string'
111+
const query = typeof eventOrQuery === 'string'
109112
? eventOrQuery
110-
: eventOrQuery.target.value.toLowerCase()
111-
).trim();
113+
: eventOrQuery.target.value.toLowerCase();
112114
this.setState({ query });
113115
this.queryBoards({ query }).then(searchResults => this.setState({ searchResults }));
114116
}
@@ -124,7 +126,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
124126
});
125127
}
126128

127-
protected queryBoards = (options: { query?: string } = {}): Promise<Array<Board & { packageName: string }>> => {
129+
protected queryBoards = (options: { query?: string } = {}): Promise<Array<BoardWithPackage>> => {
128130
return this.props.boardsServiceProvider.searchBoards(options);
129131
}
130132

@@ -145,7 +147,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
145147
this.setState({ selectedPort }, () => this.fireConfigChanged());
146148
}
147149

148-
protected selectBoard = (selectedBoard: Board & { packageName: string } | undefined) => {
150+
protected selectBoard = (selectedBoard: BoardWithPackage | undefined) => {
149151
this.setState({ selectedBoard }, () => this.fireConfigChanged());
150152
}
151153

@@ -175,7 +177,7 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
175177
}
176178

177179
protected renderBoards(): React.ReactNode {
178-
const { selectedBoard, searchResults } = this.state;
180+
const { selectedBoard, searchResults, query } = this.state;
179181
// Board names are not unique per core https://github.com/arduino/arduino-pro-ide/issues/262#issuecomment-661019560
180182
// It is tricky when the core is not yet installed, no FQBNs are available.
181183
const distinctBoards = new Map<string, Board.Detailed>();
@@ -189,11 +191,18 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
189191

190192
return <React.Fragment>
191193
<div className='search'>
192-
<input type='search' className='theia-input' placeholder='SEARCH BOARD' onChange={this.updateBoards} ref={this.focusNodeSet} />
194+
<input
195+
type='search'
196+
value={query}
197+
className='theia-input'
198+
placeholder='SEARCH BOARD'
199+
onChange={this.updateBoards}
200+
ref={this.focusNodeSet}
201+
/>
193202
<i className='fa fa-search'></i>
194203
</div>
195204
<div className='boards list'>
196-
{Array.from(distinctBoards.values()).map(board => <Item<Board & { packageName: string }>
205+
{Array.from(distinctBoards.values()).map(board => <Item<BoardWithPackage>
197206
key={`${board.name}-${board.packageName}`}
198207
item={board}
199208
label={board.name}

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
8383
}
8484
const key = this.getStorageKey(fqbn, version);
8585
let data = await this.storageService.getData<BoardsDataStore.Data | undefined>(key, undefined);
86-
if (data) {
86+
if (BoardsDataStore.Data.is(data)) {
8787
return data;
8888
}
8989

@@ -202,5 +202,10 @@ export namespace BoardsDataStore {
202202
configOptions: [],
203203
programmers: []
204204
};
205+
export function is(arg: any): arg is Data {
206+
return !!arg
207+
&& 'configOptions' in arg && Array.isArray(arg['configOptions'])
208+
&& 'programmers' in arg && Array.isArray(arg['programmers'])
209+
}
205210
}
206211
}

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

+24-5
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ import {
1010
Board,
1111
BoardsService,
1212
BoardsPackage,
13-
AttachedBoardsChangeEvent
13+
AttachedBoardsChangeEvent,
14+
BoardWithPackage
1415
} from '../../common/protocol';
1516
import { BoardsConfig } from './boards-config';
1617
import { naturalCompare } from '../../common/utils';
1718
import { compareAnything } from '../theia/monaco/comparers';
1819
import { NotificationCenter } from '../notification-center';
20+
import { CommandService } from '@theia/core';
21+
import { ArduinoCommands } from '../arduino-commands';
1922

2023
interface BoardMatch {
21-
readonly board: Board & Readonly<{ packageName: string }>;
24+
readonly board: BoardWithPackage;
2225
readonly matches: monaco.filters.IMatch[] | undefined;
2326
}
2427

@@ -37,6 +40,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
3740
@inject(BoardsService)
3841
protected boardsService: BoardsService;
3942

43+
@inject(CommandService)
44+
protected commandService: CommandService;
45+
4046
@inject(NotificationCenter)
4147
protected notificationCenter: NotificationCenter;
4248

@@ -107,6 +113,19 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
107113
};
108114
return;
109115
}
116+
// The board name can change after install.
117+
// This logic handles it "gracefully" by unselecting the board, so that we can avoid no FQBN is set error.
118+
// https://github.com/arduino/arduino-cli/issues/620
119+
// https://github.com/arduino/arduino-pro-ide/issues/374
120+
if (BoardWithPackage.is(selectedBoard) && selectedBoard.packageId === event.item.id && !installedBoard) {
121+
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 => {
122+
if (answer === 'Yes') {
123+
this.commandService.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id, selectedBoard.name);
124+
}
125+
});
126+
this.boardsConfig = {}
127+
return;
128+
}
110129
// Trigger a board re-set. See: https://github.com/arduino/arduino-cli/issues/954
111130
// E.g: install `adafruit:avr`, then select `adafruit:avr:adafruit32u4` board, and finally install the required `arduino:avr`
112131
this.boardsConfig = this.boardsConfig;
@@ -173,15 +192,15 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
173192
}
174193
}
175194

176-
async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise<Array<Board & { packageName: string }>> {
195+
async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise<Array<BoardWithPackage>> {
177196
const boards = await this.boardsService.allBoards({});
178197
const coresFilter = !!cores && cores.length
179-
? ((toFilter: { packageName: string }) => cores.some(core => core === toFilter.packageName))
198+
? ((toFilter: BoardWithPackage) => cores.some(core => core === toFilter.packageName || core === toFilter.packageId))
180199
: () => true;
181200
if (!query) {
182201
return boards.filter(coresFilter).sort(Board.compare);
183202
}
184-
const toMatch = ((toFilter: Board & { packageName: string }) => (({ board: toFilter, matches: monaco.filters.matchesFuzzy(query, toFilter.name, true) })));
203+
const toMatch = ((toFilter: BoardWithPackage) => (({ board: toFilter, matches: monaco.filters.matchesFuzzy(query, toFilter.name, true) })));
185204
const compareEntries = (left: BoardMatch, right: BoardMatch, lookFor: string) => {
186205
const leftMatches = left.matches || [];
187206
const rightMatches = right.matches || [];

Diff for: arduino-ide-extension/src/common/protocol/boards-service.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export interface BoardsService extends Installable<BoardsPackage>, Searchable<Bo
104104
getContainerBoardPackage(options: { fqbn: string }): Promise<BoardsPackage | undefined>;
105105
// The CLI cannot do fuzzy search. This method provides all boards and we do the fuzzy search (with monaco) on the frontend.
106106
// https://github.com/arduino/arduino-cli/issues/629
107-
allBoards(options: {}): Promise<Array<Board & { packageName: string }>>;
107+
allBoards(options: {}): Promise<Array<BoardWithPackage>>;
108108
}
109109

110110
export interface Port {
@@ -255,6 +255,18 @@ export interface Board {
255255
readonly port?: Port;
256256
}
257257

258+
export interface BoardWithPackage extends Board {
259+
readonly packageName: string;
260+
readonly packageId: string;
261+
}
262+
export namespace BoardWithPackage {
263+
264+
export function is(board: Board & Partial<{ packageName: string, packageId: string }>): board is BoardWithPackage {
265+
return !!board.packageId && !!board.packageName;
266+
}
267+
268+
}
269+
258270
export interface BoardDetails {
259271
readonly fqbn: string;
260272
readonly requiredTools: Tool[];
@@ -381,10 +393,10 @@ export namespace Board {
381393
return `${board.name}${fqbn}`;
382394
}
383395

384-
export type Detailed = Board & Readonly<{ selected: boolean, missing: boolean, packageName: string, details?: string }>;
396+
export type Detailed = Board & Readonly<{ selected: boolean, missing: boolean, packageName: string, packageId: string, details?: string }>;
385397
export function decorateBoards(
386398
selectedBoard: Board | undefined,
387-
boards: Array<Board & { packageName: string }>): Array<Detailed> {
399+
boards: Array<BoardWithPackage>): Array<Detailed> {
388400
// Board names are not unique. We show the corresponding core name as a detail.
389401
// https://github.com/arduino/arduino-cli/pull/294#issuecomment-513764948
390402
const distinctBoardNames = new Map<string, number>();
@@ -394,12 +406,15 @@ export namespace Board {
394406
}
395407

396408
// Due to the non-unique board names, we have to check the package name as well.
397-
const selected = (board: Board & { packageName: string }) => {
409+
const selected = (board: BoardWithPackage) => {
398410
if (!!selectedBoard) {
399411
if (Board.equals(board, selectedBoard)) {
400412
if ('packageName' in selectedBoard) {
401413
return board.packageName === (selectedBoard as any).packageName;
402414
}
415+
if ('packageId' in selectedBoard) {
416+
return board.packageId === (selectedBoard as any).packageId;
417+
}
403418
return true;
404419
}
405420
}

Diff for: arduino-ide-extension/src/node/boards-service-impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ILogger } from '@theia/core/lib/common/logger';
33
import {
44
BoardsService,
55
Installable,
6-
BoardsPackage, Board, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer, OutputService, NotificationServiceServer, AvailablePorts
6+
BoardsPackage, Board, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer, OutputService, NotificationServiceServer, AvailablePorts, BoardWithPackage
77
} from '../common/protocol';
88
import {
99
PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq,
@@ -155,9 +155,9 @@ export class BoardsServiceImpl implements BoardsService {
155155
return packages.find(({ boards }) => boards.some(({ fqbn }) => fqbn === expectedFqbn));
156156
}
157157

158-
async allBoards(options: {}): Promise<Array<Board & { packageName: string }>> {
158+
async allBoards(options: {}): Promise<Array<BoardWithPackage>> {
159159
const results = await this.search(options);
160-
return results.map(item => item.boards.map(board => ({ ...board, packageName: item.name })))
160+
return results.map(item => item.boards.map(board => ({ ...board, packageName: item.name, packageId: item.id })))
161161
.reduce((acc, curr) => acc.concat(curr), []);
162162
}
163163

0 commit comments

Comments
 (0)