fuzzy board search.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2020-07-20 11:29:05 +02:00
parent 2f3fe27da3
commit 0ee9d16b40
9 changed files with 32 additions and 17 deletions

View File

@ -170,11 +170,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
bind(BoardsServiceClient).toDynamicValue(async context => { bind(BoardsServiceClient).toDynamicValue(async context => {
const client = context.container.get(BoardsServiceClientImpl); const client = context.container.get(BoardsServiceClientImpl);
const service = context.container.get<BoardsService>(BoardsService); const service = context.container.get<BoardsService>(BoardsService);
const [attachedBoards, availablePorts] = await Promise.all([ await client.init(service);
service.getAttachedBoards(),
service.getAvailablePorts()
]);
client.init({ attachedBoards, availablePorts });
WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath, client); WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath, client);
return client; return client;
}).inSingletonScope(); }).inSingletonScope();

View File

@ -127,7 +127,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<Board & { packageName: string }>> => {
return this.props.boardsService.searchBoards(options); return this.props.boardsServiceClient.searchBoards(options);
} }
protected get availablePorts(): Promise<Port[]> { protected get availablePorts(): Promise<Port[]> {

View File

@ -5,7 +5,7 @@ import { MessageService } from '@theia/core/lib/common/message-service';
import { StorageService } from '@theia/core/lib/browser/storage-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 { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, Board, Port, BoardUninstalledEvent } from '../../common/protocol'; import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, Board, Port, BoardUninstalledEvent, BoardsService } from '../../common/protocol';
import { BoardsConfig } from './boards-config'; import { BoardsConfig } from './boards-config';
import { naturalCompare } from '../../common/utils'; import { naturalCompare } from '../../common/utils';
@ -40,6 +40,7 @@ export class BoardsServiceClientImpl implements BoardsServiceClient, FrontendApp
protected _attachedBoards: Board[] = []; // This does not contain the `Unknown` boards. They're visible from the available ports only. protected _attachedBoards: Board[] = []; // This does not contain the `Unknown` boards. They're visible from the available ports only.
protected _availablePorts: Port[] = []; protected _availablePorts: Port[] = [];
protected _availableBoards: AvailableBoard[] = []; protected _availableBoards: AvailableBoard[] = [];
protected boardsService: BoardsService;
/** /**
* Event when the state of the attached/detached boards has changed. For instance, the user have detached a physical board. * Event when the state of the attached/detached boards has changed. For instance, the user have detached a physical board.
@ -65,7 +66,12 @@ export class BoardsServiceClientImpl implements BoardsServiceClient, FrontendApp
* When the FE connects to the BE, the BE stets the known boards and ports.\ * When the FE connects to the BE, the BE stets the known boards and ports.\
* This is a DI workaround for not being able to inject the service into the client. * This is a DI workaround for not being able to inject the service into the client.
*/ */
init({ attachedBoards, availablePorts }: { attachedBoards: Board[], availablePorts: Port[] }): void { async init(boardsService: BoardsService): Promise<void> {
this.boardsService = boardsService;
const [attachedBoards, availablePorts] = await Promise.all([
this.boardsService.getAttachedBoards(),
this.boardsService.getAvailablePorts()
]);
this._attachedBoards = attachedBoards; this._attachedBoards = attachedBoards;
this._availablePorts = availablePorts; this._availablePorts = availablePorts;
this.reconcileAvailableBoards().then(() => this.tryReconnect()); this.reconcileAvailableBoards().then(() => this.tryReconnect());
@ -157,6 +163,20 @@ export class BoardsServiceClientImpl implements BoardsServiceClient, FrontendApp
} }
} }
async searchBoards({ query, cores }: { query?: string, cores?: string[] }): Promise<Array<Board & { packageName: string }>> {
const boards = await this.boardsService.allBoards({});
const coresFilter = !!cores && cores.length
? ((toFilter: { packageName: string }) => cores.some(core => core === toFilter.packageName))
: () => true;
const fuzzyFilter = !!query
? ((toFilter: Board) => !!monaco.filters.matchesFuzzy(query, toFilter.name, true))
: () => true
return boards
.filter(coresFilter)
.filter(fuzzyFilter)
.sort(Board.compare)
}
get boardsConfig(): BoardsConfig.Config { get boardsConfig(): BoardsConfig.Config {
return this._boardsConfig; return this._boardsConfig;
} }

View File

@ -159,7 +159,7 @@ export class BoardsQuickOpenService implements QuickOpenContribution, QuickOpenM
const selectedBoard = availableBoards.filter(AvailableBoard.hasPort).find(({ selected }) => selected); const selectedBoard = availableBoards.filter(AvailableBoard.hasPort).find(({ selected }) => selected);
const [configs, boards] = await Promise.all([ const [configs, boards] = await Promise.all([
selectedBoard && selectedBoard.fqbn ? this.configStore.getConfig(selectedBoard.fqbn) : Promise.resolve([]), selectedBoard && selectedBoard.fqbn ? this.configStore.getConfig(selectedBoard.fqbn) : Promise.resolve([]),
this.boardsService.searchBoards({}) this.boardsService.allBoards({})
]); ]);
this.allBoards = Board.decorateBoards(selectedBoard, boards) this.allBoards = Board.decorateBoards(selectedBoard, boards)
.filter(board => !availableBoards.some(availableBoard => Board.sameAs(availableBoard, board))); .filter(board => !availableBoards.some(availableBoard => Board.sameAs(availableBoard, board)));

View File

@ -1,5 +1,5 @@
@import './list-widget.css'; @import './list-widget.css';
@import './board-select-dialog.css'; @import './boards-config-dialog.css';
@import './main.css'; @import './main.css';
@import './monitor.css'; @import './monitor.css';
@import './arduino-select.css'; @import './arduino-select.css';

View File

@ -68,7 +68,9 @@ export interface BoardsService extends Installable<BoardsPackage>, Searchable<Bo
getBoardDetails(options: { fqbn: string }): Promise<BoardDetails>; getBoardDetails(options: { fqbn: string }): Promise<BoardDetails>;
getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>; getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>;
getContainerBoardPackage(options: { fqbn: string }): Promise<BoardsPackage | undefined>; getContainerBoardPackage(options: { fqbn: string }): Promise<BoardsPackage | undefined>;
searchBoards(options: { query?: string }): Promise<Array<Board & { packageName: string }>>; // The CLI cannot do fuzzy search. This method provides all boards and we do the fuzzy search (with monaco) on the frontend.
// https://github.com/arduino/arduino-cli/issues/629
allBoards(options: {}): Promise<Array<Board & { packageName: string }>>;
} }
export interface Port { export interface Port {

View File

@ -261,13 +261,10 @@ export class BoardsServiceImpl implements BoardsService {
return packages.find(({ boards }) => boards.some(({ fqbn }) => fqbn === expectedFqbn)); return packages.find(({ boards }) => boards.some(({ fqbn }) => fqbn === expectedFqbn));
} }
async searchBoards(options: { query?: string }): Promise<Array<Board & { packageName: string }>> { async allBoards(options: {}): Promise<Array<Board & { packageName: string }>> {
const query = (options.query || '').toLocaleLowerCase();
const results = await this.search(options); const results = await this.search(options);
return results.map(item => item.boards.map(board => ({ ...board, packageName: item.name }))) return results.map(item => item.boards.map(board => ({ ...board, packageName: item.name })))
.reduce((acc, curr) => acc.concat(curr), []) .reduce((acc, curr) => acc.concat(curr), []);
.filter(board => board.name.toLocaleLowerCase().indexOf(query) !== -1)
.sort(Board.compare);
} }
async search(options: { query?: string }): Promise<BoardsPackage[]> { async search(options: { query?: string }): Promise<BoardsPackage[]> {

View File

@ -268,7 +268,7 @@ export class MockBoardsService implements BoardsService {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
searchBoards(): Promise<Array<Board & { packageName: string; }>> { allBoards(): Promise<Array<Board & { packageName: string; }>> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }