feat: simplify board and port handling (#2165)

Use Arduino CLI revision `38479dc`

Closes #43
Closes #82
Closes #1319
Closes #1366
Closes #2143
Closes #2158

Ref: 38479dc706

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2023-08-18 14:42:50 +02:00 committed by GitHub
parent 9a6a457bc4
commit 69ae38effa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 6524 additions and 3951 deletions

7
.vscode/launch.json vendored
View File

@ -94,6 +94,13 @@
"--colors",
"**/${fileBasenameNoExtension}.js"
],
"outFiles": [
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
"${workspaceRoot}/electron-app/lib/**/*.js",
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js",
"${workspaceRoot}/node_modules/@theia/**/*.js"
],
"env": {
"TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json",
"IDE2_TEST": "true"

View File

@ -123,14 +123,14 @@
"mockdate": "^3.0.5",
"moment": "^2.24.0",
"ncp": "^2.0.0",
"protoc": "^1.0.4",
"rimraf": "^2.6.1",
"shelljs": "^0.8.3",
"uuid": "^3.2.1",
"yargs": "^11.1.0"
},
"optionalDependencies": {
"grpc-tools": "^1.9.0"
"grpc-tools": "^1.9.0",
"protoc": "^1.0.4"
},
"mocha": {
"require": [
@ -172,10 +172,14 @@
],
"arduino": {
"arduino-cli": {
"version": "0.33.1"
"version": {
"owner": "arduino",
"repo": "arduino-cli",
"commitish": "38479dc"
}
},
"arduino-fwuploader": {
"version": "2.2.2"
"version": "2.3.0"
},
"arduino-language-server": {
"version": "0.7.4"

View File

@ -113,6 +113,8 @@ function buildFromGit(command, version, destinationPath, taskName) {
shell.echo(`<<< Checked out ${commitish}.`);
}
exec('git', ['-C', tempRepoPath, 'rev-parse', '--short', 'HEAD'], shell);
shell.echo(`>>> Building the ${taskName}...`);
exec(command, ['build'], shell, { cwd: tempRepoPath, encoding: 'utf8' });
shell.echo(`<<< Done ${taskName} build.`);

View File

@ -27,7 +27,10 @@ import { SketchesServiceClientImpl } from './sketches-service-client-impl';
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
import { BoardsListWidget } from './boards/boards-list-widget';
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
import { BoardsServiceProvider } from './boards/boards-service-provider';
import {
BoardListDumper,
BoardsServiceProvider,
} from './boards/boards-service-provider';
import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { WorkspaceService } from './theia/workspace/workspace-service';
import { OutlineViewContribution as TheiaOutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
@ -61,7 +64,6 @@ import {
BoardsConfigDialog,
BoardsConfigDialogProps,
} from './boards/boards-config-dialog';
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
import { ScmContribution as TheiaScmContribution } from '@theia/scm/lib/browser/scm-contribution';
import { ScmContribution } from './theia/scm/scm-contribution';
import { SearchInWorkspaceFrontendContribution as TheiaSearchInWorkspaceFrontendContribution } from '@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution';
@ -100,7 +102,7 @@ import {
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
ApplicationConnectionStatusContribution as TheiaApplicationConnectionStatusContribution,
} from '@theia/core/lib/browser/connection-status-service';
import { BoardsDataMenuUpdater } from './boards/boards-data-menu-updater';
import { BoardsDataMenuUpdater } from './contributions/boards-data-menu-updater';
import { BoardsDataStore } from './boards/boards-data-store';
import { ILogger } from '@theia/core/lib/common/logger';
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
@ -208,7 +210,6 @@ import {
MonacoEditorFactory,
MonacoEditorProvider as TheiaMonacoEditorProvider,
} from '@theia/monaco/lib/browser/monaco-editor-provider';
import { StorageWrapper } from './storage-wrapper';
import { NotificationManager } from './theia/messages/notifications-manager';
import { NotificationManager as TheiaNotificationManager } from '@theia/messages/lib/browser/notifications-manager';
import { NotificationsRenderer as TheiaNotificationsRenderer } from '@theia/messages/lib/browser/notifications-renderer';
@ -445,11 +446,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(BoardsServiceProvider).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(BoardsServiceProvider);
bind(CommandContribution).toService(BoardsServiceProvider);
bind(BoardListDumper).toSelf().inSingletonScope();
// To be able to track, and update the menu based on the core settings (aka. board details) of the currently selected board.
bind(FrontendApplicationContribution)
.to(BoardsDataMenuUpdater)
.inSingletonScope();
bind(BoardsDataStore).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(BoardsDataStore);
// Logger for the Arduino daemon
@ -478,7 +477,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(OpenHandler).toService(BoardsListWidgetFrontendContribution);
// Board select dialog
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
bind(BoardsConfigDialog).toSelf().inSingletonScope();
bind(BoardsConfigDialogProps).toConstantValue({
title: nls.localize(
@ -751,6 +749,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, CloudSketchbookContribution);
Contribution.configure(bind, CreateCloudCopy);
Contribution.configure(bind, UpdateArduinoState);
Contribution.configure(bind, BoardsDataMenuUpdater);
bindContributionProvider(bind, StartupTaskProvider);
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
@ -879,9 +878,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
),
});
bind(StorageWrapper).toSelf().inSingletonScope();
bind(CommandContribution).toService(StorageWrapper);
bind(NotificationManager).toSelf().inSingletonScope();
rebind(TheiaNotificationManager).toService(NotificationManager);
bind(NotificationsRenderer).toSelf().inSingletonScope();

View File

@ -1,281 +1,229 @@
import { injectable, inject } from '@theia/core/shared/inversify';
import { MessageService } from '@theia/core/lib/common/message-service';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import {
BoardsService,
BoardsPackage,
Board,
Port,
} from '../../common/protocol/boards-service';
import { BoardsServiceProvider } from './boards-service-provider';
import { Installable, ResponseServiceClient } from '../../common/protocol';
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
import { nls } from '@theia/core/lib/common';
import { NotificationCenter } from '../notification-center';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { MessageService } from '@theia/core/lib/common/message-service';
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
import { nls } from '@theia/core/lib/common/nls';
import { notEmpty } from '@theia/core/lib/common/objects';
import { inject, injectable } from '@theia/core/shared/inversify';
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
import { InstallManually } from '../../common/nls';
interface AutoInstallPromptAction {
// isAcceptance, whether or not the action indicates acceptance of auto-install proposal
isAcceptance?: boolean;
key: string;
handler: (...args: unknown[]) => unknown;
}
type AutoInstallPromptActions = AutoInstallPromptAction[];
import { Installable, ResponseServiceClient } from '../../common/protocol';
import {
BoardIdentifier,
BoardsPackage,
BoardsService,
createPlatformIdentifier,
isBoardIdentifierChangeEvent,
PlatformIdentifier,
platformIdentifierEquals,
serializePlatformIdentifier,
} from '../../common/protocol/boards-service';
import { NotificationCenter } from '../notification-center';
import { BoardsServiceProvider } from './boards-service-provider';
import { BoardsListWidgetFrontendContribution } from './boards-widget-frontend-contribution';
/**
* Listens on `BoardsConfig.Config` changes, if a board is selected which does not
* Listens on `BoardsConfigChangeEvent`s, if a board is selected which does not
* have the corresponding core installed, it proposes the user to install the core.
*/
// * Cases in which we do not show the auto-install prompt:
// 1. When a related platform is already installed
// 2. When a prompt is already showing in the UI
// 3. When a board is unplugged
@injectable()
export class BoardsAutoInstaller implements FrontendApplicationContribution {
@inject(NotificationCenter)
private readonly notificationCenter: NotificationCenter;
@inject(MessageService)
protected readonly messageService: MessageService;
private readonly messageService: MessageService;
@inject(NotificationManager)
private readonly notificationManager: NotificationManager;
@inject(BoardsService)
protected readonly boardsService: BoardsService;
private readonly boardsService: BoardsService;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(ResponseServiceClient)
protected readonly responseService: ResponseServiceClient;
private readonly responseService: ResponseServiceClient;
@inject(BoardsListWidgetFrontendContribution)
protected readonly boardsManagerFrontendContribution: BoardsListWidgetFrontendContribution;
private readonly boardsManagerWidgetContribution: BoardsListWidgetFrontendContribution;
// Workaround for https://github.com/eclipse-theia/theia/issues/9349
protected notifications: Board[] = [];
// * "refusal" meaning a "prompt action" not accepting the auto-install offer ("X" or "install manually")
// we can use "portSelectedOnLastRefusal" to deduce when a board is unplugged after a user has "refused"
// an auto-install prompt. Important to know as we do not want "an unplug" to trigger a "refused" prompt
// showing again
private portSelectedOnLastRefusal: Port | undefined;
private lastRefusedPackageId: string | undefined;
private readonly installNotificationInfos: Readonly<{
boardName: string;
platformId: string;
notificationId: string;
}>[] = [];
private readonly toDispose = new DisposableCollection();
onStart(): void {
const setEventListeners = () => {
this.boardsServiceClient.onBoardsConfigChanged((config) => {
const { selectedBoard, selectedPort } = config;
const boardWasUnplugged =
!selectedPort && this.portSelectedOnLastRefusal;
this.clearLastRefusedPromptInfo();
if (
boardWasUnplugged ||
!selectedBoard ||
this.promptAlreadyShowingForBoard(selectedBoard)
) {
return;
this.toDispose.pushAll([
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
if (isBoardIdentifierChangeEvent(event)) {
this.ensureCoreExists(event.selectedBoard);
}
this.ensureCoreExists(selectedBoard, selectedPort);
});
// we "clearRefusedPackageInfo" if a "refused" package is eventually
// installed, though this is not strictly necessary. It's more of a
// cleanup, to ensure the related variables are representative of
// current state.
this.notificationCenter.onPlatformDidInstall((installed) => {
if (this.lastRefusedPackageId === installed.item.id) {
this.clearLastRefusedPromptInfo();
}
});
};
// we should invoke this.ensureCoreExists only once we're sure
// everything has been reconciled
this.boardsServiceClient.reconciled.then(() => {
const { selectedBoard, selectedPort } =
this.boardsServiceClient.boardsConfig;
if (selectedBoard) {
this.ensureCoreExists(selectedBoard, selectedPort);
}
setEventListeners();
}),
this.notificationCenter.onPlatformDidInstall((event) =>
this.clearAllNotificationForPlatform(event.item.id)
),
]);
this.boardsServiceProvider.ready.then(() => {
const { selectedBoard } = this.boardsServiceProvider.boardsConfig;
this.ensureCoreExists(selectedBoard);
});
}
private removeNotificationByBoard(selectedBoard: Board): void {
const index = this.notifications.findIndex((notification) =>
Board.sameAs(notification, selectedBoard)
);
if (index !== -1) {
this.notifications.splice(index, 1);
private async findPlatformToInstall(
selectedBoard: BoardIdentifier
): Promise<BoardsPackage | undefined> {
const platformId = await this.findPlatformIdToInstall(selectedBoard);
if (!platformId) {
return undefined;
}
const id = serializePlatformIdentifier(platformId);
const platform = await this.boardsService.getBoardPackage({ id });
if (!platform) {
console.warn(`Could not resolve platform for ID: ${id}`);
return undefined;
}
if (platform.installedVersion) {
return undefined;
}
return platform;
}
private clearLastRefusedPromptInfo(): void {
this.lastRefusedPackageId = undefined;
this.portSelectedOnLastRefusal = undefined;
}
private setLastRefusedPromptInfo(
packageId: string,
selectedPort?: Port
): void {
this.lastRefusedPackageId = packageId;
this.portSelectedOnLastRefusal = selectedPort;
}
private promptAlreadyShowingForBoard(board: Board): boolean {
return Boolean(
this.notifications.find((notification) =>
Board.sameAs(notification, board)
)
);
}
protected ensureCoreExists(selectedBoard: Board, selectedPort?: Port): void {
this.notifications.push(selectedBoard);
this.boardsService.search({}).then((packages) => {
const candidate = this.getInstallCandidate(packages, selectedBoard);
if (candidate) {
this.showAutoInstallPrompt(candidate, selectedBoard, selectedPort);
} else {
this.removeNotificationByBoard(selectedBoard);
private async findPlatformIdToInstall(
selectedBoard: BoardIdentifier
): Promise<PlatformIdentifier | undefined> {
const selectedBoardPlatformId = createPlatformIdentifier(selectedBoard);
// The board is installed or the FQBN is available from the `board list watch` for Arduino boards. The latter might change!
if (selectedBoardPlatformId) {
const installedPlatforms =
await this.boardsService.getInstalledPlatforms();
const installedPlatformIds = installedPlatforms
.map((platform) => createPlatformIdentifier(platform.id))
.filter(notEmpty);
if (
installedPlatformIds.every(
(installedPlatformId) =>
!platformIdentifierEquals(
installedPlatformId,
selectedBoardPlatformId
)
)
) {
return selectedBoardPlatformId;
}
});
} else {
// IDE2 knows that selected board is not installed. Look for board `name` match in not yet installed platforms.
// The order should be correct when there is a board name collision (e.g. Arduino Nano RP2040 from Arduino Mbed OS Nano Boards, [DEPRECATED] Arduino Mbed OS Nano Boards). The CLI boosts the platforms, so picking the first name match should be fine.
const platforms = await this.boardsService.search({});
for (const platform of platforms) {
// Ignore installed platforms
if (platform.installedVersion) {
continue;
}
if (
platform.boards.some((board) => board.name === selectedBoard.name)
) {
const platformId = createPlatformIdentifier(platform.id);
if (platformId) {
return platformId;
}
}
}
}
return undefined;
}
private getInstallCandidate(
packages: BoardsPackage[],
selectedBoard: Board
): BoardsPackage | undefined {
// filter packagesForBoard selecting matches from the cli (installed packages)
// and matches based on the board name
// NOTE: this ensures the Deprecated & new packages are all in the array
// so that we can check if any of the valid packages is already installed
const packagesForBoard = packages.filter(
(pkg) =>
BoardsPackage.contains(selectedBoard, pkg) ||
pkg.boards.some((board) => board.name === selectedBoard.name)
);
// check if one of the packages for the board is already installed. if so, no hint
if (packagesForBoard.some(({ installedVersion }) => !!installedVersion)) {
private async ensureCoreExists(
selectedBoard: BoardIdentifier | undefined
): Promise<void> {
if (!selectedBoard) {
return;
}
const candidate = await this.findPlatformToInstall(selectedBoard);
if (!candidate) {
return;
}
const platformIdToInstall = candidate.id;
const selectedBoardName = selectedBoard.name;
if (
this.installNotificationInfos.some(
({ boardName, platformId }) =>
platformIdToInstall === platformId && selectedBoardName === boardName
)
) {
// Already has a notification for the board with the same platform. Nothing to do.
return;
}
this.clearAllNotificationForPlatform(platformIdToInstall);
// filter the installable (not installed) packages,
// CLI returns the packages already sorted with the deprecated ones at the end of the list
// in order to ensure the new ones are preferred
const candidates = packagesForBoard.filter(
({ installedVersion }) => !installedVersion
);
return candidates[0];
}
private showAutoInstallPrompt(
candidate: BoardsPackage,
selectedBoard: Board,
selectedPort?: Port
): void {
const candidateName = candidate.name;
const version = candidate.availableVersions[0]
? `[v ${candidate.availableVersions[0]}]`
: '';
const info = this.generatePromptInfoText(
candidateName,
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
const message = nls.localize(
'arduino/board/installNow',
'The "{0} {1}" core has to be installed for the currently selected "{2}" board. Do you want to install it now?',
candidate.name,
version,
selectedBoard.name
);
const actions = this.createPromptActions(candidate);
const onRefuse = () => {
this.setLastRefusedPromptInfo(candidate.id, selectedPort);
};
const handleAction = this.createOnAnswerHandler(actions, onRefuse);
const onAnswer = (answer: string) => {
this.removeNotificationByBoard(selectedBoard);
handleAction(answer);
};
this.messageService
.info(info, ...actions.map((action) => action.key))
.then(onAnswer);
}
private generatePromptInfoText(
candidateName: string,
version: string,
boardName: string
): string {
return nls.localize(
'arduino/board/installNow',
'The "{0} {1}" core has to be installed for the currently selected "{2}" board. Do you want to install it now?',
candidateName,
version,
boardName
const notificationId = this.notificationId(message, InstallManually, yes);
this.installNotificationInfos.push({
boardName: selectedBoardName,
platformId: platformIdToInstall,
notificationId,
});
const answer = await this.messageService.info(
message,
InstallManually,
yes
);
}
private createPromptActions(
candidate: BoardsPackage
): AutoInstallPromptActions {
const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes');
const actions: AutoInstallPromptActions = [
{
key: InstallManually,
handler: () => {
this.boardsManagerFrontendContribution
.openView({ reveal: true })
.then((widget) =>
widget.refresh({
query: candidate.name.toLocaleLowerCase(),
type: 'All',
})
);
},
},
{
isAcceptance: true,
key: yes,
handler: () => {
return Installable.installWithProgress({
installable: this.boardsService,
item: candidate,
messageService: this.messageService,
responseService: this.responseService,
version: candidate.availableVersions[0],
});
},
},
];
return actions;
}
private createOnAnswerHandler(
actions: AutoInstallPromptActions,
onRefuse?: () => void
): (answer: string) => void {
return (answer) => {
const actionToHandle = actions.find((action) => action.key === answer);
actionToHandle?.handler();
if (!actionToHandle?.isAcceptance && onRefuse) {
onRefuse();
if (answer) {
const index = this.installNotificationInfos.findIndex(
({ boardName, platformId }) =>
platformIdToInstall === platformId && selectedBoardName === boardName
);
if (index !== -1) {
this.installNotificationInfos.splice(index, 1);
}
};
if (answer === yes) {
await Installable.installWithProgress({
installable: this.boardsService,
item: candidate,
messageService: this.messageService,
responseService: this.responseService,
version: candidate.availableVersions[0],
});
return;
}
if (answer === InstallManually) {
this.boardsManagerWidgetContribution
.openView({ reveal: true })
.then((widget) =>
widget.refresh({
query: candidate.name.toLocaleLowerCase(),
type: 'All',
})
);
}
}
}
private clearAllNotificationForPlatform(predicatePlatformId: string): void {
// Discard all install notifications for the same platform.
const notificationsLength = this.installNotificationInfos.length;
for (let i = notificationsLength - 1; i >= 0; i--) {
const { notificationId, platformId } = this.installNotificationInfos[i];
if (platformId === predicatePlatformId) {
this.installNotificationInfos.splice(i, 1);
this.notificationManager.clear(notificationId);
}
}
}
private notificationId(message: string, ...actions: string[]): string {
return this.notificationManager['getMessageId']({
text: message,
actions,
type: MessageType.Info,
});
}
}

View File

@ -0,0 +1,328 @@
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Event } from '@theia/core/lib/common/event';
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
import { nls } from '@theia/core/lib/common/nls';
import React from '@theia/core/shared/react';
import { EditBoardsConfigActionParams } from '../../common/protocol/board-list';
import {
Board,
BoardIdentifier,
BoardWithPackage,
DetectedPort,
findMatchingPortIndex,
Port,
PortIdentifier,
} from '../../common/protocol/boards-service';
import type { Defined } from '../../common/types';
import { NotificationCenter } from '../notification-center';
import { BoardsConfigDialogState } from './boards-config-dialog';
namespace BoardsConfigComponent {
export interface Props {
/**
* This is not the real config, it's only living in the dialog. Users can change it without update and can cancel any modifications.
*/
readonly boardsConfig: BoardsConfigDialogState;
readonly searchSet: BoardIdentifier[] | undefined;
readonly notificationCenter: NotificationCenter;
readonly appState: FrontendApplicationState;
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
readonly onFilteredTextDidChangeEvent: Event<
Defined<EditBoardsConfigActionParams['query']>
>;
readonly onAppStateDidChange: Event<FrontendApplicationState>;
readonly onBoardSelected: (board: BoardIdentifier) => void;
readonly onPortSelected: (port: PortIdentifier) => void;
readonly searchBoards: (query?: {
query?: string;
}) => Promise<BoardWithPackage[]>;
readonly ports: (
predicate?: (port: DetectedPort) => boolean
) => readonly DetectedPort[];
}
export interface State {
searchResults: Array<BoardWithPackage>;
showAllPorts: boolean;
query: string;
}
}
export abstract class Item<T> extends React.Component<{
item: T;
label: string;
selected: boolean;
onClick: (item: T) => void;
missing?: boolean;
details?: string;
}> {
override render(): React.ReactNode {
const { selected, label, missing, details } = this.props;
const classNames = ['item'];
if (selected) {
classNames.push('selected');
}
if (missing === true) {
classNames.push('missing');
}
return (
<div
onClick={this.onClick}
className={classNames.join(' ')}
title={`${label}${!details ? '' : details}`}
>
<div className="label">{label}</div>
{!details ? '' : <div className="details">{details}</div>}
{!selected ? (
''
) : (
<div className="selected-icon">
<i className="fa fa-check" />
</div>
)}
</div>
);
}
private readonly onClick = () => {
this.props.onClick(this.props.item);
};
}
export class BoardsConfigComponent extends React.Component<
BoardsConfigComponent.Props,
BoardsConfigComponent.State
> {
private readonly toDispose: DisposableCollection;
constructor(props: BoardsConfigComponent.Props) {
super(props);
this.state = {
searchResults: [],
showAllPorts: false,
query: '',
};
this.toDispose = new DisposableCollection();
}
override componentDidMount(): void {
this.toDispose.pushAll([
this.props.onAppStateDidChange(async (state) => {
if (state === 'ready') {
const searchResults = await this.queryBoards({});
this.setState({ searchResults });
}
}),
this.props.notificationCenter.onPlatformDidInstall(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onPlatformDidUninstall(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onIndexUpdateDidComplete(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onDaemonDidStart(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onDaemonDidStop(() =>
this.setState({ searchResults: [] })
),
this.props.onFilteredTextDidChangeEvent((query) => {
if (typeof query === 'string') {
this.setState({ query }, () => this.updateBoards(this.state.query));
}
}),
]);
}
override componentWillUnmount(): void {
this.toDispose.dispose();
}
private readonly updateBoards = (
eventOrQuery: React.ChangeEvent<HTMLInputElement> | string = ''
) => {
const query =
typeof eventOrQuery === 'string'
? eventOrQuery
: eventOrQuery.target.value.toLowerCase();
this.setState({ query });
this.queryBoards({ query }).then((searchResults) =>
this.setState({ searchResults })
);
};
private readonly queryBoards = async (
options: { query?: string } = {}
): Promise<Array<BoardWithPackage>> => {
const result = await this.props.searchBoards(options);
const { searchSet } = this.props;
if (searchSet) {
return result.filter((board) =>
searchSet.some(
(restriction) =>
restriction.fqbn === board.fqbn || restriction.name === board.fqbn
)
);
}
return result;
};
private readonly toggleFilterPorts = () => {
this.setState({ showAllPorts: !this.state.showAllPorts });
};
private readonly selectPort = (selectedPort: PortIdentifier) => {
this.props.onPortSelected(selectedPort);
};
private readonly selectBoard = (selectedBoard: BoardWithPackage) => {
this.props.onBoardSelected(selectedBoard);
};
private readonly focusNodeSet = (element: HTMLElement | null) => {
this.props.onFocusNodeSet(element || undefined);
};
override render(): React.ReactNode {
return (
<>
{this.renderContainer(
nls.localize('arduino/board/boards', 'boards'),
this.renderBoards.bind(this)
)}
{this.renderContainer(
nls.localize('arduino/board/ports', 'ports'),
this.renderPorts.bind(this),
this.renderPortsFooter.bind(this)
)}
</>
);
}
private renderContainer(
title: string,
contentRenderer: () => React.ReactNode,
footerRenderer?: () => React.ReactNode
): React.ReactNode {
return (
<div className="container">
<div className="content">
<div className="title">{title}</div>
{contentRenderer()}
<div className="footer">{footerRenderer ? footerRenderer() : ''}</div>
</div>
</div>
);
}
private renderBoards(): React.ReactNode {
const { boardsConfig } = this.props;
const { 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>();
const toKey = ({ name, packageName, fqbn }: Board.Detailed) =>
!!fqbn ? `${name}-${packageName}-${fqbn}` : `${name}-${packageName}`;
for (const board of Board.decorateBoards(
boardsConfig.selectedBoard,
searchResults
)) {
const key = toKey(board);
if (!distinctBoards.has(key)) {
distinctBoards.set(key, board);
}
}
const boardsList = Array.from(distinctBoards.values()).map((board) => (
<Item<BoardWithPackage>
key={toKey(board)}
item={board}
label={board.name}
details={board.details}
selected={board.selected}
onClick={this.selectBoard}
missing={board.missing}
/>
));
return (
<React.Fragment>
<div className="search">
<input
type="search"
value={query}
className="theia-input"
placeholder={nls.localize(
'arduino/board/searchBoard',
'Search board'
)}
onChange={this.updateBoards}
ref={this.focusNodeSet}
/>
<i className="fa fa-search"></i>
</div>
{boardsList.length > 0 ? (
<div className="boards list">{boardsList}</div>
) : (
<div className="no-result">
{nls.localize(
'arduino/board/noBoardsFound',
'No boards found for "{0}"',
query
)}
</div>
)}
</React.Fragment>
);
}
private renderPorts(): React.ReactNode {
const predicate = this.state.showAllPorts ? undefined : Port.isVisiblePort;
const detectedPorts = this.props.ports(predicate);
const matchingIndex = findMatchingPortIndex(
this.props.boardsConfig.selectedPort,
detectedPorts
);
return !detectedPorts.length ? (
<div className="no-result">
{nls.localize('arduino/board/noPortsDiscovered', 'No ports discovered')}
</div>
) : (
<div className="ports list">
{detectedPorts.map((detectedPort, index) => (
<Item<Port>
key={`${Port.keyOf(detectedPort.port)}`}
item={detectedPort.port}
label={Port.toString(detectedPort.port)}
selected={index === matchingIndex}
onClick={this.selectPort}
/>
))}
</div>
);
}
private renderPortsFooter(): React.ReactNode {
return (
<div className="noselect">
<label
title={nls.localize(
'arduino/board/showAllAvailablePorts',
'Shows all available ports when enabled'
)}
>
<input
type="checkbox"
defaultChecked={this.state.showAllPorts}
onChange={this.toggleFilterPorts}
/>
<span>
{nls.localize('arduino/board/showAllPorts', 'Show all ports')}
</span>
</label>
</div>
);
}
}

View File

@ -1,71 +0,0 @@
import React from '@theia/core/shared/react';
import { injectable, inject } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
import { ReactWidget, Message } from '@theia/core/lib/browser';
import { BoardsService } from '../../common/protocol/boards-service';
import { BoardsConfig } from './boards-config';
import { BoardsServiceProvider } from './boards-service-provider';
import { NotificationCenter } from '../notification-center';
@injectable()
export class BoardsConfigDialogWidget extends ReactWidget {
@inject(BoardsService)
protected readonly boardsService: BoardsService;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
@inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter;
protected readonly onFilterTextDidChangeEmitter = new Emitter<string>();
protected readonly onBoardConfigChangedEmitter =
new Emitter<BoardsConfig.Config>();
readonly onBoardConfigChanged = this.onBoardConfigChangedEmitter.event;
protected focusNode: HTMLElement | undefined;
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) => {
this.onBoardConfigChangedEmitter.fire(config);
};
protected setFocusNode = (element: HTMLElement | undefined) => {
this.focusNode = element;
};
protected render(): React.ReactNode {
return (
<div className="selectBoardContainer">
<BoardsConfig
boardsServiceProvider={this.boardsServiceClient}
notificationCenter={this.notificationCenter}
onConfigChange={this.fireConfigChanged}
onFocusNodeSet={this.setFocusNode}
onFilteredTextDidChangeEvent={this.onFilterTextDidChangeEmitter.event}
onAppStateDidChange={this.notificationCenter.onAppStateDidChange}
/>
</div>
);
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
if (this.focusNode instanceof HTMLInputElement) {
this.focusNode.select();
}
(this.focusNode || this.node).focus();
}
}

View File

@ -1,142 +0,0 @@
import {
injectable,
inject,
postConstruct,
} from '@theia/core/shared/inversify';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { DialogProps, Widget, DialogError } from '@theia/core/lib/browser';
import { AbstractDialog } from '../theia/dialogs/dialogs';
import { BoardsConfig } from './boards-config';
import { BoardsService } from '../../common/protocol/boards-service';
import { BoardsServiceProvider } from './boards-service-provider';
import { BoardsConfigDialogWidget } from './boards-config-dialog-widget';
import { nls } from '@theia/core/lib/common';
@injectable()
export class BoardsConfigDialogProps extends DialogProps {}
@injectable()
export class BoardsConfigDialog extends AbstractDialog<BoardsConfig.Config> {
@inject(BoardsConfigDialogWidget)
protected readonly widget: BoardsConfigDialogWidget;
@inject(BoardsService)
protected readonly boardService: BoardsService;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
protected config: BoardsConfig.Config = {};
constructor(
@inject(BoardsConfigDialogProps)
protected override readonly props: BoardsConfigDialogProps
) {
super({ ...props, maxWidth: 500 });
this.node.id = 'select-board-dialog-container';
this.contentNode.classList.add('select-board-dialog');
this.contentNode.appendChild(this.createDescription());
this.appendCloseButton(
nls.localize('vscode/issueMainService/cancel', 'Cancel')
);
this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK'));
}
@postConstruct()
protected init(): void {
this.toDispose.push(
this.boardsServiceClient.onBoardsConfigChanged((config) => {
this.config = config;
this.update();
})
);
}
/**
* Pass in an empty string if you want to reset the search term. Using `undefined` has no effect.
*/
override 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');
const text = document.createElement('div');
text.classList.add('text');
head.appendChild(text);
for (const paragraph of [
nls.localize(
'arduino/board/configDialog1',
'Select both a Board and a Port if you want to upload a sketch.'
),
nls.localize(
'arduino/board/configDialog2',
'If you only select a Board you will be able to compile, but not to upload your sketch.'
),
]) {
const p = document.createElement('div');
p.textContent = paragraph;
text.appendChild(p);
}
return head;
}
protected override onAfterAttach(msg: Message): void {
if (this.widget.isAttached) {
Widget.detach(this.widget);
}
Widget.attach(this.widget, this.contentNode);
this.toDisposeOnDetach.push(
this.widget.onBoardConfigChanged((config) => {
this.config = config;
this.update();
})
);
super.onAfterAttach(msg);
this.update();
}
protected override onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.widget.update();
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this.widget.activate();
}
protected override handleEnter(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLTextAreaElement) {
return false;
}
}
protected override isValid(value: BoardsConfig.Config): DialogError {
if (!value.selectedBoard) {
if (value.selectedPort) {
return nls.localize(
'arduino/board/pleasePickBoard',
'Please pick a board connected to the port you have selected.'
);
}
return false;
}
return '';
}
get value(): BoardsConfig.Config {
return this.config;
}
}

View File

@ -0,0 +1,202 @@
import { DialogError, DialogProps } from '@theia/core/lib/browser/dialogs';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { Emitter } from '@theia/core/lib/common/event';
import { nls } from '@theia/core/lib/common/nls';
import { deepClone } from '@theia/core/lib/common/objects';
import type { Message } from '@theia/core/shared/@phosphor/messaging';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import React from '@theia/core/shared/react';
import type { ReactNode } from '@theia/core/shared/react/index';
import { EditBoardsConfigActionParams } from '../../common/protocol/board-list';
import {
BoardIdentifier,
BoardsConfig,
BoardWithPackage,
DetectedPort,
emptyBoardsConfig,
PortIdentifier,
} from '../../common/protocol/boards-service';
import type { Defined } from '../../common/types';
import { NotificationCenter } from '../notification-center';
import { ReactDialog } from '../theia/dialogs/dialogs';
import { BoardsConfigComponent } from './boards-config-component';
import { BoardsServiceProvider } from './boards-service-provider';
@injectable()
export class BoardsConfigDialogProps extends DialogProps {}
export type BoardsConfigDialogState = Omit<BoardsConfig, 'selectedBoard'> & {
selectedBoard: BoardsConfig['selectedBoard'] | BoardWithPackage;
};
@injectable()
export class BoardsConfigDialog extends ReactDialog<BoardsConfigDialogState> {
@inject(BoardsServiceProvider)
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(NotificationCenter)
private readonly notificationCenter: NotificationCenter;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
private readonly onFilterTextDidChangeEmitter: Emitter<
Defined<EditBoardsConfigActionParams['query']>
>;
private readonly onBoardSelected = (board: BoardWithPackage): void => {
this._boardsConfig.selectedBoard = board;
this.update();
};
private readonly onPortSelected = (port: PortIdentifier): void => {
this._boardsConfig.selectedPort = port;
this.update();
};
private readonly setFocusNode = (element: HTMLElement | undefined): void => {
this.focusNode = element;
};
private readonly searchBoards = (options: {
query?: string;
}): Promise<BoardWithPackage[]> => {
return this.boardsServiceProvider.searchBoards(options);
};
private readonly ports = (
predicate?: (port: DetectedPort) => boolean
): readonly DetectedPort[] => {
return this.boardsServiceProvider.boardList.ports(predicate);
};
private _boardsConfig: BoardsConfigDialogState;
/**
* When the dialog's boards result set is limited to a subset of boards when searching, this field is set.
*/
private _searchSet: BoardIdentifier[] | undefined;
private focusNode: HTMLElement | undefined;
constructor(
@inject(BoardsConfigDialogProps)
protected override readonly props: BoardsConfigDialogProps
) {
super({ ...props, maxWidth: 500 });
this.node.id = 'select-board-dialog-container';
this.contentNode.classList.add('select-board-dialog');
this.appendCloseButton(
nls.localize('vscode/issueMainService/cancel', 'Cancel')
);
this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK'));
this._boardsConfig = emptyBoardsConfig();
this.onFilterTextDidChangeEmitter = new Emitter();
}
@postConstruct()
protected init(): void {
this.boardsServiceProvider.onBoardListDidChange(() => {
this._boardsConfig = deepClone(this.boardsServiceProvider.boardsConfig);
this.update();
});
this._boardsConfig = deepClone(this.boardsServiceProvider.boardsConfig);
}
override async open(
params?: EditBoardsConfigActionParams
): Promise<BoardsConfig | undefined> {
this._searchSet = undefined;
this._boardsConfig.selectedBoard =
this.boardsServiceProvider.boardsConfig.selectedBoard;
this._boardsConfig.selectedPort =
this.boardsServiceProvider.boardsConfig.selectedPort;
if (params) {
if (typeof params.query === 'string') {
this.onFilterTextDidChangeEmitter.fire(params.query);
}
if (params.portToSelect) {
this._boardsConfig.selectedPort = params.portToSelect;
}
if (params.boardToSelect) {
this._boardsConfig.selectedBoard = params.boardToSelect;
}
if (params.searchSet) {
this._searchSet = params.searchSet.slice();
}
}
return super.open();
}
protected override onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
this.update();
}
protected override render(): ReactNode {
return (
<>
<div className="head">
<div className="text">
<div>
{nls.localize(
'arduino/board/configDialog1',
'Select both a Board and a Port if you want to upload a sketch.'
)}
</div>
<div>
{nls.localize(
'arduino/board/configDialog2',
'If you only select a Board you will be able to compile, but not to upload your sketch.'
)}
</div>
</div>
</div>
<div id="select-board-dialog" className="p-Widget ps">
<div className="selectBoardContainer">
<BoardsConfigComponent
boardsConfig={this._boardsConfig}
searchSet={this._searchSet}
onBoardSelected={this.onBoardSelected}
onPortSelected={this.onPortSelected}
notificationCenter={this.notificationCenter}
onFocusNodeSet={this.setFocusNode}
onFilteredTextDidChangeEvent={
this.onFilterTextDidChangeEmitter.event
}
appState={this.appStateService.state}
onAppStateDidChange={this.notificationCenter.onAppStateDidChange}
searchBoards={this.searchBoards}
ports={this.ports}
/>
</div>
</div>
</>
);
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
if (this.focusNode instanceof HTMLInputElement) {
this.focusNode.select();
}
(this.focusNode || this.node).focus();
}
protected override handleEnter(event: KeyboardEvent): boolean | void {
if (event.target instanceof HTMLTextAreaElement) {
return false;
}
}
protected override isValid(value: BoardsConfig): DialogError {
if (!value.selectedBoard) {
if (value.selectedPort) {
return nls.localize(
'arduino/board/pleasePickBoard',
'Please pick a board connected to the port you have selected.'
);
}
return false;
}
return '';
}
get value(): BoardsConfigDialogState {
return this._boardsConfig;
}
}

View File

@ -1,432 +0,0 @@
import React from '@theia/core/shared/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,
BoardConfig as ProtocolBoardConfig,
BoardWithPackage,
} from '../../common/protocol/boards-service';
import { NotificationCenter } from '../notification-center';
import {
AvailableBoard,
BoardsServiceProvider,
} from './boards-service-provider';
import { naturalCompare } from '../../common/utils';
import { nls } from '@theia/core/lib/common';
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
export namespace BoardsConfig {
export type Config = ProtocolBoardConfig;
export interface Props {
readonly boardsServiceProvider: BoardsServiceProvider;
readonly notificationCenter: NotificationCenter;
readonly onConfigChange: (config: Config) => void;
readonly onFocusNodeSet: (element: HTMLElement | undefined) => void;
readonly onFilteredTextDidChangeEvent: Event<string>;
readonly onAppStateDidChange: Event<FrontendApplicationState>;
}
export interface State extends Config {
searchResults: Array<BoardWithPackage>;
knownPorts: Port[];
showAllPorts: boolean;
query: string;
}
}
export abstract class Item<T> extends React.Component<{
item: T;
label: string;
selected: boolean;
onClick: (item: T) => void;
missing?: boolean;
details?: string;
}> {
override render(): React.ReactNode {
const { selected, label, missing, details } = this.props;
const classNames = ['item'];
if (selected) {
classNames.push('selected');
}
if (missing === true) {
classNames.push('missing');
}
return (
<div
onClick={this.onClick}
className={classNames.join(' ')}
title={`${label}${!details ? '' : details}`}
>
<div className="label">{label}</div>
{!details ? '' : <div className="details">{details}</div>}
{!selected ? (
''
) : (
<div className="selected-icon">
<i className="fa fa-check" />
</div>
)}
</div>
);
}
protected onClick = () => {
this.props.onClick(this.props.item);
};
}
export class BoardsConfig extends React.Component<
BoardsConfig.Props,
BoardsConfig.State
> {
protected toDispose = new DisposableCollection();
constructor(props: BoardsConfig.Props) {
super(props);
const { boardsConfig } = props.boardsServiceProvider;
this.state = {
searchResults: [],
knownPorts: [],
showAllPorts: false,
query: '',
...boardsConfig,
};
}
override componentDidMount(): void {
this.toDispose.pushAll([
this.props.onAppStateDidChange((state) => {
if (state === 'ready') {
this.updateBoards();
this.updatePorts(
this.props.boardsServiceProvider.availableBoards
.map(({ port }) => port)
.filter(notEmpty)
);
}
}),
this.props.boardsServiceProvider.onAvailablePortsChanged(
({ newState, oldState }) => {
const removedPorts = oldState.filter(
(oldPort) =>
!newState.find((newPort) => Port.sameAs(newPort, oldPort))
);
this.updatePorts(newState, removedPorts);
}
),
this.props.boardsServiceProvider.onBoardsConfigChanged(
({ selectedBoard, selectedPort }) => {
this.setState({ selectedBoard, selectedPort }, () =>
this.fireConfigChanged()
);
}
),
this.props.notificationCenter.onPlatformDidInstall(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onPlatformDidUninstall(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onIndexUpdateDidComplete(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onDaemonDidStart(() =>
this.updateBoards(this.state.query)
),
this.props.notificationCenter.onDaemonDidStop(() =>
this.setState({ searchResults: [] })
),
this.props.onFilteredTextDidChangeEvent((query) =>
this.setState({ query }, () => this.updateBoards(this.state.query))
),
]);
}
override componentWillUnmount(): void {
this.toDispose.dispose();
}
protected fireConfigChanged(): void {
const { selectedBoard, selectedPort } = this.state;
this.props.onConfigChange({ selectedBoard, selectedPort });
}
protected updateBoards = (
eventOrQuery: React.ChangeEvent<HTMLInputElement> | string = ''
) => {
const query =
typeof eventOrQuery === 'string'
? eventOrQuery
: eventOrQuery.target.value.toLowerCase();
this.setState({ query });
this.queryBoards({ query }).then((searchResults) =>
this.setState({ searchResults })
);
};
protected updatePorts = (ports: Port[] = [], removedPorts: Port[] = []) => {
this.queryPorts(Promise.resolve(ports)).then(({ knownPorts }) => {
let { selectedPort } = this.state;
// If the currently selected port is not available anymore, unset the selected port.
if (removedPorts.some((port) => Port.sameAs(port, selectedPort))) {
selectedPort = undefined;
}
this.setState({ knownPorts, selectedPort }, () =>
this.fireConfigChanged()
);
});
};
protected queryBoards = (
options: { query?: string } = {}
): Promise<Array<BoardWithPackage>> => {
return this.props.boardsServiceProvider.searchBoards(options);
};
protected get availablePorts(): MaybePromise<Port[]> {
return this.props.boardsServiceProvider.availableBoards
.map(({ port }) => port)
.filter(notEmpty);
}
protected get availableBoards(): AvailableBoard[] {
return this.props.boardsServiceProvider.availableBoards;
}
protected queryPorts = async (
availablePorts: MaybePromise<Port[]> = this.availablePorts
) => {
// Available ports must be sorted in this order:
// 1. Serial with recognized boards
// 2. Serial with guessed boards
// 3. Serial with incomplete boards
// 4. Network with recognized boards
// 5. Other protocols with recognized boards
const ports = (await availablePorts).sort((left: Port, right: Port) => {
if (left.protocol === 'serial' && right.protocol !== 'serial') {
return -1;
} else if (left.protocol !== 'serial' && right.protocol === 'serial') {
return 1;
} else if (left.protocol === 'network' && right.protocol !== 'network') {
return -1;
} else if (left.protocol !== 'network' && right.protocol === 'network') {
return 1;
} else if (left.protocol === right.protocol) {
// We show ports, including those that have guessed
// or unrecognized boards, so we must sort those too.
const leftBoard = this.availableBoards.find(
(board) => board.port === left
);
const rightBoard = this.availableBoards.find(
(board) => board.port === right
);
if (leftBoard && !rightBoard) {
return -1;
} else if (!leftBoard && rightBoard) {
return 1;
} else if (leftBoard?.state! < rightBoard?.state!) {
return -1;
} else if (leftBoard?.state! > rightBoard?.state!) {
return 1;
}
}
return naturalCompare(left.address, right.address);
});
return { knownPorts: ports };
};
protected toggleFilterPorts = () => {
this.setState({ showAllPorts: !this.state.showAllPorts });
};
protected selectPort = (selectedPort: Port | undefined) => {
this.setState({ selectedPort }, () => this.fireConfigChanged());
};
protected selectBoard = (selectedBoard: BoardWithPackage | undefined) => {
this.setState({ selectedBoard }, () => this.fireConfigChanged());
};
protected focusNodeSet = (element: HTMLElement | null) => {
this.props.onFocusNodeSet(element || undefined);
};
override render(): React.ReactNode {
return (
<>
{this.renderContainer(
nls.localize('arduino/board/boards', 'boards'),
this.renderBoards.bind(this)
)}
{this.renderContainer(
nls.localize('arduino/board/ports', 'ports'),
this.renderPorts.bind(this),
this.renderPortsFooter.bind(this)
)}
</>
);
}
protected renderContainer(
title: string,
contentRenderer: () => React.ReactNode,
footerRenderer?: () => React.ReactNode
): React.ReactNode {
return (
<div className="container">
<div className="content">
<div className="title">{title}</div>
{contentRenderer()}
<div className="footer">{footerRenderer ? footerRenderer() : ''}</div>
</div>
</div>
);
}
protected renderBoards(): React.ReactNode {
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>();
const toKey = ({ name, packageName, fqbn }: Board.Detailed) =>
!!fqbn ? `${name}-${packageName}-${fqbn}` : `${name}-${packageName}`;
for (const board of Board.decorateBoards(selectedBoard, searchResults)) {
const key = toKey(board);
if (!distinctBoards.has(key)) {
distinctBoards.set(key, board);
}
}
const boardsList = Array.from(distinctBoards.values()).map((board) => (
<Item<BoardWithPackage>
key={toKey(board)}
item={board}
label={board.name}
details={board.details}
selected={board.selected}
onClick={this.selectBoard}
missing={board.missing}
/>
));
return (
<React.Fragment>
<div className="search">
<input
type="search"
value={query}
className="theia-input"
placeholder={nls.localize(
'arduino/board/searchBoard',
'Search board'
)}
onChange={this.updateBoards}
ref={this.focusNodeSet}
/>
<i className="fa fa-search"></i>
</div>
{boardsList.length > 0 ? (
<div className="boards list">{boardsList}</div>
) : (
<div className="no-result">
{nls.localize(
'arduino/board/noBoardsFound',
'No boards found for "{0}"',
query
)}
</div>
)}
</React.Fragment>
);
}
protected renderPorts(): React.ReactNode {
let ports = [] as Port[];
if (this.state.showAllPorts) {
ports = this.state.knownPorts;
} else {
ports = this.state.knownPorts.filter(
Port.visiblePorts(this.availableBoards)
);
}
return !ports.length ? (
<div className="no-result">
{nls.localize('arduino/board/noPortsDiscovered', 'No ports discovered')}
</div>
) : (
<div className="ports list">
{ports.map((port) => (
<Item<Port>
key={`${Port.keyOf(port)}`}
item={port}
label={Port.toString(port)}
selected={Port.sameAs(this.state.selectedPort, port)}
onClick={this.selectPort}
/>
))}
</div>
);
}
protected renderPortsFooter(): React.ReactNode {
return (
<div className="noselect">
<label
title={nls.localize(
'arduino/board/showAllAvailablePorts',
'Shows all available ports when enabled'
)}
>
<input
type="checkbox"
defaultChecked={this.state.showAllPorts}
onChange={this.toggleFilterPorts}
/>
<span>
{nls.localize('arduino/board/showAllPorts', 'Show all ports')}
</span>
</label>
</div>
);
}
}
export namespace BoardsConfig {
export namespace Config {
export function sameAs(config: Config, other: Config | Board): boolean {
const { selectedBoard, selectedPort } = config;
if (Board.is(other)) {
return (
!!selectedBoard &&
Board.equals(other, selectedBoard) &&
Port.sameAs(selectedPort, other.port)
);
}
return sameAs(config, other);
}
export function equals(left: Config, right: Config): boolean {
return (
left.selectedBoard === right.selectedBoard &&
left.selectedPort === right.selectedPort
);
}
export function toString(
config: Config,
options: { default: string } = { default: '' }
): string {
const { selectedBoard, selectedPort: port } = config;
if (!selectedBoard) {
return options.default;
}
const { name } = selectedBoard;
return `${name}${port ? ` at ${port.address}` : ''}`;
}
}
}

View File

@ -1,63 +1,64 @@
import { injectable, inject, named } from '@theia/core/shared/inversify';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { ILogger } from '@theia/core/lib/common/logger';
import { deepClone } from '@theia/core/lib/common/objects';
import { Event, Emitter } from '@theia/core/lib/common/event';
import {
FrontendApplicationContribution,
LocalStorageService,
} from '@theia/core/lib/browser';
import { notEmpty } from '../../common/utils';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import {
BoardDetails,
BoardsService,
ConfigOption,
BoardDetails,
Programmer,
} from '../../common/protocol';
import { notEmpty } from '../../common/utils';
import { NotificationCenter } from '../notification-center';
@injectable()
export class BoardsDataStore implements FrontendApplicationContribution {
@inject(ILogger)
@named('store')
protected readonly logger: ILogger;
private readonly logger: ILogger;
@inject(BoardsService)
protected readonly boardsService: BoardsService;
private readonly boardsService: BoardsService;
@inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter;
private readonly notificationCenter: NotificationCenter;
@inject(LocalStorageService)
protected readonly storageService: LocalStorageService;
private readonly storageService: LocalStorageService;
protected readonly onChangedEmitter = new Emitter<string[]>();
private readonly onChangedEmitter = new Emitter<string[]>();
private readonly toDispose = new DisposableCollection(this.onChangedEmitter);
onStart(): void {
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
const dataDidChangePerFqbn: string[] = [];
for (const fqbn of item.boards
.map(({ fqbn }) => fqbn)
.filter(notEmpty)
.filter((fqbn) => !!fqbn)) {
const key = this.getStorageKey(fqbn);
let data = await this.storageService.getData<
ConfigOption[] | undefined
>(key);
if (!data || !data.length) {
const details = await this.getBoardDetailsSafe(fqbn);
if (details) {
data = details.configOptions;
if (data.length) {
await this.storageService.setData(key, data);
dataDidChangePerFqbn.push(fqbn);
this.toDispose.push(
this.notificationCenter.onPlatformDidInstall(async ({ item }) => {
const dataDidChangePerFqbn: string[] = [];
for (const fqbn of item.boards
.map(({ fqbn }) => fqbn)
.filter(notEmpty)
.filter((fqbn) => !!fqbn)) {
const key = this.getStorageKey(fqbn);
let data = await this.storageService.getData<ConfigOption[]>(key);
if (!data || !data.length) {
const details = await this.getBoardDetailsSafe(fqbn);
if (details) {
data = details.configOptions;
if (data.length) {
await this.storageService.setData(key, data);
dataDidChangePerFqbn.push(fqbn);
}
}
}
}
}
if (dataDidChangePerFqbn.length) {
this.fireChanged(...dataDidChangePerFqbn);
}
});
if (dataDidChangePerFqbn.length) {
this.fireChanged(...dataDidChangePerFqbn);
}
})
);
}
onStop(): void {
this.toDispose.dispose();
}
get onChanged(): Event<string[]> {
@ -65,7 +66,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
}
async appendConfigToFqbn(
fqbn: string | undefined,
fqbn: string | undefined
): Promise<string | undefined> {
if (!fqbn) {
return undefined;
@ -100,12 +101,13 @@ export class BoardsDataStore implements FrontendApplicationContribution {
return data;
}
async selectProgrammer(
{
fqbn,
selectedProgrammer,
}: { fqbn: string; selectedProgrammer: Programmer },
): Promise<boolean> {
async selectProgrammer({
fqbn,
selectedProgrammer,
}: {
fqbn: string;
selectedProgrammer: Programmer;
}): Promise<boolean> {
const data = deepClone(await this.getData(fqbn));
const { programmers } = data;
if (!programmers.find((p) => Programmer.equals(selectedProgrammer, p))) {
@ -120,13 +122,15 @@ export class BoardsDataStore implements FrontendApplicationContribution {
return true;
}
async selectConfigOption(
{
fqbn,
option,
selectedValue,
}: { fqbn: string; option: string; selectedValue: string }
): Promise<boolean> {
async selectConfigOption({
fqbn,
option,
selectedValue,
}: {
fqbn: string;
option: string;
selectedValue: string;
}): Promise<boolean> {
const data = deepClone(await this.getData(fqbn));
const { configOptions } = data;
const configOption = configOptions.find((c) => c.option === option);

View File

@ -1,16 +1,21 @@
import React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import { TabBarToolbar } from '@theia/core/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar';
import { codicon } from '@theia/core/lib/browser/widgets/widget';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Port } from '../../common/protocol';
import { OpenBoardsConfig } from '../contributions/open-boards-config';
import {
BoardsServiceProvider,
AvailableBoard,
} from './boards-service-provider';
import { nls } from '@theia/core/lib/common';
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { nls } from '@theia/core/lib/common/nls';
import React from '@theia/core/shared/react';
import ReactDOM from '@theia/core/shared/react-dom';
import classNames from 'classnames';
import { BoardsConfig } from './boards-config';
import { boardIdentifierLabel, Port } from '../../common/protocol';
import { BoardListItemUI } from '../../common/protocol/board-list';
import { assertUnreachable } from '../../common/utils';
import type {
BoardListUI,
BoardsServiceProvider,
} from './boards-service-provider';
export interface BoardsDropDownListCoords {
readonly top: number;
@ -22,18 +27,18 @@ export interface BoardsDropDownListCoords {
export namespace BoardsDropDown {
export interface Props {
readonly coords: BoardsDropDownListCoords | 'hidden';
readonly items: Array<AvailableBoard & { onClick: () => void; port: Port }>;
readonly boardList: BoardListUI;
readonly openBoardsConfig: () => void;
readonly hide: () => void;
}
}
export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
protected dropdownElement: HTMLElement;
export class BoardListDropDown extends React.Component<BoardsDropDown.Props> {
private dropdownElement: HTMLElement;
private listRef: React.RefObject<HTMLDivElement>;
constructor(props: BoardsDropDown.Props) {
super(props);
this.listRef = React.createRef();
let list = document.getElementById('boards-dropdown-container');
if (!list) {
@ -51,11 +56,14 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
}
override render(): React.ReactNode {
return ReactDOM.createPortal(this.renderNode(), this.dropdownElement);
return ReactDOM.createPortal(
this.renderBoardListItems(),
this.dropdownElement
);
}
protected renderNode(): React.ReactNode {
const { coords, items } = this.props;
private renderBoardListItems(): React.ReactNode {
const { coords, boardList } = this.props;
if (coords === 'hidden') {
return '';
}
@ -74,14 +82,12 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
tabIndex={0}
>
<div className="arduino-boards-dropdown-list--items-container">
{items
.map(({ name, port, selected, onClick }) => ({
boardLabel: name,
port,
selected,
onClick,
}))
.map(this.renderItem)}
{boardList.items.map((item, index) =>
this.renderBoardListItem({
item,
selected: index === boardList.selectedIndex,
})
)}
</div>
<div
key={footerLabel}
@ -95,31 +101,43 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
);
}
protected renderItem({
boardLabel,
port,
private readonly onDefaultAction = (item: BoardListItemUI): unknown => {
const { boardList, hide } = this.props;
const { type, params } = item.defaultAction;
hide();
switch (type) {
case 'select-boards-config': {
return boardList.select(params);
}
case 'edit-boards-config': {
return boardList.edit(params);
}
default:
return assertUnreachable(type);
}
};
private renderBoardListItem({
item,
selected,
onClick,
}: {
boardLabel: string;
port: Port;
selected?: boolean;
onClick: () => void;
item: BoardListItemUI;
selected: boolean;
}): React.ReactNode {
const protocolIcon = iconNameFromProtocol(port.protocol);
const { boardLabel, portLabel, portProtocol, tooltip } = item.labels;
const port = item.port;
const onKeyUp = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
onClick();
this.onDefaultAction(item);
}
};
return (
<div
key={`board-item--${boardLabel}-${port.address}`}
key={`board-item--${Port.keyOf(port)}`}
className={classNames('arduino-boards-dropdown-item', {
'arduino-boards-dropdown-item--selected': selected,
})}
onClick={onClick}
onClick={() => this.onDefaultAction(item)}
onKeyUp={onKeyUp}
tabIndex={0}
>
@ -127,21 +145,81 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
className={classNames(
'arduino-boards-dropdown-item--protocol',
'fa',
protocolIcon
iconNameFromProtocol(portProtocol)
)}
/>
<div
className="arduino-boards-dropdown-item--label"
title={`${boardLabel}\n${port.address}`}
>
<div className="arduino-boards-dropdown-item--board-label noWrapInfo noselect">
{boardLabel}
<div className="arduino-boards-dropdown-item--label" title={tooltip}>
<div className="arduino-boards-dropdown-item--board-header">
<div className="arduino-boards-dropdown-item--board-label noWrapInfo noselect">
{boardLabel}
</div>
</div>
<div className="arduino-boards-dropdown-item--port-label noWrapInfo noselect">
{port.addressLabel}
{portLabel}
</div>
</div>
{selected ? <div className="fa fa-check" /> : ''}
{this.renderActions(item)}
</div>
);
}
private renderActions(item: BoardListItemUI): React.ReactNode {
const { boardList, hide } = this.props;
const { revert, edit } = item.otherActions;
if (!edit && !revert) {
return undefined;
}
const handleOnClick = (
event: React.MouseEvent<HTMLElement, MouseEvent>,
callback: () => void
) => {
event.preventDefault();
event.stopPropagation();
hide();
callback();
};
return (
<div className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR}>
{edit && (
<div
className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM} enabled`}
>
{
<div
id="edit"
className={codicon('pencil', true)}
title={nls.localize(
'arduino/board/editBoardsConfig',
'Edit Board and Port...'
)}
onClick={(event) =>
handleOnClick(event, () => boardList.edit(edit.params))
}
/>
}
</div>
)}
{revert && (
<div
className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM} enabled`}
>
{
<div
id="revert"
className={codicon('discard', true)}
title={nls.localize(
'arduino/board/revertBoardsConfig',
"Use '{0}' discovered on '{1}'",
boardIdentifierLabel(revert.params.selectedBoard),
item.labels.portLabel
)}
onClick={(event) =>
handleOnClick(event, () => boardList.select(revert.params))
}
/>
}
</div>
)}
</div>
);
}
@ -153,26 +231,27 @@ export class BoardsToolBarItem extends React.Component<
> {
static TOOLBAR_ID: 'boards-toolbar';
protected readonly toDispose: DisposableCollection =
new DisposableCollection();
private readonly toDispose: DisposableCollection;
constructor(props: BoardsToolBarItem.Props) {
super(props);
const { availableBoards } = props.boardsServiceProvider;
const { boardList } = props.boardsServiceProvider;
this.state = {
availableBoards,
boardList,
coords: 'hidden',
};
document.addEventListener('click', () => {
this.setState({ coords: 'hidden' });
});
const listener = () => this.setState({ coords: 'hidden' });
document.addEventListener('click', listener);
this.toDispose = new DisposableCollection(
Disposable.create(() => document.removeEventListener('click', listener))
);
}
override componentDidMount(): void {
this.props.boardsServiceProvider.onAvailableBoardsChanged(
(availableBoards) => this.setState({ availableBoards })
this.toDispose.push(
this.props.boardsServiceProvider.onBoardListDidChange((boardList) =>
this.setState({ boardList })
)
);
}
@ -180,7 +259,7 @@ export class BoardsToolBarItem extends React.Component<
this.toDispose.dispose();
}
protected readonly show = (event: React.MouseEvent<HTMLElement>): void => {
private readonly show = (event: React.MouseEvent<HTMLElement>): void => {
const { currentTarget: element } = event;
if (element instanceof HTMLElement) {
if (this.state.coords === 'hidden') {
@ -201,31 +280,26 @@ export class BoardsToolBarItem extends React.Component<
event.nativeEvent.stopImmediatePropagation();
};
private readonly hide = () => {
this.setState({ coords: 'hidden' });
};
override render(): React.ReactNode {
const { coords, availableBoards } = this.state;
const { selectedBoard, selectedPort } =
this.props.boardsServiceProvider.boardsConfig;
const boardLabel =
selectedBoard?.name ||
nls.localize('arduino/board/selectBoard', 'Select Board');
const selectedPortLabel = portLabel(selectedPort?.address);
const isConnected = Boolean(selectedBoard && selectedPort);
const protocolIcon = isConnected
? iconNameFromProtocol(selectedPort?.protocol || '')
const { coords, boardList } = this.state;
const { boardLabel, selected, portProtocol, tooltip } = boardList.labels;
const protocolIcon = portProtocol
? iconNameFromProtocol(portProtocol)
: null;
const protocolIconClassNames = classNames(
'arduino-boards-toolbar-item--protocol',
'fa',
protocolIcon
);
return (
<React.Fragment>
<div
className="arduino-boards-toolbar-item-container"
title={selectedPortLabel}
title={tooltip}
onClick={this.show}
>
{protocolIcon && <div className={protocolIconClassNames} />}
@ -234,57 +308,22 @@ export class BoardsToolBarItem extends React.Component<
'arduino-boards-toolbar-item--label',
'noWrapInfo',
'noselect',
{ 'arduino-boards-toolbar-item--label-connected': isConnected }
{ 'arduino-boards-toolbar-item--label-connected': selected }
)}
>
{boardLabel}
</div>
<div className="fa fa-caret-down caret" />
</div>
<BoardsDropDown
<BoardListDropDown
coords={coords}
items={availableBoards
.filter(AvailableBoard.hasPort)
.map((board) => ({
...board,
onClick: () => {
if (!board.fqbn) {
const previousBoardConfig =
this.props.boardsServiceProvider.boardsConfig;
this.props.boardsServiceProvider.boardsConfig = {
selectedPort: board.port,
};
this.openDialog(previousBoardConfig);
} else {
this.props.boardsServiceProvider.boardsConfig = {
selectedBoard: board,
selectedPort: board.port,
};
}
this.setState({ coords: 'hidden' });
},
}))}
openBoardsConfig={this.openDialog}
></BoardsDropDown>
boardList={boardList}
openBoardsConfig={() => boardList.edit({ query: '' })}
hide={this.hide}
/>
</React.Fragment>
);
}
protected openDialog = async (
previousBoardConfig?: BoardsConfig.Config
): Promise<void> => {
const selectedBoardConfig =
await this.props.commands.executeCommand<BoardsConfig.Config>(
OpenBoardsConfig.Commands.OPEN_DIALOG.id
);
if (
previousBoardConfig &&
(!selectedBoardConfig?.selectedPort ||
!selectedBoardConfig?.selectedBoard)
) {
this.props.boardsServiceProvider.boardsConfig = previousBoardConfig;
}
};
}
export namespace BoardsToolBarItem {
export interface Props {
@ -293,7 +332,7 @@ export namespace BoardsToolBarItem {
}
export interface State {
availableBoards: AvailableBoard[];
boardList: BoardListUI;
coords: BoardsDropDownListCoords | 'hidden';
}
}
@ -304,19 +343,10 @@ function iconNameFromProtocol(protocol: string): string {
return 'fa-arduino-technology-usb';
case 'network':
return 'fa-arduino-technology-connection';
/*
Bluetooth ports are not listed yet from the CLI;
Not sure about the naming ('bluetooth'); make sure it's correct before uncommenting the following lines
*/
// case 'bluetooth':
// return 'fa-arduino-technology-bluetooth';
// it is fine to assign dedicated icons to the protocols used by the official boards,
// but other than that it is best to avoid implementing any special handling
// for specific protocols in the IDE codebase.
default:
return 'fa-arduino-technology-3dimensionscube';
}
}
function portLabel(portName?: string): string {
return portName
? nls.localize('arduino/board/portLabel', 'Port: {0}', portName)
: nls.localize('arduino/board/disconnected', 'Disconnected');
}

View File

@ -1,57 +1,58 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import {
DisposableCollection,
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { BoardsConfig } from '../boards/boards-config';
import { MenuModelRegistry } from '@theia/core/lib/common/menu/menu-model-registry';
import type { MenuPath } from '@theia/core/lib/common/menu/menu-types';
import { nls } from '@theia/core/lib/common/nls';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { inject, injectable } from '@theia/core/shared/inversify';
import { MainMenuManager } from '../../common/main-menu-manager';
import {
BoardsService,
BoardWithPackage,
createPlatformIdentifier,
getBoardInfo,
InstalledBoardWithPackage,
platformIdentifierEquals,
Port,
serializePlatformIdentifier,
} from '../../common/protocol';
import type { BoardList } from '../../common/protocol/board-list';
import { BoardsListWidget } from '../boards/boards-list-widget';
import { NotificationCenter } from '../notification-center';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import {
ArduinoMenus,
PlaceholderMenuNode,
unregisterSubmenu,
} from '../menu/arduino-menus';
import {
BoardsService,
InstalledBoardWithPackage,
AvailablePorts,
Port,
getBoardInfo,
} from '../../common/protocol';
import { SketchContribution, Command, CommandRegistry } from './contribution';
import { nls } from '@theia/core/lib/common';
import { NotificationCenter } from '../notification-center';
import { Command, CommandRegistry, SketchContribution } from './contribution';
@injectable()
export class BoardSelection extends SketchContribution {
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
private readonly commandRegistry: CommandRegistry;
@inject(MainMenuManager)
protected readonly mainMenuManager: MainMenuManager;
private readonly mainMenuManager: MainMenuManager;
@inject(MenuModelRegistry)
protected readonly menuModelRegistry: MenuModelRegistry;
private readonly menuModelRegistry: MenuModelRegistry;
@inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter;
private readonly notificationCenter: NotificationCenter;
@inject(BoardsService)
protected readonly boardsService: BoardsService;
private readonly boardsService: BoardsService;
@inject(BoardsServiceProvider)
protected readonly boardsServiceProvider: BoardsServiceProvider;
private readonly boardsServiceProvider: BoardsServiceProvider;
protected readonly toDisposeBeforeMenuRebuild = new DisposableCollection();
private readonly toDisposeBeforeMenuRebuild = new DisposableCollection();
// do not query installed platforms on every change
private _installedBoards: Deferred<InstalledBoardWithPackage[]> | undefined;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
execute: async () => {
const boardInfo = await getBoardInfo(
this.boardsServiceProvider.boardsConfig.selectedPort,
this.boardsService.getState()
this.boardsServiceProvider.boardList
);
if (typeof boardInfo === 'string') {
this.messageService.info(boardInfo);
@ -76,34 +77,35 @@ SN: ${SN}
}
override onStart(): void {
this.notificationCenter.onPlatformDidInstall(() => this.updateMenus());
this.notificationCenter.onPlatformDidUninstall(() => this.updateMenus());
this.boardsServiceProvider.onBoardsConfigChanged(() => this.updateMenus());
this.boardsServiceProvider.onAvailableBoardsChanged(() =>
this.updateMenus()
);
this.boardsServiceProvider.onAvailablePortsChanged(() =>
this.updateMenus()
this.notificationCenter.onPlatformDidInstall(() => this.updateMenus(true));
this.notificationCenter.onPlatformDidUninstall(() =>
this.updateMenus(true)
);
this.boardsServiceProvider.onBoardListDidChange(() => this.updateMenus());
}
override async onReady(): Promise<void> {
this.updateMenus();
}
protected async updateMenus(): Promise<void> {
const [installedBoards, availablePorts, config] = await Promise.all([
this.installedBoards(),
this.boardsService.getState(),
this.boardsServiceProvider.boardsConfig,
]);
this.rebuildMenus(installedBoards, availablePorts, config);
private async updateMenus(discardCache = false): Promise<void> {
if (discardCache) {
this._installedBoards?.reject();
this._installedBoards = undefined;
}
if (!this._installedBoards) {
this._installedBoards = new Deferred();
this.installedBoards().then((installedBoards) =>
this._installedBoards?.resolve(installedBoards)
);
}
const installedBoards = await this._installedBoards.promise;
this.rebuildMenus(installedBoards, this.boardsServiceProvider.boardList);
}
protected rebuildMenus(
private rebuildMenus(
installedBoards: InstalledBoardWithPackage[],
availablePorts: AvailablePorts,
config: BoardsConfig.Config
boardList: BoardList
): void {
this.toDisposeBeforeMenuRebuild.dispose();
@ -112,7 +114,8 @@ SN: ${SN}
...ArduinoMenus.TOOLS__BOARD_SELECTION_GROUP,
'1_boards',
];
const boardsSubmenuLabel = config.selectedBoard?.name;
const { selectedBoard, selectedPort } = boardList.boardsConfig;
const boardsSubmenuLabel = selectedBoard?.name;
// Note: The submenu order starts from `100` because `Auto Format`, `Serial Monitor`, etc starts from `0` index.
// The board specific items, and the rest, have order with `z`. We needed something between `0` and `z` with natural-order.
this.menuModelRegistry.registerSubmenu(
@ -132,7 +135,7 @@ SN: ${SN}
// Ports submenu
const portsSubmenuPath = ArduinoMenus.TOOLS__PORTS_SUBMENU;
const portsSubmenuLabel = config.selectedPort?.address;
const portsSubmenuLabel = selectedPort?.address;
this.menuModelRegistry.registerSubmenu(
portsSubmenuPath,
nls.localize(
@ -171,69 +174,116 @@ SN: ${SN}
label: `${BoardsListWidget.WIDGET_LABEL}...`,
});
const selectedBoardPlatformId = selectedBoard
? createPlatformIdentifier(selectedBoard)
: undefined;
// Keys are the vendor IDs
type BoardsPerVendor = Record<string, BoardWithPackage[]>;
// Group boards by their platform names. The keys are the platform names as menu labels.
// If there is a platform name (menu label) collision, refine the menu label with the vendor ID.
const groupedBoards = new Map<string, BoardsPerVendor>();
for (const board of installedBoards) {
const { packageId, packageName } = board;
const { vendorId } = packageId;
let boardsPerPackageName = groupedBoards.get(packageName);
if (!boardsPerPackageName) {
boardsPerPackageName = {} as BoardsPerVendor;
groupedBoards.set(packageName, boardsPerPackageName);
}
let boardPerVendor: BoardWithPackage[] | undefined =
boardsPerPackageName[vendorId];
if (!boardPerVendor) {
boardPerVendor = [];
boardsPerPackageName[vendorId] = boardPerVendor;
}
boardPerVendor.push(board);
}
// Installed boards
installedBoards.forEach((board, index) => {
const { packageId, packageName, fqbn, name, manuallyInstalled } = board;
Array.from(groupedBoards.entries()).forEach(
([packageName, boardsPerPackage]) => {
const useVendorSuffix = Object.keys(boardsPerPackage).length > 1;
Object.entries(boardsPerPackage).forEach(([vendorId, boards]) => {
let platformMenuPath: MenuPath | undefined = undefined;
boards.forEach((board, index) => {
const { packageId, fqbn, name, manuallyInstalled } = board;
// create the platform submenu once.
// creating and registering the same submenu twice in Theia is a noop, though.
if (!platformMenuPath) {
let packageLabel =
packageName +
`${
manuallyInstalled
? nls.localize(
'arduino/board/inSketchbook',
' (in Sketchbook)'
)
: ''
}`;
if (
selectedBoardPlatformId &&
platformIdentifierEquals(packageId, selectedBoardPlatformId)
) {
packageLabel = `${packageLabel}`;
}
if (useVendorSuffix) {
packageLabel += ` (${vendorId})`;
}
// Platform submenu
platformMenuPath = [
...boardsPackagesGroup,
serializePlatformIdentifier(packageId),
];
this.menuModelRegistry.registerSubmenu(
platformMenuPath,
packageLabel,
{
order: packageName.toLowerCase(),
}
);
}
const packageLabel =
packageName +
`${
manuallyInstalled
? nls.localize('arduino/board/inSketchbook', ' (in Sketchbook)')
: ''
}`;
// Platform submenu
const platformMenuPath = [...boardsPackagesGroup, packageId];
// Note: Registering the same submenu twice is a noop. No need to group the boards per platform.
this.menuModelRegistry.registerSubmenu(platformMenuPath, packageLabel, {
order: packageName.toLowerCase(),
});
const id = `arduino-select-board--${fqbn}`;
const command = { id };
const handler = {
execute: () => {
if (
fqbn !== this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn
) {
this.boardsServiceProvider.boardsConfig = {
selectedBoard: {
name,
fqbn,
port: this.boardsServiceProvider.boardsConfig.selectedBoard
?.port, // TODO: verify!
},
selectedPort:
this.boardsServiceProvider.boardsConfig.selectedPort,
const id = `arduino-select-board--${fqbn}`;
const command = { id };
const handler = {
execute: () =>
this.boardsServiceProvider.updateConfig({
name: name,
fqbn: fqbn,
}),
isToggled: () => fqbn === selectedBoard?.fqbn,
};
}
},
isToggled: () =>
fqbn === this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn,
};
// Board menu
const menuAction = {
commandId: id,
label: name,
order: String(index).padStart(4), // pads with leading zeros for alphanumeric sort where order is 1, 2, 11, and NOT 1, 11, 2
};
this.commandRegistry.registerCommand(command, handler);
this.toDisposeBeforeMenuRebuild.push(
Disposable.create(() => this.commandRegistry.unregisterCommand(command))
);
this.menuModelRegistry.registerMenuAction(platformMenuPath, menuAction);
// Note: we do not dispose the menu actions individually. Calling `unregisterSubmenu` on the parent will wipe the children menu nodes recursively.
});
// Board menu
const menuAction = {
commandId: id,
label: name,
order: String(index).padStart(4), // pads with leading zeros for alphanumeric sort where order is 1, 2, 11, and NOT 1, 11, 2
};
this.commandRegistry.registerCommand(command, handler);
this.toDisposeBeforeMenuRebuild.push(
Disposable.create(() =>
this.commandRegistry.unregisterCommand(command)
)
);
this.menuModelRegistry.registerMenuAction(
platformMenuPath,
menuAction
);
// Note: we do not dispose the menu actions individually. Calling `unregisterSubmenu` on the parent will wipe the children menu nodes recursively.
});
});
}
);
// Installed ports
// Detected ports
const registerPorts = (
protocol: string,
protocolOrder: number,
ports: AvailablePorts
ports: ReturnType<BoardList['ports']>,
protocolOrder: number
) => {
const portIDs = Object.keys(ports);
if (!portIDs.length) {
if (!ports.length) {
return;
}
@ -258,46 +308,26 @@ SN: ${SN}
)
);
// First we show addresses with recognized boards connected,
// then all the rest.
const sortedIDs = Object.keys(ports).sort(
(left: string, right: string): number => {
const [, leftBoards] = ports[left];
const [, rightBoards] = ports[right];
return rightBoards.length - leftBoards.length;
}
);
for (let i = 0; i < sortedIDs.length; i++) {
const portID = sortedIDs[i];
const [port, boards] = ports[portID];
for (let i = 0; i < ports.length; i++) {
const { port, boards } = ports[i];
const portKey = Port.keyOf(port);
let label = `${port.addressLabel}`;
if (boards.length) {
if (boards?.length) {
const boardsList = boards.map((board) => board.name).join(', ');
label = `${label} (${boardsList})`;
}
const id = `arduino-select-port--${portID}`;
const id = `arduino-select-port--${portKey}`;
const command = { id };
const handler = {
execute: () => {
if (
!Port.sameAs(
port,
this.boardsServiceProvider.boardsConfig.selectedPort
)
) {
this.boardsServiceProvider.boardsConfig = {
selectedBoard:
this.boardsServiceProvider.boardsConfig.selectedBoard,
selectedPort: port,
};
}
this.boardsServiceProvider.updateConfig({
protocol: port.protocol,
address: port.address,
});
},
isToggled: () => {
return i === ports.matchingIndex;
},
isToggled: () =>
Port.sameAs(
port,
this.boardsServiceProvider.boardsConfig.selectedPort
),
};
const menuAction = {
commandId: id,
@ -314,22 +344,12 @@ SN: ${SN}
}
};
const grouped = AvailablePorts.groupByProtocol(availablePorts);
const groupedPorts = boardList.portsGroupedByProtocol();
let protocolOrder = 100;
// We first show serial and network ports, then all the rest
['serial', 'network'].forEach((protocol) => {
const ports = grouped.get(protocol);
if (ports) {
registerPorts(protocol, protocolOrder, ports);
grouped.delete(protocol);
protocolOrder = protocolOrder + 100;
}
Object.entries(groupedPorts).forEach(([protocol, ports]) => {
registerPorts(protocol, ports, protocolOrder);
protocolOrder += 100;
});
grouped.forEach((ports, protocol) => {
registerPorts(protocol, protocolOrder, ports);
protocolOrder = protocolOrder + 100;
});
this.mainMenuManager.update();
}

View File

@ -1,67 +1,66 @@
import PQueue from 'p-queue';
import { inject, injectable } from '@theia/core/shared/inversify';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { BoardsServiceProvider } from './boards-service-provider';
import { Board, ConfigOption, Programmer } from '../../common/protocol';
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { BoardsDataStore } from './boards-data-store';
import { MainMenuManager } from '../../common/main-menu-manager';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import PQueue from 'p-queue';
import {
BoardIdentifier,
ConfigOption,
isBoardIdentifierChangeEvent,
Programmer,
} from '../../common/protocol';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { ArduinoMenus, unregisterSubmenu } from '../menu/arduino-menus';
import { nls } from '@theia/core/lib/common';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
CommandRegistry,
Contribution,
MenuModelRegistry,
} from './contribution';
@injectable()
export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
export class BoardsDataMenuUpdater extends Contribution {
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
private readonly commandRegistry: CommandRegistry;
@inject(MenuModelRegistry)
protected readonly menuRegistry: MenuModelRegistry;
@inject(MainMenuManager)
protected readonly mainMenuManager: MainMenuManager;
private readonly menuRegistry: MenuModelRegistry;
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
private readonly boardsDataStore: BoardsDataStore;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
private readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
private readonly toDisposeOnBoardChange = new DisposableCollection();
protected readonly queue = new PQueue({ autoStart: true, concurrency: 1 });
protected readonly toDisposeOnBoardChange = new DisposableCollection();
async onStart(): Promise<void> {
this.appStateService
.reachedState('ready')
.then(() =>
this.updateMenuActions(
this.boardsServiceClient.boardsConfig.selectedBoard
)
);
override onStart(): void {
this.boardsDataStore.onChanged(() =>
this.updateMenuActions(
this.boardsServiceClient.boardsConfig.selectedBoard
this.boardsServiceProvider.boardsConfig.selectedBoard
)
);
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
this.updateMenuActions(selectedBoard)
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
if (isBoardIdentifierChangeEvent(event)) {
this.updateMenuActions(event.selectedBoard);
}
});
}
override onReady(): void {
this.boardsServiceProvider.ready.then(() =>
this.updateMenuActions(
this.boardsServiceProvider.boardsConfig.selectedBoard
)
);
}
protected async updateMenuActions(
selectedBoard: Board | undefined
private async updateMenuActions(
selectedBoard: BoardIdentifier | undefined
): Promise<void> {
return this.queue.add(async () => {
this.toDisposeOnBoardChange.dispose();
this.mainMenuManager.update();
this.menuManager.update();
if (selectedBoard) {
const { fqbn } = selectedBoard;
if (fqbn) {
@ -172,7 +171,7 @@ export class BoardsDataMenuUpdater implements FrontendApplicationContribution {
]);
}
}
this.mainMenuManager.update();
this.menuManager.update();
}
}
});

View File

@ -5,8 +5,10 @@ import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import { NotificationCenter } from '../notification-center';
import {
Board,
BoardIdentifier,
BoardsService,
ExecutableService,
isBoardIdentifierChangeEvent,
Sketch,
} from '../../common/protocol';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
@ -88,9 +90,11 @@ export class Debug extends SketchContribution {
: Debug.Commands.START_DEBUGGING.label
}`)
);
this.boardsServiceProvider.onBoardsConfigChanged(({ selectedBoard }) =>
this.refreshState(selectedBoard)
);
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
if (isBoardIdentifierChangeEvent(event)) {
this.refreshState(event.selectedBoard);
}
});
this.notificationCenter.onPlatformDidInstall(() => this.refreshState());
this.notificationCenter.onPlatformDidUninstall(() => this.refreshState());
}
@ -169,7 +173,7 @@ export class Debug extends SketchContribution {
}
private async startDebug(
board: Board | undefined = this.boardsServiceProvider.boardsConfig
board: BoardIdentifier | undefined = this.boardsServiceProvider.boardsConfig
.selectedBoard
): Promise<void> {
if (!board) {

View File

@ -28,6 +28,8 @@ import {
CoreService,
SketchesService,
Sketch,
isBoardIdentifierChangeEvent,
BoardIdentifier,
} from '../../common/protocol';
import { nls } from '@theia/core/lib/common/nls';
import { unregisterSubmenu } from '../menu/arduino-menus';
@ -108,7 +110,7 @@ export abstract class Examples extends SketchContribution {
protected readonly coreService: CoreService;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
protected readonly boardsServiceProvider: BoardsServiceProvider;
@inject(NotificationCenter)
protected readonly notificationCenter: NotificationCenter;
@ -117,12 +119,14 @@ export abstract class Examples extends SketchContribution {
protected override init(): void {
super.init();
this.boardsServiceClient.onBoardsConfigChanged(({ selectedBoard }) =>
this.handleBoardChanged(selectedBoard)
);
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
if (isBoardIdentifierChangeEvent(event)) {
this.handleBoardChanged(event.selectedBoard);
}
});
this.notificationCenter.onDidReinitialize(() =>
this.update({
board: this.boardsServiceClient.boardsConfig.selectedBoard,
board: this.boardsServiceProvider.boardsConfig.selectedBoard,
// No force refresh. The core client was already refreshed.
})
);
@ -134,7 +138,7 @@ export abstract class Examples extends SketchContribution {
}
protected abstract update(options?: {
board?: Board | undefined;
board?: BoardIdentifier | undefined;
forceRefresh?: boolean;
}): void;
@ -225,7 +229,7 @@ export abstract class Examples extends SketchContribution {
protected createHandler(uri: string): CommandHandler {
const forceUpdate = () =>
this.update({
board: this.boardsServiceClient.boardsConfig.selectedBoard,
board: this.boardsServiceProvider.boardsConfig.selectedBoard,
forceRefresh: true,
});
return {
@ -306,7 +310,7 @@ export class LibraryExamples extends Examples {
protected override async update(
options: { board?: Board; forceRefresh?: boolean } = {
board: this.boardsServiceClient.boardsConfig.selectedBoard,
board: this.boardsServiceProvider.boardsConfig.selectedBoard,
}
): Promise<void> {
const { board, forceRefresh } = options;

View File

@ -37,7 +37,7 @@ export class IncludeLibrary extends SketchContribution {
protected readonly notificationCenter: NotificationCenter;
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
protected readonly boardsServiceProvider: BoardsServiceProvider;
@inject(LibraryService)
protected readonly libraryService: LibraryService;
@ -46,7 +46,7 @@ export class IncludeLibrary extends SketchContribution {
protected readonly toDispose = new DisposableCollection();
override onStart(): void {
this.boardsServiceClient.onBoardsConfigChanged(() =>
this.boardsServiceProvider.onBoardsConfigDidChange(() =>
this.updateMenuActions()
);
this.notificationCenter.onLibraryDidInstall(() => this.updateMenuActions());
@ -98,7 +98,7 @@ export class IncludeLibrary extends SketchContribution {
this.toDispose.dispose();
this.mainMenuManager.update();
const libraries: LibraryPackage[] = [];
const fqbn = this.boardsServiceClient.boardsConfig.selectedBoard?.fqbn;
const fqbn = this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
// Show all libraries, when no board is selected.
// Otherwise, show libraries only for the selected board.
libraries.push(...(await this.libraryService.list({ fqbn })));

View File

@ -7,12 +7,13 @@ import { Mutex } from 'async-mutex';
import {
ArduinoDaemon,
assertSanitizedFqbn,
BoardIdentifier,
BoardsService,
ExecutableService,
isBoardIdentifierChangeEvent,
sanitizeFqbn,
} from '../../common/protocol';
import { CurrentSketch } from '../sketches-service-client-impl';
import { BoardsConfig } from '../boards/boards-config';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { HostedPluginEvents } from '../hosted-plugin-events';
import { NotificationCenter } from '../notification-center';
@ -48,7 +49,7 @@ export class InoLanguage extends SketchContribution {
override onReady(): void {
const start = (
{ selectedBoard }: BoardsConfig.Config,
selectedBoard: BoardIdentifier | undefined,
forceStart = false
) => {
if (selectedBoard) {
@ -59,12 +60,16 @@ export class InoLanguage extends SketchContribution {
}
};
const forceRestart = () => {
start(this.boardsServiceProvider.boardsConfig, true);
start(this.boardsServiceProvider.boardsConfig.selectedBoard, true);
};
this.toDispose.pushAll([
this.boardsServiceProvider.onBoardsConfigChanged(start),
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
if (isBoardIdentifierChangeEvent(event)) {
start(event.selectedBoard);
}
}),
this.hostedPluginEvents.onPluginsDidStart(() =>
start(this.boardsServiceProvider.boardsConfig)
start(this.boardsServiceProvider.boardsConfig.selectedBoard)
),
this.hostedPluginEvents.onPluginsWillUnload(
() => (this.languageServerFqbn = undefined)
@ -101,12 +106,14 @@ export class InoLanguage extends SketchContribution {
matchingFqbn &&
boardsConfig.selectedBoard?.fqbn === matchingFqbn
) {
start(boardsConfig);
start(boardsConfig.selectedBoard);
}
}
}),
]);
start(this.boardsServiceProvider.boardsConfig);
this.boardsServiceProvider.ready.then(() =>
start(this.boardsServiceProvider.boardsConfig.selectedBoard)
);
}
onStop(): void {

View File

@ -1,25 +1,18 @@
import { CommandRegistry } from '@theia/core';
import type { Command, CommandRegistry } from '@theia/core/lib/common/command';
import { inject, injectable } from '@theia/core/shared/inversify';
import type { EditBoardsConfigActionParams } from '../../common/protocol/board-list';
import { BoardsConfigDialog } from '../boards/boards-config-dialog';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { Contribution, Command } from './contribution';
import { Contribution } from './contribution';
@injectable()
export class OpenBoardsConfig extends Contribution {
@inject(BoardsServiceProvider)
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(BoardsConfigDialog)
private readonly boardsConfigDialog: BoardsConfigDialog;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(OpenBoardsConfig.Commands.OPEN_DIALOG, {
execute: async (query?: string | undefined) => {
const boardsConfig = await this.boardsConfigDialog.open(query);
if (boardsConfig) {
return (this.boardsServiceProvider.boardsConfig = boardsConfig);
}
},
execute: async (params?: EditBoardsConfigActionParams) =>
this.boardsConfigDialog.open(params),
});
}
}

View File

@ -4,7 +4,10 @@ import {
} from '@theia/core/lib/browser/status-bar/status-bar';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import { BoardsConfig } from '../boards/boards-config';
import type {
BoardList,
BoardListItem,
} from '../../common/protocol/board-list';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { Contribution } from './contribution';
@ -12,21 +15,21 @@ import { Contribution } from './contribution';
export class SelectedBoard extends Contribution {
@inject(StatusBar)
private readonly statusBar: StatusBar;
@inject(BoardsServiceProvider)
private readonly boardsServiceProvider: BoardsServiceProvider;
override onStart(): void {
this.boardsServiceProvider.onBoardsConfigChanged((config) =>
this.update(config)
this.boardsServiceProvider.onBoardListDidChange(() =>
this.update(this.boardsServiceProvider.boardList)
);
}
override onReady(): void {
this.update(this.boardsServiceProvider.boardsConfig);
this.update(this.boardsServiceProvider.boardList);
}
private update({ selectedBoard, selectedPort }: BoardsConfig.Config): void {
private update(boardList: BoardList): void {
const { selectedBoard, selectedPort } = boardList.boardsConfig;
this.statusBar.setElement('arduino-selected-board', {
alignment: StatusBarAlignment.RIGHT,
text: selectedBoard
@ -38,17 +41,30 @@ export class SelectedBoard extends Contribution {
className: 'arduino-selected-board',
});
if (selectedBoard) {
const notConnectedLabel = nls.localize(
'arduino/common/notConnected',
'[not connected]'
);
let portLabel = notConnectedLabel;
if (selectedPort) {
portLabel = nls.localize(
'arduino/common/selectedOn',
'on {0}',
selectedPort.address
);
const selectedItem: BoardListItem | undefined =
boardList.items[boardList.selectedIndex];
if (!selectedItem) {
portLabel += ` ${notConnectedLabel}`; // append ` [not connected]` when the port is selected but it's not detected by the CLI
}
}
this.statusBar.setElement('arduino-selected-port', {
alignment: StatusBarAlignment.RIGHT,
text: selectedPort
? nls.localize(
'arduino/common/selectedOn',
'on {0}',
selectedPort.address
)
: nls.localize('arduino/common/notConnected', '[not connected]'),
text: portLabel,
className: 'arduino-selected-port',
});
} else {
this.statusBar.removeElement('arduino-selected-port');
}
}
}

View File

@ -6,15 +6,16 @@ import type { ArduinoState } from 'vscode-arduino-api';
import {
BoardsService,
CompileSummary,
Port,
isCompileSummary,
BoardsConfig,
PortIdentifier,
resolveDetectedPort,
} from '../../common/protocol';
import {
toApiBoardDetails,
toApiCompileSummary,
toApiPort,
} from '../../common/protocol/arduino-context-mapper';
import type { BoardsConfig } from '../boards/boards-config';
import { BoardsDataStore } from '../boards/boards-data-store';
import { BoardsServiceProvider } from '../boards/boards-service-provider';
import { CurrentSketch } from '../sketches-service-client-impl';
@ -44,8 +45,8 @@ export class UpdateArduinoState extends SketchContribution {
override onStart(): void {
this.toDispose.pushAll([
this.boardsServiceProvider.onBoardsConfigChanged((config) =>
this.updateBoardsConfig(config)
this.boardsServiceProvider.onBoardsConfigDidChange(() =>
this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig)
),
this.sketchServiceClient.onCurrentSketchDidChange((sketch) =>
this.updateSketchPath(sketch)
@ -75,9 +76,7 @@ export class UpdateArduinoState extends SketchContribution {
}
override onReady(): void {
this.boardsServiceProvider.reconciled.then(() => {
this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig);
});
this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig); // TODO: verify!
this.updateSketchPath(this.sketchServiceClient.tryGetCurrentSketch());
this.updateUserDirPath(this.configService.tryGetSketchDirUri());
this.updateDataDirPath(this.configService.tryGetDataDirUri());
@ -106,9 +105,7 @@ export class UpdateArduinoState extends SketchContribution {
});
}
private async updateBoardsConfig(
boardsConfig: BoardsConfig.Config
): Promise<void> {
private async updateBoardsConfig(boardsConfig: BoardsConfig): Promise<void> {
const fqbn = boardsConfig.selectedBoard?.fqbn;
const port = boardsConfig.selectedPort;
await this.updateFqbn(fqbn);
@ -146,8 +143,11 @@ export class UpdateArduinoState extends SketchContribution {
});
}
private async updatePort(port: Port | undefined): Promise<void> {
const apiPort = port && toApiPort(port);
private async updatePort(port: PortIdentifier | undefined): Promise<void> {
const resolvedPort =
port &&
resolveDetectedPort(port, this.boardsServiceProvider.detectedPorts);
const apiPort = resolvedPort && toApiPort(resolvedPort);
return this.updateState({ key: 'port', value: apiPort });
}
@ -171,9 +171,6 @@ export class UpdateArduinoState extends SketchContribution {
params: UpdateStateParams<T>
): Promise<void> {
await this.hostedPluginSupport.didStart;
return this.commandService.executeCommand(
'arduinoAPI.updateState',
params
);
return this.commandService.executeCommand('arduinoAPI.updateState', params);
}
}

View File

@ -1,30 +1,30 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
import { CoreService, Port, sanitizeFqbn } from '../../common/protocol';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import { CoreService, sanitizeFqbn } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { CurrentSketch } from '../sketches-service-client-impl';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import {
Command,
CommandRegistry,
MenuModelRegistry,
KeybindingRegistry,
TabBarToolbarRegistry,
CoreServiceContribution,
KeybindingRegistry,
MenuModelRegistry,
TabBarToolbarRegistry,
} from './contribution';
import { deepClone, nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../sketches-service-client-impl';
import type { VerifySketchParams } from './verify-sketch';
import { UserFields } from './user-fields';
import type { VerifySketchParams } from './verify-sketch';
@injectable()
export class UploadSketch extends CoreServiceContribution {
@inject(UserFields)
private readonly userFields: UserFields;
private readonly onDidChangeEmitter = new Emitter<void>();
private readonly onDidChange = this.onDidChangeEmitter.event;
private uploadInProgress = false;
@inject(UserFields)
private readonly userFields: UserFields;
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
execute: async () => {
@ -107,7 +107,6 @@ export class UploadSketch extends CoreServiceContribution {
// uploadInProgress will be set to false whether the upload fails or not
this.uploadInProgress = true;
this.menuManager.update();
this.boardsServiceProvider.snapshotBoardDiscoveryOnUpload();
this.onDidChangeEmitter.fire();
this.clearVisibleNotification();
@ -135,12 +134,14 @@ export class UploadSketch extends CoreServiceContribution {
return;
}
await this.doWithProgress({
const uploadResponse = await this.doWithProgress({
progressText: nls.localize('arduino/sketch/uploading', 'Uploading...'),
task: (progressId, coreService) =>
coreService.upload({ ...uploadOptions, progressId }),
keepOutput: true,
});
// the port update is NOOP if nothing has changed
this.boardsServiceProvider.updateConfig(uploadResponse.portAfterUpload);
this.messageService.info(
nls.localize('arduino/sketch/doneUploading', 'Done uploading.'),
@ -150,9 +151,10 @@ export class UploadSketch extends CoreServiceContribution {
this.userFields.notifyFailedWithError(e);
this.handleError(e);
} finally {
// TODO: here comes the port change if happened during the upload
// https://github.com/arduino/arduino-cli/issues/2245
this.uploadInProgress = false;
this.menuManager.update();
this.boardsServiceProvider.attemptPostUploadAutoSelect();
this.onDidChangeEmitter.fire();
}
}
@ -174,7 +176,7 @@ export class UploadSketch extends CoreServiceContribution {
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
const port = this.maybeUpdatePortProperties(boardsConfig.selectedPort);
const port = boardsConfig.selectedPort;
return {
sketch,
fqbn,
@ -185,28 +187,6 @@ export class UploadSketch extends CoreServiceContribution {
userFields,
};
}
/**
* This is a hack to ensure that the port object has the `properties` when uploading.(https://github.com/arduino/arduino-ide/issues/740)
* This method works around a bug when restoring a `port` persisted by an older version of IDE2. See the bug [here](https://github.com/arduino/arduino-ide/pull/1335#issuecomment-1224355236).
*
* Before the upload, this method checks the available ports and makes sure that the `properties` of an available port, and the port selected by the user have the same `properties`.
* This method does not update any state (for example, the `BoardsConfig.Config`) but uses the correct `properties` for the `upload`.
*/
private maybeUpdatePortProperties(port: Port | undefined): Port | undefined {
if (port) {
const key = Port.keyOf(port);
for (const candidate of this.boardsServiceProvider.availablePorts) {
if (key === Port.keyOf(candidate) && candidate.properties) {
return {
...port,
properties: deepClone(candidate.properties),
};
}
}
}
return port;
}
}
export namespace UploadSketch {

View File

@ -21,7 +21,7 @@ export class UserFields extends Contribution {
protected override init(): void {
super.init();
this.boardsServiceProvider.onBoardsConfigChanged(async () => {
this.boardsServiceProvider.onBoardsConfigDidChange(async () => {
const userFields =
await this.boardsServiceProvider.selectedBoardUserFields();
this.boardRequiresUserFields = userFields.length > 0;
@ -43,10 +43,7 @@ export class UserFields extends Contribution {
if (!fqbn) {
return undefined;
}
const address =
boardsConfig.selectedBoard?.port?.address ||
boardsConfig.selectedPort?.address ||
'';
const address = boardsConfig.selectedPort?.address || '';
return fqbn + '|' + address;
}

View File

@ -1,20 +1,30 @@
import { nls } from '@theia/core/lib/common/nls';
import React from '@theia/core/shared/react';
import Tippy from '@tippyjs/react';
import { AvailableBoard } from '../../boards/boards-service-provider';
import { CertificateListComponent } from './certificate-list';
import { SelectBoardComponent } from './select-board-components';
import {
BoardList,
isInferredBoardListItem,
} from '../../../common/protocol/board-list';
import {
boardIdentifierEquals,
portIdentifierEquals,
} from '../../../common/protocol/boards-service';
import { CertificateAddComponent } from './certificate-add-new';
import { nls } from '@theia/core/lib/common';
import { CertificateListComponent } from './certificate-list';
import {
BoardOptionValue,
SelectBoardComponent,
} from './select-board-components';
export const CertificateUploaderComponent = ({
availableBoards,
boardList,
certificates,
addCertificate,
updatableFqbns,
uploadCertificates,
openContextMenu,
}: {
availableBoards: AvailableBoard[];
boardList: BoardList;
certificates: string[];
addCertificate: (cert: string) => void;
updatableFqbns: string[];
@ -33,11 +43,17 @@ export const CertificateUploaderComponent = ({
const [selectedCerts, setSelectedCerts] = React.useState<string[]>([]);
const [selectedBoard, setSelectedBoard] =
React.useState<AvailableBoard | null>(null);
const [selectedItem, setSelectedItem] =
React.useState<BoardOptionValue | null>(null);
const installCertificates = async () => {
if (!selectedBoard || !selectedBoard.fqbn || !selectedBoard.port) {
if (!selectedItem) {
return;
}
const board = isInferredBoardListItem(selectedItem)
? selectedItem.inferredBoard
: selectedItem.board;
if (!board.fqbn) {
return;
}
@ -45,8 +61,8 @@ export const CertificateUploaderComponent = ({
try {
await uploadCertificates(
selectedBoard.fqbn,
selectedBoard.port.address,
board.fqbn,
selectedItem.port.address,
selectedCerts
);
setInstallFeedback('ok');
@ -55,17 +71,29 @@ export const CertificateUploaderComponent = ({
}
};
const onBoardSelect = React.useCallback(
(board: AvailableBoard) => {
const newFqbn = (board && board.fqbn) || null;
const prevFqbn = (selectedBoard && selectedBoard.fqbn) || null;
const onItemSelect = React.useCallback(
(item: BoardOptionValue | null) => {
if (!item) {
return;
}
const board = isInferredBoardListItem(item)
? item.inferredBoard
: item.board;
const selectedBoard = isInferredBoardListItem(selectedItem)
? selectedItem.inferredBoard
: selectedItem?.board;
const port = item.port;
const selectedPort = selectedItem?.port;
if (newFqbn !== prevFqbn) {
if (
!boardIdentifierEquals(board, selectedBoard) ||
!portIdentifierEquals(port, selectedPort)
) {
setInstallFeedback(null);
setSelectedBoard(board);
setSelectedItem(item);
}
},
[selectedBoard]
[selectedItem]
);
return (
@ -125,10 +153,10 @@ export const CertificateUploaderComponent = ({
<div className="dialogRow">
<div className="fl1">
<SelectBoardComponent
availableBoards={availableBoards}
boardList={boardList}
updatableFqbns={updatableFqbns}
onBoardSelect={onBoardSelect}
selectedBoard={selectedBoard}
onItemSelect={onItemSelect}
selectedItem={selectedItem}
busy={installFeedback === 'installing'}
/>
</div>
@ -167,7 +195,7 @@ export const CertificateUploaderComponent = ({
type="button"
className="theia-button primary install-cert-btn"
onClick={installCertificates}
disabled={selectedCerts.length === 0 || !selectedBoard}
disabled={selectedCerts.length === 0 || !selectedItem}
>
{nls.localize('arduino/certificate/upload', 'Upload')}
</button>

View File

@ -1,62 +1,51 @@
import React from '@theia/core/shared/react';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
PreferenceScope,
PreferenceService,
} from '@theia/core/lib/browser/preferences/preference-service';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { nls } from '@theia/core/lib/common/nls';
import type { Message } from '@theia/core/shared/@phosphor/messaging';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import {
AvailableBoard,
BoardsServiceProvider,
} from '../../boards/boards-service-provider';
import { CertificateUploaderComponent } from './certificate-uploader-component';
import { ArduinoPreferences } from '../../arduino-preferences';
import {
PreferenceScope,
PreferenceService,
} from '@theia/core/lib/browser/preferences/preference-service';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { certificateList, sanifyCertString } from './utils';
import React from '@theia/core/shared/react';
import { ArduinoFirmwareUploader } from '../../../common/protocol/arduino-firmware-uploader';
import { nls } from '@theia/core/lib/common';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { createBoardList } from '../../../common/protocol/board-list';
import { ArduinoPreferences } from '../../arduino-preferences';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { AbstractDialog } from '../../theia/dialogs/dialogs';
import { CertificateUploaderComponent } from './certificate-uploader-component';
import { certificateList, sanifyCertString } from './utils';
@injectable()
export class UploadCertificateDialogWidget extends ReactWidget {
@inject(BoardsServiceProvider)
protected readonly boardsServiceClient: BoardsServiceProvider;
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(ArduinoPreferences)
protected readonly arduinoPreferences: ArduinoPreferences;
private readonly arduinoPreferences: ArduinoPreferences;
@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;
private readonly preferenceService: PreferenceService;
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
private readonly commandRegistry: CommandRegistry;
@inject(ArduinoFirmwareUploader)
protected readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
private readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
protected certificates: string[] = [];
protected updatableFqbns: string[] = [];
protected availableBoards: AvailableBoard[] = [];
private certificates: string[] = [];
private updatableFqbns: string[] = [];
private boardList = createBoardList({});
public busyCallback = (busy: boolean) => {
busyCallback = (busy: boolean) => {
return;
};
constructor() {
super();
}
@postConstruct()
protected init(): void {
this.arduinoPreferences.ready.then(() => {
@ -81,8 +70,8 @@ export class UploadCertificateDialogWidget extends ReactWidget {
})
);
this.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
this.availableBoards = availableBoards;
this.boardsServiceProvider.onBoardListDidChange((boardList) => {
this.boardList = boardList;
this.update();
});
}
@ -126,7 +115,7 @@ export class UploadCertificateDialogWidget extends ReactWidget {
protected render(): React.ReactNode {
return (
<CertificateUploaderComponent
availableBoards={this.availableBoards}
boardList={this.boardList}
certificates={this.certificates}
updatableFqbns={this.updatableFqbns}
addCertificate={this.addCertificate.bind(this)}
@ -143,7 +132,7 @@ export class UploadCertificateDialogProps extends DialogProps {}
@injectable()
export class UploadCertificateDialog extends AbstractDialog<void> {
@inject(UploadCertificateDialogWidget)
protected readonly widget: UploadCertificateDialogWidget;
private readonly widget: UploadCertificateDialogWidget;
private busy = false;

View File

@ -1,37 +1,38 @@
import { nls } from '@theia/core/lib/common';
import React from '@theia/core/shared/react';
import { AvailableBoard } from '../../boards/boards-service-provider';
import {
BoardList,
BoardListItemWithBoard,
InferredBoardListItem,
isInferredBoardListItem,
} from '../../../common/protocol/board-list';
import { ArduinoSelect } from '../../widgets/arduino-select';
type BoardOption = { value: string; label: string };
export type BoardOptionValue = BoardListItemWithBoard | InferredBoardListItem;
type BoardOption = { value: BoardOptionValue | undefined; label: string };
export const SelectBoardComponent = ({
availableBoards,
boardList,
updatableFqbns,
onBoardSelect,
selectedBoard,
onItemSelect,
selectedItem,
busy,
}: {
availableBoards: AvailableBoard[];
boardList: BoardList;
updatableFqbns: string[];
onBoardSelect: (board: AvailableBoard | null) => void;
selectedBoard: AvailableBoard | null;
onItemSelect: (item: BoardOptionValue | null) => void;
selectedItem: BoardOptionValue | null;
busy: boolean;
}): React.ReactElement => {
const [selectOptions, setSelectOptions] = React.useState<BoardOption[]>([]);
const [selectBoardPlaceholder, setSelectBoardPlaceholder] =
React.useState('');
const [selectItemPlaceholder, setSelectBoardPlaceholder] = React.useState('');
const selectOption = React.useCallback(
(boardOpt: BoardOption) => {
onBoardSelect(
(boardOpt &&
availableBoards.find((board) => board.fqbn === boardOpt.value)) ||
null
);
(boardOpt: BoardOption | null) => {
onItemSelect(boardOpt?.value ?? null);
},
[availableBoards, onBoardSelect]
[onItemSelect]
);
React.useEffect(() => {
@ -44,26 +45,33 @@ export const SelectBoardComponent = ({
'arduino/certificate/selectBoard',
'Select a board...'
);
const updatableBoards = boardList.boards.filter((item) => {
const fqbn = (
isInferredBoardListItem(item) ? item.inferredBoard : item.board
).fqbn;
return fqbn && updatableFqbns.includes(fqbn);
});
let selBoard = -1;
const updatableBoards = availableBoards.filter(
(board) => board.port && board.fqbn && updatableFqbns.includes(board.fqbn)
);
const boardsList: BoardOption[] = updatableBoards.map((board, i) => {
if (board.selected) {
const boardOptions: BoardOption[] = updatableBoards.map((item, i) => {
if (selectedItem === item) {
selBoard = i;
}
const board = isInferredBoardListItem(item)
? item.inferredBoard
: item.board;
return {
label: nls.localize(
'arduino/certificate/boardAtPort',
'{0} at {1}',
board.name,
board.port?.address ?? ''
item.port?.address ?? ''
),
value: board.fqbn || '',
value: item,
};
});
if (boardsList.length === 0) {
if (boardOptions.length === 0) {
placeholderTxt = nls.localize(
'arduino/certificate/noSupportedBoardConnected',
'No supported board connected'
@ -71,32 +79,32 @@ export const SelectBoardComponent = ({
}
setSelectBoardPlaceholder(placeholderTxt);
setSelectOptions(boardsList);
setSelectOptions(boardOptions);
if (selectedBoard) {
selBoard = boardsList
.map((boardOpt) => boardOpt.value)
.indexOf(selectedBoard.fqbn || '');
if (selectedItem) {
selBoard = updatableBoards.indexOf(selectedItem);
}
selectOption(boardsList[selBoard] || null);
}, [busy, availableBoards, selectOption, updatableFqbns, selectedBoard]);
selectOption(boardOptions[selBoard] || null);
}, [busy, boardList, selectOption, updatableFqbns, selectedItem]);
return (
<ArduinoSelect
id="board-select"
menuPosition="fixed"
isDisabled={selectOptions.length === 0 || busy}
placeholder={selectBoardPlaceholder}
placeholder={selectItemPlaceholder}
options={selectOptions}
value={
(selectedBoard && {
value: selectedBoard.fqbn,
(selectedItem && {
value: selectedItem,
label: nls.localize(
'arduino/certificate/boardAtPort',
'{0} at {1}',
selectedBoard.name,
selectedBoard.port?.address ?? ''
(isInferredBoardListItem(selectedItem)
? selectedItem.inferredBoard
: selectedItem.board
).name,
selectedItem.port.address ?? ''
),
}) ||
null

View File

@ -1,24 +1,32 @@
import { nls } from '@theia/core/lib/common';
import React from '@theia/core/shared/react';
import { Port } from '../../../common/protocol';
import {
boardIdentifierEquals,
Port,
portIdentifierEquals,
} from '../../../common/protocol';
import {
ArduinoFirmwareUploader,
FirmwareInfo,
} from '../../../common/protocol/arduino-firmware-uploader';
import { AvailableBoard } from '../../boards/boards-service-provider';
import {
BoardList,
BoardListItemWithBoard,
isInferredBoardListItem,
} from '../../../common/protocol/board-list';
import { ArduinoSelect } from '../../widgets/arduino-select';
import { SelectBoardComponent } from '../certificate-uploader/select-board-components';
type FirmwareOption = { value: string; label: string };
export const FirmwareUploaderComponent = ({
availableBoards,
boardList,
firmwareUploader,
updatableFqbns,
flashFirmware,
isOpen,
}: {
availableBoards: AvailableBoard[];
boardList: BoardList;
firmwareUploader: ArduinoFirmwareUploader;
updatableFqbns: string[];
flashFirmware: (firmware: FirmwareInfo, port: Port) => Promise<any>;
@ -31,8 +39,8 @@ export const FirmwareUploaderComponent = ({
'ok' | 'fail' | 'installing' | null
>(null);
const [selectedBoard, setSelectedBoard] =
React.useState<AvailableBoard | null>(null);
const [selectedItem, setSelectedItem] =
React.useState<BoardListItemWithBoard | null>(null);
const [availableFirmwares, setAvailableFirmwares] = React.useState<
FirmwareInfo[]
@ -50,13 +58,16 @@ export const FirmwareUploaderComponent = ({
const fetchFirmwares = React.useCallback(async () => {
setInstallFeedback(null);
setFirmwaresFetching(true);
if (!selectedBoard) {
if (!selectedItem) {
return;
}
// fetch the firmwares for the selected board
const board = isInferredBoardListItem(selectedItem)
? selectedItem.inferredBoard
: selectedItem.board;
const firmwaresForFqbn = await firmwareUploader.availableFirmwares(
selectedBoard.fqbn || ''
board.fqbn || ''
);
setAvailableFirmwares(firmwaresForFqbn);
@ -69,7 +80,7 @@ export const FirmwareUploaderComponent = ({
if (firmwaresForFqbn.length > 0) setSelectedFirmware(firmwaresOpts[0]);
setFirmwaresFetching(false);
}, [firmwareUploader, selectedBoard]);
}, [firmwareUploader, selectedItem]);
const installFirmware = React.useCallback(async () => {
setInstallFeedback('installing');
@ -81,27 +92,39 @@ export const FirmwareUploaderComponent = ({
try {
const installStatus =
!!firmwareToFlash &&
!!selectedBoard?.port &&
(await flashFirmware(firmwareToFlash, selectedBoard?.port));
!!selectedItem?.board &&
(await flashFirmware(firmwareToFlash, selectedItem?.port));
setInstallFeedback((installStatus && 'ok') || 'fail');
} catch {
setInstallFeedback('fail');
}
}, [firmwareUploader, selectedBoard, selectedFirmware, availableFirmwares]);
}, [selectedItem, selectedFirmware, availableFirmwares, flashFirmware]);
const onBoardSelect = React.useCallback(
(board: AvailableBoard) => {
const newFqbn = (board && board.fqbn) || null;
const prevFqbn = (selectedBoard && selectedBoard.fqbn) || null;
const onItemSelect = React.useCallback(
(item: BoardListItemWithBoard | null) => {
if (!item) {
return;
}
const board = isInferredBoardListItem(item)
? item.inferredBoard
: item.board;
const selectedBoard = isInferredBoardListItem(selectedItem)
? selectedItem.inferredBoard
: selectedItem?.board;
const port = item.port;
const selectedPort = selectedItem?.port;
if (newFqbn !== prevFqbn) {
if (
!boardIdentifierEquals(board, selectedBoard) ||
!portIdentifierEquals(port, selectedPort)
) {
setInstallFeedback(null);
setAvailableFirmwares([]);
setSelectedBoard(board);
setSelectedItem(item);
}
},
[selectedBoard]
[selectedItem]
);
return (
@ -115,10 +138,10 @@ export const FirmwareUploaderComponent = ({
<div className="dialogRow">
<div className="fl1">
<SelectBoardComponent
availableBoards={availableBoards}
boardList={boardList}
updatableFqbns={updatableFqbns}
onBoardSelect={onBoardSelect}
selectedBoard={selectedBoard}
onItemSelect={onItemSelect}
selectedItem={selectedItem}
busy={installFeedback === 'installing'}
/>
</div>
@ -126,7 +149,7 @@ export const FirmwareUploaderComponent = ({
type="button"
className="theia-button secondary"
disabled={
selectedBoard === null ||
selectedItem === null ||
firmwaresFetching ||
installFeedback === 'installing'
}
@ -150,7 +173,7 @@ export const FirmwareUploaderComponent = ({
id="firmware-select"
menuPosition="fixed"
isDisabled={
!selectedBoard ||
!selectedItem ||
firmwaresFetching ||
installFeedback === 'installing'
}

View File

@ -1,24 +1,21 @@
import React from '@theia/core/shared/react';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import type { Message } from '@theia/core/shared/@phosphor/messaging';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { ReactDialog } from '../../theia/dialogs/dialogs';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import {
AvailableBoard,
BoardsServiceProvider,
} from '../../boards/boards-service-provider';
import React from '@theia/core/shared/react';
import {
ArduinoFirmwareUploader,
FirmwareInfo,
} from '../../../common/protocol/arduino-firmware-uploader';
import { FirmwareUploaderComponent } from './firmware-uploader-component';
import type { Port } from '../../../common/protocol/boards-service';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { UploadFirmware } from '../../contributions/upload-firmware';
import { Port } from '../../../common/protocol';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { ReactDialog } from '../../theia/dialogs/dialogs';
import { FirmwareUploaderComponent } from './firmware-uploader-component';
@injectable()
export class UploadFirmwareDialogProps extends DialogProps {}
@ -26,14 +23,13 @@ export class UploadFirmwareDialogProps extends DialogProps {}
@injectable()
export class UploadFirmwareDialog extends ReactDialog<void> {
@inject(BoardsServiceProvider)
private readonly boardsServiceClient: BoardsServiceProvider;
private readonly boardsServiceProvider: BoardsServiceProvider;
@inject(ArduinoFirmwareUploader)
private readonly arduinoFirmwareUploader: ArduinoFirmwareUploader;
@inject(FrontendApplicationStateService)
private readonly appStatusService: FrontendApplicationStateService;
private readonly appStateService: FrontendApplicationStateService;
private updatableFqbns: string[] = [];
private availableBoards: AvailableBoard[] = [];
private isOpen = new Object();
private busy = false;
@ -49,16 +45,12 @@ export class UploadFirmwareDialog extends ReactDialog<void> {
@postConstruct()
protected init(): void {
this.appStatusService.reachedState('ready').then(async () => {
this.appStateService.reachedState('ready').then(async () => {
const fqbns = await this.arduinoFirmwareUploader.updatableBoards();
this.updatableFqbns = fqbns;
this.update();
});
this.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
this.availableBoards = availableBoards;
this.update();
});
this.boardsServiceProvider.onBoardListDidChange(() => this.update());
}
get value(): void {
@ -70,7 +62,7 @@ export class UploadFirmwareDialog extends ReactDialog<void> {
<div>
<form>
<FirmwareUploaderComponent
availableBoards={this.availableBoards}
boardList={this.boardsServiceProvider.boardList}
firmwareUploader={this.arduinoFirmwareUploader}
flashFirmware={this.flashFirmware.bind(this)}
updatableFqbns={this.updatableFqbns}

View File

@ -1,15 +1,18 @@
import {
ApplicationError,
Disposable,
Emitter,
MessageService,
nls,
} from '@theia/core';
import { ApplicationError } from '@theia/core/lib/common/application-error';
import { Disposable } from '@theia/core/lib/common/disposable';
import { Emitter } from '@theia/core/lib/common/event';
import { MessageService } from '@theia/core/lib/common/message-service';
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
import { nls } from '@theia/core/lib/common/nls';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { inject, injectable } from '@theia/core/shared/inversify';
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
import { Board, Port } from '../common/protocol';
import { BoardIdentifier, PortIdentifier } from '../common/protocol';
import {
BoardListItem,
boardListItemEquals,
getInferredBoardOrBoard,
} from '../common/protocol/board-list';
import {
Monitor,
MonitorManagerProxyClient,
@ -17,7 +20,6 @@ import {
MonitorSettings,
PluggableMonitorSettings,
} from '../common/protocol/monitor-service';
import { BoardsConfig } from './boards/boards-config';
import { BoardsServiceProvider } from './boards/boards-service-provider';
@injectable()
@ -55,8 +57,8 @@ export class MonitorManagerProxyClientImpl
// frontend and backend.
private webSocket?: WebSocket;
private wsPort?: number;
private lastConnectedBoard: BoardsConfig.Config;
private onBoardsConfigChanged: Disposable | undefined;
private lastConnectedBoard: BoardListItem | undefined;
private onBoardListDidChange: Disposable | undefined;
getWebSocketPort(): number | undefined {
return this.wsPort;
@ -110,8 +112,8 @@ export class MonitorManagerProxyClientImpl
if (!this.webSocket) {
return;
}
this.onBoardsConfigChanged?.dispose();
this.onBoardsConfigChanged = undefined;
this.onBoardListDidChange?.dispose();
this.onBoardListDidChange = undefined;
try {
this.webSocket.close();
this.webSocket = undefined;
@ -134,51 +136,52 @@ export class MonitorManagerProxyClientImpl
}
async startMonitor(settings?: PluggableMonitorSettings): Promise<void> {
await this.boardsServiceProvider.reconciled;
this.lastConnectedBoard = {
selectedBoard: this.boardsServiceProvider.boardsConfig.selectedBoard,
selectedPort: this.boardsServiceProvider.boardsConfig.selectedPort,
};
if (!this.onBoardsConfigChanged) {
this.onBoardsConfigChanged =
this.boardsServiceProvider.onBoardsConfigChanged(
async ({ selectedBoard, selectedPort }) => {
if (
typeof selectedBoard === 'undefined' ||
typeof selectedPort === 'undefined'
)
const { boardList } = this.boardsServiceProvider;
this.lastConnectedBoard = boardList.items[boardList.selectedIndex];
if (!this.onBoardListDidChange) {
this.onBoardListDidChange =
this.boardsServiceProvider.onBoardListDidChange(
async (newBoardList) => {
const currentConnectedBoard =
newBoardList.items[newBoardList.selectedIndex];
if (!currentConnectedBoard) {
return;
}
// a board is plugged and it's different from the old connected board
if (
selectedBoard?.fqbn !==
this.lastConnectedBoard?.selectedBoard?.fqbn ||
Port.keyOf(selectedPort) !==
(this.lastConnectedBoard.selectedPort
? Port.keyOf(this.lastConnectedBoard.selectedPort)
: undefined)
!this.lastConnectedBoard ||
boardListItemEquals(
currentConnectedBoard,
this.lastConnectedBoard
)
) {
this.lastConnectedBoard = {
selectedBoard: selectedBoard,
selectedPort: selectedPort,
};
this.onMonitorShouldResetEmitter.fire();
} else {
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
// recreate the listener callback
this.startMonitor();
} else {
// a board is plugged and it's different from the old connected board
this.lastConnectedBoard = currentConnectedBoard;
this.onMonitorShouldResetEmitter.fire();
}
}
);
}
const { selectedBoard, selectedPort } =
this.boardsServiceProvider.boardsConfig;
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
if (!this.lastConnectedBoard) {
return;
}
const board = getInferredBoardOrBoard(this.lastConnectedBoard);
if (!board) {
return;
}
try {
this.clearVisibleNotification();
await this.server().startMonitor(selectedBoard, selectedPort, settings);
await this.server().startMonitor(
board,
this.lastConnectedBoard.port,
settings
);
} catch (err) {
const message = ApplicationError.is(err) ? err.message : String(err);
this.previousNotificationId = this.notificationId(message);
@ -186,7 +189,10 @@ export class MonitorManagerProxyClientImpl
}
}
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> {
getCurrentSettings(
board: BoardIdentifier,
port: PortIdentifier
): Promise<MonitorSettings> {
return this.server().getCurrentSettings(board, port);
}

View File

@ -14,13 +14,13 @@ import {
NotificationServiceClient,
NotificationServiceServer,
} from '../common/protocol/notification-service';
import {
AttachedBoardsChangeEvent,
import type {
BoardsPackage,
LibraryPackage,
ConfigState,
Sketch,
ProgressMessage,
DetectedPorts,
} from '../common/protocol';
import {
FrontendApplicationStateService,
@ -61,8 +61,9 @@ export class NotificationCenter
private readonly libraryDidUninstallEmitter = new Emitter<{
item: LibraryPackage;
}>();
private readonly attachedBoardsDidChangeEmitter =
new Emitter<AttachedBoardsChangeEvent>();
private readonly detectedPortsDidChangeEmitter = new Emitter<{
detectedPorts: DetectedPorts;
}>();
private readonly recentSketchesChangedEmitter = new Emitter<{
sketches: Sketch[];
}>();
@ -82,7 +83,7 @@ export class NotificationCenter
this.platformDidUninstallEmitter,
this.libraryDidInstallEmitter,
this.libraryDidUninstallEmitter,
this.attachedBoardsDidChangeEmitter
this.detectedPortsDidChangeEmitter
);
readonly onDidReinitialize = this.didReinitializeEmitter.event;
@ -97,8 +98,7 @@ export class NotificationCenter
readonly onPlatformDidUninstall = this.platformDidUninstallEmitter.event;
readonly onLibraryDidInstall = this.libraryDidInstallEmitter.event;
readonly onLibraryDidUninstall = this.libraryDidUninstallEmitter.event;
readonly onAttachedBoardsDidChange =
this.attachedBoardsDidChangeEmitter.event;
readonly onDetectedPortsDidChange = this.detectedPortsDidChangeEmitter.event;
readonly onRecentSketchesDidChange = this.recentSketchesChangedEmitter.event;
readonly onAppStateDidChange = this.onAppStateDidChangeEmitter.event;
@ -166,8 +166,8 @@ export class NotificationCenter
this.libraryDidUninstallEmitter.fire(event);
}
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void {
this.attachedBoardsDidChangeEmitter.fire(event);
notifyDetectedPortsDidChange(event: { detectedPorts: DetectedPorts }): void {
this.detectedPortsDidChangeEmitter.fire(event);
}
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void {

View File

@ -173,7 +173,6 @@ export class MonitorWidget extends ReactWidget {
private async startMonitor(): Promise<void> {
await this.appStateService.reachedState('ready');
await this.boardsServiceProvider.reconciled;
await this.syncSettings();
await this.monitorManagerProxy.startMonitor();
}

View File

@ -1,37 +0,0 @@
import { injectable, inject } from '@theia/core/shared/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',
};
}
}

View File

@ -172,20 +172,19 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
width: 210px;
}
.arduino-boards-toolbar-item--protocol,
.arduino-boards-toolbar-item--protocol,
.arduino-boards-dropdown-item--protocol {
align-items: center;
display: flex;
font-size: 16px;
}
.arduino-boards-toolbar-item--protocol ,
.arduino-boards-toolbar-item--protocol,
.arduino-boards-dropdown-item--protocol {
color: var(--theia-arduino-toolbar-dropdown-label);
}
.arduino-boards-toolbar-item-container
.arduino-boards-toolbar-item {
.arduino-boards-toolbar-item-container .arduino-boards-toolbar-item {
display: flex;
align-items: baseline;
width: 100%;
@ -196,7 +195,10 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
}
.arduino-boards-toolbar-item--label-connected {
font-family: 'Open Sans Bold';
font-style: normal;
font-weight: 700;
font-size: 14px;
}
.arduino-boards-toolbar-item-container .caret {
@ -208,6 +210,10 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
margin: -1px;
z-index: 1;
border: 1px solid var(--theia-arduino-toolbar-dropdown-border);
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-size: 12px;
}
.arduino-boards-dropdown-list:focus {
@ -230,20 +236,47 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
cursor: default;
display: flex;
font-size: var(--theia-ui-font-size1);
gap: 10px;
justify-content: space-between;
padding: 10px;
}
.arduino-boards-dropdown-item--board-header {
display: flex;
align-items: center;
}
.arduino-boards-dropdown-item--label {
overflow: hidden;
flex: 1;
}
/* Redefine default codicon size https://github.com/microsoft/vscode/commit/38cd0a377b7abef34fb07fe770fc633e68819ba6 */
.arduino-boards-dropdown-item .codicon[class*='codicon-'] {
font-size: 14px;
}
.arduino-boards-dropdown-item .p-TabBar-toolbar {
padding: 0px;
margin: 0px;
flex-direction: column;
}
.arduino-boards-dropdown-item .p-TabBar-toolbar .item {
margin: 0px;
}
.arduino-boards-dropdown-item .p-TabBar-toolbar .item .action-label {
padding: 0px;
}
.arduino-boards-dropdown-item--board-label {
font-size: 14px;
}
.arduino-boards-dropdown-item .arduino-boards-dropdown-item--protocol {
margin-right: 10px;
}
.arduino-boards-dropdown-item--port-label {
font-size: 12px;
}
@ -267,10 +300,6 @@ div#select-board-dialog .selectBoardContainer .list .item.selected i {
color: var(--theia-arduino-toolbar-dropdown-iconSelected);
}
.arduino-boards-dropdown-item .fa-check {
align-self: center;
}
.arduino-board-dropdown-footer {
color: var(--theia-secondaryButton-foreground);
border-top: 1px solid var(--theia-dropdown-border);

View File

@ -3,14 +3,9 @@ import {
DialogProps,
} from '@theia/core/lib/browser/dialogs';
import { ReactDialog as TheiaReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
import { codiconArray, Message } from '@theia/core/lib/browser/widgets/widget';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { codiconArray } from '@theia/core/lib/browser/widgets/widget';
import type { Message } from '@theia/core/shared/@phosphor/messaging';
import { inject, injectable } from '@theia/core/shared/inversify';
import React from '@theia/core/shared/react';
import { createRoot } from '@theia/core/shared/react-dom/client';
@injectable()
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
@ -18,7 +13,6 @@ export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
@inject(DialogProps) protected override readonly props: DialogProps
) {
super(props);
this.closeCrossNode.classList.remove(...codiconArray('close'));
this.closeCrossNode.classList.add('fa', 'fa-close');
}
@ -26,38 +20,26 @@ export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
@injectable()
export abstract class ReactDialog<T> extends TheiaReactDialog<T> {
protected override onUpdateRequest(msg: Message): void {
// This is tricky to bypass the default Theia code.
// Otherwise, there is a warning when opening the dialog for the second time.
// You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
const disposables = new DisposableCollection();
if (!this.isMounted) {
// toggle the `isMounted` logic for the time being of the super call so that the `createRoot` does not run
this.isMounted = true;
disposables.push(Disposable.create(() => (this.isMounted = false)));
private _isOnCloseRequestInProgress = false;
override dispose(): void {
// There is a bug in Theia, and the React component's `componentWillUnmount` will not be called, as the Theia widget is already disposed when closing and reopening a dialog.
// Widget lifecycle issue in Theia: https://github.com/eclipse-theia/theia/issues/12093
// Bogus react widget lifecycle management PR: https://github.com/eclipse-theia/theia/pull/11687
// Do not call super. Do not let the Phosphor widget to be disposed on dialog close.
if (this._isOnCloseRequestInProgress) {
// Do not let the widget dispose on close.
return;
}
super.dispose();
}
// Always unset the `contentNodeRoot` so there is no double update when calling super.
const restoreContentNodeRoot = this.contentNodeRoot;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.contentNodeRoot as any) = undefined;
disposables.push(
Disposable.create(() => (this.contentNodeRoot = restoreContentNodeRoot))
);
protected override onCloseRequest(message: Message): void {
this._isOnCloseRequestInProgress = true;
try {
super.onUpdateRequest(msg);
super.onCloseRequest(message);
} finally {
disposables.dispose();
this._isOnCloseRequestInProgress = false;
}
// Use the patched rendering.
if (!this.isMounted) {
this.contentNodeRoot = createRoot(this.contentNode);
// Resetting the prop is missing from the Theia code.
// https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/browser/dialogs/react-dialog.tsx#L41-L47
this.isMounted = true;
}
this.contentNodeRoot?.render(<>{this.render()}</>);
}
}

View File

@ -1,5 +1,6 @@
import { nls } from '@theia/core/lib/common/nls';
// TODO: rename constants: `Unknown` should be `unknownLabel`, change `Later` to `laterLabel`, etc.
export const Unknown = nls.localize('arduino/common/unknown', 'Unknown');
export const Later = nls.localize('arduino/common/later', 'Later');
export const Updatable = nls.localize('arduino/common/updateable', 'Updatable');

View File

@ -0,0 +1,694 @@
import type { Mutable } from '@theia/core/lib/common/types';
import { Unknown } from '../nls';
import type { Defined } from '../types';
import { naturalCompare } from '../utils';
import {
BoardIdentifier,
boardIdentifierComparator,
boardIdentifierEquals,
BoardsConfig,
DetectedPort,
DetectedPorts,
emptyBoardsConfig,
findMatchingPortIndex,
isBoardIdentifier,
isDefinedBoardsConfig,
Port,
PortIdentifier,
portIdentifierEquals,
portProtocolComparator,
selectBoard,
unconfirmedBoard,
notConnected,
boardIdentifierLabel,
} from './boards-service';
/**
* Representation of a detected port with an optional board.
*/
export interface BoardListItem {
readonly port: Port;
readonly board?: BoardIdentifier;
}
/**
* Representation of a detected port with multiple discovered boards on the same port. For example Arduino Nano ESP32 from `esp32:esp32:nano_nora` and `arduino:esp32:nano_nora`.
* If multiple boards are detected, but the board names are the same, the `board` will be the `first` element of the `boards` array.
* If multiple boards are detected, but the board names are not identical, the `board` will be missing.
*/
export interface MultiBoardsBoardListItem extends BoardListItem {
readonly boards: readonly BoardIdentifier[];
}
function findUniqueBoardName(
item: MultiBoardsBoardListItem
): string | undefined {
const distinctNames = new Set(item.boards.map(({ name }) => name));
if (distinctNames.size === 1) {
const name = Array.from(distinctNames.keys()).shift();
if (name) {
return name;
}
}
return undefined;
}
export function isMultiBoardsBoardListItem(
arg: unknown
): arg is MultiBoardsBoardListItem {
return (
isBoardListItem(arg) &&
(<MultiBoardsBoardListItem>arg).boards !== undefined &&
Array.isArray((<MultiBoardsBoardListItem>arg).boards) &&
Boolean((<MultiBoardsBoardListItem>arg).boards.length) &&
(<MultiBoardsBoardListItem>arg).boards.every(isBoardIdentifier)
);
}
/**
* Base inferred board list item type.
* The the type of the inferred board can be:
* - manually specified board for a detected port where no boards were discovered,
* - the board has been overridden for detected port discovered board pair.
*/
export type InferredBoardListItem =
| ManuallySelectedBoardListItem
| BoardOverriddenBoardListItem;
/**
* No boards have been discovered for a detected port, it has been manually selected by the user.
*/
export interface ManuallySelectedBoardListItem extends BoardListItem {
readonly inferredBoard: BoardIdentifier;
readonly type: 'manually-selected';
}
/**
* One or more boards have been discovered for a detected port, but the board has been overridden by a manual action.
*/
export interface BoardOverriddenBoardListItem extends BoardListItem {
readonly inferredBoard: BoardIdentifier;
readonly board: BoardIdentifier;
readonly type: 'board-overridden';
}
export function isBoardListItem(arg: unknown): arg is BoardListItem {
return (
Boolean(arg) &&
typeof arg === 'object' &&
(<BoardListItem>arg).port !== undefined &&
Port.is((<BoardListItem>arg).port) &&
((<BoardListItem>arg).board === undefined ||
((<BoardListItem>arg).board !== undefined &&
isBoardIdentifier((<BoardListItem>arg).board)))
);
}
export function boardListItemEquals(
left: BoardListItem,
right: BoardListItem
): boolean {
if (portIdentifierEquals(left.port, right.port)) {
const leftBoard = getBoardOrInferredBoard(left);
const rightBoard = getBoardOrInferredBoard(right);
if (boardIdentifierEquals(leftBoard, rightBoard)) {
const leftInferredBoard = getInferredBoardOrBoard(left);
const rightInferredBoard = getInferredBoardOrBoard(right);
return boardIdentifierEquals(leftInferredBoard, rightInferredBoard);
}
}
return false;
}
export interface BoardListItemWithBoard extends BoardListItem {
readonly board: BoardIdentifier;
}
function getBoardOrInferredBoard(
item: BoardListItem
): BoardIdentifier | undefined {
let board: BoardIdentifier | undefined = undefined;
board = item.board;
if (!board && isInferredBoardListItem(item)) {
board = item.inferredBoard;
}
return board;
}
export function getInferredBoardOrBoard(
item: BoardListItem
): BoardIdentifier | undefined {
if (isInferredBoardListItem(item)) {
return item.inferredBoard;
}
return item.board;
}
export function isInferredBoardListItem(
arg: unknown
): arg is InferredBoardListItem {
return (
isBoardListItem(arg) &&
(<InferredBoardListItem>arg).type !== undefined &&
isInferenceType((<InferredBoardListItem>arg).type) &&
(<InferredBoardListItem>arg).inferredBoard !== undefined &&
isBoardIdentifier((<InferredBoardListItem>arg).inferredBoard)
);
}
/**
* Stores historical info about boards manually specified for detected boards. The key are generated with `Port#keyOf`.
*/
export type BoardListHistory = Readonly<Record<string, BoardIdentifier>>;
export function isBoardListHistory(arg: unknown): arg is BoardListHistory {
return (
Boolean(arg) &&
typeof arg === 'object' &&
Object.entries(<object>arg).every(([, value]) => isBoardIdentifier(value))
);
}
const inferenceTypeLiterals = [
/**
* The user has manually selected the board (FQBN) for the detected port of a 3rd party board (no matching boards were detected by the CLI for the port)
*/
'manually-selected',
/**
* The user has manually edited the detected FQBN of a recognized board from a detected port (there are matching boards for a detected port, but the user decided to use another FQBN)
*/
'board-overridden',
] as const;
type InferenceType = (typeof inferenceTypeLiterals)[number];
function isInferenceType(arg: unknown): arg is InferenceType {
return (
typeof arg === 'string' &&
inferenceTypeLiterals.includes(<InferenceType>arg)
);
}
/**
* Compare precedence:
* 1. `BoardListItem#port#protocol`: `'serial'`, `'network'`, then natural compare of the `protocol` string.
* 1. `BoardListItem`s with a `board` comes before items without a `board`.
* 1. `BoardListItem#board`:
* 1. Items with `'arduino'` vendor ID in the `fqbn` come before other vendors.
* 1. Natural compare of the `name`.
* 1. If the `BoardListItem`s do not have a `board` property:
* 1. Ambiguous boards come before no boards.
* 1. `BoardListItem#port#address` natural compare is the fallback.
*/
function boardListItemComparator(
left: BoardListItem,
right: BoardListItem
): number {
// sort by port protocol
let result = portProtocolComparator(left.port, right.port);
if (result) {
return result;
}
// compare by board
result = boardIdentifierComparator(
getBoardOrInferredBoard(left),
getBoardOrInferredBoard(right)
);
if (result) {
return result;
}
// detected ports with multiple discovered boards come before any other unknown items
if (isMultiBoardsBoardListItem(left) && !isMultiBoardsBoardListItem(right)) {
return -1;
}
if (!isMultiBoardsBoardListItem(left) && !isMultiBoardsBoardListItem(right)) {
return 1;
}
// ambiguous boards with a unique board name comes first than other ambiguous ones
if (isMultiBoardsBoardListItem(left) && isMultiBoardsBoardListItem(right)) {
const leftUniqueName = findUniqueBoardName(left);
const rightUniqueName = findUniqueBoardName(right);
if (leftUniqueName && !rightUniqueName) {
return -1;
}
if (!leftUniqueName && rightUniqueName) {
return 1;
}
if (leftUniqueName && rightUniqueName) {
return naturalCompare(leftUniqueName, rightUniqueName);
}
}
// fallback compare based on the address
return naturalCompare(left.port.address, right.port.address);
}
/**
* What is shown in the UI for the entire board list.
*/
export interface BoardListLabels {
readonly boardLabel: string;
readonly portProtocol: string | undefined;
readonly tooltip: string;
/**
* The client's board+port selection matches with one of the board list items.
*/
readonly selected: boolean;
}
function createBoardListLabels(
boardsConfig: BoardsConfig,
allPorts: readonly DetectedPort[],
selectedItem: BoardListItem | undefined
): BoardListLabels {
const { selectedBoard, selectedPort } = boardsConfig;
const boardLabel = selectedBoard?.name || selectBoard;
let tooltip = '';
if (!selectedBoard && !selectedPort) {
tooltip = selectBoard;
} else {
if (selectedBoard) {
tooltip += boardIdentifierLabel(selectedBoard);
}
if (selectedPort) {
if (tooltip) {
tooltip += '\n';
}
tooltip += selectedPort.address;
const index = findMatchingPortIndex(selectedPort, allPorts);
if (index < 0) {
tooltip += ` ${notConnected}`;
}
}
}
return {
boardLabel,
portProtocol: selectedBoard ? selectedPort?.protocol : undefined,
tooltip,
selected: Boolean(selectedItem),
};
}
/**
* What is show in the UI for a particular board with all its refinements, fallbacks, and tooltips.
*/
export interface BoardListItemLabels {
readonly boardLabel: string;
readonly boardLabelWithFqbn: string;
readonly portLabel: string;
readonly portProtocol: string;
readonly tooltip: string;
}
export interface BoardListItemUI extends BoardListItem {
readonly labels: BoardListItemLabels;
readonly defaultAction: BoardListItemAction;
readonly otherActions: Readonly<{
edit?: EditBoardsConfigAction;
revert?: SelectBoardsConfigAction;
}>;
}
function createBoardListItemLabels(item: BoardListItem): BoardListItemLabels {
const { port } = item;
const portLabel = port.address;
const portProtocol = port.protocol;
let board = item.board; // use default board label if any
if (isInferredBoardListItem(item)) {
board = item.inferredBoard; // inferred board overrides any discovered boards
}
// if the board is still missing, maybe it's ambiguous
if (!board && isMultiBoardsBoardListItem(item)) {
const name =
// get a unique board name
findUniqueBoardName(item) ??
// or fall back to something else than unknown board
unconfirmedBoard;
board = { name, fqbn: undefined };
}
const boardLabel = board?.name ?? Unknown;
let boardLabelWithFqbn = boardLabel;
if (board?.fqbn) {
boardLabelWithFqbn += ` (${board.fqbn})`;
}
return {
boardLabel,
boardLabelWithFqbn,
portLabel,
portProtocol,
tooltip: `${boardLabelWithFqbn}\n${portLabel}`,
};
}
/**
* A list of boards discovered by the Arduino CLI. With the `board list --watch` gRPC equivalent command,
* the CLI provides a `1..*` mapping between a port and the matching boards list. This type inverts the mapping
* and makes a `1..1` association between a board identifier and the port it belongs to.
*/
export interface BoardList {
readonly labels: BoardListLabels;
/**
* All detected ports with zero to many boards and optional inferred information based on historical selection/usage.
*/
readonly items: readonly BoardListItemUI[];
/**
* A snapshot of the board and port configuration this board list has been initialized with.
*/
readonly boardsConfig: Readonly<BoardsConfig>;
/**
* Index of the board+port item that is currently "selected". A board list item is selected, if matches the board+port combination of `boardsConfig`.
*/
readonly selectedIndex: number;
/**
* Contains all boards recognized from the detected port, and an optional unrecognized one that is derived from the detected port and the `initParam#selectedBoard`.
*/
readonly boards: readonly (BoardListItemWithBoard | InferredBoardListItem)[];
/**
* If `predicate` is not defined, no ports are filtered.
*/
ports(
predicate?: (detectedPort: DetectedPort) => boolean
): readonly DetectedPort[] & Readonly<{ matchingIndex: number }>;
/**
* Sugar for `#ports` with additional grouping based on the port `protocol`.
*/
portsGroupedByProtocol(): Readonly<
Record<'serial' | 'network' | string, ReturnType<BoardList['ports']>>
>;
/**
* For dumping the current state of board list for debugging purposes.
*/
toString(): string;
}
export type SelectBoardsConfigActionParams = Readonly<Defined<BoardsConfig>>;
export interface SelectBoardsConfigAction {
readonly type: 'select-boards-config';
readonly params: SelectBoardsConfigActionParams;
}
export interface EditBoardsConfigActionParams {
readonly portToSelect?: PortIdentifier;
readonly boardToSelect?: BoardIdentifier;
readonly query?: string;
readonly searchSet?: readonly BoardIdentifier[];
}
export interface EditBoardsConfigAction {
readonly type: 'edit-boards-config';
readonly params: EditBoardsConfigActionParams;
}
export type BoardListItemAction =
| SelectBoardsConfigAction
| EditBoardsConfigAction;
export function createBoardList(
detectedPorts: DetectedPorts,
boardsConfig: Readonly<BoardsConfig> = emptyBoardsConfig(),
boardListHistory: BoardListHistory = {}
): BoardList {
const items: BoardListItemUI[] = [];
for (const detectedPort of Object.values(detectedPorts)) {
const item = createBoardListItemUI(detectedPort, boardListHistory);
items.push(item);
}
items.sort(boardListItemComparator);
const selectedIndex = findSelectedIndex(boardsConfig, items);
const boards = collectBoards(items);
const allPorts = collectPorts(items, detectedPorts);
const labels = createBoardListLabels(
boardsConfig,
allPorts,
items[selectedIndex]
);
return {
labels,
items,
boardsConfig,
boards,
selectedIndex,
ports(predicate?: (detectedPort: DetectedPort) => boolean) {
return filterPorts(allPorts, boardsConfig.selectedPort, predicate);
},
portsGroupedByProtocol() {
const _allPorts = filterPorts(allPorts, boardsConfig.selectedPort);
return portsGroupedByProtocol(_allPorts);
},
toString() {
return JSON.stringify(
{
labels,
detectedPorts,
boardsConfig,
items,
selectedIndex,
boardListHistory,
},
null,
2
);
},
};
}
function portsGroupedByProtocol(
allPorts: ReturnType<BoardList['ports']>
): ReturnType<BoardList['portsGroupedByProtocol']> {
const result: Record<string, DetectedPort[] & { matchingIndex: number }> = {};
for (const detectedPort of allPorts) {
const protocol = detectedPort.port.protocol;
if (!result[protocol]) {
result[protocol] = Object.assign([], {
matchingIndex: -1,
});
}
const portsOnProtocol = result[protocol];
portsOnProtocol.push(detectedPort);
}
const matchItem = allPorts[allPorts.matchingIndex];
// the cached match index is per all ports. Here, IDE2 needs to adjust the match index per grouped protocol
if (matchItem) {
const matchProtocol = matchItem.port.protocol;
const matchPorts = result[matchProtocol];
matchPorts.matchingIndex = matchPorts.indexOf(matchItem);
}
return result;
}
function filterPorts(
allPorts: readonly DetectedPort[],
selectedPort: PortIdentifier | undefined,
predicate: (detectedPort: DetectedPort) => boolean = () => true
): ReturnType<BoardList['ports']> {
const ports = allPorts.filter(predicate);
const matchingIndex = findMatchingPortIndex(selectedPort, ports);
return Object.assign(ports, { matchingIndex });
}
function collectPorts(
items: readonly BoardListItem[],
detectedPorts: DetectedPorts
): DetectedPort[] {
const allPorts: DetectedPort[] = [];
// to keep the order or the detected ports
const visitedPortKeys = new Set<string>();
for (let i = 0; i < items.length; i++) {
const { port } = items[i];
const portKey = Port.keyOf(port);
if (!visitedPortKeys.has(portKey)) {
visitedPortKeys.add(portKey);
const detectedPort = detectedPorts[portKey];
if (detectedPort) {
allPorts.push(detectedPort);
}
}
}
return allPorts;
}
function collectBoards(
items: readonly BoardListItem[]
): readonly (BoardListItemWithBoard | InferredBoardListItem)[] {
const boards: (BoardListItemWithBoard | InferredBoardListItem)[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (isInferredBoardListItem(item)) {
boards.push(item);
} else if (item.board?.fqbn) {
boards.push(<Required<BoardListItem>>item);
}
}
return boards;
}
function findSelectedIndex(
boardsConfig: BoardsConfig,
items: readonly BoardListItem[]
): number {
if (!isDefinedBoardsConfig(boardsConfig)) {
return -1;
}
const length = items.length;
const { selectedPort, selectedBoard } = boardsConfig;
const portKey = Port.keyOf(selectedPort);
// find the exact match of the board and port combination
for (let index = 0; index < length; index++) {
const item = items[index];
const { board, port } = item;
if (!board) {
continue;
}
if (
Port.keyOf(port) === portKey &&
boardIdentifierEquals(board, selectedBoard)
) {
return index;
}
}
// find match from inferred board
for (let index = 0; index < length; index++) {
const item = items[index];
if (!isInferredBoardListItem(item)) {
continue;
}
const { inferredBoard, port } = item;
if (
Port.keyOf(port) === portKey &&
boardIdentifierEquals(inferredBoard, boardsConfig.selectedBoard)
) {
return index;
}
}
return -1;
}
function createBoardListItemUI(
detectedPort: DetectedPort,
boardListHistory: BoardListHistory
): BoardListItemUI {
const item = createBoardListItem(detectedPort, boardListHistory);
const labels = createBoardListItemLabels(item);
const defaultAction = createDefaultAction(item);
const otherActions = createOtherActions(item);
return Object.assign(item, { labels, defaultAction, otherActions });
}
function createBoardListItem(
detectedPort: DetectedPort,
boardListHistory: BoardListHistory
): BoardListItem {
const { port, boards } = detectedPort;
// boards with arduino vendor should come first
boards?.sort(boardIdentifierComparator);
const portKey = Port.keyOf(port);
const inferredBoard = boardListHistory[portKey];
if (!boards?.length) {
let unknownItem: BoardListItem | InferredBoardListItem = { port };
// Infer unrecognized boards from the history
if (inferredBoard) {
unknownItem = {
...unknownItem,
inferredBoard,
type: 'manually-selected',
};
}
return unknownItem;
} else if (boards.length === 1) {
const board = boards[0];
let detectedItem: BoardListItemWithBoard | InferredBoardListItem = {
port,
board,
};
if (
inferredBoard &&
// ignore the inferred item if it's the same as the discovered board
!boardIdentifierEquals(board, inferredBoard)
) {
detectedItem = {
...detectedItem,
inferredBoard,
type: 'board-overridden',
};
}
return detectedItem;
} else {
let ambiguousItem: MultiBoardsBoardListItem | InferredBoardListItem = {
port,
boards,
};
if (inferredBoard) {
ambiguousItem = {
...ambiguousItem,
inferredBoard,
type: 'manually-selected',
};
}
return ambiguousItem;
}
}
function createDefaultAction(item: BoardListItem): BoardListItemAction {
if (isInferredBoardListItem(item)) {
return createSelectAction({
selectedBoard: item.inferredBoard,
selectedPort: item.port,
});
}
if (item.board) {
return createSelectAction({
selectedBoard: item.board,
selectedPort: item.port,
});
}
return createEditAction(item);
}
function createOtherActions(
item: BoardListItem
): BoardListItemUI['otherActions'] {
if (isInferredBoardListItem(item)) {
const edit = createEditAction(item);
if (item.type === 'board-overridden') {
const revert = createSelectAction({
selectedBoard: item.board,
selectedPort: item.port,
});
return { edit, revert };
}
return { edit };
}
return {};
}
function createSelectAction(
params: SelectBoardsConfigActionParams
): SelectBoardsConfigAction {
return {
type: 'select-boards-config',
params,
};
}
function createEditAction(item: BoardListItem): EditBoardsConfigAction {
const params: Mutable<EditBoardsConfigActionParams> = {
portToSelect: item.port,
};
if (isMultiBoardsBoardListItem(item)) {
const uniqueBoardName = findUniqueBoardName(item);
params.query = uniqueBoardName ?? '';
params.searchSet = item.boards;
} else if (isInferredBoardListItem(item)) {
params.query = item.inferredBoard.name;
} else if (item.board) {
params.query = item.board.name;
} else {
params.query = '';
}
return {
type: 'edit-boards-config',
params,
};
}

View File

@ -1,8 +1,6 @@
import { naturalCompare } from './../utils';
import { Searchable } from './searchable';
import { Installable } from './installable';
import { ArduinoComponent } from './arduino-component';
import { nls } from '@theia/core/lib/common/nls';
import type { MaybePromise } from '@theia/core/lib/common/types';
import type URI from '@theia/core/lib/common/uri';
import {
All,
Contributed,
@ -10,131 +8,46 @@ import {
Type as TypeLabel,
Updatable,
} from '../nls';
import URI from '@theia/core/lib/common/uri';
import { MaybePromise } from '@theia/core/lib/common/types';
import type { Defined } from '../types';
import { naturalCompare } from './../utils';
import type { ArduinoComponent } from './arduino-component';
import type { BoardList } from './board-list';
import { Installable } from './installable';
import { Searchable } from './searchable';
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
export namespace AvailablePorts {
export function groupByProtocol(
availablePorts: AvailablePorts
): Map<string, AvailablePorts> {
const grouped = new Map<string, AvailablePorts>();
for (const portID of Object.keys(availablePorts)) {
const [port, boards] = availablePorts[portID];
let ports = grouped.get(port.protocol);
if (!ports) {
ports = {} as AvailablePorts;
}
ports[portID] = [port, boards];
grouped.set(port.protocol, ports);
}
return grouped;
}
export function split(
state: AvailablePorts
): Readonly<{ boards: Board[]; ports: Port[] }> {
const availablePorts: Port[] = [];
const attachedBoards: Board[] = [];
for (const key of Object.keys(state)) {
const [port, boards] = state[key];
availablePorts.push(port);
attachedBoards.push(...boards);
}
return {
boards: attachedBoards,
ports: availablePorts,
};
}
export interface DetectedPort {
readonly port: Port;
readonly boards?: Pick<Board, 'name' | 'fqbn'>[];
}
export interface AttachedBoardsChangeEvent {
readonly oldState: Readonly<{ boards: Board[]; ports: Port[] }>;
readonly newState: Readonly<{ boards: Board[]; ports: Port[] }>;
readonly uploadInProgress: boolean;
export function findMatchingPortIndex(
toFind: PortIdentifier | undefined,
ports: readonly DetectedPort[] | readonly Port[]
): number {
if (!toFind) {
return -1;
}
const toFindPortKey = Port.keyOf(toFind);
return ports.findIndex((port) => Port.keyOf(port) === toFindPortKey);
}
export namespace AttachedBoardsChangeEvent {
export function isEmpty(event: AttachedBoardsChangeEvent): boolean {
const { detached, attached } = diff(event);
return (
!!detached.boards.length &&
!!detached.ports.length &&
!!attached.boards.length &&
!!attached.ports.length
);
}
export function toString(event: AttachedBoardsChangeEvent): string {
const rows: string[] = [];
if (!isEmpty(event)) {
const { attached, detached } = diff(event);
const visitedAttachedPorts: Port[] = [];
const visitedDetachedPorts: Port[] = [];
for (const board of attached.boards) {
const port = board.port ? ` on ${Port.toString(board.port)}` : '';
rows.push(` - Attached board: ${Board.toString(board)}${port}`);
if (board.port) {
visitedAttachedPorts.push(board.port);
}
}
for (const board of detached.boards) {
const port = board.port ? ` from ${Port.toString(board.port)}` : '';
rows.push(` - Detached board: ${Board.toString(board)}${port}`);
if (board.port) {
visitedDetachedPorts.push(board.port);
}
}
for (const port of attached.ports) {
if (!visitedAttachedPorts.find((p) => Port.sameAs(port, p))) {
rows.push(` - New port is available on ${Port.toString(port)}`);
}
}
for (const port of detached.ports) {
if (!visitedDetachedPorts.find((p) => Port.sameAs(port, p))) {
rows.push(` - Port is no longer available on ${Port.toString(port)}`);
}
}
}
return rows.length ? rows.join('\n') : 'No changes.';
}
/**
* The closest representation what the Arduino CLI detects with the `board list --watch` gRPC equivalent.
* The keys are unique identifiers generated from the port object (via `Port#keyOf`).
* The values are the detected ports with all their optional `properties` and matching board list.
*/
export type DetectedPorts = Readonly<Record<string, DetectedPort>>;
export function diff(event: AttachedBoardsChangeEvent): Readonly<{
attached: {
boards: Board[];
ports: Port[];
};
detached: {
boards: Board[];
ports: Port[];
};
}> {
// In `lefts` AND not in `rights`.
const diff = <T>(
lefts: T[],
rights: T[],
sameAs: (left: T, right: T) => boolean
) => {
return lefts.filter(
(left) => rights.findIndex((right) => sameAs(left, right)) === -1
);
};
const { boards: newBoards } = event.newState;
const { boards: oldBoards } = event.oldState;
const { ports: newPorts } = event.newState;
const { ports: oldPorts } = event.oldState;
const boardSameAs = (left: Board, right: Board) =>
Board.sameAs(left, right);
const portSameAs = (left: Port, right: Port) => Port.sameAs(left, right);
return {
detached: {
boards: diff(oldBoards, newBoards, boardSameAs),
ports: diff(oldPorts, newPorts, portSameAs),
},
attached: {
boards: diff(newBoards, oldBoards, boardSameAs),
ports: diff(newPorts, oldPorts, portSameAs),
},
};
export function resolveDetectedPort(
port: PortIdentifier,
detectedPorts: DetectedPorts
): Port | undefined {
const portKey = Port.keyOf(port);
const detectedPort = detectedPorts[portKey];
if (detectedPort) {
return detectedPort.port;
}
return undefined;
}
export const BoardsServicePath = '/services/boards-service';
@ -152,14 +65,17 @@ export interface BoardsService
*/
skipPostInstall?: boolean;
}): Promise<void>;
getState(): Promise<AvailablePorts>;
getDetectedPorts(): Promise<DetectedPorts>;
getBoardDetails(options: { fqbn: string }): Promise<BoardDetails | undefined>;
getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>;
getBoardPackage(options: {
id: string /* TODO: change to PlatformIdentifier type? */;
}): Promise<BoardsPackage | undefined>;
getContainerBoardPackage(options: {
fqbn: string;
}): Promise<BoardsPackage | undefined>;
searchBoards({ query }: { query?: string }): Promise<BoardWithPackage[]>;
getInstalledBoards(): Promise<BoardWithPackage[]>;
getInstalledPlatforms(): Promise<BoardsPackage[]>;
getBoardUserFields(options: {
fqbn: string;
protocol: string;
@ -180,7 +96,7 @@ export namespace BoardSearch {
'Partner',
'Arduino@Heart',
] as const;
export type Type = typeof TypeLiterals[number];
export type Type = (typeof TypeLiterals)[number];
export namespace Type {
export function is(arg: unknown): arg is Type {
return typeof arg === 'string' && TypeLiterals.includes(arg as Type);
@ -252,9 +168,9 @@ export namespace Port {
export namespace Properties {
export function create(
properties: [string, string][] | undefined
): Properties {
if (!properties) {
return {};
): Properties | undefined {
if (!properties || !properties.length) {
return undefined;
}
return properties.reduce((acc, curr) => {
const [key, value] = curr;
@ -282,10 +198,13 @@ export namespace Port {
}
/**
* Key is the combination of address and protocol formatted like `'${address}|${protocol}'` used to uniquely identify a port.
* Key is the combination of address and protocol formatted like `'arduino+${protocol}://${address}'` used to uniquely identify a port.
*/
export function keyOf({ address, protocol }: Port): string {
return `${address}|${protocol}`;
export function keyOf(port: PortIdentifier | Port | DetectedPort): string {
if (isPortIdentifier(port)) {
return `arduino+${port.protocol}://${port.address}`;
}
return keyOf(port.port);
}
export function toString({ addressLabel, protocolLabel }: Port): string {
@ -297,16 +216,8 @@ export namespace Port {
// 1. Serial
// 2. Network
// 3. Other protocols
if (left.protocol === 'serial' && right.protocol !== 'serial') {
return -1;
} else if (left.protocol !== 'serial' && right.protocol === 'serial') {
return 1;
} else if (left.protocol === 'network' && right.protocol !== 'network') {
return -1;
} else if (left.protocol !== 'network' && right.protocol === 'network') {
return 1;
}
return naturalCompare(left.address!, right.address!);
const priorityResult = portProtocolComparator(left, right);
return priorityResult || naturalCompare(left.address, right.address);
}
export function sameAs(
@ -324,35 +235,28 @@ export namespace Port {
/**
* All ports with `'serial'` or `'network'` `protocol`, or any other port `protocol` that has at least one recognized board connected to.
*/
export function visiblePorts(
boardsHaystack: ReadonlyArray<Board>
): (port: Port) => boolean {
return (port: Port) => {
if (port.protocol === 'serial' || port.protocol === 'network') {
// Allow all `serial` and `network` boards.
// IDE2 must support better label for unrecognized `network` boards: https://github.com/arduino/arduino-ide/issues/1331
return true;
}
// All other ports with different protocol are
// only shown if there is a recognized board
// connected
for (const board of boardsHaystack) {
if (board.port?.address === port.address) {
return true;
}
}
return false;
};
export function isVisiblePort(detectedPort: DetectedPort): boolean {
const protocol = detectedPort.port.protocol;
if (protocol === 'serial' || protocol === 'network') {
// Allow all `serial` and `network` boards.
// IDE2 must support better label for unrecognized `network` boards: https://github.com/arduino/arduino-ide/issues/1331
return true;
}
// All other ports with different protocol are
// only shown if there is a recognized board
// connected
return Boolean(detectedPort?.boards?.length);
}
export namespace Protocols {
// IDE2 does not want to handle any other port protocols in a special way
export const KnownProtocolLiterals = ['serial', 'network'] as const;
export type KnownProtocol = typeof KnownProtocolLiterals[number];
export type KnownProtocol = (typeof KnownProtocolLiterals)[number];
export namespace KnownProtocol {
export function is(protocol: unknown): protocol is KnownProtocol {
return (
typeof protocol === 'string' &&
KnownProtocolLiterals.indexOf(protocol as KnownProtocol) >= 0
KnownProtocolLiterals.includes(protocol as KnownProtocol)
);
}
}
@ -377,29 +281,12 @@ export namespace BoardsPackage {
export function equals(left: BoardsPackage, right: BoardsPackage): boolean {
return left.id === right.id;
}
export function contains(
selectedBoard: Board,
{ id, boards }: BoardsPackage
): boolean {
if (boards.some((board) => Board.sameAs(board, selectedBoard))) {
return true;
}
if (selectedBoard.fqbn) {
const [platform, architecture] = selectedBoard.fqbn.split(':');
if (platform && architecture) {
return `${platform}:${architecture}` === id;
}
}
return false;
}
}
export interface Board {
readonly name: string;
readonly fqbn?: string;
readonly port?: Port;
}
/**
* @deprecated user `BoardIdentifier` instead.
*/
export type Board = BoardIdentifier;
export interface BoardUserField {
readonly toolId: string;
@ -411,14 +298,19 @@ export interface BoardUserField {
export interface BoardWithPackage extends Board {
readonly packageName: string;
readonly packageId: string;
readonly packageId: PlatformIdentifier;
readonly manuallyInstalled: boolean;
}
export namespace BoardWithPackage {
export function is(
board: Board & Partial<{ packageName: string; packageId: string }>
): board is BoardWithPackage {
return !!board.packageId && !!board.packageName;
export function is(arg: unknown): arg is BoardWithPackage {
return (
isBoardIdentifier(arg) &&
(<BoardWithPackage>arg).packageName !== undefined &&
typeof (<BoardWithPackage>arg).packageName === 'string' &&
isPlatformIdentifier((<BoardWithPackage>arg).packageId) &&
(<BoardWithPackage>arg).manuallyInstalled !== undefined &&
typeof (<BoardWithPackage>arg).manuallyInstalled === 'boolean'
);
}
}
@ -456,18 +348,6 @@ export interface ConfigOption {
readonly values: ConfigValue[];
}
export namespace ConfigOption {
export function is(arg: any): arg is ConfigOption {
return (
!!arg &&
'option' in arg &&
'label' in arg &&
'values' in arg &&
typeof arg['option'] === 'string' &&
typeof arg['label'] === 'string' &&
Array.isArray(arg['values'])
);
}
/**
* Appends the configuration options to the `fqbn` argument.
* Throws an error if the `fqbn` does not have the `segment(':'segment)*` format.
@ -555,24 +435,15 @@ export namespace Board {
return left.name === right.name && left.fqbn === right.fqbn;
}
export function hardwareIdEquals(left: Board, right: Board): boolean {
if (left.port && right.port) {
const { hardwareId: leftHardwareId } = left.port;
const { hardwareId: rightHardwareId } = right.port;
if (leftHardwareId && rightHardwareId) {
return leftHardwareId === rightHardwareId;
}
}
return false;
}
export function sameAs(left: Board, right: string | Board): boolean {
export function sameAs(
left: BoardIdentifier,
right: string | BoardIdentifier
): boolean {
// How to associate a selected board with one of the available cores: https://typefox.slack.com/archives/CJJHJCJSJ/p1571142327059200
// 1. How to use the FQBN if any and infer the package ID from it: https://typefox.slack.com/archives/CJJHJCJSJ/p1571147549069100
// 2. How to trim the `/Genuino` from the name: https://arduino.slack.com/archives/CJJHJCJSJ/p1571146951066800?thread_ts=1571142327.059200&cid=CJJHJCJSJ
const other = typeof right === 'string' ? { name: right } : right;
const other: BoardIdentifier =
typeof right === 'string' ? { name: right, fqbn: undefined } : right;
if (left.fqbn && other.fqbn) {
return left.fqbn === other.fqbn;
}
@ -594,7 +465,7 @@ export namespace Board {
}
export function toString(
board: Board,
board: BoardIdentifier,
options: { useFqbn: boolean } = { useFqbn: true }
): string {
const fqbn =
@ -607,14 +478,15 @@ export namespace Board {
selected: boolean;
missing: boolean;
packageName: string;
packageId: string;
packageId: PlatformIdentifier;
details?: string;
manuallyInstalled: boolean;
}>;
export function decorateBoards(
selectedBoard: Board | undefined,
selectedBoard: BoardIdentifier | BoardWithPackage | undefined,
boards: Array<BoardWithPackage>
): Array<Detailed> {
let foundSelected = false;
// Board names are not unique. We show the corresponding core name as a detail.
// https://github.com/arduino/arduino-cli/pull/294#issuecomment-513764948
const distinctBoardNames = new Map<string, number>();
@ -622,21 +494,42 @@ export namespace Board {
const counter = distinctBoardNames.get(name) || 0;
distinctBoardNames.set(name, counter + 1);
}
// Due to the non-unique board names, we have to check the package name as well.
const selected = (board: BoardWithPackage) => {
if (!!selectedBoard) {
if (Board.equals(board, selectedBoard)) {
if ('packageName' in selectedBoard) {
return board.packageName === (selectedBoard as any).packageName;
}
if ('packageId' in selectedBoard) {
return board.packageId === (selectedBoard as any).packageId;
}
return true;
const selectedBoardPackageId = selectedBoard
? createPlatformIdentifier(selectedBoard)
: undefined;
const selectedBoardFqbn = selectedBoard?.fqbn;
// Due to the non-unique board names, IDE2 has to check the package name when boards are not installed and the FQBN is absent.
const isSelected = (board: BoardWithPackage) => {
if (!selectedBoard) {
return false;
}
if (foundSelected) {
return false;
}
let selected = false;
if (board.fqbn && selectedBoardFqbn) {
if (boardIdentifierEquals(board, selectedBoard)) {
selected = true;
}
}
return false;
if (!selected) {
if (board.name === selectedBoard.name) {
if (selectedBoardPackageId) {
const boardPackageId = createPlatformIdentifier(board);
if (boardPackageId) {
if (
platformIdentifierEquals(boardPackageId, selectedBoardPackageId)
) {
selected = true;
}
}
}
}
}
if (selected) {
foundSelected = true;
}
return selected;
};
return boards.map((board) => ({
...board,
@ -644,7 +537,7 @@ export namespace Board {
(distinctBoardNames.get(board.name) || 0) > 1
? ` - ${board.packageName}`
: undefined,
selected: selected(board),
selected: isSelected(board),
missing: !installed(board),
}));
}
@ -674,11 +567,267 @@ export function sanitizeFqbn(fqbn: string | undefined): string | undefined {
return `${vendor}:${arch}:${id}`;
}
export interface BoardConfig {
selectedBoard?: Board;
selectedPort?: Port;
export type PlatformIdentifier = Readonly<{ vendorId: string; arch: string }>;
export function createPlatformIdentifier(
board: BoardWithPackage
): PlatformIdentifier;
export function createPlatformIdentifier(
board: BoardIdentifier
): PlatformIdentifier | undefined;
export function createPlatformIdentifier(
fqbn: string
): PlatformIdentifier | undefined;
export function createPlatformIdentifier(
arg: BoardIdentifier | BoardWithPackage | string
): PlatformIdentifier | undefined {
if (BoardWithPackage.is(arg)) {
return arg.packageId;
}
const toSplit = typeof arg === 'string' ? arg : arg.fqbn;
if (toSplit) {
const [vendorId, arch] = toSplit.split(':');
if (vendorId && arch) {
return { vendorId, arch };
}
}
return undefined;
}
export function isPlatformIdentifier(arg: unknown): arg is PlatformIdentifier {
return (
Boolean(arg) &&
typeof arg === 'object' &&
(<PlatformIdentifier>arg).vendorId !== undefined &&
typeof (<PlatformIdentifier>arg).vendorId === 'string' &&
(<PlatformIdentifier>arg).arch !== undefined &&
typeof (<PlatformIdentifier>arg).arch === 'string'
);
}
export function serializePlatformIdentifier({
vendorId,
arch,
}: PlatformIdentifier): string {
return `${vendorId}:${arch}`;
}
export function platformIdentifierEquals(
left: PlatformIdentifier,
right: PlatformIdentifier
) {
return left.vendorId === right.vendorId && left.arch === right.arch;
}
/**
* Bare minimum information to identify port.
*/
export type PortIdentifier = Readonly<Pick<Port, 'protocol' | 'address'>>;
export function portIdentifierEquals(
left: PortIdentifier | undefined,
right: PortIdentifier | undefined
): boolean {
if (!left) {
return !right;
}
if (!right) {
return !left;
}
return left.protocol === right.protocol && left.address === right.address;
}
export function isPortIdentifier(arg: unknown): arg is PortIdentifier {
return (
Boolean(arg) &&
typeof arg === 'object' &&
(<PortIdentifier>arg).protocol !== undefined &&
typeof (<PortIdentifier>arg).protocol === 'string' &&
(<PortIdentifier>arg).address !== undefined &&
typeof (<PortIdentifier>arg).address === 'string'
);
}
// the smaller the number, the higher the priority
const portProtocolPriorities: Record<string, number> = {
serial: 0,
network: 1,
} as const;
/**
* See `boardListItemComparator`.
*/
export function portProtocolComparator(
left: PortIdentifier,
right: PortIdentifier
): number {
const leftPriority =
portProtocolPriorities[left.protocol] ?? Number.MAX_SAFE_INTEGER;
const rightPriority =
portProtocolPriorities[right.protocol] ?? Number.MAX_SAFE_INTEGER;
return leftPriority - rightPriority;
}
/**
* Lightweight information to identify a board.\
* \
* Note: the `name` property of the board identifier must never participate in the board's identification.
* Hence, it should only be used as the final fallback for the UI when the board's platform is not installed and only the board's name is available.
*/
export interface BoardIdentifier {
/**
* The name of the board. It's only purpose is to provide a fallback for the UI. Preferably do not use this property for any sophisticated logic. When
*/
readonly name: string;
/**
* The FQBN might contain boards config options if selected from the discovered ports (see [arduino/arduino-ide#1588](https://github.com/arduino/arduino-ide/issues/1588)).
*/
// TODO: decide whether to persist the boards config if any
readonly fqbn: string | undefined;
}
export function isBoardIdentifier(arg: unknown): arg is BoardIdentifier {
return (
Boolean(arg) &&
typeof arg === 'object' &&
(<BoardIdentifier>arg).name !== undefined &&
typeof (<BoardIdentifier>arg).name === 'string' &&
((<BoardIdentifier>arg).fqbn === undefined ||
((<BoardIdentifier>arg).fqbn !== undefined &&
typeof (<BoardIdentifier>arg).fqbn === 'string'))
);
}
/**
* @param options if `looseFqbn` is `true`, FQBN config options are ignored. Hence, `{ name: 'x', fqbn: 'a:b:c:o1=v1 }` equals `{ name: 'y', fqbn: 'a:b:c' }`. It's `true` by default.
*/
export function boardIdentifierEquals(
left: BoardIdentifier | undefined,
right: BoardIdentifier | undefined,
options: { looseFqbn: boolean } = { looseFqbn: true }
): boolean {
if (!left) {
return !right;
}
if (!right) {
return !left;
}
if ((left.fqbn && !right.fqbn) || (!left.fqbn && right.fqbn)) {
// This can be very tricky when comparing boards
// the CLI's board search returns with falsy FQBN when the platform is not installed
// the CLI's board list returns with the full FQBN (for detected boards) even if the platform is not installed
// when there are multiple boards with the same name (Arduino Nano RP2040) from different platforms (Mbed Nano OS vs. the deprecated global Mbed OS)
// maybe add some 3rd party platform overhead (https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json)
// and it will get very tricky when comparing a board which has a FQBN and which does not.
return false; // TODO: This a strict now. Maybe compare name in the future.
}
if (left.fqbn && right.fqbn) {
const leftFqbn = options.looseFqbn ? sanitizeFqbn(left.fqbn) : left.fqbn;
const rightFqbn = options.looseFqbn ? sanitizeFqbn(right.fqbn) : right.fqbn;
return leftFqbn === rightFqbn;
}
// No more Genuino hack.
// https://github.com/arduino/arduino-ide/blob/f6a43254f5c416a2e4fa888875358336b42dd4d5/arduino-ide-extension/src/common/protocol/boards-service.ts#L572-L581
return left.name === right.name;
}
/**
* See `boardListItemComparator`.
*/
export function boardIdentifierComparator(
left: BoardIdentifier | undefined,
right: BoardIdentifier | undefined
): number {
if (!left) {
return right ? 1 : 0;
}
if (!right) {
return left ? -1 : 0;
}
let leftVendor: string | undefined = undefined;
let rightVendor: string | undefined = undefined;
if (left.fqbn) {
const [vendor] = left.fqbn.split(':');
leftVendor = vendor;
}
if (right.fqbn) {
const [vendor] = right.fqbn.split(':');
rightVendor = vendor;
}
if (leftVendor === 'arduino' && rightVendor !== 'arduino') {
return -1;
}
if (leftVendor !== 'arduino' && rightVendor === 'arduino') {
return 1;
}
return naturalCompare(left.name, right.name);
}
export function boardIdentifierLabel(
board: BoardIdentifier,
showFqbn = true
): string {
const { name, fqbn } = board;
let label = name;
if (fqbn && showFqbn) {
label += ` (${fqbn})`;
}
return label;
}
export interface BoardsConfig {
selectedBoard: BoardIdentifier | undefined;
selectedPort: PortIdentifier | undefined;
}
/**
* Creates a new board config object with `undefined` properties.
*/
export function emptyBoardsConfig(): BoardsConfig {
return {
selectedBoard: undefined,
selectedPort: undefined,
};
}
export function isDefinedBoardsConfig(
boardsConfig: BoardsConfig | undefined
): boardsConfig is Defined<BoardsConfig> {
if (!boardsConfig) {
return false;
}
return (
boardsConfig.selectedBoard !== undefined &&
boardsConfig.selectedPort !== undefined
);
}
export interface BoardIdentifierChangeEvent {
readonly previousSelectedBoard: BoardIdentifier | undefined;
readonly selectedBoard: BoardIdentifier | undefined;
}
export function isBoardIdentifierChangeEvent(
event: BoardsConfigChangeEvent
): event is BoardIdentifierChangeEvent {
return 'previousSelectedBoard' in event && 'selectedBoard' in event;
}
export interface PortIdentifierChangeEvent {
readonly previousSelectedPort: PortIdentifier | undefined;
readonly selectedPort: PortIdentifier | undefined;
}
export function isPortIdentifierChangeEvent(
event: BoardsConfigChangeEvent
): event is PortIdentifierChangeEvent {
return 'previousSelectedPort' in event && 'selectedPort' in event;
}
export type BoardsConfigChangeEvent =
| BoardIdentifierChangeEvent
| PortIdentifierChangeEvent
| (BoardIdentifierChangeEvent & PortIdentifierChangeEvent);
export interface BoardInfo {
/**
* Board name. Could be `'Unknown board`'.
@ -714,50 +863,57 @@ export const unknownBoard = nls.localize(
'arduino/board/unknownBoard',
'Unknown board'
);
export const unconfirmedBoard = nls.localize(
'arduino/board/unconfirmedBoard',
'Unconfirmed board'
);
export const selectBoard = nls.localize(
'arduino/board/selectBoard',
'Select Board'
);
export const notConnected = nls.localize(
'arduino/common/notConnected',
'[not connected]'
);
/**
* The returned promise resolves to a `BoardInfo` if available to show in the UI or an info message explaining why showing the board info is not possible.
*/
export async function getBoardInfo(
selectedPort: Port | undefined,
availablePorts: MaybePromise<AvailablePorts>
boardListProvider: MaybePromise<BoardList>
): Promise<BoardInfo | string> {
if (!selectedPort) {
const boardList = await boardListProvider;
const ports = boardList.ports();
const detectedPort = ports[ports.matchingIndex];
if (!detectedPort) {
return selectPortForInfo;
}
const { port: selectedPort, boards } = detectedPort;
// IDE2 must show the board info based on the selected port.
// https://github.com/arduino/arduino-ide/issues/1489
// IDE 1.x supports only serial port protocol
if (selectedPort.protocol !== 'serial') {
return nonSerialPort;
}
const selectedPortKey = Port.keyOf(selectedPort);
const state = await availablePorts;
const boardListOnSelectedPort = Object.entries(state).filter(
([portKey, [port]]) =>
portKey === selectedPortKey && isNonNativeSerial(port)
);
if (!boardListOnSelectedPort.length) {
if (!isNonNativeSerial(selectedPort)) {
return noNativeSerialPort;
}
const [, [port, boards]] = boardListOnSelectedPort[0];
if (boardListOnSelectedPort.length > 1 || boards.length > 1) {
if (boards && boards.length > 1) {
console.warn(
`Detected more than one available boards on the selected port : ${JSON.stringify(
selectedPort
detectedPort
)}. Detected boards were: ${JSON.stringify(
boardListOnSelectedPort
)}. Using the first one: ${JSON.stringify([port, boards])}`
boards
)}. Using the first one: ${JSON.stringify(boards[0])}`
);
}
const board = boards[0];
const board = boards ? boards[0] : undefined;
const BN = board?.name ?? unknownBoard;
const VID = readProperty('vid', port);
const PID = readProperty('pid', port);
const SN = readProperty('serialNumber', port);
const VID = readProperty('vid', selectedPort);
const PID = readProperty('pid', selectedPort);
const SN = readProperty('serialNumber', selectedPort);
return { VID, PID, SN, BN };
}

View File

@ -1,15 +1,15 @@
import { nls } from '@theia/core/lib/common/nls';
import { ApplicationError } from '@theia/core/lib/common/application-error';
import { nls } from '@theia/core/lib/common/nls';
import type {
Location,
Range,
Position,
Range,
} from '@theia/core/shared/vscode-languageserver-protocol';
import type { BoardUserField, Port, Installable } from '../../common/protocol/';
import type { Programmer } from './boards-service';
import type { Sketch } from './sketches-service';
import { IndexUpdateSummary } from './notification-service';
import type { CompileSummary as ApiCompileSummary } from 'vscode-arduino-api';
import type { BoardUserField, Installable } from '../../common/protocol/';
import { isPortIdentifier, PortIdentifier, Programmer } from './boards-service';
import type { IndexUpdateSummary } from './notification-service';
import type { Sketch } from './sketches-service';
export const CompilerWarningLiterals = [
'None',
@ -148,11 +148,22 @@ export function isCompileSummary(arg: unknown): arg is CompileSummary {
);
}
export interface UploadResponse {
readonly portAfterUpload: PortIdentifier;
}
export function isUploadResponse(arg: unknown): arg is UploadResponse {
return (
Boolean(arg) &&
typeof arg === 'object' &&
isPortIdentifier((<UploadResponse>arg).portAfterUpload)
);
}
export const CoreServicePath = '/services/core-service';
export const CoreService = Symbol('CoreService');
export interface CoreService {
compile(options: CoreService.Options.Compile): Promise<void>;
upload(options: CoreService.Options.Upload): Promise<void>;
upload(options: CoreService.Options.Upload): Promise<UploadResponse>;
burnBootloader(options: CoreService.Options.Bootloader): Promise<void>;
/**
* Refreshes the underling core gRPC client for the Arduino CLI.
@ -198,7 +209,7 @@ export namespace CoreService {
readonly sketch: Sketch;
}
export interface BoardBased {
readonly port?: Port;
readonly port?: PortIdentifier;
readonly programmer?: Programmer | undefined;
/**
* For the _Verify after upload_ setting.

View File

@ -1,5 +1,8 @@
import { ApplicationError, Event, JsonRpcServer, nls } from '@theia/core';
import { Board, Port } from './boards-service';
import { ApplicationError } from '@theia/core/lib/common/application-error';
import type { Event } from '@theia/core/lib/common/event';
import type { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import { nls } from '@theia/core/lib/common/nls';
import type { BoardIdentifier, PortIdentifier } from './boards-service';
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
export interface MonitorSettings {
@ -15,17 +18,20 @@ export const MonitorManagerProxy = Symbol('MonitorManagerProxy');
export interface MonitorManagerProxy
extends JsonRpcServer<MonitorManagerProxyClient> {
startMonitor(
board: Board,
port: Port,
board: BoardIdentifier,
port: PortIdentifier,
settings?: PluggableMonitorSettings
): Promise<void>;
changeMonitorSettings(
board: Board,
port: Port,
board: BoardIdentifier,
port: PortIdentifier,
settings: MonitorSettings
): Promise<void>;
stopMonitor(board: Board, port: Port): Promise<void>;
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings>;
stopMonitor(board: BoardIdentifier, port: PortIdentifier): Promise<void>;
getCurrentSettings(
board: BoardIdentifier,
port: PortIdentifier
): Promise<MonitorSettings>;
}
export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient');
@ -38,7 +44,10 @@ export interface MonitorManagerProxyClient {
getWebSocketPort(): number | undefined;
isWSConnected(): Promise<boolean>;
startMonitor(settings?: PluggableMonitorSettings): Promise<void>;
getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings>;
getCurrentSettings(
board: BoardIdentifier,
port: PortIdentifier
): Promise<MonitorSettings>;
send(message: string): void;
changeSettings(settings: MonitorSettings): void;
}
@ -95,7 +104,7 @@ export const MissingConfigurationError = declareMonitorError(
);
export function createConnectionFailedError(
port: Port,
port: PortIdentifier,
details?: string
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
@ -120,7 +129,7 @@ export function createConnectionFailedError(
return ConnectionFailedError(message, { protocol, address });
}
export function createNotConnectedError(
port: Port
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return NotConnectedError(
@ -134,7 +143,7 @@ export function createNotConnectedError(
);
}
export function createAlreadyConnectedError(
port: Port
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return AlreadyConnectedError(
@ -148,7 +157,7 @@ export function createAlreadyConnectedError(
);
}
export function createMissingConfigurationError(
port: Port
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return MissingConfigurationError(

View File

@ -1,11 +1,11 @@
import type { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import type {
AttachedBoardsChangeEvent,
BoardsPackage,
ConfigState,
DetectedPorts,
IndexType,
ProgressMessage,
Sketch,
IndexType,
} from '../protocol';
import type { LibraryPackage } from './library-service';
@ -68,7 +68,9 @@ export interface NotificationServiceClient {
notifyLibraryDidUninstall(event: { item: LibraryPackage }): void;
// Boards discovery
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void;
notifyDetectedPortsDidChange(event: { detectedPorts: DetectedPorts }): void;
// Sketches
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void;
}

View File

@ -1,3 +1,7 @@
export type RecursiveRequired<T> = {
[P in keyof T]-?: RecursiveRequired<T[P]>;
};
export type Defined<T> = {
[P in keyof T]: NonNullable<T[P]>;
};

View File

@ -13,6 +13,7 @@ import { promises as fs, rm, rmSync } from 'node:fs';
import type { MaybePromise, Mutable } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
import { environment } from '@theia/application-package/lib/environment';
import {
ElectronMainApplication as TheiaElectronMainApplication,
ElectronMainExecutionParams,
@ -702,6 +703,12 @@ class InterruptWorkspaceRestoreError extends Error {
async function updateFrontendApplicationConfigFromPackageJson(
config: FrontendApplicationConfig
): Promise<FrontendApplicationConfig> {
if (environment.electron.isDevMode()) {
console.debug(
'Skipping frontend application configuration customizations. Running in dev mode.'
);
return config;
}
try {
const modulePath = __filename;
// must go from `./lib/backend/electron-main.js` to `./package.json` when the app is webpacked.

View File

@ -66,7 +66,7 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
]);
return output;
} finally {
await this.monitorManager.notifyUploadFinished(board.fqbn, port);
await this.monitorManager.notifyUploadFinished(board.fqbn, port, port); // here the before and after ports are assumed to be always the same
}
}

View File

@ -1,18 +1,22 @@
import { ClientDuplexStream } from '@grpc/grpc-js';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import type { ClientDuplexStream } from '@grpc/grpc-js';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { ILogger } from '@theia/core/lib/common/logger';
import { deepClone } from '@theia/core/lib/common/objects';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { BackendApplicationContribution } from '@theia/core/lib/node';
import type { Mutable } from '@theia/core/lib/common/types';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import { Disposable } from '@theia/core/lib/common/disposable';
import { isDeepStrictEqual } from 'util';
import { v4 } from 'uuid';
import { Unknown } from '../common/nls';
import {
AttachedBoardsChangeEvent,
AvailablePorts,
Board,
DetectedPort,
DetectedPorts,
NotificationServiceServer,
Port,
} from '../common/protocol';
@ -22,7 +26,7 @@ import {
DetectedPort as RpcDetectedPort,
} from './cli-protocol/cc/arduino/cli/commands/v1/board_pb';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import type { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { CoreClientAware } from './core-client-provider';
import { ServiceError } from './service-error';
@ -57,23 +61,9 @@ export class BoardDiscovery
private readonly onStreamDidCancelEmitter = new Emitter<void>(); // when the watcher is canceled by the IDE2
private readonly toDisposeOnStopWatch = new DisposableCollection();
private uploadInProgress = false;
/**
* Keys are the `address` of the ports.
*
* The `protocol` is ignored because the board detach event does not carry the protocol information,
* just the address.
* ```json
* {
* "type": "remove",
* "address": "/dev/cu.usbmodem14101"
* }
* ```
*/
private _availablePorts: AvailablePorts = {};
get availablePorts(): AvailablePorts {
return this._availablePorts;
private _detectedPorts: DetectedPorts = {};
get detectedPorts(): DetectedPorts {
return this._detectedPorts;
}
onStart(): void {
@ -120,10 +110,6 @@ export class BoardDiscovery
});
}
setUploadInProgress(uploadInProgress: boolean): void {
this.uploadInProgress = uploadInProgress;
}
private createTimeout(
after: number,
onTimeout: (error: Error) => void
@ -202,18 +188,6 @@ export class BoardDiscovery
return wrapper;
}
private toJson(arg: BoardListWatchRequest | BoardListWatchResponse): string {
let object: Record<string, unknown> | undefined = undefined;
if (arg instanceof BoardListWatchRequest) {
object = BoardListWatchRequest.toObject(false, arg);
} else if (arg instanceof BoardListWatchResponse) {
object = BoardListWatchResponse.toObject(false, arg);
} else {
throw new Error(`Unhandled object type: ${arg}`);
}
return JSON.stringify(object);
}
async start(): Promise<void> {
this.logger.info('start');
if (this.stopping) {
@ -240,9 +214,8 @@ export class BoardDiscovery
this.logger.info('start resolved watching');
}
// XXX: make this `protected` and override for tests if IDE2 wants to mock events from the CLI.
private onBoardListWatchResponse(resp: BoardListWatchResponse): void {
this.logger.info(this.toJson(resp));
protected onBoardListWatchResponse(resp: BoardListWatchResponse): void {
this.logger.info(JSON.stringify(resp.toObject(false)));
const eventType = EventType.parse(resp.getEventType());
if (eventType === EventType.Quit) {
@ -251,69 +224,36 @@ export class BoardDiscovery
return;
}
const detectedPort = resp.getPort();
if (detectedPort) {
const { port, boards } = this.fromRpc(detectedPort);
if (!port) {
if (!!boards.length) {
console.warn(
`Could not detect the port, but unexpectedly received discovered boards. This is most likely a bug! Response was: ${this.toJson(
resp
)}`
);
}
return;
const rpcDetectedPort = resp.getPort();
if (rpcDetectedPort) {
const detectedPort = this.fromRpc(rpcDetectedPort);
if (detectedPort) {
this.fireSoon({ detectedPort, eventType });
} else {
this.logger.warn(
`Could not extract the detected port from ${rpcDetectedPort.toObject(
false
)}`
);
}
const oldState = deepClone(this._availablePorts);
const newState = deepClone(this._availablePorts);
const key = Port.keyOf(port);
if (eventType === EventType.Add) {
if (newState[key]) {
const [, knownBoards] = newState[key];
this.logger.warn(
`Port '${Port.toString(
port
)}' was already available. Known boards before override: ${JSON.stringify(
knownBoards
)}`
);
}
newState[key] = [port, boards];
} else if (eventType === EventType.Remove) {
if (!newState[key]) {
this.logger.warn(
`Port '${Port.toString(port)}' was not available. Skipping`
);
return;
}
delete newState[key];
}
const event: AttachedBoardsChangeEvent = {
oldState: {
...AvailablePorts.split(oldState),
},
newState: {
...AvailablePorts.split(newState),
},
uploadInProgress: this.uploadInProgress,
};
this._availablePorts = newState;
this.notificationService.notifyAttachedBoardsDidChange(event);
} else if (resp.getError()) {
this.logger.error(
`Could not extract any detected 'port' from the board list watch response. An 'error' has occurred: ${resp.getError()}`
);
}
}
private fromRpc(detectedPort: RpcDetectedPort): DetectedPort {
private fromRpc(detectedPort: RpcDetectedPort): DetectedPort | undefined {
const rpcPort = detectedPort.getPort();
const port = rpcPort && this.fromRpcPort(rpcPort);
if (!rpcPort) {
return undefined;
}
const port = createApiPort(rpcPort);
const boards = detectedPort.getMatchingBoardsList().map(
(board) =>
({
fqbn: board.getFqbn(),
fqbn: board.getFqbn() || undefined, // prefer undefined fqbn over empty string
name: board.getName() || Unknown,
port,
} as Board)
);
return {
@ -322,15 +262,55 @@ export class BoardDiscovery
};
}
private fromRpcPort(rpcPort: RpcPort): Port {
return {
address: rpcPort.getAddress(),
addressLabel: rpcPort.getLabel(),
protocol: rpcPort.getProtocol(),
protocolLabel: rpcPort.getProtocolLabel(),
properties: Port.Properties.create(rpcPort.getPropertiesMap().toObject()),
hardwareId: rpcPort.getHardwareId(),
};
private fireSoonHandle: NodeJS.Timeout | undefined;
private readonly bufferedEvents: DetectedPortChangeEvent[] = [];
private fireSoon(event: DetectedPortChangeEvent): void {
this.bufferedEvents.push(event);
clearTimeout(this.fireSoonHandle);
this.fireSoonHandle = setTimeout(() => {
const current = deepClone(this.detectedPorts);
const newState = this.calculateNewState(this.bufferedEvents, current);
if (!isDeepStrictEqual(current, newState)) {
this._detectedPorts = newState;
this.notificationService.notifyDetectedPortsDidChange({
detectedPorts: this._detectedPorts,
});
}
this.bufferedEvents.length = 0;
}, 100);
}
private calculateNewState(
events: DetectedPortChangeEvent[],
prevState: Mutable<DetectedPorts>
): DetectedPorts {
const newState = deepClone(prevState);
for (const { detectedPort, eventType } of events) {
const { port, boards } = detectedPort;
const key = Port.keyOf(port);
if (eventType === EventType.Add) {
const alreadyDetectedPort = newState[key];
if (alreadyDetectedPort) {
console.warn(
`Detected a new port that has been already discovered. The old value will be overridden. Old value: ${JSON.stringify(
alreadyDetectedPort
)}, new value: ${JSON.stringify(detectedPort)}`
);
}
newState[key] = { port, boards };
} else if (eventType === EventType.Remove) {
const alreadyDetectedPort = newState[key];
if (!alreadyDetectedPort) {
console.warn(
`Detected a port removal but it has not been discovered. This is most likely a bug! Detected port was: ${JSON.stringify(
detectedPort
)}`
);
}
delete newState[key];
}
}
return newState;
}
}
@ -357,7 +337,18 @@ namespace EventType {
}
}
interface DetectedPort {
port: Port | undefined;
boards: Board[];
interface DetectedPortChangeEvent {
readonly detectedPort: DetectedPort;
readonly eventType: EventType.Add | EventType.Remove;
}
export function createApiPort(rpcPort: RpcPort): Port {
return {
address: rpcPort.getAddress(),
addressLabel: rpcPort.getLabel(),
protocol: rpcPort.getProtocol(),
protocolLabel: rpcPort.getProtocolLabel(),
properties: Port.Properties.create(rpcPort.getPropertiesMap().toObject()),
hardwareId: rpcPort.getHardwareId() || undefined, // prefer undefined over empty string
};
}

View File

@ -12,13 +12,15 @@ import {
Programmer,
ResponseService,
NotificationServiceServer,
AvailablePorts,
DetectedPorts,
BoardWithPackage,
BoardUserField,
BoardSearch,
sortComponents,
SortGroup,
platformInstallFailed,
createPlatformIdentifier,
platformIdentifierEquals,
} from '../common/protocol';
import {
PlatformInstallRequest,
@ -65,8 +67,8 @@ export class BoardsServiceImpl
@inject(BoardDiscovery)
protected readonly boardDiscovery: BoardDiscovery;
async getState(): Promise<AvailablePorts> {
return this.boardDiscovery.availablePorts;
async getDetectedPorts(): Promise<DetectedPorts> {
return this.boardDiscovery.detectedPorts;
}
async getBoardDetails(options: {
@ -165,7 +167,7 @@ export class BoardsServiceImpl
debuggingSupported,
VID,
PID,
buildProperties
buildProperties,
};
}
@ -212,6 +214,28 @@ export class BoardsServiceImpl
return this.handleListBoards(client.boardListAll.bind(client), req);
}
async getInstalledPlatforms(): Promise<BoardsPackage[]> {
const { instance, client } = await this.coreClient;
return new Promise<BoardsPackage[]>((resolve, reject) => {
client.platformList(
new PlatformListRequest().setInstance(instance),
(err, response) => {
if (err) {
reject(err);
return;
}
resolve(
response
.getInstalledPlatformsList()
.map((platform, _, installedPlatforms) =>
toBoardsPackage(platform, installedPlatforms)
)
);
}
);
});
}
private async handleListBoards(
getBoards: (
request: BoardListAllRequest | BoardSearchRequest,
@ -232,10 +256,38 @@ export class BoardsServiceImpl
for (const board of resp.getBoardsList()) {
const platform = board.getPlatform();
if (platform) {
const platformId = platform.getId();
const fqbn = board.getFqbn() || undefined; // prefer undefined over empty string
const parsedPlatformId = createPlatformIdentifier(platformId);
if (!parsedPlatformId) {
console.warn(
`Could not create platform identifier from platform ID input: ${platform.getId()}. Skipping`
);
continue;
}
if (fqbn) {
const checkPlatformId = createPlatformIdentifier(board.getFqbn());
if (!checkPlatformId) {
console.warn(
`Could not create platform identifier from FQBN input: ${board.getFqbn()}. Skipping`
);
continue;
}
if (
!platformIdentifierEquals(parsedPlatformId, checkPlatformId)
) {
console.warn(
`Mismatching platform identifiers. Platform: ${JSON.stringify(
parsedPlatformId
)}, FQBN: ${JSON.stringify(checkPlatformId)}. Skipping`
);
continue;
}
}
boards.push({
name: board.getName(),
fqbn: board.getFqbn(),
packageId: platform.getId(),
packageId: parsedPlatformId,
packageName: platform.getName(),
manuallyInstalled: platform.getManuallyInstalled(),
});
@ -316,38 +368,6 @@ export class BoardsServiceImpl
}
);
const packages = new Map<string, BoardsPackage>();
const toPackage = (platform: Platform) => {
let installedVersion: string | undefined;
const matchingPlatform = installedPlatforms.find(
(ip) => ip.getId() === platform.getId()
);
if (!!matchingPlatform) {
installedVersion = matchingPlatform.getInstalled();
}
return {
id: platform.getId(),
name: platform.getName(),
author: platform.getMaintainer(),
availableVersions: [platform.getLatest()],
description: platform
.getBoardsList()
.map((b) => b.getName())
.join(', '),
installable: true,
types: platform.getTypeList(),
deprecated: platform.getDeprecated(),
summary: nls.localize(
'arduino/component/boardsIncluded',
'Boards included in this package:'
),
installedVersion,
boards: platform
.getBoardsList()
.map((b) => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
moreInfoLink: platform.getWebsite(),
};
};
// We must group the cores by ID, and sort platforms by, first the installed version, then version alphabetical order.
// Otherwise we lose the FQBN information.
const groupedById: Map<string, Platform[]> = new Map();
@ -400,7 +420,7 @@ export class BoardsServiceImpl
pkg.availableVersions.push(platform.getLatest());
pkg.availableVersions.sort(Installable.Version.COMPARATOR).reverse();
} else {
packages.set(id, toPackage(platform));
packages.set(id, toBoardsPackage(platform, installedPlatforms));
}
}
}
@ -572,3 +592,37 @@ function boardsPackageSortGroup(boardsPackage: BoardsPackage): SortGroup {
}
return types.join('-') as SortGroup;
}
function toBoardsPackage(
platform: Platform,
installedPlatforms: Platform[]
): BoardsPackage {
let installedVersion: string | undefined;
const matchingPlatform = installedPlatforms.find(
(ip) => ip.getId() === platform.getId()
);
if (!!matchingPlatform) {
installedVersion = matchingPlatform.getInstalled();
}
return {
id: platform.getId(),
name: platform.getName(),
author: platform.getMaintainer(),
availableVersions: [platform.getLatest()],
description: platform
.getBoardsList()
.map((b) => b.getName())
.join(', '),
types: platform.getTypeList(),
deprecated: platform.getDeprecated(),
summary: nls.localize(
'arduino/component/boardsIncluded',
'Boards included in this package:'
),
installedVersion,
boards: platform
.getBoardsList()
.map((b) => <Board>{ name: b.getName(), fqbn: b.getFqbn() }),
moreInfoLink: platform.getWebsite(),
};
}

View File

@ -14,14 +14,11 @@ export class BoardDetailsRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardDetailsRequest;
getFqbn(): string;
setFqbn(value: string): BoardDetailsRequest;
getDoNotExpandBuildProperties(): boolean;
setDoNotExpandBuildProperties(value: boolean): BoardDetailsRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardDetailsRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardDetailsRequest): BoardDetailsRequest.AsObject;
@ -43,66 +40,51 @@ export namespace BoardDetailsRequest {
export class BoardDetailsResponse extends jspb.Message {
getFqbn(): string;
setFqbn(value: string): BoardDetailsResponse;
getName(): string;
setName(value: string): BoardDetailsResponse;
getVersion(): string;
setVersion(value: string): BoardDetailsResponse;
getPropertiesId(): string;
setPropertiesId(value: string): BoardDetailsResponse;
getAlias(): string;
setAlias(value: string): BoardDetailsResponse;
getOfficial(): boolean;
setOfficial(value: boolean): BoardDetailsResponse;
getPinout(): string;
setPinout(value: string): BoardDetailsResponse;
hasPackage(): boolean;
clearPackage(): void;
getPackage(): Package | undefined;
setPackage(value?: Package): BoardDetailsResponse;
hasPlatform(): boolean;
clearPlatform(): void;
getPlatform(): BoardPlatform | undefined;
setPlatform(value?: BoardPlatform): BoardDetailsResponse;
clearToolsDependenciesList(): void;
getToolsDependenciesList(): Array<ToolsDependencies>;
setToolsDependenciesList(value: Array<ToolsDependencies>): BoardDetailsResponse;
addToolsDependencies(value?: ToolsDependencies, index?: number): ToolsDependencies;
clearConfigOptionsList(): void;
getConfigOptionsList(): Array<ConfigOption>;
setConfigOptionsList(value: Array<ConfigOption>): BoardDetailsResponse;
addConfigOptions(value?: ConfigOption, index?: number): ConfigOption;
clearProgrammersList(): void;
getProgrammersList(): Array<cc_arduino_cli_commands_v1_common_pb.Programmer>;
setProgrammersList(value: Array<cc_arduino_cli_commands_v1_common_pb.Programmer>): BoardDetailsResponse;
addProgrammers(value?: cc_arduino_cli_commands_v1_common_pb.Programmer, index?: number): cc_arduino_cli_commands_v1_common_pb.Programmer;
getDebuggingSupported(): boolean;
setDebuggingSupported(value: boolean): BoardDetailsResponse;
clearIdentificationPropertiesList(): void;
getIdentificationPropertiesList(): Array<BoardIdentificationProperties>;
setIdentificationPropertiesList(value: Array<BoardIdentificationProperties>): BoardDetailsResponse;
addIdentificationProperties(value?: BoardIdentificationProperties, index?: number): BoardIdentificationProperties;
clearBuildPropertiesList(): void;
getBuildPropertiesList(): Array<string>;
setBuildPropertiesList(value: Array<string>): BoardDetailsResponse;
addBuildProperties(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardDetailsResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardDetailsResponse): BoardDetailsResponse.AsObject;
@ -138,7 +120,6 @@ export class BoardIdentificationProperties extends jspb.Message {
getPropertiesMap(): jspb.Map<string, string>;
clearPropertiesMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardIdentificationProperties.AsObject;
static toObject(includeInstance: boolean, msg: BoardIdentificationProperties): BoardIdentificationProperties.AsObject;
@ -159,26 +140,20 @@ export namespace BoardIdentificationProperties {
export class Package extends jspb.Message {
getMaintainer(): string;
setMaintainer(value: string): Package;
getUrl(): string;
setUrl(value: string): Package;
getWebsiteUrl(): string;
setWebsiteUrl(value: string): Package;
getEmail(): string;
setEmail(value: string): Package;
getName(): string;
setName(value: string): Package;
hasHelp(): boolean;
clearHelp(): void;
getHelp(): Help | undefined;
setHelp(value?: Help): Package;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Package.AsObject;
static toObject(includeInstance: boolean, msg: Package): Package.AsObject;
@ -204,7 +179,6 @@ export class Help extends jspb.Message {
getOnline(): string;
setOnline(value: string): Help;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Help.AsObject;
static toObject(includeInstance: boolean, msg: Help): Help.AsObject;
@ -224,26 +198,19 @@ export namespace Help {
export class BoardPlatform extends jspb.Message {
getArchitecture(): string;
setArchitecture(value: string): BoardPlatform;
getCategory(): string;
setCategory(value: string): BoardPlatform;
getUrl(): string;
setUrl(value: string): BoardPlatform;
getArchiveFilename(): string;
setArchiveFilename(value: string): BoardPlatform;
getChecksum(): string;
setChecksum(value: string): BoardPlatform;
getSize(): number;
setSize(value: number): BoardPlatform;
getName(): string;
setName(value: string): BoardPlatform;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardPlatform.AsObject;
static toObject(includeInstance: boolean, msg: BoardPlatform): BoardPlatform.AsObject;
@ -269,19 +236,15 @@ export namespace BoardPlatform {
export class ToolsDependencies extends jspb.Message {
getPackager(): string;
setPackager(value: string): ToolsDependencies;
getName(): string;
setName(value: string): ToolsDependencies;
getVersion(): string;
setVersion(value: string): ToolsDependencies;
clearSystemsList(): void;
getSystemsList(): Array<Systems>;
setSystemsList(value: Array<Systems>): ToolsDependencies;
addSystems(value?: Systems, index?: number): Systems;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ToolsDependencies.AsObject;
static toObject(includeInstance: boolean, msg: ToolsDependencies): ToolsDependencies.AsObject;
@ -304,20 +267,15 @@ export namespace ToolsDependencies {
export class Systems extends jspb.Message {
getChecksum(): string;
setChecksum(value: string): Systems;
getHost(): string;
setHost(value: string): Systems;
getArchiveFilename(): string;
setArchiveFilename(value: string): Systems;
getUrl(): string;
setUrl(value: string): Systems;
getSize(): number;
setSize(value: number): Systems;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Systems.AsObject;
static toObject(includeInstance: boolean, msg: Systems): Systems.AsObject;
@ -341,16 +299,13 @@ export namespace Systems {
export class ConfigOption extends jspb.Message {
getOption(): string;
setOption(value: string): ConfigOption;
getOptionLabel(): string;
setOptionLabel(value: string): ConfigOption;
clearValuesList(): void;
getValuesList(): Array<ConfigValue>;
setValuesList(value: Array<ConfigValue>): ConfigOption;
addValues(value?: ConfigValue, index?: number): ConfigValue;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ConfigOption.AsObject;
static toObject(includeInstance: boolean, msg: ConfigOption): ConfigOption.AsObject;
@ -372,14 +327,11 @@ export namespace ConfigOption {
export class ConfigValue extends jspb.Message {
getValue(): string;
setValue(value: string): ConfigValue;
getValueLabel(): string;
setValueLabel(value: string): ConfigValue;
getSelected(): boolean;
setSelected(value: boolean): ConfigValue;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ConfigValue.AsObject;
static toObject(includeInstance: boolean, msg: ConfigValue): ConfigValue.AsObject;
@ -404,14 +356,11 @@ export class BoardListRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardListRequest;
getTimeout(): number;
setTimeout(value: number): BoardListRequest;
getFqbn(): string;
setFqbn(value: string): BoardListRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardListRequest): BoardListRequest.AsObject;
@ -436,7 +385,6 @@ export class BoardListResponse extends jspb.Message {
setPortsList(value: Array<DetectedPort>): BoardListResponse;
addPorts(value?: DetectedPort, index?: number): DetectedPort;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardListResponse): BoardListResponse.AsObject;
@ -459,13 +407,11 @@ export class DetectedPort extends jspb.Message {
setMatchingBoardsList(value: Array<BoardListItem>): DetectedPort;
addMatchingBoards(value?: BoardListItem, index?: number): BoardListItem;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): DetectedPort;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DetectedPort.AsObject;
static toObject(includeInstance: boolean, msg: DetectedPort): DetectedPort.AsObject;
@ -489,16 +435,13 @@ export class BoardListAllRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardListAllRequest;
clearSearchArgsList(): void;
getSearchArgsList(): Array<string>;
setSearchArgsList(value: Array<string>): BoardListAllRequest;
addSearchArgs(value: string, index?: number): string;
getIncludeHiddenBoards(): boolean;
setIncludeHiddenBoards(value: boolean): BoardListAllRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListAllRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardListAllRequest): BoardListAllRequest.AsObject;
@ -523,7 +466,6 @@ export class BoardListAllResponse extends jspb.Message {
setBoardsList(value: Array<BoardListItem>): BoardListAllResponse;
addBoards(value?: BoardListItem, index?: number): BoardListItem;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListAllResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardListAllResponse): BoardListAllResponse.AsObject;
@ -546,11 +488,9 @@ export class BoardListWatchRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardListWatchRequest;
getInterrupt(): boolean;
setInterrupt(value: boolean): BoardListWatchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListWatchRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardListWatchRequest): BoardListWatchRequest.AsObject;
@ -572,16 +512,13 @@ export class BoardListWatchResponse extends jspb.Message {
getEventType(): string;
setEventType(value: string): BoardListWatchResponse;
hasPort(): boolean;
clearPort(): void;
getPort(): DetectedPort | undefined;
setPort(value?: DetectedPort): BoardListWatchResponse;
getError(): string;
setError(value: string): BoardListWatchResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListWatchResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardListWatchResponse): BoardListWatchResponse.AsObject;
@ -603,20 +540,16 @@ export namespace BoardListWatchResponse {
export class BoardListItem extends jspb.Message {
getName(): string;
setName(value: string): BoardListItem;
getFqbn(): string;
setFqbn(value: string): BoardListItem;
getIsHidden(): boolean;
setIsHidden(value: boolean): BoardListItem;
hasPlatform(): boolean;
clearPlatform(): void;
getPlatform(): cc_arduino_cli_commands_v1_common_pb.Platform | undefined;
setPlatform(value?: cc_arduino_cli_commands_v1_common_pb.Platform): BoardListItem;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardListItem.AsObject;
static toObject(includeInstance: boolean, msg: BoardListItem): BoardListItem.AsObject;
@ -642,14 +575,11 @@ export class BoardSearchRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BoardSearchRequest;
getSearchArgs(): string;
setSearchArgs(value: string): BoardSearchRequest;
getIncludeHiddenBoards(): boolean;
setIncludeHiddenBoards(value: boolean): BoardSearchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardSearchRequest.AsObject;
static toObject(includeInstance: boolean, msg: BoardSearchRequest): BoardSearchRequest.AsObject;
@ -674,7 +604,6 @@ export class BoardSearchResponse extends jspb.Message {
setBoardsList(value: Array<BoardListItem>): BoardSearchResponse;
addBoards(value?: BoardListItem, index?: number): BoardListItem;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BoardSearchResponse.AsObject;
static toObject(includeInstance: boolean, msg: BoardSearchResponse): BoardSearchResponse.AsObject;

View File

@ -5,7 +5,6 @@
/* eslint-disable */
import * as grpc from "@grpc/grpc-js";
import {handleClientStreamingCall} from "@grpc/grpc-js/build/src/server-call";
import * as cc_arduino_cli_commands_v1_commands_pb from "../../../../../cc/arduino/cli/commands/v1/commands_pb";
import * as google_rpc_status_pb from "../../../../../google/rpc/status_pb";
import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino/cli/commands/v1/common_pb";
@ -26,6 +25,7 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition<grpc.Untyped
newSketch: IArduinoCoreServiceService_INewSketch;
loadSketch: IArduinoCoreServiceService_ILoadSketch;
archiveSketch: IArduinoCoreServiceService_IArchiveSketch;
setSketchDefaults: IArduinoCoreServiceService_ISetSketchDefaults;
boardDetails: IArduinoCoreServiceService_IBoardDetails;
boardList: IArduinoCoreServiceService_IBoardList;
boardListAll: IArduinoCoreServiceService_IBoardListAll;
@ -138,6 +138,15 @@ interface IArduinoCoreServiceService_IArchiveSketch extends grpc.MethodDefinitio
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse>;
}
interface IArduinoCoreServiceService_ISetSketchDefaults extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/SetSketchDefaults";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse>;
}
interface IArduinoCoreServiceService_IBoardDetails extends grpc.MethodDefinition<cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse> {
path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/BoardDetails";
requestStream: false;
@ -402,7 +411,7 @@ interface IArduinoCoreServiceService_IEnumerateMonitorPortSettings extends grpc.
export const ArduinoCoreServiceService: IArduinoCoreServiceService;
export interface IArduinoCoreServiceServer {
export interface IArduinoCoreServiceServer extends grpc.UntypedServiceImplementation {
create: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.CreateRequest, cc_arduino_cli_commands_v1_commands_pb.CreateResponse>;
init: grpc.handleServerStreamingCall<cc_arduino_cli_commands_v1_commands_pb.InitRequest, cc_arduino_cli_commands_v1_commands_pb.InitResponse>;
destroy: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.DestroyRequest, cc_arduino_cli_commands_v1_commands_pb.DestroyResponse>;
@ -412,6 +421,7 @@ export interface IArduinoCoreServiceServer {
newSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.NewSketchRequest, cc_arduino_cli_commands_v1_commands_pb.NewSketchResponse>;
loadSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.LoadSketchRequest, cc_arduino_cli_commands_v1_commands_pb.LoadSketchResponse>;
archiveSketch: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse>;
setSketchDefaults: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse>;
boardDetails: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse>;
boardList: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListRequest, cc_arduino_cli_commands_v1_board_pb.BoardListResponse>;
boardListAll: grpc.handleUnaryCall<cc_arduino_cli_commands_v1_board_pb.BoardListAllRequest, cc_arduino_cli_commands_v1_board_pb.BoardListAllResponse>;
@ -468,6 +478,9 @@ export interface IArduinoCoreServiceClient {
archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
@ -568,6 +581,9 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor
public archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
public archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
public archiveSketch(request: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.ArchiveSketchResponse) => void): grpc.ClientUnaryCall;
public setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
public setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
public setSketchDefaults(request: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse) => void): grpc.ClientUnaryCall;
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;
public boardDetails(request: cc_arduino_cli_commands_v1_board_pb.BoardDetailsRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_commands_v1_board_pb.BoardDetailsResponse) => void): grpc.ClientUnaryCall;

View File

@ -709,6 +709,28 @@ function deserialize_cc_arduino_cli_commands_v1_PlatformUpgradeResponse(buffer_a
return cc_arduino_cli_commands_v1_core_pb.PlatformUpgradeResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_SetSketchDefaultsRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.SetSketchDefaultsRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_SetSketchDefaultsRequest(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_SetSketchDefaultsResponse(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.SetSketchDefaultsResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_commands_v1_SetSketchDefaultsResponse(buffer_arg) {
return cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_commands_v1_SupportedUserFieldsRequest(arg) {
if (!(arg instanceof cc_arduino_cli_commands_v1_upload_pb.SupportedUserFieldsRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.commands.v1.SupportedUserFieldsRequest');
@ -975,6 +997,20 @@ archiveSketch: {
responseSerialize: serialize_cc_arduino_cli_commands_v1_ArchiveSketchResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_ArchiveSketchResponse,
},
// Sets the sketch default FQBN and Port Address/Protocol in
// the sketch project file (sketch.yaml). These metadata can be retrieved
// using LoadSketch.
setSketchDefaults: {
path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/SetSketchDefaults',
requestStream: false,
responseStream: false,
requestType: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsRequest,
responseType: cc_arduino_cli_commands_v1_commands_pb.SetSketchDefaultsResponse,
requestSerialize: serialize_cc_arduino_cli_commands_v1_SetSketchDefaultsRequest,
requestDeserialize: deserialize_cc_arduino_cli_commands_v1_SetSketchDefaultsRequest,
responseSerialize: serialize_cc_arduino_cli_commands_v1_SetSketchDefaultsResponse,
responseDeserialize: deserialize_cc_arduino_cli_commands_v1_SetSketchDefaultsResponse,
},
// BOARD COMMANDS
// --------------
//

View File

@ -38,7 +38,6 @@ export class CreateResponse extends jspb.Message {
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): CreateResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CreateResponse.AsObject;
static toObject(includeInstance: boolean, msg: CreateResponse): CreateResponse.AsObject;
@ -61,14 +60,11 @@ export class InitRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): InitRequest;
getProfile(): string;
setProfile(value: string): InitRequest;
getSketchPath(): string;
setSketchPath(value: string): InitRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): InitRequest.AsObject;
static toObject(includeInstance: boolean, msg: InitRequest): InitRequest.AsObject;
@ -94,19 +90,16 @@ export class InitResponse extends jspb.Message {
getInitProgress(): InitResponse.Progress | undefined;
setInitProgress(value?: InitResponse.Progress): InitResponse;
hasError(): boolean;
clearError(): void;
getError(): google_rpc_status_pb.Status | undefined;
setError(value?: google_rpc_status_pb.Status): InitResponse;
hasProfile(): boolean;
clearProfile(): void;
getProfile(): cc_arduino_cli_commands_v1_common_pb.Profile | undefined;
setProfile(value?: cc_arduino_cli_commands_v1_common_pb.Profile): InitResponse;
getMessageCase(): InitResponse.MessageCase;
serializeBinary(): Uint8Array;
@ -134,13 +127,11 @@ export namespace InitResponse {
getDownloadProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setDownloadProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): Progress;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): Progress;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Progress.AsObject;
static toObject(includeInstance: boolean, msg: Progress): Progress.AsObject;
@ -161,13 +152,9 @@ export namespace InitResponse {
export enum MessageCase {
MESSAGE_NOT_SET = 0,
INIT_PROGRESS = 1,
ERROR = 2,
PROFILE = 3,
INIT_PROGRESS = 1,
ERROR = 2,
PROFILE = 3,
}
}
@ -175,11 +162,9 @@ export namespace InitResponse {
export class FailedInstanceInitError extends jspb.Message {
getReason(): FailedInstanceInitReason;
setReason(value: FailedInstanceInitReason): FailedInstanceInitError;
getMessage(): string;
setMessage(value: string): FailedInstanceInitError;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FailedInstanceInitError.AsObject;
static toObject(includeInstance: boolean, msg: FailedInstanceInitError): FailedInstanceInitError.AsObject;
@ -204,7 +189,6 @@ export class DestroyRequest extends jspb.Message {
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): DestroyRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DestroyRequest.AsObject;
static toObject(includeInstance: boolean, msg: DestroyRequest): DestroyRequest.AsObject;
@ -244,11 +228,9 @@ export class UpdateIndexRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UpdateIndexRequest;
getIgnoreCustomPackageIndexes(): boolean;
setIgnoreCustomPackageIndexes(value: boolean): UpdateIndexRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateIndexRequest.AsObject;
static toObject(includeInstance: boolean, msg: UpdateIndexRequest): UpdateIndexRequest.AsObject;
@ -273,7 +255,6 @@ export class UpdateIndexResponse extends jspb.Message {
getDownloadProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setDownloadProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): UpdateIndexResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateIndexResponse.AsObject;
static toObject(includeInstance: boolean, msg: UpdateIndexResponse): UpdateIndexResponse.AsObject;
@ -297,7 +278,6 @@ export class UpdateLibrariesIndexRequest extends jspb.Message {
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UpdateLibrariesIndexRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateLibrariesIndexRequest.AsObject;
static toObject(includeInstance: boolean, msg: UpdateLibrariesIndexRequest): UpdateLibrariesIndexRequest.AsObject;
@ -321,7 +301,6 @@ export class UpdateLibrariesIndexResponse extends jspb.Message {
getDownloadProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setDownloadProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): UpdateLibrariesIndexResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UpdateLibrariesIndexResponse.AsObject;
static toObject(includeInstance: boolean, msg: UpdateLibrariesIndexResponse): UpdateLibrariesIndexResponse.AsObject;
@ -359,7 +338,6 @@ export class VersionResponse extends jspb.Message {
getVersion(): string;
setVersion(value: string): VersionResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): VersionResponse.AsObject;
static toObject(includeInstance: boolean, msg: VersionResponse): VersionResponse.AsObject;
@ -377,22 +355,13 @@ export namespace VersionResponse {
}
export class NewSketchRequest extends jspb.Message {
hasInstance(): boolean;
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): NewSketchRequest;
getSketchName(): string;
setSketchName(value: string): NewSketchRequest;
getSketchDir(): string;
setSketchDir(value: string): NewSketchRequest;
getOverwrite(): boolean;
setOverwrite(value: boolean): NewSketchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NewSketchRequest.AsObject;
static toObject(includeInstance: boolean, msg: NewSketchRequest): NewSketchRequest.AsObject;
@ -405,7 +374,6 @@ export class NewSketchRequest extends jspb.Message {
export namespace NewSketchRequest {
export type AsObject = {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
sketchName: string,
sketchDir: string,
overwrite: boolean,
@ -416,7 +384,6 @@ export class NewSketchResponse extends jspb.Message {
getMainFile(): string;
setMainFile(value: string): NewSketchResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NewSketchResponse.AsObject;
static toObject(includeInstance: boolean, msg: NewSketchResponse): NewSketchResponse.AsObject;
@ -434,16 +401,9 @@ export namespace NewSketchResponse {
}
export class LoadSketchRequest extends jspb.Message {
hasInstance(): boolean;
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LoadSketchRequest;
getSketchPath(): string;
setSketchPath(value: string): LoadSketchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LoadSketchRequest.AsObject;
static toObject(includeInstance: boolean, msg: LoadSketchRequest): LoadSketchRequest.AsObject;
@ -456,7 +416,6 @@ export class LoadSketchRequest extends jspb.Message {
export namespace LoadSketchRequest {
export type AsObject = {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
sketchPath: string,
}
}
@ -464,25 +423,26 @@ export namespace LoadSketchRequest {
export class LoadSketchResponse extends jspb.Message {
getMainFile(): string;
setMainFile(value: string): LoadSketchResponse;
getLocationPath(): string;
setLocationPath(value: string): LoadSketchResponse;
clearOtherSketchFilesList(): void;
getOtherSketchFilesList(): Array<string>;
setOtherSketchFilesList(value: Array<string>): LoadSketchResponse;
addOtherSketchFiles(value: string, index?: number): string;
clearAdditionalFilesList(): void;
getAdditionalFilesList(): Array<string>;
setAdditionalFilesList(value: Array<string>): LoadSketchResponse;
addAdditionalFiles(value: string, index?: number): string;
clearRootFolderFilesList(): void;
getRootFolderFilesList(): Array<string>;
setRootFolderFilesList(value: Array<string>): LoadSketchResponse;
addRootFolderFiles(value: string, index?: number): string;
getDefaultFqbn(): string;
setDefaultFqbn(value: string): LoadSketchResponse;
getDefaultPort(): string;
setDefaultPort(value: string): LoadSketchResponse;
getDefaultProtocol(): string;
setDefaultProtocol(value: string): LoadSketchResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LoadSketchResponse.AsObject;
@ -501,23 +461,22 @@ export namespace LoadSketchResponse {
otherSketchFilesList: Array<string>,
additionalFilesList: Array<string>,
rootFolderFilesList: Array<string>,
defaultFqbn: string,
defaultPort: string,
defaultProtocol: string,
}
}
export class ArchiveSketchRequest extends jspb.Message {
getSketchPath(): string;
setSketchPath(value: string): ArchiveSketchRequest;
getArchivePath(): string;
setArchivePath(value: string): ArchiveSketchRequest;
getIncludeBuildDir(): boolean;
setIncludeBuildDir(value: boolean): ArchiveSketchRequest;
getOverwrite(): boolean;
setOverwrite(value: boolean): ArchiveSketchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ArchiveSketchRequest.AsObject;
static toObject(includeInstance: boolean, msg: ArchiveSketchRequest): ArchiveSketchRequest.AsObject;
@ -554,9 +513,65 @@ export namespace ArchiveSketchResponse {
}
}
export class SetSketchDefaultsRequest extends jspb.Message {
getSketchPath(): string;
setSketchPath(value: string): SetSketchDefaultsRequest;
getDefaultFqbn(): string;
setDefaultFqbn(value: string): SetSketchDefaultsRequest;
getDefaultPortAddress(): string;
setDefaultPortAddress(value: string): SetSketchDefaultsRequest;
getDefaultPortProtocol(): string;
setDefaultPortProtocol(value: string): SetSketchDefaultsRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SetSketchDefaultsRequest.AsObject;
static toObject(includeInstance: boolean, msg: SetSketchDefaultsRequest): SetSketchDefaultsRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SetSketchDefaultsRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SetSketchDefaultsRequest;
static deserializeBinaryFromReader(message: SetSketchDefaultsRequest, reader: jspb.BinaryReader): SetSketchDefaultsRequest;
}
export namespace SetSketchDefaultsRequest {
export type AsObject = {
sketchPath: string,
defaultFqbn: string,
defaultPortAddress: string,
defaultPortProtocol: string,
}
}
export class SetSketchDefaultsResponse extends jspb.Message {
getDefaultFqbn(): string;
setDefaultFqbn(value: string): SetSketchDefaultsResponse;
getDefaultPortAddress(): string;
setDefaultPortAddress(value: string): SetSketchDefaultsResponse;
getDefaultPortProtocol(): string;
setDefaultPortProtocol(value: string): SetSketchDefaultsResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SetSketchDefaultsResponse.AsObject;
static toObject(includeInstance: boolean, msg: SetSketchDefaultsResponse): SetSketchDefaultsResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SetSketchDefaultsResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SetSketchDefaultsResponse;
static deserializeBinaryFromReader(message: SetSketchDefaultsResponse, reader: jspb.BinaryReader): SetSketchDefaultsResponse;
}
export namespace SetSketchDefaultsResponse {
export type AsObject = {
defaultFqbn: string,
defaultPortAddress: string,
defaultPortProtocol: string,
}
}
export enum FailedInstanceInitReason {
FAILED_INSTANCE_INIT_REASON_UNSPECIFIED = 0,
FAILED_INSTANCE_INIT_REASON_INVALID_INDEX_URL = 1,
FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR = 2,
FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR = 3,
FAILED_INSTANCE_INIT_REASON_INDEX_DOWNLOAD_ERROR = 4,
}

View File

@ -53,6 +53,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchRequest', null, gl
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LoadSketchResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.NewSketchResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UpdateIndexRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UpdateIndexResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UpdateLibrariesIndexRequest', null, global);
@ -479,6 +481,48 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.commands.v1.ArchiveSketchResponse.displayName = 'proto.cc.arduino.cli.commands.v1.ArchiveSketchResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.displayName = 'proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.displayName = 'proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse';
}
@ -2733,7 +2777,6 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.toObject = function(
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.toObject = function(includeInstance, msg) {
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
sketchName: jspb.Message.getFieldWithDefault(msg, 2, ""),
sketchDir: jspb.Message.getFieldWithDefault(msg, 3, ""),
overwrite: jspb.Message.getBooleanFieldWithDefault(msg, 4, false)
@ -2773,11 +2816,6 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.deserializeBinaryFromReader =
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new cc_arduino_cli_commands_v1_common_pb.Instance;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
msg.setInstance(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setSketchName(value);
@ -2819,14 +2857,6 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.serializeBinary = fu
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getInstance();
if (f != null) {
writer.writeMessage(
1,
f,
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
);
}
f = message.getSketchName();
if (f.length > 0) {
writer.writeString(
@ -2851,43 +2881,6 @@ proto.cc.arduino.cli.commands.v1.NewSketchRequest.serializeBinaryToWriter = func
};
/**
* optional Instance instance = 1;
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.getInstance = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.Instance} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.Instance, 1));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.Instance|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.setInstance = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.NewSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.clearInstance = function() {
return this.setInstance(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.NewSketchRequest.prototype.hasInstance = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* optional string sketch_name = 2;
* @return {string}
@ -3104,7 +3097,6 @@ proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.toObject = function
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.toObject = function(includeInstance, msg) {
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
sketchPath: jspb.Message.getFieldWithDefault(msg, 2, "")
};
@ -3142,11 +3134,6 @@ proto.cc.arduino.cli.commands.v1.LoadSketchRequest.deserializeBinaryFromReader =
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new cc_arduino_cli_commands_v1_common_pb.Instance;
reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.Instance.deserializeBinaryFromReader);
msg.setInstance(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setSketchPath(value);
@ -3180,14 +3167,6 @@ proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.serializeBinary = f
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getInstance();
if (f != null) {
writer.writeMessage(
1,
f,
cc_arduino_cli_commands_v1_common_pb.Instance.serializeBinaryToWriter
);
}
f = message.getSketchPath();
if (f.length > 0) {
writer.writeString(
@ -3198,43 +3177,6 @@ proto.cc.arduino.cli.commands.v1.LoadSketchRequest.serializeBinaryToWriter = fun
};
/**
* optional Instance instance = 1;
* @return {?proto.cc.arduino.cli.commands.v1.Instance}
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.getInstance = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.Instance} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.Instance, 1));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.Instance|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.LoadSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.setInstance = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.LoadSketchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.clearInstance = function() {
return this.setInstance(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.LoadSketchRequest.prototype.hasInstance = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* optional string sketch_path = 2;
* @return {string}
@ -3296,7 +3238,10 @@ proto.cc.arduino.cli.commands.v1.LoadSketchResponse.toObject = function(includeI
locationPath: jspb.Message.getFieldWithDefault(msg, 2, ""),
otherSketchFilesList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f,
additionalFilesList: (f = jspb.Message.getRepeatedField(msg, 4)) == null ? undefined : f,
rootFolderFilesList: (f = jspb.Message.getRepeatedField(msg, 5)) == null ? undefined : f
rootFolderFilesList: (f = jspb.Message.getRepeatedField(msg, 5)) == null ? undefined : f,
defaultFqbn: jspb.Message.getFieldWithDefault(msg, 6, ""),
defaultPort: jspb.Message.getFieldWithDefault(msg, 7, ""),
defaultProtocol: jspb.Message.getFieldWithDefault(msg, 8, "")
};
if (includeInstance) {
@ -3353,6 +3298,18 @@ proto.cc.arduino.cli.commands.v1.LoadSketchResponse.deserializeBinaryFromReader
var value = /** @type {string} */ (reader.readString());
msg.addRootFolderFiles(value);
break;
case 6:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultFqbn(value);
break;
case 7:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultPort(value);
break;
case 8:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultProtocol(value);
break;
default:
reader.skipField();
break;
@ -3417,6 +3374,27 @@ proto.cc.arduino.cli.commands.v1.LoadSketchResponse.serializeBinaryToWriter = fu
f
);
}
f = message.getDefaultFqbn();
if (f.length > 0) {
writer.writeString(
6,
f
);
}
f = message.getDefaultPort();
if (f.length > 0) {
writer.writeString(
7,
f
);
}
f = message.getDefaultProtocol();
if (f.length > 0) {
writer.writeString(
8,
f
);
}
};
@ -3567,6 +3545,60 @@ proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.clearRootFolderFil
};
/**
* optional string default_fqbn = 6;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.getDefaultFqbn = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.LoadSketchResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.setDefaultFqbn = function(value) {
return jspb.Message.setProto3StringField(this, 6, value);
};
/**
* optional string default_port = 7;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.getDefaultPort = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.LoadSketchResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.setDefaultPort = function(value) {
return jspb.Message.setProto3StringField(this, 7, value);
};
/**
* optional string default_protocol = 8;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.getDefaultProtocol = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.LoadSketchResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.LoadSketchResponse.prototype.setDefaultProtocol = function(value) {
return jspb.Message.setProto3StringField(this, 8, value);
};
@ -3888,6 +3920,416 @@ proto.cc.arduino.cli.commands.v1.ArchiveSketchResponse.serializeBinaryToWriter =
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.toObject = function(includeInstance, msg) {
var f, obj = {
sketchPath: jspb.Message.getFieldWithDefault(msg, 1, ""),
defaultFqbn: jspb.Message.getFieldWithDefault(msg, 2, ""),
defaultPortAddress: jspb.Message.getFieldWithDefault(msg, 3, ""),
defaultPortProtocol: jspb.Message.getFieldWithDefault(msg, 4, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest;
return proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setSketchPath(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultFqbn(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultPortAddress(value);
break;
case 4:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultPortProtocol(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getSketchPath();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
f = message.getDefaultFqbn();
if (f.length > 0) {
writer.writeString(
2,
f
);
}
f = message.getDefaultPortAddress();
if (f.length > 0) {
writer.writeString(
3,
f
);
}
f = message.getDefaultPortProtocol();
if (f.length > 0) {
writer.writeString(
4,
f
);
}
};
/**
* optional string sketch_path = 1;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.getSketchPath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.setSketchPath = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
/**
* optional string default_fqbn = 2;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.getDefaultFqbn = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.setDefaultFqbn = function(value) {
return jspb.Message.setProto3StringField(this, 2, value);
};
/**
* optional string default_port_address = 3;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.getDefaultPortAddress = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.setDefaultPortAddress = function(value) {
return jspb.Message.setProto3StringField(this, 3, value);
};
/**
* optional string default_port_protocol = 4;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.getDefaultPortProtocol = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsRequest.prototype.setDefaultPortProtocol = function(value) {
return jspb.Message.setProto3StringField(this, 4, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.toObject = function(includeInstance, msg) {
var f, obj = {
defaultFqbn: jspb.Message.getFieldWithDefault(msg, 1, ""),
defaultPortAddress: jspb.Message.getFieldWithDefault(msg, 2, ""),
defaultPortProtocol: jspb.Message.getFieldWithDefault(msg, 3, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse;
return proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultFqbn(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultPortAddress(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setDefaultPortProtocol(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getDefaultFqbn();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
f = message.getDefaultPortAddress();
if (f.length > 0) {
writer.writeString(
2,
f
);
}
f = message.getDefaultPortProtocol();
if (f.length > 0) {
writer.writeString(
3,
f
);
}
};
/**
* optional string default_fqbn = 1;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.getDefaultFqbn = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.setDefaultFqbn = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
/**
* optional string default_port_address = 2;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.getDefaultPortAddress = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.setDefaultPortAddress = function(value) {
return jspb.Message.setProto3StringField(this, 2, value);
};
/**
* optional string default_port_protocol = 3;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.getDefaultPortProtocol = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.SetSketchDefaultsResponse.prototype.setDefaultPortProtocol = function(value) {
return jspb.Message.setProto3StringField(this, 3, value);
};
/**
* @enum {number}
*/
@ -3895,7 +4337,8 @@ proto.cc.arduino.cli.commands.v1.FailedInstanceInitReason = {
FAILED_INSTANCE_INIT_REASON_UNSPECIFIED: 0,
FAILED_INSTANCE_INIT_REASON_INVALID_INDEX_URL: 1,
FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR: 2,
FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR: 3
FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR: 3,
FAILED_INSTANCE_INIT_REASON_INDEX_DOWNLOAD_ERROR: 4
};
goog.object.extend(exports, proto.cc.arduino.cli.commands.v1);

View File

@ -10,7 +10,6 @@ export class Instance extends jspb.Message {
getId(): number;
setId(value: number): Instance;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Instance.AsObject;
static toObject(includeInstance: boolean, msg: Instance): Instance.AsObject;
@ -34,19 +33,16 @@ export class DownloadProgress extends jspb.Message {
getStart(): DownloadProgressStart | undefined;
setStart(value?: DownloadProgressStart): DownloadProgress;
hasUpdate(): boolean;
clearUpdate(): void;
getUpdate(): DownloadProgressUpdate | undefined;
setUpdate(value?: DownloadProgressUpdate): DownloadProgress;
hasEnd(): boolean;
clearEnd(): void;
getEnd(): DownloadProgressEnd | undefined;
setEnd(value?: DownloadProgressEnd): DownloadProgress;
getMessageCase(): DownloadProgress.MessageCase;
serializeBinary(): Uint8Array;
@ -68,13 +64,9 @@ export namespace DownloadProgress {
export enum MessageCase {
MESSAGE_NOT_SET = 0,
START = 1,
UPDATE = 2,
END = 3,
START = 1,
UPDATE = 2,
END = 3,
}
}
@ -82,11 +74,9 @@ export namespace DownloadProgress {
export class DownloadProgressStart extends jspb.Message {
getUrl(): string;
setUrl(value: string): DownloadProgressStart;
getLabel(): string;
setLabel(value: string): DownloadProgressStart;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DownloadProgressStart.AsObject;
static toObject(includeInstance: boolean, msg: DownloadProgressStart): DownloadProgressStart.AsObject;
@ -107,11 +97,9 @@ export namespace DownloadProgressStart {
export class DownloadProgressUpdate extends jspb.Message {
getDownloaded(): number;
setDownloaded(value: number): DownloadProgressUpdate;
getTotalSize(): number;
setTotalSize(value: number): DownloadProgressUpdate;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DownloadProgressUpdate.AsObject;
static toObject(includeInstance: boolean, msg: DownloadProgressUpdate): DownloadProgressUpdate.AsObject;
@ -132,11 +120,9 @@ export namespace DownloadProgressUpdate {
export class DownloadProgressEnd extends jspb.Message {
getSuccess(): boolean;
setSuccess(value: boolean): DownloadProgressEnd;
getMessage(): string;
setMessage(value: string): DownloadProgressEnd;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DownloadProgressEnd.AsObject;
static toObject(includeInstance: boolean, msg: DownloadProgressEnd): DownloadProgressEnd.AsObject;
@ -157,17 +143,13 @@ export namespace DownloadProgressEnd {
export class TaskProgress extends jspb.Message {
getName(): string;
setName(value: string): TaskProgress;
getMessage(): string;
setMessage(value: string): TaskProgress;
getCompleted(): boolean;
setCompleted(value: boolean): TaskProgress;
getPercent(): number;
setPercent(value: number): TaskProgress;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): TaskProgress.AsObject;
static toObject(includeInstance: boolean, msg: TaskProgress): TaskProgress.AsObject;
@ -190,14 +172,11 @@ export namespace TaskProgress {
export class Programmer extends jspb.Message {
getPlatform(): string;
setPlatform(value: string): Programmer;
getId(): string;
setId(value: string): Programmer;
getName(): string;
setName(value: string): Programmer;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Programmer.AsObject;
static toObject(includeInstance: boolean, msg: Programmer): Programmer.AsObject;
@ -219,54 +198,40 @@ export namespace Programmer {
export class Platform extends jspb.Message {
getId(): string;
setId(value: string): Platform;
getInstalled(): string;
setInstalled(value: string): Platform;
getLatest(): string;
setLatest(value: string): Platform;
getName(): string;
setName(value: string): Platform;
getMaintainer(): string;
setMaintainer(value: string): Platform;
getWebsite(): string;
setWebsite(value: string): Platform;
getEmail(): string;
setEmail(value: string): Platform;
clearBoardsList(): void;
getBoardsList(): Array<Board>;
setBoardsList(value: Array<Board>): Platform;
addBoards(value?: Board, index?: number): Board;
getManuallyInstalled(): boolean;
setManuallyInstalled(value: boolean): Platform;
getDeprecated(): boolean;
setDeprecated(value: boolean): Platform;
clearTypeList(): void;
getTypeList(): Array<string>;
setTypeList(value: Array<string>): Platform;
addType(value: string, index?: number): string;
hasHelp(): boolean;
clearHelp(): void;
getHelp(): HelpResources | undefined;
setHelp(value?: HelpResources): Platform;
getIndexed(): boolean;
setIndexed(value: boolean): Platform;
getMissingMetadata(): boolean;
setMissingMetadata(value: boolean): Platform;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Platform.AsObject;
static toObject(includeInstance: boolean, msg: Platform): Platform.AsObject;
@ -299,17 +264,13 @@ export namespace Platform {
export class InstalledPlatformReference extends jspb.Message {
getId(): string;
setId(value: string): InstalledPlatformReference;
getVersion(): string;
setVersion(value: string): InstalledPlatformReference;
getInstallDir(): string;
setInstallDir(value: string): InstalledPlatformReference;
getPackageUrl(): string;
setPackageUrl(value: string): InstalledPlatformReference;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): InstalledPlatformReference.AsObject;
static toObject(includeInstance: boolean, msg: InstalledPlatformReference): InstalledPlatformReference.AsObject;
@ -332,11 +293,9 @@ export namespace InstalledPlatformReference {
export class Board extends jspb.Message {
getName(): string;
setName(value: string): Board;
getFqbn(): string;
setFqbn(value: string): Board;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Board.AsObject;
static toObject(includeInstance: boolean, msg: Board): Board.AsObject;
@ -357,11 +316,9 @@ export namespace Board {
export class Profile extends jspb.Message {
getName(): string;
setName(value: string): Profile;
getFqbn(): string;
setFqbn(value: string): Profile;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Profile.AsObject;
static toObject(includeInstance: boolean, msg: Profile): Profile.AsObject;
@ -383,7 +340,6 @@ export class HelpResources extends jspb.Message {
getOnline(): string;
setOnline(value: string): HelpResources;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): HelpResources.AsObject;
static toObject(includeInstance: boolean, msg: HelpResources): HelpResources.AsObject;

View File

@ -15,90 +15,65 @@ export class CompileRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): CompileRequest;
getFqbn(): string;
setFqbn(value: string): CompileRequest;
getSketchPath(): string;
setSketchPath(value: string): CompileRequest;
getShowProperties(): boolean;
setShowProperties(value: boolean): CompileRequest;
getPreprocess(): boolean;
setPreprocess(value: boolean): CompileRequest;
getBuildCachePath(): string;
setBuildCachePath(value: string): CompileRequest;
getBuildPath(): string;
setBuildPath(value: string): CompileRequest;
clearBuildPropertiesList(): void;
getBuildPropertiesList(): Array<string>;
setBuildPropertiesList(value: Array<string>): CompileRequest;
addBuildProperties(value: string, index?: number): string;
getWarnings(): string;
setWarnings(value: string): CompileRequest;
getVerbose(): boolean;
setVerbose(value: boolean): CompileRequest;
getQuiet(): boolean;
setQuiet(value: boolean): CompileRequest;
getJobs(): number;
setJobs(value: number): CompileRequest;
clearLibrariesList(): void;
getLibrariesList(): Array<string>;
setLibrariesList(value: Array<string>): CompileRequest;
addLibraries(value: string, index?: number): string;
getOptimizeForDebug(): boolean;
setOptimizeForDebug(value: boolean): CompileRequest;
getExportDir(): string;
setExportDir(value: string): CompileRequest;
getClean(): boolean;
setClean(value: boolean): CompileRequest;
getCreateCompilationDatabaseOnly(): boolean;
setCreateCompilationDatabaseOnly(value: boolean): CompileRequest;
getSourceOverrideMap(): jspb.Map<string, string>;
clearSourceOverrideMap(): void;
hasExportBinaries(): boolean;
clearExportBinaries(): void;
getExportBinaries(): google_protobuf_wrappers_pb.BoolValue | undefined;
setExportBinaries(value?: google_protobuf_wrappers_pb.BoolValue): CompileRequest;
clearLibraryList(): void;
getLibraryList(): Array<string>;
setLibraryList(value: Array<string>): CompileRequest;
addLibrary(value: string, index?: number): string;
getKeysKeychain(): string;
setKeysKeychain(value: string): CompileRequest;
getSignKey(): string;
setSignKey(value: string): CompileRequest;
getEncryptKey(): string;
setEncryptKey(value: string): CompileRequest;
getSkipLibrariesDiscovery(): boolean;
setSkipLibrariesDiscovery(value: boolean): CompileRequest;
getDoNotExpandBuildProperties(): boolean;
setDoNotExpandBuildProperties(value: boolean): CompileRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CompileRequest.AsObject;
static toObject(includeInstance: boolean, msg: CompileRequest): CompileRequest.AsObject;
@ -145,49 +120,40 @@ export class CompileResponse extends jspb.Message {
getOutStream_asU8(): Uint8Array;
getOutStream_asB64(): string;
setOutStream(value: Uint8Array | string): CompileResponse;
getErrStream(): Uint8Array | string;
getErrStream_asU8(): Uint8Array;
getErrStream_asB64(): string;
setErrStream(value: Uint8Array | string): CompileResponse;
getBuildPath(): string;
setBuildPath(value: string): CompileResponse;
clearUsedLibrariesList(): void;
getUsedLibrariesList(): Array<cc_arduino_cli_commands_v1_lib_pb.Library>;
setUsedLibrariesList(value: Array<cc_arduino_cli_commands_v1_lib_pb.Library>): CompileResponse;
addUsedLibraries(value?: cc_arduino_cli_commands_v1_lib_pb.Library, index?: number): cc_arduino_cli_commands_v1_lib_pb.Library;
clearExecutableSectionsSizeList(): void;
getExecutableSectionsSizeList(): Array<ExecutableSectionSize>;
setExecutableSectionsSizeList(value: Array<ExecutableSectionSize>): CompileResponse;
addExecutableSectionsSize(value?: ExecutableSectionSize, index?: number): ExecutableSectionSize;
hasBoardPlatform(): boolean;
clearBoardPlatform(): void;
getBoardPlatform(): cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference | undefined;
setBoardPlatform(value?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference): CompileResponse;
hasBuildPlatform(): boolean;
clearBuildPlatform(): void;
getBuildPlatform(): cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference | undefined;
setBuildPlatform(value?: cc_arduino_cli_commands_v1_common_pb.InstalledPlatformReference): CompileResponse;
hasProgress(): boolean;
clearProgress(): void;
getProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): CompileResponse;
clearBuildPropertiesList(): void;
getBuildPropertiesList(): Array<string>;
setBuildPropertiesList(value: Array<string>): CompileResponse;
addBuildProperties(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CompileResponse.AsObject;
static toObject(includeInstance: boolean, msg: CompileResponse): CompileResponse.AsObject;
@ -215,14 +181,11 @@ export namespace CompileResponse {
export class ExecutableSectionSize extends jspb.Message {
getName(): string;
setName(value: string): ExecutableSectionSize;
getSize(): number;
setSize(value: number): ExecutableSectionSize;
getMaxSize(): number;
setMaxSize(value: number): ExecutableSectionSize;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ExecutableSectionSize.AsObject;
static toObject(includeInstance: boolean, msg: ExecutableSectionSize): ExecutableSectionSize.AsObject;

View File

@ -13,23 +13,17 @@ export class PlatformInstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformInstallRequest;
getPlatformPackage(): string;
setPlatformPackage(value: string): PlatformInstallRequest;
getArchitecture(): string;
setArchitecture(value: string): PlatformInstallRequest;
getVersion(): string;
setVersion(value: string): PlatformInstallRequest;
getSkipPostInstall(): boolean;
setSkipPostInstall(value: boolean): PlatformInstallRequest;
getNoOverwrite(): boolean;
setNoOverwrite(value: boolean): PlatformInstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformInstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformInstallRequest): PlatformInstallRequest.AsObject;
@ -58,13 +52,11 @@ export class PlatformInstallResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): PlatformInstallResponse;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): PlatformInstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformInstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformInstallResponse): PlatformInstallResponse.AsObject;
@ -105,17 +97,13 @@ export class PlatformDownloadRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformDownloadRequest;
getPlatformPackage(): string;
setPlatformPackage(value: string): PlatformDownloadRequest;
getArchitecture(): string;
setArchitecture(value: string): PlatformDownloadRequest;
getVersion(): string;
setVersion(value: string): PlatformDownloadRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformDownloadRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformDownloadRequest): PlatformDownloadRequest.AsObject;
@ -142,7 +130,6 @@ export class PlatformDownloadResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): PlatformDownloadResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformDownloadResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformDownloadResponse): PlatformDownloadResponse.AsObject;
@ -165,14 +152,11 @@ export class PlatformUninstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformUninstallRequest;
getPlatformPackage(): string;
setPlatformPackage(value: string): PlatformUninstallRequest;
getArchitecture(): string;
setArchitecture(value: string): PlatformUninstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformUninstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformUninstallRequest): PlatformUninstallRequest.AsObject;
@ -198,7 +182,6 @@ export class PlatformUninstallResponse extends jspb.Message {
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): PlatformUninstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformUninstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformUninstallResponse): PlatformUninstallResponse.AsObject;
@ -238,17 +221,13 @@ export class PlatformUpgradeRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformUpgradeRequest;
getPlatformPackage(): string;
setPlatformPackage(value: string): PlatformUpgradeRequest;
getArchitecture(): string;
setArchitecture(value: string): PlatformUpgradeRequest;
getSkipPostInstall(): boolean;
setSkipPostInstall(value: boolean): PlatformUpgradeRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformUpgradeRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformUpgradeRequest): PlatformUpgradeRequest.AsObject;
@ -275,19 +254,16 @@ export class PlatformUpgradeResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): PlatformUpgradeResponse;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): PlatformUpgradeResponse;
hasPlatform(): boolean;
clearPlatform(): void;
getPlatform(): cc_arduino_cli_commands_v1_common_pb.Platform | undefined;
setPlatform(value?: cc_arduino_cli_commands_v1_common_pb.Platform): PlatformUpgradeResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformUpgradeResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformUpgradeResponse): PlatformUpgradeResponse.AsObject;
@ -312,14 +288,11 @@ export class PlatformSearchRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformSearchRequest;
getSearchArgs(): string;
setSearchArgs(value: string): PlatformSearchRequest;
getAllVersions(): boolean;
setAllVersions(value: boolean): PlatformSearchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformSearchRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformSearchRequest): PlatformSearchRequest.AsObject;
@ -344,7 +317,6 @@ export class PlatformSearchResponse extends jspb.Message {
setSearchOutputList(value: Array<cc_arduino_cli_commands_v1_common_pb.Platform>): PlatformSearchResponse;
addSearchOutput(value?: cc_arduino_cli_commands_v1_common_pb.Platform, index?: number): cc_arduino_cli_commands_v1_common_pb.Platform;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformSearchResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformSearchResponse): PlatformSearchResponse.AsObject;
@ -367,14 +339,11 @@ export class PlatformListRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): PlatformListRequest;
getUpdatableOnly(): boolean;
setUpdatableOnly(value: boolean): PlatformListRequest;
getAll(): boolean;
setAll(value: boolean): PlatformListRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformListRequest.AsObject;
static toObject(includeInstance: boolean, msg: PlatformListRequest): PlatformListRequest.AsObject;
@ -399,7 +368,6 @@ export class PlatformListResponse extends jspb.Message {
setInstalledPlatformsList(value: Array<cc_arduino_cli_commands_v1_common_pb.Platform>): PlatformListResponse;
addInstalledPlatforms(value?: cc_arduino_cli_commands_v1_common_pb.Platform, index?: number): cc_arduino_cli_commands_v1_common_pb.Platform;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): PlatformListResponse.AsObject;
static toObject(includeInstance: boolean, msg: PlatformListResponse): PlatformListResponse.AsObject;

View File

@ -13,14 +13,11 @@ export class LibraryDownloadRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryDownloadRequest;
getName(): string;
setName(value: string): LibraryDownloadRequest;
getVersion(): string;
setVersion(value: string): LibraryDownloadRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryDownloadRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryDownloadRequest): LibraryDownloadRequest.AsObject;
@ -46,7 +43,6 @@ export class LibraryDownloadResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): LibraryDownloadResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryDownloadResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryDownloadResponse): LibraryDownloadResponse.AsObject;
@ -69,23 +65,17 @@ export class LibraryInstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryInstallRequest;
getName(): string;
setName(value: string): LibraryInstallRequest;
getVersion(): string;
setVersion(value: string): LibraryInstallRequest;
getNoDeps(): boolean;
setNoDeps(value: boolean): LibraryInstallRequest;
getNoOverwrite(): boolean;
setNoOverwrite(value: boolean): LibraryInstallRequest;
getInstallLocation(): LibraryInstallLocation;
setInstallLocation(value: LibraryInstallLocation): LibraryInstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryInstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryInstallRequest): LibraryInstallRequest.AsObject;
@ -114,13 +104,11 @@ export class LibraryInstallResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): LibraryInstallResponse;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): LibraryInstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryInstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryInstallResponse): LibraryInstallResponse.AsObject;
@ -144,14 +132,11 @@ export class LibraryUpgradeRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryUpgradeRequest;
getName(): string;
setName(value: string): LibraryUpgradeRequest;
getNoDeps(): boolean;
setNoDeps(value: boolean): LibraryUpgradeRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUpgradeRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUpgradeRequest): LibraryUpgradeRequest.AsObject;
@ -177,13 +162,11 @@ export class LibraryUpgradeResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): LibraryUpgradeResponse;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): LibraryUpgradeResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUpgradeResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUpgradeResponse): LibraryUpgradeResponse.AsObject;
@ -207,14 +190,11 @@ export class LibraryUninstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryUninstallRequest;
getName(): string;
setName(value: string): LibraryUninstallRequest;
getVersion(): string;
setVersion(value: string): LibraryUninstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUninstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUninstallRequest): LibraryUninstallRequest.AsObject;
@ -240,7 +220,6 @@ export class LibraryUninstallResponse extends jspb.Message {
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): LibraryUninstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUninstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUninstallResponse): LibraryUninstallResponse.AsObject;
@ -264,7 +243,6 @@ export class LibraryUpgradeAllRequest extends jspb.Message {
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryUpgradeAllRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUpgradeAllRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUpgradeAllRequest): LibraryUpgradeAllRequest.AsObject;
@ -288,13 +266,11 @@ export class LibraryUpgradeAllResponse extends jspb.Message {
getProgress(): cc_arduino_cli_commands_v1_common_pb.DownloadProgress | undefined;
setProgress(value?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress): LibraryUpgradeAllResponse;
hasTaskProgress(): boolean;
clearTaskProgress(): void;
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): LibraryUpgradeAllResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryUpgradeAllResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryUpgradeAllResponse): LibraryUpgradeAllResponse.AsObject;
@ -318,14 +294,11 @@ export class LibraryResolveDependenciesRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryResolveDependenciesRequest;
getName(): string;
setName(value: string): LibraryResolveDependenciesRequest;
getVersion(): string;
setVersion(value: string): LibraryResolveDependenciesRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryResolveDependenciesRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryResolveDependenciesRequest): LibraryResolveDependenciesRequest.AsObject;
@ -350,7 +323,6 @@ export class LibraryResolveDependenciesResponse extends jspb.Message {
setDependenciesList(value: Array<LibraryDependencyStatus>): LibraryResolveDependenciesResponse;
addDependencies(value?: LibraryDependencyStatus, index?: number): LibraryDependencyStatus;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryResolveDependenciesResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryResolveDependenciesResponse): LibraryResolveDependenciesResponse.AsObject;
@ -370,14 +342,11 @@ export namespace LibraryResolveDependenciesResponse {
export class LibraryDependencyStatus extends jspb.Message {
getName(): string;
setName(value: string): LibraryDependencyStatus;
getVersionRequired(): string;
setVersionRequired(value: string): LibraryDependencyStatus;
getVersionInstalled(): string;
setVersionInstalled(value: string): LibraryDependencyStatus;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryDependencyStatus.AsObject;
static toObject(includeInstance: boolean, msg: LibraryDependencyStatus): LibraryDependencyStatus.AsObject;
@ -402,13 +371,12 @@ export class LibrarySearchRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibrarySearchRequest;
getQuery(): string;
setQuery(value: string): LibrarySearchRequest;
getOmitReleasesDetails(): boolean;
setOmitReleasesDetails(value: boolean): LibrarySearchRequest;
getSearchArgs(): string;
setSearchArgs(value: string): LibrarySearchRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibrarySearchRequest.AsObject;
@ -425,6 +393,7 @@ export namespace LibrarySearchRequest {
instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject,
query: string,
omitReleasesDetails: boolean,
searchArgs: string,
}
}
@ -433,11 +402,9 @@ export class LibrarySearchResponse extends jspb.Message {
getLibrariesList(): Array<SearchedLibrary>;
setLibrariesList(value: Array<SearchedLibrary>): LibrarySearchResponse;
addLibraries(value?: SearchedLibrary, index?: number): SearchedLibrary;
getStatus(): LibrarySearchStatus;
setStatus(value: LibrarySearchStatus): LibrarySearchResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibrarySearchResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibrarySearchResponse): LibrarySearchResponse.AsObject;
@ -459,22 +426,18 @@ export class SearchedLibrary extends jspb.Message {
getName(): string;
setName(value: string): SearchedLibrary;
getReleasesMap(): jspb.Map<string, LibraryRelease>;
clearReleasesMap(): void;
hasLatest(): boolean;
clearLatest(): void;
getLatest(): LibraryRelease | undefined;
setLatest(value?: LibraryRelease): SearchedLibrary;
clearAvailableVersionsList(): void;
getAvailableVersionsList(): Array<string>;
setAvailableVersionsList(value: Array<string>): SearchedLibrary;
addAvailableVersions(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SearchedLibrary.AsObject;
static toObject(includeInstance: boolean, msg: SearchedLibrary): SearchedLibrary.AsObject;
@ -498,55 +461,42 @@ export namespace SearchedLibrary {
export class LibraryRelease extends jspb.Message {
getAuthor(): string;
setAuthor(value: string): LibraryRelease;
getVersion(): string;
setVersion(value: string): LibraryRelease;
getMaintainer(): string;
setMaintainer(value: string): LibraryRelease;
getSentence(): string;
setSentence(value: string): LibraryRelease;
getParagraph(): string;
setParagraph(value: string): LibraryRelease;
getWebsite(): string;
setWebsite(value: string): LibraryRelease;
getCategory(): string;
setCategory(value: string): LibraryRelease;
clearArchitecturesList(): void;
getArchitecturesList(): Array<string>;
setArchitecturesList(value: Array<string>): LibraryRelease;
addArchitectures(value: string, index?: number): string;
clearTypesList(): void;
getTypesList(): Array<string>;
setTypesList(value: Array<string>): LibraryRelease;
addTypes(value: string, index?: number): string;
hasResources(): boolean;
clearResources(): void;
getResources(): DownloadResource | undefined;
setResources(value?: DownloadResource): LibraryRelease;
getLicense(): string;
setLicense(value: string): LibraryRelease;
clearProvidesIncludesList(): void;
getProvidesIncludesList(): Array<string>;
setProvidesIncludesList(value: Array<string>): LibraryRelease;
addProvidesIncludes(value: string, index?: number): string;
clearDependenciesList(): void;
getDependenciesList(): Array<LibraryDependency>;
setDependenciesList(value: Array<LibraryDependency>): LibraryRelease;
addDependencies(value?: LibraryDependency, index?: number): LibraryDependency;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryRelease.AsObject;
static toObject(includeInstance: boolean, msg: LibraryRelease): LibraryRelease.AsObject;
@ -578,11 +528,9 @@ export namespace LibraryRelease {
export class LibraryDependency extends jspb.Message {
getName(): string;
setName(value: string): LibraryDependency;
getVersionConstraint(): string;
setVersionConstraint(value: string): LibraryDependency;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryDependency.AsObject;
static toObject(includeInstance: boolean, msg: LibraryDependency): LibraryDependency.AsObject;
@ -603,20 +551,15 @@ export namespace LibraryDependency {
export class DownloadResource extends jspb.Message {
getUrl(): string;
setUrl(value: string): DownloadResource;
getArchiveFilename(): string;
setArchiveFilename(value: string): DownloadResource;
getChecksum(): string;
setChecksum(value: string): DownloadResource;
getSize(): number;
setSize(value: number): DownloadResource;
getCachePath(): string;
setCachePath(value: string): DownloadResource;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DownloadResource.AsObject;
static toObject(includeInstance: boolean, msg: DownloadResource): DownloadResource.AsObject;
@ -643,20 +586,15 @@ export class LibraryListRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): LibraryListRequest;
getAll(): boolean;
setAll(value: boolean): LibraryListRequest;
getUpdatable(): boolean;
setUpdatable(value: boolean): LibraryListRequest;
getName(): string;
setName(value: string): LibraryListRequest;
getFqbn(): string;
setFqbn(value: string): LibraryListRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryListRequest.AsObject;
static toObject(includeInstance: boolean, msg: LibraryListRequest): LibraryListRequest.AsObject;
@ -683,7 +621,6 @@ export class LibraryListResponse extends jspb.Message {
setInstalledLibrariesList(value: Array<InstalledLibrary>): LibraryListResponse;
addInstalledLibraries(value?: InstalledLibrary, index?: number): InstalledLibrary;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): LibraryListResponse.AsObject;
static toObject(includeInstance: boolean, msg: LibraryListResponse): LibraryListResponse.AsObject;
@ -707,13 +644,11 @@ export class InstalledLibrary extends jspb.Message {
getLibrary(): Library | undefined;
setLibrary(value?: Library): InstalledLibrary;
hasRelease(): boolean;
clearRelease(): void;
getRelease(): LibraryRelease | undefined;
setRelease(value?: LibraryRelease): InstalledLibrary;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): InstalledLibrary.AsObject;
static toObject(includeInstance: boolean, msg: InstalledLibrary): InstalledLibrary.AsObject;
@ -734,93 +669,67 @@ export namespace InstalledLibrary {
export class Library extends jspb.Message {
getName(): string;
setName(value: string): Library;
getAuthor(): string;
setAuthor(value: string): Library;
getMaintainer(): string;
setMaintainer(value: string): Library;
getSentence(): string;
setSentence(value: string): Library;
getParagraph(): string;
setParagraph(value: string): Library;
getWebsite(): string;
setWebsite(value: string): Library;
getCategory(): string;
setCategory(value: string): Library;
clearArchitecturesList(): void;
getArchitecturesList(): Array<string>;
setArchitecturesList(value: Array<string>): Library;
addArchitectures(value: string, index?: number): string;
clearTypesList(): void;
getTypesList(): Array<string>;
setTypesList(value: Array<string>): Library;
addTypes(value: string, index?: number): string;
getInstallDir(): string;
setInstallDir(value: string): Library;
getSourceDir(): string;
setSourceDir(value: string): Library;
getUtilityDir(): string;
setUtilityDir(value: string): Library;
getContainerPlatform(): string;
setContainerPlatform(value: string): Library;
getDotALinkage(): boolean;
setDotALinkage(value: boolean): Library;
getPrecompiled(): boolean;
setPrecompiled(value: boolean): Library;
getLdFlags(): string;
setLdFlags(value: string): Library;
getIsLegacy(): boolean;
setIsLegacy(value: boolean): Library;
getVersion(): string;
setVersion(value: string): Library;
getLicense(): string;
setLicense(value: string): Library;
getPropertiesMap(): jspb.Map<string, string>;
clearPropertiesMap(): void;
getLocation(): LibraryLocation;
setLocation(value: LibraryLocation): Library;
getLayout(): LibraryLayout;
setLayout(value: LibraryLayout): Library;
clearExamplesList(): void;
getExamplesList(): Array<string>;
setExamplesList(value: Array<string>): Library;
addExamples(value: string, index?: number): string;
clearProvidesIncludesList(): void;
getProvidesIncludesList(): Array<string>;
setProvidesIncludesList(value: Array<string>): Library;
addProvidesIncludes(value: string, index?: number): string;
getCompatibleWithMap(): jspb.Map<string, boolean>;
clearCompatibleWithMap(): void;
getInDevelopment(): boolean;
setInDevelopment(value: boolean): Library;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Library.AsObject;
static toObject(includeInstance: boolean, msg: Library): Library.AsObject;
@ -870,14 +779,11 @@ export class ZipLibraryInstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): ZipLibraryInstallRequest;
getPath(): string;
setPath(value: string): ZipLibraryInstallRequest;
getOverwrite(): boolean;
setOverwrite(value: boolean): ZipLibraryInstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ZipLibraryInstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: ZipLibraryInstallRequest): ZipLibraryInstallRequest.AsObject;
@ -903,7 +809,6 @@ export class ZipLibraryInstallResponse extends jspb.Message {
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): ZipLibraryInstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ZipLibraryInstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: ZipLibraryInstallResponse): ZipLibraryInstallResponse.AsObject;
@ -926,14 +831,11 @@ export class GitLibraryInstallRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): GitLibraryInstallRequest;
getUrl(): string;
setUrl(value: string): GitLibraryInstallRequest;
getOverwrite(): boolean;
setOverwrite(value: boolean): GitLibraryInstallRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GitLibraryInstallRequest.AsObject;
static toObject(includeInstance: boolean, msg: GitLibraryInstallRequest): GitLibraryInstallRequest.AsObject;
@ -959,7 +861,6 @@ export class GitLibraryInstallResponse extends jspb.Message {
getTaskProgress(): cc_arduino_cli_commands_v1_common_pb.TaskProgress | undefined;
setTaskProgress(value?: cc_arduino_cli_commands_v1_common_pb.TaskProgress): GitLibraryInstallResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GitLibraryInstallResponse.AsObject;
static toObject(includeInstance: boolean, msg: GitLibraryInstallResponse): GitLibraryInstallResponse.AsObject;

View File

@ -3209,7 +3209,8 @@ proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.toObject = function(includ
var f, obj = {
instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f),
query: jspb.Message.getFieldWithDefault(msg, 2, ""),
omitReleasesDetails: jspb.Message.getBooleanFieldWithDefault(msg, 3, false)
omitReleasesDetails: jspb.Message.getBooleanFieldWithDefault(msg, 3, false),
searchArgs: jspb.Message.getFieldWithDefault(msg, 4, "")
};
if (includeInstance) {
@ -3259,6 +3260,10 @@ proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.deserializeBinaryFromReade
var value = /** @type {boolean} */ (reader.readBool());
msg.setOmitReleasesDetails(value);
break;
case 4:
var value = /** @type {string} */ (reader.readString());
msg.setSearchArgs(value);
break;
default:
reader.skipField();
break;
@ -3310,6 +3315,13 @@ proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.serializeBinaryToWriter =
f
);
}
f = message.getSearchArgs();
if (f.length > 0) {
writer.writeString(
4,
f
);
}
};
@ -3386,6 +3398,24 @@ proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.prototype.setOmitReleasesD
};
/**
* optional string search_args = 4;
* @return {string}
*/
proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.prototype.getSearchArgs = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.commands.v1.LibrarySearchRequest} returns this
*/
proto.cc.arduino.cli.commands.v1.LibrarySearchRequest.prototype.setSearchArgs = function(value) {
return jspb.Message.setProto3StringField(this, 4, value);
};
/**
* List of repeated fields within this message type.

View File

@ -15,27 +15,22 @@ export class MonitorRequest extends jspb.Message {
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): MonitorRequest;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): MonitorRequest;
getFqbn(): string;
setFqbn(value: string): MonitorRequest;
getTxData(): Uint8Array | string;
getTxData_asU8(): Uint8Array;
getTxData_asB64(): string;
setTxData(value: Uint8Array | string): MonitorRequest;
hasPortConfiguration(): boolean;
clearPortConfiguration(): void;
getPortConfiguration(): MonitorPortConfiguration | undefined;
setPortConfiguration(value?: MonitorPortConfiguration): MonitorRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MonitorRequest.AsObject;
static toObject(includeInstance: boolean, msg: MonitorRequest): MonitorRequest.AsObject;
@ -62,7 +57,6 @@ export class MonitorPortConfiguration extends jspb.Message {
setSettingsList(value: Array<MonitorPortSetting>): MonitorPortConfiguration;
addSettings(value?: MonitorPortSetting, index?: number): MonitorPortSetting;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MonitorPortConfiguration.AsObject;
static toObject(includeInstance: boolean, msg: MonitorPortConfiguration): MonitorPortConfiguration.AsObject;
@ -82,21 +76,17 @@ export namespace MonitorPortConfiguration {
export class MonitorResponse extends jspb.Message {
getError(): string;
setError(value: string): MonitorResponse;
getRxData(): Uint8Array | string;
getRxData_asU8(): Uint8Array;
getRxData_asB64(): string;
setRxData(value: Uint8Array | string): MonitorResponse;
clearAppliedSettingsList(): void;
getAppliedSettingsList(): Array<MonitorPortSetting>;
setAppliedSettingsList(value: Array<MonitorPortSetting>): MonitorResponse;
addAppliedSettings(value?: MonitorPortSetting, index?: number): MonitorPortSetting;
getSuccess(): boolean;
setSuccess(value: boolean): MonitorResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MonitorResponse.AsObject;
static toObject(includeInstance: boolean, msg: MonitorResponse): MonitorResponse.AsObject;
@ -119,11 +109,9 @@ export namespace MonitorResponse {
export class MonitorPortSetting extends jspb.Message {
getSettingId(): string;
setSettingId(value: string): MonitorPortSetting;
getValue(): string;
setValue(value: string): MonitorPortSetting;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MonitorPortSetting.AsObject;
static toObject(includeInstance: boolean, msg: MonitorPortSetting): MonitorPortSetting.AsObject;
@ -147,14 +135,11 @@ export class EnumerateMonitorPortSettingsRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): EnumerateMonitorPortSettingsRequest;
getPortProtocol(): string;
setPortProtocol(value: string): EnumerateMonitorPortSettingsRequest;
getFqbn(): string;
setFqbn(value: string): EnumerateMonitorPortSettingsRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): EnumerateMonitorPortSettingsRequest.AsObject;
static toObject(includeInstance: boolean, msg: EnumerateMonitorPortSettingsRequest): EnumerateMonitorPortSettingsRequest.AsObject;
@ -179,7 +164,6 @@ export class EnumerateMonitorPortSettingsResponse extends jspb.Message {
setSettingsList(value: Array<MonitorPortSettingDescriptor>): EnumerateMonitorPortSettingsResponse;
addSettings(value?: MonitorPortSettingDescriptor, index?: number): MonitorPortSettingDescriptor;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): EnumerateMonitorPortSettingsResponse.AsObject;
static toObject(includeInstance: boolean, msg: EnumerateMonitorPortSettingsResponse): EnumerateMonitorPortSettingsResponse.AsObject;
@ -199,22 +183,17 @@ export namespace EnumerateMonitorPortSettingsResponse {
export class MonitorPortSettingDescriptor extends jspb.Message {
getSettingId(): string;
setSettingId(value: string): MonitorPortSettingDescriptor;
getLabel(): string;
setLabel(value: string): MonitorPortSettingDescriptor;
getType(): string;
setType(value: string): MonitorPortSettingDescriptor;
clearEnumValuesList(): void;
getEnumValuesList(): Array<string>;
setEnumValuesList(value: Array<string>): MonitorPortSettingDescriptor;
addEnumValues(value: string, index?: number): string;
getValue(): string;
setValue(value: string): MonitorPortSettingDescriptor;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MonitorPortSettingDescriptor.AsObject;
static toObject(includeInstance: boolean, msg: MonitorPortSettingDescriptor): MonitorPortSettingDescriptor.AsObject;

View File

@ -9,24 +9,18 @@ import * as jspb from "google-protobuf";
export class Port extends jspb.Message {
getAddress(): string;
setAddress(value: string): Port;
getLabel(): string;
setLabel(value: string): Port;
getProtocol(): string;
setProtocol(value: string): Port;
getProtocolLabel(): string;
setProtocolLabel(value: string): Port;
getPropertiesMap(): jspb.Map<string, string>;
clearPropertiesMap(): void;
getHardwareId(): string;
setHardwareId(value: string): Port;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Port.AsObject;
static toObject(includeInstance: boolean, msg: Port): Port.AsObject;

View File

@ -14,42 +14,31 @@ export class UploadRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UploadRequest;
getFqbn(): string;
setFqbn(value: string): UploadRequest;
getSketchPath(): string;
setSketchPath(value: string): UploadRequest;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): UploadRequest;
getVerbose(): boolean;
setVerbose(value: boolean): UploadRequest;
getVerify(): boolean;
setVerify(value: boolean): UploadRequest;
getImportFile(): string;
setImportFile(value: string): UploadRequest;
getImportDir(): string;
setImportDir(value: string): UploadRequest;
getProgrammer(): string;
setProgrammer(value: string): UploadRequest;
getDryRun(): boolean;
setDryRun(value: boolean): UploadRequest;
getUserFieldsMap(): jspb.Map<string, string>;
clearUserFieldsMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UploadRequest.AsObject;
static toObject(includeInstance: boolean, msg: UploadRequest): UploadRequest.AsObject;
@ -78,16 +67,27 @@ export namespace UploadRequest {
}
export class UploadResponse extends jspb.Message {
hasOutStream(): boolean;
clearOutStream(): void;
getOutStream(): Uint8Array | string;
getOutStream_asU8(): Uint8Array;
getOutStream_asB64(): string;
setOutStream(value: Uint8Array | string): UploadResponse;
hasErrStream(): boolean;
clearErrStream(): void;
getErrStream(): Uint8Array | string;
getErrStream_asU8(): Uint8Array;
getErrStream_asB64(): string;
setErrStream(value: Uint8Array | string): UploadResponse;
hasResult(): boolean;
clearResult(): void;
getResult(): UploadResult | undefined;
setResult(value?: UploadResult): UploadResponse;
getMessageCase(): UploadResponse.MessageCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UploadResponse.AsObject;
@ -103,6 +103,38 @@ export namespace UploadResponse {
export type AsObject = {
outStream: Uint8Array | string,
errStream: Uint8Array | string,
result?: UploadResult.AsObject,
}
export enum MessageCase {
MESSAGE_NOT_SET = 0,
OUT_STREAM = 1,
ERR_STREAM = 2,
RESULT = 3,
}
}
export class UploadResult extends jspb.Message {
hasUpdatedUploadPort(): boolean;
clearUpdatedUploadPort(): void;
getUpdatedUploadPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setUpdatedUploadPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): UploadResult;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UploadResult.AsObject;
static toObject(includeInstance: boolean, msg: UploadResult): UploadResult.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UploadResult, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UploadResult;
static deserializeBinaryFromReader(message: UploadResult, reader: jspb.BinaryReader): UploadResult;
}
export namespace UploadResult {
export type AsObject = {
updatedUploadPort?: cc_arduino_cli_commands_v1_port_pb.Port.AsObject,
}
}
@ -129,42 +161,31 @@ export class UploadUsingProgrammerRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): UploadUsingProgrammerRequest;
getFqbn(): string;
setFqbn(value: string): UploadUsingProgrammerRequest;
getSketchPath(): string;
setSketchPath(value: string): UploadUsingProgrammerRequest;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): UploadUsingProgrammerRequest;
getVerbose(): boolean;
setVerbose(value: boolean): UploadUsingProgrammerRequest;
getVerify(): boolean;
setVerify(value: boolean): UploadUsingProgrammerRequest;
getImportFile(): string;
setImportFile(value: string): UploadUsingProgrammerRequest;
getImportDir(): string;
setImportDir(value: string): UploadUsingProgrammerRequest;
getProgrammer(): string;
setProgrammer(value: string): UploadUsingProgrammerRequest;
getDryRun(): boolean;
setDryRun(value: boolean): UploadUsingProgrammerRequest;
getUserFieldsMap(): jspb.Map<string, string>;
clearUserFieldsMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UploadUsingProgrammerRequest.AsObject;
static toObject(includeInstance: boolean, msg: UploadUsingProgrammerRequest): UploadUsingProgrammerRequest.AsObject;
@ -197,13 +218,11 @@ export class UploadUsingProgrammerResponse extends jspb.Message {
getOutStream_asU8(): Uint8Array;
getOutStream_asB64(): string;
setOutStream(value: Uint8Array | string): UploadUsingProgrammerResponse;
getErrStream(): Uint8Array | string;
getErrStream_asU8(): Uint8Array;
getErrStream_asB64(): string;
setErrStream(value: Uint8Array | string): UploadUsingProgrammerResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UploadUsingProgrammerResponse.AsObject;
static toObject(includeInstance: boolean, msg: UploadUsingProgrammerResponse): UploadUsingProgrammerResponse.AsObject;
@ -227,33 +246,25 @@ export class BurnBootloaderRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): BurnBootloaderRequest;
getFqbn(): string;
setFqbn(value: string): BurnBootloaderRequest;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): BurnBootloaderRequest;
getVerbose(): boolean;
setVerbose(value: boolean): BurnBootloaderRequest;
getVerify(): boolean;
setVerify(value: boolean): BurnBootloaderRequest;
getProgrammer(): string;
setProgrammer(value: string): BurnBootloaderRequest;
getDryRun(): boolean;
setDryRun(value: boolean): BurnBootloaderRequest;
getUserFieldsMap(): jspb.Map<string, string>;
clearUserFieldsMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BurnBootloaderRequest.AsObject;
static toObject(includeInstance: boolean, msg: BurnBootloaderRequest): BurnBootloaderRequest.AsObject;
@ -283,13 +294,11 @@ export class BurnBootloaderResponse extends jspb.Message {
getOutStream_asU8(): Uint8Array;
getOutStream_asB64(): string;
setOutStream(value: Uint8Array | string): BurnBootloaderResponse;
getErrStream(): Uint8Array | string;
getErrStream_asU8(): Uint8Array;
getErrStream_asB64(): string;
setErrStream(value: Uint8Array | string): BurnBootloaderResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BurnBootloaderResponse.AsObject;
static toObject(includeInstance: boolean, msg: BurnBootloaderResponse): BurnBootloaderResponse.AsObject;
@ -313,11 +322,9 @@ export class ListProgrammersAvailableForUploadRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): ListProgrammersAvailableForUploadRequest;
getFqbn(): string;
setFqbn(value: string): ListProgrammersAvailableForUploadRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ListProgrammersAvailableForUploadRequest.AsObject;
static toObject(includeInstance: boolean, msg: ListProgrammersAvailableForUploadRequest): ListProgrammersAvailableForUploadRequest.AsObject;
@ -341,7 +348,6 @@ export class ListProgrammersAvailableForUploadResponse extends jspb.Message {
setProgrammersList(value: Array<cc_arduino_cli_commands_v1_common_pb.Programmer>): ListProgrammersAvailableForUploadResponse;
addProgrammers(value?: cc_arduino_cli_commands_v1_common_pb.Programmer, index?: number): cc_arduino_cli_commands_v1_common_pb.Programmer;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ListProgrammersAvailableForUploadResponse.AsObject;
static toObject(includeInstance: boolean, msg: ListProgrammersAvailableForUploadResponse): ListProgrammersAvailableForUploadResponse.AsObject;
@ -364,14 +370,11 @@ export class SupportedUserFieldsRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): SupportedUserFieldsRequest;
getFqbn(): string;
setFqbn(value: string): SupportedUserFieldsRequest;
getProtocol(): string;
setProtocol(value: string): SupportedUserFieldsRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SupportedUserFieldsRequest.AsObject;
static toObject(includeInstance: boolean, msg: SupportedUserFieldsRequest): SupportedUserFieldsRequest.AsObject;
@ -393,17 +396,13 @@ export namespace SupportedUserFieldsRequest {
export class UserField extends jspb.Message {
getToolId(): string;
setToolId(value: string): UserField;
getName(): string;
setName(value: string): UserField;
getLabel(): string;
setLabel(value: string): UserField;
getSecret(): boolean;
setSecret(value: boolean): UserField;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UserField.AsObject;
static toObject(includeInstance: boolean, msg: UserField): UserField.AsObject;
@ -429,7 +428,6 @@ export class SupportedUserFieldsResponse extends jspb.Message {
setUserFieldsList(value: Array<UserField>): SupportedUserFieldsResponse;
addUserFields(value?: UserField, index?: number): UserField;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SupportedUserFieldsResponse.AsObject;
static toObject(includeInstance: boolean, msg: SupportedUserFieldsResponse): SupportedUserFieldsResponse.AsObject;

View File

@ -34,6 +34,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.SupportedUserFieldsRequest',
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.SupportedUserFieldsResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadResponse.MessageCase', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadResult', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadUsingProgrammerRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UploadUsingProgrammerResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.commands.v1.UserField', null, global);
@ -69,7 +71,7 @@ if (goog.DEBUG && !COMPILED) {
* @constructor
*/
proto.cc.arduino.cli.commands.v1.UploadResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
jspb.Message.initialize(this, opt_data, 0, -1, null, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.UploadResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
@ -79,6 +81,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.displayName = 'proto.cc.arduino.cli.commands.v1.UploadResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.commands.v1.UploadResult = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.commands.v1.UploadResult, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.commands.v1.UploadResult.displayName = 'proto.cc.arduino.cli.commands.v1.UploadResult';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@ -765,6 +788,33 @@ proto.cc.arduino.cli.commands.v1.UploadRequest.prototype.clearUserFieldsMap = fu
/**
* Oneof group definitions for this message. Each group defines the field
* numbers belonging to that group. When of these fields' value is set, all
* other fields in the group are cleared. During deserialization, if multiple
* fields are encountered for a group, only the last value seen will be kept.
* @private {!Array<!Array<number>>}
* @const
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_ = [[1,2,3]];
/**
* @enum {number}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.MessageCase = {
MESSAGE_NOT_SET: 0,
OUT_STREAM: 1,
ERR_STREAM: 2,
RESULT: 3
};
/**
* @return {proto.cc.arduino.cli.commands.v1.UploadResponse.MessageCase}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.getMessageCase = function() {
return /** @type {proto.cc.arduino.cli.commands.v1.UploadResponse.MessageCase} */(jspb.Message.computeOneofCase(this, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0]));
};
if (jspb.Message.GENERATE_TO_OBJECT) {
@ -797,7 +847,8 @@ proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.toObject = function(op
proto.cc.arduino.cli.commands.v1.UploadResponse.toObject = function(includeInstance, msg) {
var f, obj = {
outStream: msg.getOutStream_asB64(),
errStream: msg.getErrStream_asB64()
errStream: msg.getErrStream_asB64(),
result: (f = msg.getResult()) && proto.cc.arduino.cli.commands.v1.UploadResult.toObject(includeInstance, f)
};
if (includeInstance) {
@ -842,6 +893,11 @@ proto.cc.arduino.cli.commands.v1.UploadResponse.deserializeBinaryFromReader = fu
var value = /** @type {!Uint8Array} */ (reader.readBytes());
msg.setErrStream(value);
break;
case 3:
var value = new proto.cc.arduino.cli.commands.v1.UploadResult;
reader.readMessage(value,proto.cc.arduino.cli.commands.v1.UploadResult.deserializeBinaryFromReader);
msg.setResult(value);
break;
default:
reader.skipField();
break;
@ -871,20 +927,28 @@ proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.serializeBinary = func
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getOutStream_asU8();
if (f.length > 0) {
f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 1));
if (f != null) {
writer.writeBytes(
1,
f
);
}
f = message.getErrStream_asU8();
if (f.length > 0) {
f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2));
if (f != null) {
writer.writeBytes(
2,
f
);
}
f = message.getResult();
if (f != null) {
writer.writeMessage(
3,
f,
proto.cc.arduino.cli.commands.v1.UploadResult.serializeBinaryToWriter
);
}
};
@ -926,7 +990,25 @@ proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.getOutStream_asU8 = fu
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.setOutStream = function(value) {
return jspb.Message.setProto3BytesField(this, 1, value);
return jspb.Message.setOneofField(this, 1, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0], value);
};
/**
* Clears the field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.clearOutStream = function() {
return jspb.Message.setOneofField(this, 1, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0], undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.hasOutStream = function() {
return jspb.Message.getField(this, 1) != null;
};
@ -968,7 +1050,213 @@ proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.getErrStream_asU8 = fu
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.setErrStream = function(value) {
return jspb.Message.setProto3BytesField(this, 2, value);
return jspb.Message.setOneofField(this, 2, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0], value);
};
/**
* Clears the field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.clearErrStream = function() {
return jspb.Message.setOneofField(this, 2, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0], undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.hasErrStream = function() {
return jspb.Message.getField(this, 2) != null;
};
/**
* optional UploadResult result = 3;
* @return {?proto.cc.arduino.cli.commands.v1.UploadResult}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.getResult = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.UploadResult} */ (
jspb.Message.getWrapperField(this, proto.cc.arduino.cli.commands.v1.UploadResult, 3));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.UploadResult|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.setResult = function(value) {
return jspb.Message.setOneofWrapperField(this, 3, proto.cc.arduino.cli.commands.v1.UploadResponse.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResponse} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.clearResult = function() {
return this.setResult(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.UploadResponse.prototype.hasResult = function() {
return jspb.Message.getField(this, 3) != null;
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.commands.v1.UploadResult.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.commands.v1.UploadResult} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.UploadResult.toObject = function(includeInstance, msg) {
var f, obj = {
updatedUploadPort: (f = msg.getUpdatedUploadPort()) && cc_arduino_cli_commands_v1_port_pb.Port.toObject(includeInstance, f)
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResult}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.commands.v1.UploadResult;
return proto.cc.arduino.cli.commands.v1.UploadResult.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.commands.v1.UploadResult} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResult}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new cc_arduino_cli_commands_v1_port_pb.Port;
reader.readMessage(value,cc_arduino_cli_commands_v1_port_pb.Port.deserializeBinaryFromReader);
msg.setUpdatedUploadPort(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.commands.v1.UploadResult.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.commands.v1.UploadResult} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.commands.v1.UploadResult.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getUpdatedUploadPort();
if (f != null) {
writer.writeMessage(
1,
f,
cc_arduino_cli_commands_v1_port_pb.Port.serializeBinaryToWriter
);
}
};
/**
* optional Port updated_upload_port = 1;
* @return {?proto.cc.arduino.cli.commands.v1.Port}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.getUpdatedUploadPort = function() {
return /** @type{?proto.cc.arduino.cli.commands.v1.Port} */ (
jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_port_pb.Port, 1));
};
/**
* @param {?proto.cc.arduino.cli.commands.v1.Port|undefined} value
* @return {!proto.cc.arduino.cli.commands.v1.UploadResult} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.setUpdatedUploadPort = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.cc.arduino.cli.commands.v1.UploadResult} returns this
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.clearUpdatedUploadPort = function() {
return this.setUpdatedUploadPort(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.cc.arduino.cli.commands.v1.UploadResult.prototype.hasUpdatedUploadPort = function() {
return jspb.Message.getField(this, 1) != null;
};

View File

@ -5,7 +5,6 @@
/* eslint-disable */
import * as grpc from "@grpc/grpc-js";
import {handleClientStreamingCall} from "@grpc/grpc-js/build/src/server-call";
import * as cc_arduino_cli_debug_v1_debug_pb from "../../../../../cc/arduino/cli/debug/v1/debug_pb";
import * as cc_arduino_cli_commands_v1_common_pb from "../../../../../cc/arduino/cli/commands/v1/common_pb";
import * as cc_arduino_cli_commands_v1_port_pb from "../../../../../cc/arduino/cli/commands/v1/port_pb";
@ -36,7 +35,7 @@ interface IDebugServiceService_IGetDebugConfig extends grpc.MethodDefinition<cc_
export const DebugServiceService: IDebugServiceService;
export interface IDebugServiceServer {
export interface IDebugServiceServer extends grpc.UntypedServiceImplementation {
debug: grpc.handleBidiStreamingCall<cc_arduino_cli_debug_v1_debug_pb.DebugRequest, cc_arduino_cli_debug_v1_debug_pb.DebugResponse>;
getDebugConfig: grpc.handleUnaryCall<cc_arduino_cli_debug_v1_debug_pb.DebugConfigRequest, cc_arduino_cli_debug_v1_debug_pb.GetDebugConfigResponse>;
}

View File

@ -14,16 +14,13 @@ export class DebugRequest extends jspb.Message {
clearDebugRequest(): void;
getDebugRequest(): DebugConfigRequest | undefined;
setDebugRequest(value?: DebugConfigRequest): DebugRequest;
getData(): Uint8Array | string;
getData_asU8(): Uint8Array;
getData_asB64(): string;
setData(value: Uint8Array | string): DebugRequest;
getSendInterrupt(): boolean;
setSendInterrupt(value: boolean): DebugRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DebugRequest.AsObject;
static toObject(includeInstance: boolean, msg: DebugRequest): DebugRequest.AsObject;
@ -48,29 +45,22 @@ export class DebugConfigRequest extends jspb.Message {
clearInstance(): void;
getInstance(): cc_arduino_cli_commands_v1_common_pb.Instance | undefined;
setInstance(value?: cc_arduino_cli_commands_v1_common_pb.Instance): DebugConfigRequest;
getFqbn(): string;
setFqbn(value: string): DebugConfigRequest;
getSketchPath(): string;
setSketchPath(value: string): DebugConfigRequest;
hasPort(): boolean;
clearPort(): void;
getPort(): cc_arduino_cli_commands_v1_port_pb.Port | undefined;
setPort(value?: cc_arduino_cli_commands_v1_port_pb.Port): DebugConfigRequest;
getInterpreter(): string;
setInterpreter(value: string): DebugConfigRequest;
getImportDir(): string;
setImportDir(value: string): DebugConfigRequest;
getProgrammer(): string;
setProgrammer(value: string): DebugConfigRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DebugConfigRequest.AsObject;
static toObject(includeInstance: boolean, msg: DebugConfigRequest): DebugConfigRequest.AsObject;
@ -98,11 +88,9 @@ export class DebugResponse extends jspb.Message {
getData_asU8(): Uint8Array;
getData_asB64(): string;
setData(value: Uint8Array | string): DebugResponse;
getError(): string;
setError(value: string): DebugResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DebugResponse.AsObject;
static toObject(includeInstance: boolean, msg: DebugResponse): DebugResponse.AsObject;
@ -123,31 +111,23 @@ export namespace DebugResponse {
export class GetDebugConfigResponse extends jspb.Message {
getExecutable(): string;
setExecutable(value: string): GetDebugConfigResponse;
getToolchain(): string;
setToolchain(value: string): GetDebugConfigResponse;
getToolchainPath(): string;
setToolchainPath(value: string): GetDebugConfigResponse;
getToolchainPrefix(): string;
setToolchainPrefix(value: string): GetDebugConfigResponse;
getServer(): string;
setServer(value: string): GetDebugConfigResponse;
getServerPath(): string;
setServerPath(value: string): GetDebugConfigResponse;
getToolchainConfigurationMap(): jspb.Map<string, string>;
clearToolchainConfigurationMap(): void;
getServerConfigurationMap(): jspb.Map<string, string>;
clearServerConfigurationMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GetDebugConfigResponse.AsObject;
static toObject(includeInstance: boolean, msg: GetDebugConfigResponse): GetDebugConfigResponse.AsObject;

View File

@ -5,7 +5,6 @@
/* eslint-disable */
import * as grpc from "@grpc/grpc-js";
import {handleClientStreamingCall} from "@grpc/grpc-js/build/src/server-call";
import * as cc_arduino_cli_settings_v1_settings_pb from "../../../../../cc/arduino/cli/settings/v1/settings_pb";
interface ISettingsServiceService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
@ -14,6 +13,7 @@ interface ISettingsServiceService extends grpc.ServiceDefinition<grpc.UntypedSer
getValue: ISettingsServiceService_IGetValue;
setValue: ISettingsServiceService_ISetValue;
write: ISettingsServiceService_IWrite;
delete: ISettingsServiceService_IDelete;
}
interface ISettingsServiceService_IGetAll extends grpc.MethodDefinition<cc_arduino_cli_settings_v1_settings_pb.GetAllRequest, cc_arduino_cli_settings_v1_settings_pb.GetAllResponse> {
@ -61,15 +61,25 @@ interface ISettingsServiceService_IWrite extends grpc.MethodDefinition<cc_arduin
responseSerialize: grpc.serialize<cc_arduino_cli_settings_v1_settings_pb.WriteResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_settings_v1_settings_pb.WriteResponse>;
}
interface ISettingsServiceService_IDelete extends grpc.MethodDefinition<cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, cc_arduino_cli_settings_v1_settings_pb.DeleteResponse> {
path: "/cc.arduino.cli.settings.v1.SettingsService/Delete";
requestStream: false;
responseStream: false;
requestSerialize: grpc.serialize<cc_arduino_cli_settings_v1_settings_pb.DeleteRequest>;
requestDeserialize: grpc.deserialize<cc_arduino_cli_settings_v1_settings_pb.DeleteRequest>;
responseSerialize: grpc.serialize<cc_arduino_cli_settings_v1_settings_pb.DeleteResponse>;
responseDeserialize: grpc.deserialize<cc_arduino_cli_settings_v1_settings_pb.DeleteResponse>;
}
export const SettingsServiceService: ISettingsServiceService;
export interface ISettingsServiceServer {
export interface ISettingsServiceServer extends grpc.UntypedServiceImplementation {
getAll: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.GetAllRequest, cc_arduino_cli_settings_v1_settings_pb.GetAllResponse>;
merge: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.MergeRequest, cc_arduino_cli_settings_v1_settings_pb.MergeResponse>;
getValue: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.GetValueRequest, cc_arduino_cli_settings_v1_settings_pb.GetValueResponse>;
setValue: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.SetValueRequest, cc_arduino_cli_settings_v1_settings_pb.SetValueResponse>;
write: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.WriteRequest, cc_arduino_cli_settings_v1_settings_pb.WriteResponse>;
delete: grpc.handleUnaryCall<cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, cc_arduino_cli_settings_v1_settings_pb.DeleteResponse>;
}
export interface ISettingsServiceClient {
@ -88,6 +98,9 @@ export interface ISettingsServiceClient {
write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
}
export class SettingsServiceClient extends grpc.Client implements ISettingsServiceClient {
@ -107,4 +120,7 @@ export class SettingsServiceClient extends grpc.Client implements ISettingsServi
public write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
public write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
public write(request: cc_arduino_cli_settings_v1_settings_pb.WriteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.WriteResponse) => void): grpc.ClientUnaryCall;
public delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
public delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
public delete(request: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse) => void): grpc.ClientUnaryCall;
}

View File

@ -19,6 +19,28 @@
'use strict';
var cc_arduino_cli_settings_v1_settings_pb = require('../../../../../cc/arduino/cli/settings/v1/settings_pb.js');
function serialize_cc_arduino_cli_settings_v1_DeleteRequest(arg) {
if (!(arg instanceof cc_arduino_cli_settings_v1_settings_pb.DeleteRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.settings.v1.DeleteRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_settings_v1_DeleteRequest(buffer_arg) {
return cc_arduino_cli_settings_v1_settings_pb.DeleteRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_settings_v1_DeleteResponse(arg) {
if (!(arg instanceof cc_arduino_cli_settings_v1_settings_pb.DeleteResponse)) {
throw new Error('Expected argument of type cc.arduino.cli.settings.v1.DeleteResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_cc_arduino_cli_settings_v1_DeleteResponse(buffer_arg) {
return cc_arduino_cli_settings_v1_settings_pb.DeleteResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_cc_arduino_cli_settings_v1_GetAllRequest(arg) {
if (!(arg instanceof cc_arduino_cli_settings_v1_settings_pb.GetAllRequest)) {
throw new Error('Expected argument of type cc.arduino.cli.settings.v1.GetAllRequest');
@ -193,5 +215,17 @@ write: {
responseSerialize: serialize_cc_arduino_cli_settings_v1_WriteResponse,
responseDeserialize: deserialize_cc_arduino_cli_settings_v1_WriteResponse,
},
// Deletes an entry and rewrites the file settings
delete: {
path: '/cc.arduino.cli.settings.v1.SettingsService/Delete',
requestStream: false,
responseStream: false,
requestType: cc_arduino_cli_settings_v1_settings_pb.DeleteRequest,
responseType: cc_arduino_cli_settings_v1_settings_pb.DeleteResponse,
requestSerialize: serialize_cc_arduino_cli_settings_v1_DeleteRequest,
requestDeserialize: deserialize_cc_arduino_cli_settings_v1_DeleteRequest,
responseSerialize: serialize_cc_arduino_cli_settings_v1_DeleteResponse,
responseDeserialize: deserialize_cc_arduino_cli_settings_v1_DeleteResponse,
},
};

View File

@ -10,7 +10,6 @@ export class GetAllResponse extends jspb.Message {
getJsonData(): string;
setJsonData(value: string): GetAllResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GetAllResponse.AsObject;
static toObject(includeInstance: boolean, msg: GetAllResponse): GetAllResponse.AsObject;
@ -31,7 +30,6 @@ export class MergeRequest extends jspb.Message {
getJsonData(): string;
setJsonData(value: string): MergeRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MergeRequest.AsObject;
static toObject(includeInstance: boolean, msg: MergeRequest): MergeRequest.AsObject;
@ -51,11 +49,9 @@ export namespace MergeRequest {
export class GetValueResponse extends jspb.Message {
getKey(): string;
setKey(value: string): GetValueResponse;
getJsonData(): string;
setJsonData(value: string): GetValueResponse;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GetValueResponse.AsObject;
static toObject(includeInstance: boolean, msg: GetValueResponse): GetValueResponse.AsObject;
@ -76,11 +72,9 @@ export namespace GetValueResponse {
export class SetValueRequest extends jspb.Message {
getKey(): string;
setKey(value: string): SetValueRequest;
getJsonData(): string;
setJsonData(value: string): SetValueRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SetValueRequest.AsObject;
static toObject(includeInstance: boolean, msg: SetValueRequest): SetValueRequest.AsObject;
@ -119,7 +113,6 @@ export class GetValueRequest extends jspb.Message {
getKey(): string;
setKey(value: string): GetValueRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): GetValueRequest.AsObject;
static toObject(includeInstance: boolean, msg: GetValueRequest): GetValueRequest.AsObject;
@ -174,7 +167,6 @@ export class WriteRequest extends jspb.Message {
getFilePath(): string;
setFilePath(value: string): WriteRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): WriteRequest.AsObject;
static toObject(includeInstance: boolean, msg: WriteRequest): WriteRequest.AsObject;
@ -207,3 +199,40 @@ export namespace WriteResponse {
export type AsObject = {
}
}
export class DeleteRequest extends jspb.Message {
getKey(): string;
setKey(value: string): DeleteRequest;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DeleteRequest.AsObject;
static toObject(includeInstance: boolean, msg: DeleteRequest): DeleteRequest.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: DeleteRequest, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): DeleteRequest;
static deserializeBinaryFromReader(message: DeleteRequest, reader: jspb.BinaryReader): DeleteRequest;
}
export namespace DeleteRequest {
export type AsObject = {
key: string,
}
}
export class DeleteResponse extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): DeleteResponse.AsObject;
static toObject(includeInstance: boolean, msg: DeleteResponse): DeleteResponse.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: DeleteResponse, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): DeleteResponse;
static deserializeBinaryFromReader(message: DeleteResponse, reader: jspb.BinaryReader): DeleteResponse;
}
export namespace DeleteResponse {
export type AsObject = {
}
}

View File

@ -21,6 +21,8 @@ var global = (function() {
return Function('return this')();
}.call(null));
goog.exportSymbol('proto.cc.arduino.cli.settings.v1.DeleteRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.v1.DeleteResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.v1.GetAllRequest', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.v1.GetAllResponse', null, global);
goog.exportSymbol('proto.cc.arduino.cli.settings.v1.GetValueRequest', null, global);
@ -241,6 +243,48 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.cc.arduino.cli.settings.v1.WriteResponse.displayName = 'proto.cc.arduino.cli.settings.v1.WriteResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.settings.v1.DeleteRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.displayName = 'proto.cc.arduino.cli.settings.v1.DeleteRequest';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.cc.arduino.cli.settings.v1.DeleteResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.displayName = 'proto.cc.arduino.cli.settings.v1.DeleteResponse';
}
@ -1485,4 +1529,235 @@ proto.cc.arduino.cli.settings.v1.WriteResponse.serializeBinaryToWriter = functio
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.settings.v1.DeleteRequest.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.settings.v1.DeleteRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.toObject = function(includeInstance, msg) {
var f, obj = {
key: jspb.Message.getFieldWithDefault(msg, 1, "")
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.settings.v1.DeleteRequest}
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.settings.v1.DeleteRequest;
return proto.cc.arduino.cli.settings.v1.DeleteRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.settings.v1.DeleteRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.settings.v1.DeleteRequest}
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readString());
msg.setKey(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.settings.v1.DeleteRequest.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.settings.v1.DeleteRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getKey();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string key = 1;
* @return {string}
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.prototype.getKey = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.cc.arduino.cli.settings.v1.DeleteRequest} returns this
*/
proto.cc.arduino.cli.settings.v1.DeleteRequest.prototype.setKey = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* Optional fields that are not set will be set to undefined.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* net/proto2/compiler/js/internal/generator.cc#kKeyword.
* @param {boolean=} opt_includeInstance Deprecated. whether to include the
* JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @return {!Object}
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.prototype.toObject = function(opt_includeInstance) {
return proto.cc.arduino.cli.settings.v1.DeleteResponse.toObject(opt_includeInstance, this);
};
/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Deprecated. Whether to include
* the JSPB instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.cc.arduino.cli.settings.v1.DeleteResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.toObject = function(includeInstance, msg) {
var f, obj = {
};
if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}
/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.cc.arduino.cli.settings.v1.DeleteResponse}
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.cc.arduino.cli.settings.v1.DeleteResponse;
return proto.cc.arduino.cli.settings.v1.DeleteResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.cc.arduino.cli.settings.v1.DeleteResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.cc.arduino.cli.settings.v1.DeleteResponse}
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.cc.arduino.cli.settings.v1.DeleteResponse.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.cc.arduino.cli.settings.v1.DeleteResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.cc.arduino.cli.settings.v1.DeleteResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
};
goog.object.extend(exports, proto.cc.arduino.cli.settings.v1);

View File

@ -10,16 +10,13 @@ import * as google_protobuf_any_pb from "google-protobuf/google/protobuf/any_pb"
export class Status extends jspb.Message {
getCode(): number;
setCode(value: number): Status;
getMessage(): string;
setMessage(value: string): Status;
clearDetailsList(): void;
getDetailsList(): Array<google_protobuf_any_pb.Any>;
setDetailsList(value: Array<google_protobuf_any_pb.Any>): Status;
addDetails(value?: google_protobuf_any_pb.Any, index?: number): google_protobuf_any_pb.Any;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Status.AsObject;
static toObject(includeInstance: boolean, msg: Status): Status.AsObject;

View File

@ -3,13 +3,14 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import { relative } from 'node:path';
import * as jspb from 'google-protobuf';
import { BoolValue } from 'google-protobuf/google/protobuf/wrappers_pb';
import { ClientReadableStream } from '@grpc/grpc-js';
import type { ClientReadableStream } from '@grpc/grpc-js';
import {
CompilerWarnings,
CoreService,
CoreError,
CompileSummary,
isCompileSummary,
isUploadResponse,
} from '../common/protocol/core-service';
import {
CompileRequest,
@ -25,7 +26,13 @@ import {
UploadUsingProgrammerResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
import { ResponseService } from '../common/protocol/response-service';
import { OutputMessage, Port } from '../common/protocol';
import {
resolveDetectedPort,
OutputMessage,
PortIdentifier,
Port,
UploadResponse as ApiUploadResponse,
} from '../common/protocol';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
@ -36,8 +43,8 @@ import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';
import { firstToUpperCase, notEmpty } from '../common/utils';
import { ServiceError } from './service-error';
import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
import { BoardDiscovery } from './board-discovery';
import { Mutable } from '@theia/core/lib/common/types';
import type { Mutable } from '@theia/core/lib/common/types';
import { BoardDiscovery, createApiPort } from './board-discovery';
namespace Uploadable {
export type Request = UploadRequest | UploadUsingProgrammerRequest;
@ -50,13 +57,10 @@ type CompileSummaryFragment = Partial<Mutable<CompileSummary>>;
export class CoreServiceImpl extends CoreClientAware implements CoreService {
@inject(ResponseService)
private readonly responseService: ResponseService;
@inject(MonitorManager)
private readonly monitorManager: MonitorManager;
@inject(CommandService)
private readonly commandService: CommandService;
@inject(BoardDiscovery)
private readonly boardDiscovery: BoardDiscovery;
@ -172,7 +176,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
return request;
}
upload(options: CoreService.Options.Upload): Promise<void> {
upload(options: CoreService.Options.Upload): Promise<ApiUploadResponse> {
const { usingProgrammer } = options;
return this.doUpload(
options,
@ -201,14 +205,45 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
) => (request: REQ) => ClientReadableStream<RESP>,
errorCtor: ApplicationError.Constructor<number, CoreError.ErrorLocation[]>,
task: string
): Promise<void> {
): Promise<ApiUploadResponse> {
const portBeforeUpload = options.port;
const uploadResponseFragment: Mutable<Partial<ApiUploadResponse>> = {
portAfterUpload: options.port, // assume no port changes during the upload
};
const coreClient = await this.coreClient;
const { client, instance } = coreClient;
const progressHandler = this.createProgressHandler(options);
const handler = this.createOnDataHandler(progressHandler);
// Track responses for port changes. No port changes are expected when uploading using a programmer.
const updateUploadResponseFragmentHandler = (response: RESP) => {
if (response instanceof UploadResponse) {
// TODO: this instanceof should not be here but in `upload`. the upload and upload using programmer gRPC APIs are not symmetric
const uploadResult = response.getResult();
if (uploadResult) {
const port = uploadResult.getUpdatedUploadPort();
if (port) {
uploadResponseFragment.portAfterUpload = createApiPort(port);
console.info(
`Received port after upload [${
options.port ? Port.keyOf(options.port) : ''
}, ${options.fqbn}, ${
options.sketch.name
}]. Before port: ${JSON.stringify(
portBeforeUpload
)}, after port: ${JSON.stringify(
uploadResponseFragment.portAfterUpload
)}`
);
}
}
}
};
const handler = this.createOnDataHandler(
progressHandler,
updateUploadResponseFragmentHandler
);
const grpcCall = responseFactory(client);
return this.notifyUploadWillStart(options).then(() =>
new Promise<void>((resolve, reject) => {
new Promise<ApiUploadResponse>((resolve, reject) => {
grpcCall(this.initUploadRequest(request, options, instance))
.on('data', handler.onData)
.on('error', (error) => {
@ -234,10 +269,28 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
);
}
})
.on('end', resolve);
.on('end', () => {
if (isUploadResponse(uploadResponseFragment)) {
resolve(uploadResponseFragment);
} else {
reject(
new Error(
`Could not detect the port after the upload. Upload options were: ${JSON.stringify(
options
)}, upload response was: ${JSON.stringify(
uploadResponseFragment
)}`
)
);
}
});
}).finally(async () => {
handler.dispose();
await this.notifyUploadDidFinish(options);
await this.notifyUploadDidFinish(
Object.assign(options, {
afterPort: uploadResponseFragment.portAfterUpload,
})
);
})
);
}
@ -302,7 +355,9 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
.on('end', resolve);
}).finally(async () => {
handler.dispose();
await this.notifyUploadDidFinish(options);
await this.notifyUploadDidFinish(
Object.assign(options, { afterPort: options.port })
);
})
);
}
@ -379,21 +434,25 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
port,
}: {
fqbn?: string | undefined;
port?: Port | undefined;
port?: PortIdentifier;
}): Promise<void> {
this.boardDiscovery.setUploadInProgress(true);
return this.monitorManager.notifyUploadStarted(fqbn, port);
if (fqbn && port) {
return this.monitorManager.notifyUploadStarted(fqbn, port);
}
}
private async notifyUploadDidFinish({
fqbn,
port,
afterPort,
}: {
fqbn?: string | undefined;
port?: Port | undefined;
port?: PortIdentifier;
afterPort?: PortIdentifier;
}): Promise<void> {
this.boardDiscovery.setUploadInProgress(false);
return this.monitorManager.notifyUploadFinished(fqbn, port);
if (fqbn && port && afterPort) {
return this.monitorManager.notifyUploadFinished(fqbn, port, afterPort);
}
}
private mergeSourceOverrides(
@ -410,21 +469,28 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
}
private createPort(port: Port | undefined): RpcPort | undefined {
private createPort(
port: PortIdentifier | undefined,
resolve: (port: PortIdentifier) => Port | undefined = (port) =>
resolveDetectedPort(port, this.boardDiscovery.detectedPorts)
): RpcPort | undefined {
if (!port) {
return undefined;
}
const resolvedPort = resolve(port);
const rpcPort = new RpcPort();
rpcPort.setAddress(port.address);
rpcPort.setLabel(port.addressLabel);
rpcPort.setProtocol(port.protocol);
rpcPort.setProtocolLabel(port.protocolLabel);
if (port.hardwareId !== undefined) {
rpcPort.setHardwareId(port.hardwareId);
}
if (port.properties) {
for (const [key, value] of Object.entries(port.properties)) {
rpcPort.getPropertiesMap().set(key, value);
rpcPort.setAddress(port.address);
if (resolvedPort) {
rpcPort.setLabel(resolvedPort.addressLabel);
rpcPort.setProtocolLabel(resolvedPort.protocolLabel);
if (resolvedPort.hardwareId !== undefined) {
rpcPort.setHardwareId(resolvedPort.hardwareId);
}
if (resolvedPort.properties) {
for (const [key, value] of Object.entries(resolvedPort.properties)) {
rpcPort.getPropertiesMap().set(key, value);
}
}
}
return rpcPort;

View File

@ -7,6 +7,8 @@ import {
MonitorSettings,
PluggableMonitorSettings,
Port,
PortIdentifier,
portIdentifierEquals,
} from '../common/protocol';
import { CoreClientAware } from './core-client-provider';
import { MonitorService } from './monitor-service';
@ -180,13 +182,7 @@ export class MonitorManager extends CoreClientAware {
* @param fqbn the FQBN of the board connected to port
* @param port port to monitor
*/
async notifyUploadStarted(fqbn?: string, port?: Port): Promise<void> {
if (!fqbn || !port) {
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
return;
}
async notifyUploadStarted(fqbn: string, port: PortIdentifier): Promise<void> {
const monitorID = this.monitorID(fqbn, port);
this.addToMonitorIDsByUploadState('uploadInProgress', monitorID);
@ -204,41 +200,44 @@ export class MonitorManager extends CoreClientAware {
* Notifies the monitor service of that board/port combination
* that an upload process started on that exact board/port combination.
* @param fqbn the FQBN of the board connected to port
* @param port port to monitor
* @param beforePort port to monitor
* @returns a Status object to know if the process has been
* started or if there have been errors.
*/
async notifyUploadFinished(
fqbn?: string | undefined,
port?: Port
fqbn: string | undefined,
beforePort: PortIdentifier,
afterPort: PortIdentifier
): Promise<void> {
let portDidChangeOnUpload = false;
const beforeMonitorID = this.monitorID(fqbn, beforePort);
this.removeFromMonitorIDsByUploadState('uploadInProgress', beforeMonitorID);
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
if (fqbn && port) {
const monitorID = this.monitorID(fqbn, port);
this.removeFromMonitorIDsByUploadState('uploadInProgress', monitorID);
const monitor = this.monitorServices.get(monitorID);
if (monitor) {
const monitor = this.monitorServices.get(beforeMonitorID);
if (monitor) {
if (portIdentifierEquals(beforePort, afterPort)) {
await monitor.start();
} else {
await monitor.stop();
}
// this monitorID will only be present in "disposedForUpload"
// if the upload changed the board port
portDidChangeOnUpload = this.monitorIDIsInUploadState(
'disposedForUpload',
monitorID
);
if (portDidChangeOnUpload) {
this.removeFromMonitorIDsByUploadState('disposedForUpload', monitorID);
}
// in case a service was paused but not disposed
this.removeFromMonitorIDsByUploadState('pausedForUpload', monitorID);
}
// this monitorID will only be present in "disposedForUpload"
// if the upload changed the board port
portDidChangeOnUpload = this.monitorIDIsInUploadState(
'disposedForUpload',
beforeMonitorID
);
if (portDidChangeOnUpload) {
this.removeFromMonitorIDsByUploadState(
'disposedForUpload',
beforeMonitorID
);
}
// in case a service was paused but not disposed
this.removeFromMonitorIDsByUploadState('pausedForUpload', beforeMonitorID);
await this.startQueuedServices(portDidChangeOnUpload);
}
@ -256,7 +255,7 @@ export class MonitorManager extends CoreClientAware {
serviceStartParams: [, port],
connectToClient,
} of queued) {
const boardsState = await this.boardsService.getState();
const boardsState = await this.boardsService.getDetectedPorts();
const boardIsStillOnPort = Object.keys(boardsState)
.map((connection: string) => {
const portAddress = connection.split('|')[0];
@ -355,7 +354,7 @@ export class MonitorManager extends CoreClientAware {
* @param port
* @returns a unique monitor ID
*/
private monitorID(fqbn: string | undefined, port: Port): MonitorID {
private monitorID(fqbn: string | undefined, port: PortIdentifier): MonitorID {
const splitFqbn = fqbn?.split(':') || [];
const shortenedFqbn = splitFqbn.slice(0, 3).join(':') || '';
return `${shortenedFqbn}-${port.address}-${port.protocol}`;

View File

@ -2,7 +2,6 @@ import { injectable } from '@theia/core/shared/inversify';
import type {
NotificationServiceServer,
NotificationServiceClient,
AttachedBoardsChangeEvent,
BoardsPackage,
LibraryPackage,
ConfigState,
@ -11,6 +10,7 @@ import type {
IndexUpdateWillStartParams,
IndexUpdateDidCompleteParams,
IndexUpdateDidFailParams,
DetectedPorts,
} from '../common/protocol';
@injectable()
@ -69,9 +69,9 @@ export class NotificationServiceServerImpl
this.clients.forEach((client) => client.notifyLibraryDidUninstall(event));
}
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void {
notifyDetectedPortsDidChange(event: { detectedPorts: DetectedPorts }): void {
this.clients.forEach((client) =>
client.notifyAttachedBoardsDidChange(event)
client.notifyDetectedPortsDidChange(event)
);
}

View File

@ -6,7 +6,7 @@ import path from 'node:path';
import glob from 'glob';
import crypto from 'node:crypto';
import PQueue from 'p-queue';
import { Mutable } from '@theia/core/lib/common/types';
import type { Mutable } from '@theia/core/lib/common/types';
import URI from '@theia/core/lib/common/uri';
import { ILogger } from '@theia/core/lib/common/logger';
import { FileUri } from '@theia/core/lib/node/file-uri';
@ -128,11 +128,11 @@ export class SketchesServiceImpl
uri: string,
detectInvalidSketchNameError = true
): Promise<SketchWithDetails> {
const { client, instance } = await this.coreClient;
const { client } = await this.coreClient;
const req = new LoadSketchRequest();
const requestSketchPath = FileUri.fsPath(uri);
req.setSketchPath(requestSketchPath);
req.setInstance(instance);
// TODO: since the instance is not required on the request, can IDE2 do this faster or have a dedicated client for the sketch loading?
const stat = new Deferred<Stats | Error>();
lstat(requestSketchPath, (err, result) =>
err ? stat.resolve(err) : stat.resolve(result)

View File

@ -0,0 +1,426 @@
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
const disableJSDOM = enableJSDOM();
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
FrontendApplicationConfigProvider.set({});
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import {
LocalStorageService,
StorageService,
} from '@theia/core/lib/browser/storage-service';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { MessageService } from '@theia/core/lib/common/message-service';
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
import { Container, ContainerModule } from '@theia/core/shared/inversify';
import { expect } from 'chai';
import { BoardsDataStore } from '../../browser/boards/boards-data-store';
import { BoardsServiceProvider } from '../../browser/boards/boards-service-provider';
import { NotificationCenter } from '../../browser/notification-center';
import {
BoardIdentifierChangeEvent,
BoardsConfig,
BoardsConfigChangeEvent,
BoardsService,
DetectedPorts,
Port,
PortIdentifierChangeEvent,
} from '../../common/protocol/boards-service';
import { NotificationServiceServer } from '../../common/protocol/notification-service';
import { bindCommon, ConsoleLogger } from '../common/common-test-bindings';
import {
detectedPort,
esp32S3DevModule,
mkr1000,
mkr1000SerialPort,
undiscoveredSerialPort,
uno,
unoSerialPort,
} from '../common/fixtures';
disableJSDOM();
describe('board-service-provider', () => {
let toDisposeAfterEach: DisposableCollection;
let boardsServiceProvider: BoardsServiceProvider;
let notificationCenter: NotificationCenter;
beforeEach(async () => {
const container = createContainer();
container.get<FrontendApplicationStateService>(
FrontendApplicationStateService
).state = 'ready';
boardsServiceProvider = container.get<BoardsServiceProvider>(
BoardsServiceProvider
);
notificationCenter = container.get<NotificationCenter>(NotificationCenter);
toDisposeAfterEach = new DisposableCollection(
Disposable.create(() => boardsServiceProvider.onStop())
);
boardsServiceProvider.onStart();
await boardsServiceProvider.ready;
});
afterEach(() => {
toDisposeAfterEach.dispose();
});
it('should update the port (port identifier)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig(mkr1000SerialPort);
expect(didUpdate).to.be.true;
const expectedEvent: PortIdentifierChangeEvent = {
previousSelectedPort: unoSerialPort,
selectedPort: mkr1000SerialPort,
};
expect(events).deep.equals([expectedEvent]);
});
it('should update the port (boards config)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig({
selectedPort: mkr1000SerialPort,
selectedBoard: uno,
});
expect(didUpdate).to.be.true;
const expectedEvent: PortIdentifierChangeEvent = {
previousSelectedPort: unoSerialPort,
selectedPort: mkr1000SerialPort,
};
expect(events).deep.equals([expectedEvent]);
});
it('should not update the port if did not change (port identifier)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig(unoSerialPort);
expect(didUpdate).to.be.false;
expect(events).to.be.empty;
});
it('should update the board (board identifier)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig(mkr1000);
expect(didUpdate).to.be.true;
const expectedEvent: BoardIdentifierChangeEvent = {
previousSelectedBoard: uno,
selectedBoard: mkr1000,
};
expect(events).deep.equals([expectedEvent]);
});
it('should update the board (boards config)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig({
selectedBoard: mkr1000,
selectedPort: unoSerialPort,
});
expect(didUpdate).to.be.true;
const expectedEvent: BoardIdentifierChangeEvent = {
previousSelectedBoard: uno,
selectedBoard: mkr1000,
};
expect(events).deep.equals([expectedEvent]);
});
it('should not update the board if did not change (board identifier)', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig(uno);
expect(didUpdate).to.be.false;
expect(events).to.be.empty;
});
it('should update both the board and port', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig({
selectedBoard: mkr1000,
selectedPort: mkr1000SerialPort,
});
expect(didUpdate).to.be.true;
const expectedEvent: BoardIdentifierChangeEvent &
PortIdentifierChangeEvent = {
previousSelectedBoard: uno,
selectedBoard: mkr1000,
previousSelectedPort: unoSerialPort,
selectedPort: mkr1000SerialPort,
};
expect(events).deep.equals([expectedEvent]);
});
it('should update neither the board nor the port if did not change', () => {
boardsServiceProvider['_boardsConfig'] = {
selectedBoard: uno,
selectedPort: unoSerialPort,
};
const events: BoardsConfigChangeEvent[] = [];
toDisposeAfterEach.push(
boardsServiceProvider.onBoardsConfigDidChange((event) =>
events.push(event)
)
);
const didUpdate = boardsServiceProvider.updateConfig({
selectedBoard: uno,
selectedPort: unoSerialPort,
});
expect(didUpdate).to.be.false;
expect(events).to.be.empty;
});
it('should detect a port change and find selection index', () => {
let boardList = boardsServiceProvider.boardList;
const didUpdate = boardsServiceProvider.updateConfig({
selectedBoard: uno,
selectedPort: unoSerialPort,
});
expect(didUpdate).to.be.true;
expect(boardList.selectedIndex).to.be.equal(-1);
let selectedItem = boardList.items[boardList.selectedIndex];
expect(selectedItem).to.be.undefined;
// attach board
notificationCenter.notifyDetectedPortsDidChange({
detectedPorts: {
...detectedPort(unoSerialPort, uno),
},
});
boardList = boardsServiceProvider.boardList;
expect(boardsServiceProvider.boardList.selectedIndex).to.be.equal(0);
selectedItem = boardList.items[boardList.selectedIndex];
expect(selectedItem.board).to.be.deep.equal(uno);
expect(selectedItem.port).to.be.deep.equal(unoSerialPort);
// detach board
notificationCenter.notifyDetectedPortsDidChange({
detectedPorts: {},
});
boardList = boardsServiceProvider.boardList;
expect(boardsServiceProvider.boardList.selectedIndex).to.be.equal(-1);
selectedItem = boardList.items[boardList.selectedIndex];
expect(selectedItem).to.be.undefined;
});
it('should update the board selection history for the port', () => {
notificationCenter.notifyDetectedPortsDidChange({
detectedPorts: {
...detectedPort(undiscoveredSerialPort),
...detectedPort(unoSerialPort, uno),
...detectedPort(mkr1000SerialPort, mkr1000),
},
});
boardsServiceProvider.updateConfig({
selectedBoard: esp32S3DevModule,
selectedPort: undiscoveredSerialPort,
});
expect(boardsServiceProvider['_boardListHistory']).to.be.deep.equal({
[Port.keyOf(undiscoveredSerialPort)]: esp32S3DevModule,
});
boardsServiceProvider.updateConfig({
selectedBoard: esp32S3DevModule,
selectedPort: unoSerialPort,
});
expect(boardsServiceProvider['_boardListHistory']).to.be.deep.equal({
[Port.keyOf(undiscoveredSerialPort)]: esp32S3DevModule,
[Port.keyOf(unoSerialPort)]: esp32S3DevModule,
});
boardsServiceProvider.updateConfig({
selectedBoard: uno,
selectedPort: unoSerialPort,
});
expect(boardsServiceProvider['_boardListHistory']).to.be.deep.equal({
[Port.keyOf(undiscoveredSerialPort)]: esp32S3DevModule,
});
});
type UpdateBoardListHistoryParams = Parameters<
BoardsServiceProvider['maybeUpdateBoardListHistory']
>[0];
type BoardListHistoryUpdateResult = ReturnType<
BoardsServiceProvider['maybeUpdateBoardListHistory']
>;
interface BoardListHistoryTestSuite {
readonly init: BoardsConfig;
readonly detectedPorts?: DetectedPorts;
readonly params: UpdateBoardListHistoryParams;
readonly expected: BoardListHistoryUpdateResult;
/**
* Optional test title extension.
*/
readonly description?: string;
/**
* Optional test assertions.
*/
readonly assert?: (
actual: BoardListHistoryUpdateResult,
service: BoardsServiceProvider
) => void;
}
const boardListHistoryTestSuites: BoardListHistoryTestSuite[] = [
{
description: "'portToSelect' is undefined",
init: { selectedBoard: uno, selectedPort: unoSerialPort },
params: { boardToSelect: mkr1000, portToSelect: undefined },
expected: undefined,
},
{
description: "'boardToSelect' is undefined",
init: { selectedBoard: uno, selectedPort: unoSerialPort },
params: { boardToSelect: undefined, portToSelect: mkr1000SerialPort },
expected: undefined,
},
{
description: "'selectedBoard' fallback when 'ignore-board'",
init: { selectedBoard: uno, selectedPort: unoSerialPort },
params: {
boardToSelect: 'ignore-board',
portToSelect: mkr1000SerialPort,
},
expected: { [Port.keyOf(mkr1000SerialPort)]: uno },
},
{
description: "'selectedPort' fallback when 'ignore-port'",
init: { selectedBoard: uno, selectedPort: unoSerialPort },
params: {
boardToSelect: mkr1000,
portToSelect: 'ignore-port',
},
expected: { [Port.keyOf(unoSerialPort)]: mkr1000 },
},
{
description:
'unsets history when board+port is from a detected port from a discovered board',
init: { selectedBoard: undefined, selectedPort: undefined },
params: {
boardToSelect: uno,
portToSelect: unoSerialPort,
},
detectedPorts: {
...detectedPort(unoSerialPort, uno),
},
expected: { [Port.keyOf(unoSerialPort)]: undefined },
},
];
boardListHistoryTestSuites.forEach((suite, index) =>
it(`should handle board list history updates (${
suite.description ? suite.description : `#${index + 1}`
})`, () => {
const { init, params, expected, assert, detectedPorts } = suite;
boardsServiceProvider['_boardsConfig'] = init;
if (detectedPorts) {
notificationCenter.notifyDetectedPortsDidChange({ detectedPorts });
}
const actual =
boardsServiceProvider['maybeUpdateBoardListHistory'](params);
expect(actual).to.be.deep.equal(expected);
assert?.(actual, boardsServiceProvider);
})
);
function createContainer(): Container {
const container = new Container({ defaultScope: 'Singleton' });
container.load(
new ContainerModule((bind, unbind, isBound, rebind) => {
bindCommon(bind);
bind(MessageService).toConstantValue(<MessageService>{});
bind(BoardsService).toConstantValue(<BoardsService>{
getDetectedPorts() {
return {};
},
});
bind(NotificationCenter).toSelf().inSingletonScope();
bind(NotificationServiceServer).toConstantValue(<
NotificationServiceServer
>{
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setClient(_) {
// nothing
},
});
bind(FrontendApplicationStateService).toSelf().inSingletonScope();
bind(BoardsDataStore).toConstantValue(<BoardsDataStore>{});
bind(LocalStorageService).toSelf().inSingletonScope();
bind(WindowService).toConstantValue(<WindowService>{});
bind(StorageService).toService(LocalStorageService);
bind(BoardsServiceProvider).toSelf().inSingletonScope();
// IDE2's test console logger does not support `Loggable` arg.
// Rebind logger to suppress `[Function (anonymous)]` messages in tests when the storage service is initialized without `window.localStorage`.
// https://github.com/eclipse-theia/theia/blob/04c8cf07843ea67402131132e033cdd54900c010/packages/core/src/browser/storage-service.ts#L60
bind(MockLogger).toSelf().inSingletonScope();
rebind(ConsoleLogger).toService(MockLogger);
})
);
return container;
}
});

View File

@ -1,247 +0,0 @@
// import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
// const disableJSDOM = enableJSDOM();
// import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
// import { ApplicationProps } from '@theia/application-package/lib/application-props';
// FrontendApplicationConfigProvider.set({
// ...ApplicationProps.DEFAULT.frontend.config,
// });
// import { MessageService } from '@theia/core';
// import { BoardsServiceProvider } from '../../browser/boards/boards-service-provider';
// import { BoardsListWidgetFrontendContribution } from '../../browser/boards/boards-widget-frontend-contribution';
// import {
// Board,
// BoardsPackage,
// BoardsService,
// Port,
// ResponseServiceArduino,
// } from '../../common/protocol';
// import { IMock, It, Mock, Times } from 'typemoq';
// import { Container, ContainerModule } from '@theia/core/shared/inversify';
// import { BoardsAutoInstaller } from '../../browser/boards/boards-auto-installer';
// import { BoardsConfig } from '../../browser/boards/boards-config';
// import { tick } from '../utils';
// import { ListWidget } from '../../browser/widgets/component-list/list-widget';
// disableJSDOM();
// const aBoard: Board = {
// fqbn: 'some:board:fqbn',
// name: 'Some Arduino Board',
// port: { address: '/lol/port1234', protocol: 'serial' },
// };
// const aPort: Port = {
// address: aBoard.port!.address,
// protocol: aBoard.port!.protocol,
// };
// const aBoardConfig: BoardsConfig.Config = {
// selectedBoard: aBoard,
// selectedPort: aPort,
// };
// const aPackage: BoardsPackage = {
// author: 'someAuthor',
// availableVersions: ['some.ver.sion', 'some.other.version'],
// boards: [aBoard],
// deprecated: false,
// description: 'Some Arduino Board, Some Other Arduino Board',
// id: 'some:arduinoCoreId',
// installable: true,
// moreInfoLink: 'http://www.some-url.lol/',
// name: 'Some Arduino Package',
// summary: 'Boards included in this package:',
// };
// const anInstalledPackage: BoardsPackage = {
// ...aPackage,
// installedVersion: 'some.ver.sion',
// };
// describe('BoardsAutoInstaller', () => {
// let subject: BoardsAutoInstaller;
// let messageService: IMock<MessageService>;
// let boardsService: IMock<BoardsService>;
// let boardsServiceClient: IMock<BoardsServiceProvider>;
// let responseService: IMock<ResponseServiceArduino>;
// let boardsManagerFrontendContribution: IMock<BoardsListWidgetFrontendContribution>;
// let boardsManagerWidget: IMock<ListWidget<BoardsPackage>>;
// let testContainer: Container;
// beforeEach(() => {
// testContainer = new Container();
// messageService = Mock.ofType<MessageService>();
// boardsService = Mock.ofType<BoardsService>();
// boardsServiceClient = Mock.ofType<BoardsServiceProvider>();
// responseService = Mock.ofType<ResponseServiceArduino>();
// boardsManagerFrontendContribution =
// Mock.ofType<BoardsListWidgetFrontendContribution>();
// boardsManagerWidget = Mock.ofType<ListWidget<BoardsPackage>>();
// boardsManagerWidget.setup((b) =>
// b.refresh(aPackage.name.toLocaleLowerCase())
// );
// boardsManagerFrontendContribution
// .setup((b) => b.openView({ reveal: true }))
// .returns(async () => boardsManagerWidget.object);
// messageService
// .setup((m) => m.showProgress(It.isAny(), It.isAny()))
// .returns(async () => ({
// cancel: () => null,
// id: '',
// report: () => null,
// result: Promise.resolve(''),
// }));
// responseService
// .setup((r) => r.onProgressDidChange(It.isAny()))
// .returns(() => ({ dispose: () => null }));
// const module = new ContainerModule((bind) => {
// bind(BoardsAutoInstaller).toSelf();
// bind(MessageService).toConstantValue(messageService.object);
// bind(BoardsService).toConstantValue(boardsService.object);
// bind(BoardsServiceProvider).toConstantValue(boardsServiceClient.object);
// bind(ResponseServiceArduino).toConstantValue(responseService.object);
// bind(BoardsListWidgetFrontendContribution).toConstantValue(
// boardsManagerFrontendContribution.object
// );
// });
// testContainer.load(module);
// subject = testContainer.get(BoardsAutoInstaller);
// });
// context('when it starts', () => {
// it('should register to the BoardsServiceClient in order to check the packages every a new board is plugged in', () => {
// subject.onStart();
// boardsServiceClient.verify(
// (b) => b.onBoardsConfigChanged(It.isAny()),
// Times.once()
// );
// });
// context('and it checks the installable packages', () => {
// context(`and a port and a board a selected`, () => {
// beforeEach(() => {
// boardsServiceClient
// .setup((b) => b.boardsConfig)
// .returns(() => aBoardConfig);
// });
// context('if no package for the board is already installed', () => {
// context('if a candidate package for the board is found', () => {
// beforeEach(() => {
// boardsService
// .setup((b) => b.search(It.isValue({})))
// .returns(async () => [aPackage]);
// });
// it('should show a notification suggesting to install that package', async () => {
// messageService
// .setup((m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString())
// )
// .returns(() => Promise.resolve('Install Manually'));
// subject.onStart();
// await tick();
// messageService.verify(
// (m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString()),
// Times.once()
// );
// });
// context(`if the answer to the message is 'Yes'`, () => {
// beforeEach(() => {
// messageService
// .setup((m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString())
// )
// .returns(() => Promise.resolve('Yes'));
// });
// it('should install the package', async () => {
// subject.onStart();
// await tick();
// messageService.verify(
// (m) => m.showProgress(It.isAny(), It.isAny()),
// Times.once()
// );
// });
// });
// context(
// `if the answer to the message is 'Install Manually'`,
// () => {
// beforeEach(() => {
// messageService
// .setup((m) =>
// m.info(
// It.isAnyString(),
// It.isAnyString(),
// It.isAnyString()
// )
// )
// .returns(() => Promise.resolve('Install Manually'));
// });
// it('should open the boards manager widget', () => {
// subject.onStart();
// });
// }
// );
// });
// context('if a candidate package for the board is not found', () => {
// beforeEach(() => {
// boardsService
// .setup((b) => b.search(It.isValue({})))
// .returns(async () => []);
// });
// it('should do nothing', async () => {
// subject.onStart();
// await tick();
// messageService.verify(
// (m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString()),
// Times.never()
// );
// });
// });
// });
// context(
// 'if one of the packages for the board is already installed',
// () => {
// beforeEach(() => {
// boardsService
// .setup((b) => b.search(It.isValue({})))
// .returns(async () => [aPackage, anInstalledPackage]);
// messageService
// .setup((m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString())
// )
// .returns(() => Promise.resolve('Yes'));
// });
// it('should do nothing', async () => {
// subject.onStart();
// await tick();
// messageService.verify(
// (m) =>
// m.info(It.isAnyString(), It.isAnyString(), It.isAnyString()),
// Times.never()
// );
// });
// }
// );
// });
// context('and there is no selected board or port', () => {
// it('should do nothing', async () => {
// subject.onStart();
// await tick();
// messageService.verify(
// (m) => m.info(It.isAnyString(), It.isAnyString(), It.isAnyString()),
// Times.never()
// );
// });
// });
// });
// });
// });

View File

@ -0,0 +1,8 @@
import { Container, ContainerModule } from '@theia/core/shared/inversify';
import { bindCommon } from '../common/common-test-bindings';
export function createBaseContainer(): Container {
const container = new Container({ defaultScope: 'Singleton' });
container.load(new ContainerModule((bind) => bindCommon(bind)));
return container;
}

View File

@ -1,44 +1,36 @@
import { BoardsConfig } from '../../../browser/boards/boards-config';
import { Board, BoardsPackage, Port } from '../../../common/protocol';
import type {
Board,
BoardsConfig,
BoardsPackage,
Port,
} from '../../../common/protocol';
export const aBoard: Board = {
fqbn: 'some:board:fqbn',
name: 'Some Arduino Board',
port: {
address: '/lol/port1234',
addressLabel: '/lol/port1234',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
},
};
export const aPort: Port = {
address: aBoard.port!.address,
addressLabel: aBoard.port!.addressLabel,
protocol: aBoard.port!.protocol,
protocolLabel: aBoard.port!.protocolLabel,
const aPort: Port = {
address: '/lol/port1234',
addressLabel: '/lol/port1234',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
};
export const aBoardConfig: BoardsConfig.Config = {
selectedBoard: aBoard,
export const aBoardsConfig: BoardsConfig = {
selectedBoard: { name: aBoard.name, fqbn: aBoard.fqbn },
selectedPort: aPort,
};
export const anotherBoard: Board = {
fqbn: 'another:board:fqbn',
name: 'Another Arduino Board',
port: {
address: '/kek/port5678',
addressLabel: '/kek/port5678',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
},
};
export const anotherPort: Port = {
address: anotherBoard.port!.address,
addressLabel: anotherBoard.port!.addressLabel,
protocol: anotherBoard.port!.protocol,
protocolLabel: anotherBoard.port!.protocolLabel,
address: '/kek/port5678',
addressLabel: '/kek/port5678',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
};
export const anotherBoardConfig: BoardsConfig.Config = {
selectedBoard: anotherBoard,
export const anotherBoardsConfig: BoardsConfig = {
selectedBoard: { name: anotherBoard.name, fqbn: anotherBoard.fqbn },
selectedPort: anotherPort,
};

View File

@ -0,0 +1,594 @@
import { expect } from 'chai';
import { Unknown } from '../../common/nls';
import {
BoardListLabels,
createBoardList,
EditBoardsConfigActionParams,
isInferredBoardListItem,
isMultiBoardsBoardListItem,
SelectBoardsConfigActionParams,
} from '../../common/protocol/board-list';
import {
emptyBoardsConfig,
notConnected,
selectBoard,
unconfirmedBoard,
} from '../../common/protocol/boards-service';
import {
arduinoNanoEsp32,
bluetoothSerialPort,
builtinSerialPort,
createPort,
detectedPort,
detectedPorts,
esp32NanoEsp32,
esp32S3Box,
esp32S3DevModule,
history,
mkr1000,
mkr1000NetworkPort,
mkr1000SerialPort,
nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
nanoEsp32SerialPort,
undiscoveredSerialPort,
undiscoveredUsbToUARTSerialPort,
uno,
unoSerialPort,
} from './fixtures';
describe('board-list', () => {
describe('boardList#labels', () => {
it('should handle no selected board+port', () => {
const { labels } = createBoardList({});
const expected: BoardListLabels = {
boardLabel: selectBoard,
portProtocol: undefined,
selected: false,
tooltip: selectBoard,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle port selected (port detected)', () => {
const { labels } = createBoardList(
{
...detectedPort(unoSerialPort, uno),
},
{ selectedBoard: undefined, selectedPort: unoSerialPort }
);
const expected: BoardListLabels = {
boardLabel: selectBoard,
portProtocol: undefined,
selected: false,
tooltip: unoSerialPort.address,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle port selected (port not detected)', () => {
const { labels } = createBoardList(
{
...detectedPort(mkr1000SerialPort, mkr1000),
},
{ selectedBoard: undefined, selectedPort: unoSerialPort }
);
const expected: BoardListLabels = {
boardLabel: selectBoard,
portProtocol: undefined,
selected: false,
tooltip: `${unoSerialPort.address} ${notConnected}`,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle board selected (with FQBN)', () => {
const { labels } = createBoardList(
{},
{ selectedBoard: uno, selectedPort: undefined }
);
const expected: BoardListLabels = {
boardLabel: uno.name,
portProtocol: undefined,
selected: false,
tooltip: `${uno.name} (${uno.fqbn})`,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle board selected (no FQBN)', () => {
const { labels } = createBoardList(
{},
{
selectedBoard: { name: 'my board', fqbn: undefined },
selectedPort: undefined,
}
);
const expected: BoardListLabels = {
boardLabel: 'my board',
portProtocol: undefined,
selected: false,
tooltip: 'my board',
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle both selected (port not detected)', () => {
const { labels } = createBoardList(
{
...detectedPort(mkr1000SerialPort, mkr1000),
},
{ selectedBoard: mkr1000, selectedPort: unoSerialPort }
);
const expected: BoardListLabels = {
boardLabel: mkr1000.name,
portProtocol: 'serial',
selected: false,
tooltip: `${mkr1000.name} (${mkr1000.fqbn})\n${unoSerialPort.address} ${notConnected}`,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle both selected (board not discovered)', () => {
const { labels } = createBoardList(
{
...detectedPort(unoSerialPort, uno),
},
{ selectedBoard: mkr1000, selectedPort: unoSerialPort }
);
const expected: BoardListLabels = {
boardLabel: mkr1000.name,
portProtocol: 'serial',
selected: false,
tooltip: `${mkr1000.name} (${mkr1000.fqbn})\n${unoSerialPort.address}`,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle both selected (no FQBN)', () => {
const { labels } = createBoardList(
{
...detectedPort(unoSerialPort, { name: 'my board', fqbn: undefined }),
},
{
selectedBoard: { name: 'my board', fqbn: undefined },
selectedPort: unoSerialPort,
}
);
const expected: BoardListLabels = {
boardLabel: 'my board',
portProtocol: 'serial',
selected: true,
tooltip: `my board\n${unoSerialPort.address}`,
};
expect(labels).to.be.deep.equal(expected);
});
it('should handle both selected', () => {
const { labels } = createBoardList(
{
...detectedPort(mkr1000NetworkPort, mkr1000),
},
{ selectedBoard: mkr1000, selectedPort: mkr1000NetworkPort }
);
const expected: BoardListLabels = {
boardLabel: mkr1000.name,
portProtocol: 'network',
selected: true,
tooltip: `${mkr1000.name} (${mkr1000.fqbn})\n${mkr1000NetworkPort.address}`,
};
expect(labels).to.be.deep.equal(expected);
});
});
describe('createBoardList', () => {
it('should sort the items deterministically', () => {
const { items } = createBoardList(detectedPorts);
expect(items.length).to.be.equal(Object.keys(detectedPorts).length);
expect(items[0].board).deep.equal(mkr1000);
expect(items[1].board).deep.equal(uno);
expect(items[2].board).is.undefined;
expect(isMultiBoardsBoardListItem(items[2])).to.be.true;
const boards2 = isMultiBoardsBoardListItem(items[2])
? items[2].boards
: undefined;
expect(boards2).deep.equal([arduinoNanoEsp32, esp32NanoEsp32]);
expect(items[3].board).is.undefined;
expect(isMultiBoardsBoardListItem(items[3])).to.be.true;
const boards3 = isMultiBoardsBoardListItem(items[3])
? items[3].boards
: undefined;
expect(boards3).deep.equal([esp32S3Box, esp32S3DevModule]);
expect(items[4].port).deep.equal(builtinSerialPort);
expect(items[5].port).deep.equal(bluetoothSerialPort);
expect(items[6].port).deep.equal(undiscoveredSerialPort);
expect(items[7].port).deep.equal(undiscoveredUsbToUARTSerialPort);
expect(items[8].port.protocol).equal('network');
expect(items[8].board).deep.equal(mkr1000);
});
it('should sort Arduino items before others', () => {
const detectedPorts = {
...detectedPort(createPort('a'), { name: 'aa', fqbn: 'arduino:a:a' }),
...detectedPort(createPort('b'), { name: 'ab', fqbn: 'other:a:b' }),
...detectedPort(createPort('c'), { name: 'ac', fqbn: 'arduino:a:c' }),
};
const { items } = createBoardList(detectedPorts);
expect(items.length).to.be.equal(3);
expect(items[0].board?.name).to.be.equal('aa');
expect(items[1].board?.name).to.be.equal('ac');
expect(items[2].board?.name).to.be.equal('ab');
});
it('should sort items by inferred board if any', () => {
const portA = createPort('portA');
const portB = createPort('portB');
const detectedPorts = {
...detectedPort(portA),
...detectedPort(portB),
};
const boardListHistory = {
...history(portA, { name: 'bbb', fqbn: undefined }),
...history(portB, { name: 'aaa', fqbn: undefined }),
};
const { items } = createBoardList(
detectedPorts,
emptyBoardsConfig(),
boardListHistory
);
expect(items.length).to.be.equal(2);
expect(items[0].port.address).to.be.equal('portB');
expect(items[0].board).to.be.undefined;
const inferredBoardA = isInferredBoardListItem(items[0])
? items[0].inferredBoard
: undefined;
expect(inferredBoardA).to.be.not.undefined;
expect(inferredBoardA?.name).to.be.equal('aaa');
expect(items[1].port.address).to.be.equal('portA');
expect(items[1].board).to.be.undefined;
expect(isInferredBoardListItem(items[1])).to.be.true;
const inferredBoardB = isInferredBoardListItem(items[1])
? items[1].inferredBoard
: undefined;
expect(inferredBoardB).to.be.not.undefined;
expect(inferredBoardB?.name).to.be.equal('bbb');
});
it('should sort ambiguous boards with unique board name before other ambiguous boards', () => {
const portA = createPort('portA');
const portB = createPort('portB');
const unique_ArduinoZZZ = { fqbn: 'arduino:e:f', name: 'zzz' };
const unique_OtherZZZ = { fqbn: 'a:b:c', name: 'zzz' };
const nonUnique_AAA = { fqbn: 'g:h:i', name: 'aaa' };
const nonUnique_BBB = { fqbn: 'j:k:l', name: 'bbb' };
const detectedPorts = {
...detectedPort(portA, nonUnique_AAA, nonUnique_BBB),
...detectedPort(portB, unique_OtherZZZ, unique_ArduinoZZZ),
};
const { items } = createBoardList(detectedPorts);
expect(items.length).to.be.equal(2);
expect(isMultiBoardsBoardListItem(items[0])).to.be.true;
const ambiguousBoardWithUniqueName = isMultiBoardsBoardListItem(items[0])
? items[0]
: undefined;
expect(ambiguousBoardWithUniqueName).to.be.not.undefined;
expect(ambiguousBoardWithUniqueName?.labels.boardLabel).to.be.equal(
unique_ArduinoZZZ.name
);
expect(ambiguousBoardWithUniqueName?.port).to.be.deep.equal(portB);
expect(ambiguousBoardWithUniqueName?.boards).to.be.deep.equal([
unique_ArduinoZZZ,
unique_OtherZZZ,
]);
expect(isMultiBoardsBoardListItem(items[1])).to.be.true;
const ambiguousBoardWithoutName = isMultiBoardsBoardListItem(items[1])
? items[1]
: undefined;
expect(ambiguousBoardWithoutName).to.be.not.undefined;
expect(ambiguousBoardWithoutName?.labels.boardLabel).to.be.equal(
unconfirmedBoard
);
expect(ambiguousBoardWithoutName?.port).to.be.deep.equal(portA);
expect(ambiguousBoardWithoutName?.boards).to.be.deep.equal([
nonUnique_AAA,
nonUnique_BBB,
]);
});
it('should detect when a discovered board is overridden by a historical selection', () => {
const otherBoard = { name: 'other', fqbn: 'a:b:c' };
const detectedPorts = {
...detectedPort(unoSerialPort, uno),
};
const boardListHistory = {
...history(unoSerialPort, otherBoard),
};
const { items } = createBoardList(
detectedPorts,
emptyBoardsConfig(),
boardListHistory
);
expect(items.length).to.be.equal(1);
const inferredBoard = isInferredBoardListItem(items[0])
? items[0]
: undefined;
expect(inferredBoard).is.not.undefined;
expect(inferredBoard?.inferredBoard).to.be.deep.equal(otherBoard);
expect(inferredBoard?.board).to.be.deep.equal(uno);
});
it(`should use the '${Unknown}' as the board label when no boards were discovered on a detected port`, () => {
const { items } = createBoardList({ ...detectedPort(unoSerialPort) });
expect(items[0].labels.boardLabel).to.be.equal(Unknown);
});
describe('defaultAction', () => {
it("'select' should be the default action for identifier boards", () => {
const { items } = createBoardList({
...detectedPort(mkr1000SerialPort, mkr1000),
});
const item = items[0];
expect(item.defaultAction.type).to.be.equal('select-boards-config');
expect(item.defaultAction.params).to.be.deep.equal({
selectedPort: mkr1000SerialPort,
selectedBoard: mkr1000,
});
});
it("'select' should be the default action for manually selected items (no discovered boards)", () => {
const { items } = createBoardList(
{
...detectedPort(undiscoveredSerialPort),
},
emptyBoardsConfig(),
{
...history(undiscoveredSerialPort, uno),
}
);
const item = items[0];
expect(item.defaultAction.type).to.be.equal('select-boards-config');
expect(item.defaultAction.params).to.be.deep.equal({
selectedPort: undiscoveredSerialPort,
selectedBoard: uno,
});
});
it("'select' should be the default action for manually selected items (ambiguous boards)", () => {
const { items } = createBoardList(
{
...detectedPort(
nanoEsp32SerialPort,
arduinoNanoEsp32,
esp32NanoEsp32
),
},
emptyBoardsConfig(),
{
...history(nanoEsp32SerialPort, mkr1000),
}
);
const item = items[0];
expect(item.defaultAction.type).to.be.equal('select-boards-config');
expect(item.defaultAction.params).to.be.deep.equal({
selectedBoard: mkr1000,
selectedPort: nanoEsp32SerialPort,
});
});
it("'edit' should be the default action for ports with no boards", () => {
const { items } = createBoardList({
...detectedPort(undiscoveredSerialPort),
});
const item = items[0];
expect(item).to.be.not.undefined;
expect(item.defaultAction.type).to.be.equal('edit-boards-config');
const params = <EditBoardsConfigActionParams>item.defaultAction.params;
const expectedParams: EditBoardsConfigActionParams = {
query: '',
portToSelect: undiscoveredSerialPort,
};
expect(params).to.be.deep.equal(expectedParams);
});
it("'edit' should be the default action for ports with multiple boards (unique board name)", () => {
const { items } = createBoardList({
...detectedPort(
nanoEsp32SerialPort,
arduinoNanoEsp32,
esp32NanoEsp32
),
});
const item = items[0];
expect(item).to.be.not.undefined;
expect(item.defaultAction.type).to.be.equal('edit-boards-config');
const params = <EditBoardsConfigActionParams>item.defaultAction.params;
const expectedParams: EditBoardsConfigActionParams = {
query: arduinoNanoEsp32.name,
portToSelect: nanoEsp32SerialPort,
searchSet: [arduinoNanoEsp32, esp32NanoEsp32],
};
expect(params).to.be.deep.equal(expectedParams);
});
it("'edit' should be the default action for ports with multiple boards (no unique board name)", () => {
const { items } = createBoardList({
...detectedPort(
nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
esp32S3DevModule,
esp32S3Box
),
});
const item = items[0];
expect(item).to.be.not.undefined;
expect(item.defaultAction.type).to.be.equal('edit-boards-config');
const params = <EditBoardsConfigActionParams>item.defaultAction.params;
const expectedParams: EditBoardsConfigActionParams = {
query: '',
portToSelect: nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
searchSet: [esp32S3Box, esp32S3DevModule],
};
expect(params).to.be.deep.equal(expectedParams);
});
});
describe('otherActions', () => {
it('should provide no other actions for identified board', () => {
const { items } = createBoardList({
...detectedPort(mkr1000SerialPort, mkr1000),
});
const item = items[0];
expect(item.otherActions).to.be.empty;
});
it('should provide no other actions for identified board (when historical revision is self)', () => {
const { items } = createBoardList(
{
...detectedPort(mkr1000SerialPort, mkr1000),
},
emptyBoardsConfig(),
{
...history(mkr1000SerialPort, mkr1000),
}
);
const item = items[0];
expect(item.otherActions).to.be.empty;
});
it('should provide no other actions for unknown boards', () => {
const { items } = createBoardList({
...detectedPort(undiscoveredSerialPort),
});
const item = items[0];
expect(item.otherActions).to.be.empty;
});
it('should provide no other actions for ambiguous boards', () => {
const { items } = createBoardList({
...detectedPort(
nanoEsp32SerialPort,
arduinoNanoEsp32,
esp32NanoEsp32
),
});
const item = items[0];
expect(item.otherActions).to.be.empty;
});
it("should provide 'edit' action for unidentified items with manually selected board", () => {
const { items } = createBoardList(
{
...detectedPort(undiscoveredSerialPort),
},
emptyBoardsConfig(),
{
...history(undiscoveredSerialPort, uno),
}
);
const item = items[0];
const expectedParams: EditBoardsConfigActionParams = {
query: uno.name,
portToSelect: undiscoveredSerialPort,
};
expect(item.otherActions).to.be.deep.equal({
edit: {
params: expectedParams,
type: 'edit-boards-config',
},
});
});
it("should provide 'edit' action for ambiguous items with manually selected board (unique board name)", () => {
const { items } = createBoardList(
{
...detectedPort(
nanoEsp32SerialPort,
esp32NanoEsp32,
arduinoNanoEsp32
),
},
emptyBoardsConfig(),
{
...history(nanoEsp32SerialPort, arduinoNanoEsp32),
}
);
const item = items[0];
const expectedParams: EditBoardsConfigActionParams = {
query: arduinoNanoEsp32.name,
portToSelect: nanoEsp32SerialPort,
searchSet: [arduinoNanoEsp32, esp32NanoEsp32],
};
expect(item.otherActions).to.be.deep.equal({
edit: {
params: expectedParams,
type: 'edit-boards-config',
},
});
});
it("should provide 'edit' action for ambiguous items with manually selected board (no unique board name)", () => {
const { items } = createBoardList(
{
...detectedPort(
nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
esp32S3Box,
esp32S3DevModule
),
},
emptyBoardsConfig(),
{
...history(nanoEsp32DetectsMultipleEsp32BoardsSerialPort, uno),
}
);
const item = items[0];
const expectedParams: EditBoardsConfigActionParams = {
query: '',
portToSelect: nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
searchSet: [esp32S3Box, esp32S3DevModule],
};
expect(item.otherActions).to.be.deep.equal({
edit: {
params: expectedParams,
type: 'edit-boards-config',
},
});
});
it("should provide 'edit' and 'revert' actions for identified items with a manually overridden board", () => {
const { items } = createBoardList(
{
...detectedPort(mkr1000SerialPort, mkr1000),
},
emptyBoardsConfig(),
{
...history(mkr1000SerialPort, uno),
}
);
const item = items[0];
const expectedEditParams: EditBoardsConfigActionParams = {
query: uno.name,
portToSelect: mkr1000SerialPort,
};
const expectedRevertParams: SelectBoardsConfigActionParams = {
selectedBoard: mkr1000,
selectedPort: item.port,
};
expect(item.otherActions).to.be.deep.equal({
edit: {
params: expectedEditParams,
type: 'edit-boards-config',
},
revert: {
params: expectedRevertParams,
type: 'select-boards-config',
},
});
});
});
});
});

View File

@ -1,8 +1,8 @@
import { Deferred } from '@theia/core/lib/common/promise-util';
import { Mutable } from '@theia/core/lib/common/types';
import type { Mutable } from '@theia/core/lib/common/types';
import { expect } from 'chai';
import {
AttachedBoardsChangeEvent,
boardIdentifierEquals,
boardIdentifierComparator,
BoardInfo,
getBoardInfo,
noNativeSerialPort,
@ -11,86 +11,114 @@ import {
selectPortForInfo,
unknownBoard,
} from '../../common/protocol';
import { createBoardList } from '../../common/protocol/board-list';
import { firstToUpperCase } from '../../common/utils';
describe('boards-service', () => {
describe('AttachedBoardsChangeEvent', () => {
it('should detect one attached port', () => {
const event = <AttachedBoardsChangeEvent & any>{
oldState: {
boards: [
{
name: 'Arduino MKR1000',
fqbn: 'arduino:samd:mkr1000',
port: '/dev/cu.usbmodem14601',
},
{
name: 'Arduino Uno',
fqbn: 'arduino:avr:uno',
port: '/dev/cu.usbmodem14501',
},
],
ports: [
{
protocol: 'serial',
address: '/dev/cu.usbmodem14501',
},
{
protocol: 'serial',
address: '/dev/cu.usbmodem14601',
},
{
protocol: 'serial',
address: '/dev/cu.Bluetooth-Incoming-Port',
},
{ protocol: 'serial', address: '/dev/cu.MALS' },
{ protocol: 'serial', address: '/dev/cu.SOC' },
],
},
newState: {
boards: [
{
name: 'Arduino MKR1000',
fqbn: 'arduino:samd:mkr1000',
port: '/dev/cu.usbmodem1460',
},
{
name: 'Arduino Uno',
fqbn: 'arduino:avr:uno',
port: '/dev/cu.usbmodem14501',
},
],
ports: [
{
protocol: 'serial',
address: '/dev/cu.SLAB_USBtoUART',
},
{
protocol: 'serial',
address: '/dev/cu.usbmodem14501',
},
{
protocol: 'serial',
address: '/dev/cu.usbmodem14601',
},
{
protocol: 'serial',
address: '/dev/cu.Bluetooth-Incoming-Port',
},
{ protocol: 'serial', address: '/dev/cu.MALS' },
{ protocol: 'serial', address: '/dev/cu.SOC' },
],
},
};
const diff = AttachedBoardsChangeEvent.diff(event);
expect(diff.attached.boards).to.be.empty; // tslint:disable-line:no-unused-expression
expect(diff.detached.boards).to.be.empty; // tslint:disable-line:no-unused-expression
expect(diff.detached.ports).to.be.empty; // tslint:disable-line:no-unused-expression
expect(diff.attached.ports.length).to.be.equal(1);
expect(diff.attached.ports[0].address).to.be.equal(
'/dev/cu.SLAB_USBtoUART'
describe('boardIdentifierEquals', () => {
it('should not be equal when the names equal but the FQBNs are different', () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: 'a:b:c' },
{ name: 'a', fqbn: 'x:y:z' }
);
expect(actual).to.be.false;
});
it('should not be equal when the names equal but the FQBNs are different (undefined)', () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: 'a:b:c' },
{ name: 'a', fqbn: undefined }
);
expect(actual).to.be.false;
});
it("should be equal when the names do not match but the FQBNs are the same (it's something IDE2 assumes to be handled by the platform or CLI)", () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: 'a:b:c' },
{ name: 'b', fqbn: 'a:b:c' }
);
expect(actual).to.be.true;
});
it('should be equal when the names equal and the FQBNs are missing', () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: undefined },
{ name: 'a', fqbn: undefined }
);
expect(actual).to.be.true;
});
it('should be equal when both the name and FQBN are the same, but one of the FQBN has board config options', () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: 'a:b:c:menu_1=value' },
{ name: 'a', fqbn: 'a:b:c' }
);
expect(actual).to.be.true;
});
it('should not be equal when both the name and FQBN are the same, but one of the FQBN has board config options (looseFqbn: false)', () => {
const actual = boardIdentifierEquals(
{ name: 'a', fqbn: 'a:b:c:menu_1=value' },
{ name: 'a', fqbn: 'a:b:c' },
{ looseFqbn: false }
);
expect(actual).to.be.false;
});
});
describe('boardIdentifierComparator', () => {
it('should sort items before falsy', () =>
expect(
boardIdentifierComparator({ name: 'a', fqbn: 'a:b:c' }, undefined)
).to.be.equal(-1));
it("should sort 'arduino' boards before others", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: 'arduino:b:c' },
{ name: 'a', fqbn: 'x:y:z' }
)
).to.be.equal(-1));
it("should sort 'arduino' boards before others (other is falsy)", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: 'arduino:b:c' },
{ name: 'a', fqbn: undefined }
)
).to.be.equal(-1));
it("should sort boards by 'name' (with FQBNs)", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: 'a:b:c' },
{ name: 'a', fqbn: 'x:y:z' }
)
).to.be.equal(1));
it("should sort boards by 'name' (no FQBNs)", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: undefined },
{ name: 'a', fqbn: undefined }
)
).to.be.equal(1));
it("should sort boards by 'name' (one FQBN)", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: 'a:b:c' },
{ name: 'a', fqbn: undefined }
)
).to.be.equal(1));
it("should sort boards by 'name' (both 'arduino' vendor)", () =>
expect(
boardIdentifierComparator(
{ name: 'b', fqbn: 'arduino:b:c' },
{ name: 'a', fqbn: 'arduino:y:z' }
)
).to.be.equal(1));
});
describe('getBoardInfo', () => {
@ -112,7 +140,7 @@ describe('boards-service', () => {
});
it('should handle when no port is selected', async () => {
const info = await getBoardInfo(undefined, never());
const info = await getBoardInfo(createBoardList({}));
expect(info).to.be.equal(selectPortForInfo);
});
@ -125,7 +153,11 @@ describe('boards-service', () => {
protocolLabel: firstToUpperCase(protocol),
protocol,
};
const info = await getBoardInfo(selectedPort, never());
const boardList = createBoardList(
{ [Port.keyOf(selectedPort)]: { port: selectedPort } },
{ selectedPort, selectedBoard: undefined }
);
const info = await getBoardInfo(boardList);
expect(info).to.be.equal(nonSerialPort);
})
);
@ -140,18 +172,26 @@ describe('boards-service', () => {
];
for (const properties of insufficientProperties) {
const port = selectedPort(properties);
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
const boardList = createBoardList(
{
[Port.keyOf(port)]: { port },
},
{ selectedPort: port, selectedBoard: undefined }
);
const info = await getBoardInfo(boardList);
expect(info).to.be.equal(noNativeSerialPort);
}
});
it("should detect a port as non-native serial, if protocol is 'serial' and VID/PID are available", async () => {
const port = selectedPort({ vid, pid });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
const boardList = createBoardList(
{
[Port.keyOf(port)]: { port },
},
{ selectedPort: port, selectedBoard: undefined }
);
const info = await getBoardInfo(boardList);
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
@ -162,9 +202,13 @@ describe('boards-service', () => {
it("should show the 'SN' even if no matching board was detected for the port", async () => {
const port = selectedPort({ vid, pid, serialNumber });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
const boardList = createBoardList(
{
[Port.keyOf(port)]: { port },
},
{ selectedPort: port, selectedBoard: undefined }
);
const info = await getBoardInfo(boardList);
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
@ -175,9 +219,13 @@ describe('boards-service', () => {
it("should use the name of the matching board as 'BN' if available", async () => {
const port = selectedPort({ vid, pid });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, [selectedBoard]],
});
const boardList = createBoardList(
{
[Port.keyOf(port)]: { port, boards: [selectedBoard] },
},
{ selectedPort: port, selectedBoard: undefined }
);
const info = await getBoardInfo(boardList);
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
@ -187,7 +235,3 @@ describe('boards-service', () => {
});
});
});
function never<T>(): Promise<T> {
return new Deferred<T>().promise;
}

View File

@ -0,0 +1,97 @@
import {
CommandContribution,
CommandRegistry,
CommandService,
} from '@theia/core/lib/common/command';
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
import { ILogger, Loggable } from '@theia/core/lib/common/logger';
import { LogLevel } from '@theia/core/lib/common/logger-protocol';
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
import { injectable, interfaces } from '@theia/core/shared/inversify';
export function bindCommon(bind: interfaces.Bind): interfaces.Bind {
bind(ConsoleLogger).toSelf().inSingletonScope();
bind(ILogger).toService(ConsoleLogger);
bind(CommandRegistry).toSelf().inSingletonScope();
bind(CommandService).toService(CommandRegistry);
bindContributionProvider(bind, CommandContribution);
return bind;
}
@injectable()
export class ConsoleLogger extends MockLogger {
override log(
logLevel: number,
arg2: string | Loggable | Error,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (arg2 instanceof Error) {
return this.error(String(arg2), params);
}
switch (logLevel) {
case LogLevel.INFO:
return this.info(arg2, params);
case LogLevel.WARN:
return this.warn(arg2, params);
case LogLevel.TRACE:
return this.trace(arg2, params);
case LogLevel.ERROR:
return this.error(arg2, params);
case LogLevel.FATAL:
return this.fatal(arg2, params);
default:
return this.info(arg2, params);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async info(arg: string | Loggable, ...params: any[]): Promise<void> {
if (params.length) {
console.info(arg, ...params);
} else {
console.info(arg);
}
}
override async trace(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (params.length) {
console.trace(arg, ...params);
} else {
console.trace(arg);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async warn(arg: string | Loggable, ...params: any[]): Promise<void> {
if (params.length) {
console.warn(arg, ...params);
} else {
console.warn(arg);
}
}
override async error(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (params.length) {
console.error(arg, ...params);
} else {
console.error(arg);
}
}
override async fatal(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
return this.error(arg, params);
}
}

View File

@ -0,0 +1,176 @@
import {
BoardIdentifier,
DetectedPort,
DetectedPorts,
Port,
} from '../../common/protocol/boards-service';
export const mkr1000: BoardIdentifier = {
name: 'Arduino MKR1000',
fqbn: 'arduino:samd:mkr1000',
};
export const uno: BoardIdentifier = {
name: 'Arduino Uno',
fqbn: 'arduino:avr:uno',
};
export const arduinoNanoEsp32: BoardIdentifier = {
fqbn: 'arduino:esp32:nano_nora',
name: 'Arduino Nano ESP32',
};
export const esp32NanoEsp32: BoardIdentifier = {
fqbn: 'esp32:esp32:nano_nora',
name: 'Arduino Nano ESP32',
};
export const esp32S3DevModule: BoardIdentifier = {
name: 'ESP32S3 Dev Module',
fqbn: 'esp32:esp32:esp32s3',
};
export const esp32S3Box: BoardIdentifier = {
name: 'ESP32-S3-Box',
fqbn: 'esp32:esp32:esp32s3box',
};
export const bluetoothSerialPort: Port = {
address: '/dev/cu.Bluetooth-Incoming-Port',
addressLabel: '/dev/cu.Bluetooth-Incoming-Port',
protocol: 'serial',
protocolLabel: 'Serial Port',
properties: {},
hardwareId: '',
};
export const builtinSerialPort: Port = {
address: '/dev/cu.BLTH',
addressLabel: '/dev/cu.BLTH',
protocol: 'serial',
protocolLabel: 'Serial Port',
properties: {},
hardwareId: '',
};
export const undiscoveredSerialPort: Port = {
address: '/dev/cu.usbserial-0001',
addressLabel: '/dev/cu.usbserial-0001',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0xEA60',
serialNumber: '0001',
vid: '0x10C4',
},
hardwareId: '0001',
};
export const mkr1000NetworkPort: Port = {
address: '192.168.0.104',
addressLabel: 'Arduino at 192.168.0.104',
protocol: 'network',
protocolLabel: 'Network Port',
properties: {
'.': 'mkr1000',
auth_upload: 'yes',
board: 'mkr1000',
hostname: 'Arduino.local.',
port: '65280',
ssh_upload: 'no',
tcp_check: 'no',
},
hardwareId: '',
};
export const undiscoveredUsbToUARTSerialPort: Port = {
address: '/dev/cu.SLAB_USBtoUART',
addressLabel: '/dev/cu.SLAB_USBtoUART',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0xEA60',
serialNumber: '0001',
vid: '0x10C4',
},
hardwareId: '0001',
};
export const mkr1000SerialPort: Port = {
address: '/dev/cu.usbmodem14301',
addressLabel: '/dev/cu.usbmodem14301',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0x804E',
serialNumber: '94A3397C5150435437202020FF150838',
vid: '0x2341',
},
hardwareId: '94A3397C5150435437202020FF150838',
};
export const unoSerialPort: Port = {
address: '/dev/cu.usbmodem14201',
addressLabel: '/dev/cu.usbmodem14201',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0x0043',
serialNumber: '75830303934351618212',
vid: '0x2341',
},
hardwareId: '75830303934351618212',
};
export const nanoEsp32SerialPort: Port = {
address: '/dev/cu.usbmodem3485187BD9882',
addressLabel: '/dev/cu.usbmodem3485187BD9882',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0x0070',
serialNumber: '3485187BD988',
vid: '0x2341',
},
hardwareId: '3485187BD988',
};
export const nanoEsp32DetectsMultipleEsp32BoardsSerialPort: Port = {
address: 'COM41',
addressLabel: 'COM41',
protocol: 'serial',
protocolLabel: 'Serial Port (USB)',
properties: {
pid: '0x1001',
serialNumber: '',
vid: '0x303A',
},
};
export function createPort(address: string, protocol = 'serial'): Port {
return {
address,
addressLabel: `Address label: ${address}`,
protocol,
protocolLabel: `Protocol label: ${protocol}`,
};
}
export function detectedPort(
port: Port,
...boards: BoardIdentifier[]
): { [portKey: string]: DetectedPort } {
return { [Port.keyOf(port)]: boards.length ? { port, boards } : { port } };
}
export function history(
port: Port,
board: BoardIdentifier
): { [portKey: string]: BoardIdentifier } {
return { [Port.keyOf(port)]: board };
}
export const detectedPorts: DetectedPorts = {
...detectedPort(builtinSerialPort),
...detectedPort(bluetoothSerialPort),
...detectedPort(unoSerialPort, uno),
...detectedPort(mkr1000SerialPort, mkr1000),
...detectedPort(mkr1000NetworkPort, mkr1000),
...detectedPort(undiscoveredSerialPort),
...detectedPort(undiscoveredUsbToUARTSerialPort),
// multiple discovered on the same port with different board names
...detectedPort(
nanoEsp32DetectsMultipleEsp32BoardsSerialPort,
esp32S3DevModule,
esp32S3Box
),
// multiple discovered on the same port with the same board name
...detectedPort(nanoEsp32SerialPort, arduinoNanoEsp32, esp32NanoEsp32),
};

View File

@ -2,7 +2,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Container } from '@theia/core/shared/inversify';
import { expect } from 'chai';
import { BoardSearch, BoardsService } from '../../common/protocol';
import { createBaseContainer, startDaemon } from './test-bindings';
import { createBaseContainer, startDaemon } from './node-test-bindings';
describe('boards-service-impl', () => {
let boardService: BoardsService;

View File

@ -12,7 +12,7 @@ import {
ClangFormatter,
} from '../../node/clang-formatter';
import { spawnCommand } from '../../node/exec-util';
import { createBaseContainer, startDaemon } from './test-bindings';
import { createBaseContainer, startDaemon } from './node-test-bindings';
const unformattedContent = `void setup ( ) { pinMode(LED_BUILTIN, OUTPUT);
}

View File

@ -3,6 +3,7 @@ import type { MaybePromise } from '@theia/core/lib/common/types';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { Container } from '@theia/core/shared/inversify';
import { expect } from 'chai';
import { dump, load } from 'js-yaml';
import { promises as fs } from 'node:fs';
import { join } from 'node:path';
import { sync as deleteSync } from 'rimraf';
@ -23,7 +24,7 @@ import {
createCliConfig,
newTempConfigDirPath,
startDaemon,
} from './test-bindings';
} from './node-test-bindings';
const timeout = 5 * 60 * 1_000; // five minutes
@ -66,16 +67,11 @@ describe('core-client-provider', () => {
const configDirPath = await prepareTestConfigDir();
deleteSync(join(configDirPath, 'data'));
const now = new Date().toISOString();
const container = await startCli(configDirPath, toDispose);
await assertFunctionalCli(container, ({ coreClientProvider }) => {
const { indexUpdateSummaryBeforeInit } = coreClientProvider;
const libUpdateTimestamp = indexUpdateSummaryBeforeInit['library'];
expect(libUpdateTimestamp).to.be.not.empty;
expect(libUpdateTimestamp.localeCompare(now)).to.be.greaterThan(0);
const platformUpdateTimestamp = indexUpdateSummaryBeforeInit['platform'];
expect(platformUpdateTimestamp).to.be.not.empty;
expect(platformUpdateTimestamp.localeCompare(now)).to.be.greaterThan(0);
expect(indexUpdateSummaryBeforeInit).to.be.not.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.empty;
});
});
@ -90,14 +86,11 @@ describe('core-client-provider', () => {
);
deleteSync(primaryPackageIndexPath);
const now = new Date().toISOString();
const container = await startCli(configDirPath, toDispose);
await assertFunctionalCli(container, ({ coreClientProvider }) => {
const { indexUpdateSummaryBeforeInit } = coreClientProvider;
expect(indexUpdateSummaryBeforeInit['library']).to.be.undefined;
const platformUpdateTimestamp = indexUpdateSummaryBeforeInit['platform'];
expect(platformUpdateTimestamp).to.be.not.empty;
expect(platformUpdateTimestamp.localeCompare(now)).to.be.greaterThan(0);
expect(indexUpdateSummaryBeforeInit).to.be.not.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.empty;
});
const rawJson = await fs.readFile(primaryPackageIndexPath, {
encoding: 'utf8',
@ -149,14 +142,11 @@ describe('core-client-provider', () => {
);
deleteSync(libraryPackageIndexPath);
const now = new Date().toISOString();
const container = await startCli(configDirPath, toDispose);
await assertFunctionalCli(container, ({ coreClientProvider }) => {
const { indexUpdateSummaryBeforeInit } = coreClientProvider;
const libUpdateTimestamp = indexUpdateSummaryBeforeInit['library'];
expect(libUpdateTimestamp).to.be.not.empty;
expect(libUpdateTimestamp.localeCompare(now)).to.be.greaterThan(0);
expect(indexUpdateSummaryBeforeInit['platform']).to.be.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.not.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.empty;
});
const rawJson = await fs.readFile(libraryPackageIndexPath, {
encoding: 'utf8',
@ -191,20 +181,38 @@ describe('core-client-provider', () => {
const container = await startCli(configDirPath, toDispose);
await assertFunctionalCli(
container,
async ({ coreClientProvider, boardsService, coreService }) => {
async ({ coreClientProvider, boardsService }) => {
const { indexUpdateSummaryBeforeInit } = coreClientProvider;
expect(indexUpdateSummaryBeforeInit).to.be.not.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.empty;
// IDE2 cannot recover from a 3rd party package index issue.
// Only when the primary package or library index is corrupt.
// https://github.com/arduino/arduino-ide/issues/2021
await coreService.updateIndex({ types: ['platform'] });
await assertTeensyAvailable(boardsService);
}
);
});
it("should recover when invalid 3rd package URL is defined in the CLI config and the 'directories.data' folder is missing", async function () {
this.timeout(timeout);
const configDirPath = await prepareTestConfigDir();
deleteSync(join(configDirPath, 'data'));
// set an invalid URL so the CLI will try to download it
const cliConfigPath = join(configDirPath, 'arduino-cli.yaml');
const rawYaml = await fs.readFile(cliConfigPath, { encoding: 'utf8' });
const config: DefaultCliConfig = load(rawYaml);
expect(config.board_manager).to.be.undefined;
config.board_manager = { additional_urls: ['https://invalidUrl'] };
expect(config.board_manager?.additional_urls?.[0]).to.be.equal(
'https://invalidUrl'
);
await fs.writeFile(cliConfigPath, dump(config));
const container = await startCli(configDirPath, toDispose);
await assertFunctionalCli(container, ({ coreClientProvider }) => {
const { indexUpdateSummaryBeforeInit } = coreClientProvider;
expect(indexUpdateSummaryBeforeInit).to.be.not.undefined;
expect(indexUpdateSummaryBeforeInit).to.be.empty;
});
});
});
interface Services {
@ -277,7 +285,7 @@ async function prepareTestConfigDir(
const params = { configDirPath: newTempConfigDirPath(), configOverrides };
const container = await createContainer(params);
const daemon = container.get<ArduinoDaemonImpl>(ArduinoDaemonImpl);
const cliPath = await daemon.getExecPath();
const cliPath = daemon.getExecPath();
const configDirUriProvider =
container.get<ConfigDirUriProvider>(ConfigDirUriProvider);
const configDirPath = FileUri.fsPath(configDirUriProvider.configDirUri());

View File

@ -11,7 +11,7 @@ import {
SketchesService,
isCompileSummary,
} from '../../common/protocol';
import { createBaseContainer, startDaemon } from './test-bindings';
import { createBaseContainer, startDaemon } from './node-test-bindings';
const testTimeout = 30_000;
const setupTimeout = 5 * 60 * 1_000; // five minutes

View File

@ -1,5 +1,10 @@
import { expect } from 'chai';
import { Port } from '../../node/cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import {
PortIdentifier,
portIdentifierEquals,
Port,
} from '../../common/protocol/boards-service';
import { Port as RpcPort } from '../../node/cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { CoreServiceImpl } from '../../node/core-service-impl';
describe('core-service-impl', () => {
@ -22,9 +27,18 @@ describe('core-service-impl', () => {
protocolLabel: 'serial port',
properties,
} as const;
const actual = new CoreServiceImpl()['createPort'](port);
const resolve = (toResolve: PortIdentifier): Port | undefined => {
if (portIdentifierEquals(toResolve, port)) {
return port;
}
return undefined;
};
const actual = new CoreServiceImpl()['createPort'](
{ protocol: port.protocol, address: port.address },
resolve
);
expect(actual).to.be.not.undefined;
const expected = new Port()
const expected = new RpcPort()
.setAddress(port.address)
.setHardwareId(port.hardwareId)
.setLabel(port.addressLabel)
@ -33,7 +47,7 @@ describe('core-service-impl', () => {
Object.entries(properties).forEach(([key, value]) =>
expected.getPropertiesMap().set(key, value)
);
expect((<Port>actual).toObject(false)).to.be.deep.equal(
expect((<RpcPort>actual).toObject(false)).to.be.deep.equal(
expected.toObject(false)
);
});

View File

@ -2,7 +2,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Container } from '@theia/core/shared/inversify';
import { expect } from 'chai';
import { LibrarySearch, LibraryService } from '../../common/protocol';
import { createBaseContainer, startDaemon } from './test-bindings';
import { createBaseContainer, startDaemon } from './node-test-bindings';
describe('library-service-impl', () => {
let libraryService: LibraryService;

View File

@ -1,18 +1,9 @@
import {
CommandContribution,
CommandRegistry,
CommandService,
} from '@theia/core/lib/common/command';
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { ILogger, Loggable } from '@theia/core/lib/common/logger';
import { LogLevel } from '@theia/core/lib/common/logger-protocol';
import { waitForEvent } from '@theia/core/lib/common/promise-util';
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
import URI from '@theia/core/lib/common/uri';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { ProcessUtils } from '@theia/core/lib/node/process-utils';
@ -23,19 +14,18 @@ import {
interfaces,
} from '@theia/core/shared/inversify';
import deepmerge from 'deepmerge';
import { promises as fs, mkdirSync } from 'node:fs';
import { dump as dumpYaml } from 'js-yaml';
import { mkdirSync, promises as fs } from 'node:fs';
import { join } from 'node:path';
import { path as tempPath, track } from 'temp';
import {
ArduinoDaemon,
AttachedBoardsChangeEvent,
AvailablePorts,
BoardsPackage,
BoardsService,
ConfigService,
ConfigState,
CoreService,
DetectedPorts,
IndexUpdateDidCompleteParams,
IndexUpdateDidFailParams,
IndexUpdateParams,
@ -52,7 +42,7 @@ import {
import { ArduinoDaemonImpl } from '../../node/arduino-daemon-impl';
import { BoardDiscovery } from '../../node/board-discovery';
import { BoardsServiceImpl } from '../../node/boards-service-impl';
import { CLI_CONFIG, CliConfig, DefaultCliConfig } from '../../node/cli-config';
import { CliConfig, CLI_CONFIG, DefaultCliConfig } from '../../node/cli-config';
import { ConfigServiceImpl } from '../../node/config-service-impl';
import { CoreClientProvider } from '../../node/core-client-provider';
import { CoreServiceImpl } from '../../node/core-service-impl';
@ -70,87 +60,10 @@ import {
ConfigDirUriProvider,
EnvVariablesServer,
} from '../../node/theia/env-variables/env-variables-server';
import { bindCommon } from '../common/common-test-bindings';
const tracked = track();
@injectable()
class ConsoleLogger extends MockLogger {
override log(
logLevel: number,
arg2: string | Loggable | Error,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (arg2 instanceof Error) {
return this.error(String(arg2), params);
}
switch (logLevel) {
case LogLevel.INFO:
return this.info(arg2, params);
case LogLevel.WARN:
return this.warn(arg2, params);
case LogLevel.TRACE:
return this.trace(arg2, params);
case LogLevel.ERROR:
return this.error(arg2, params);
case LogLevel.FATAL:
return this.fatal(arg2, params);
default:
return this.info(arg2, params);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async info(arg: string | Loggable, ...params: any[]): Promise<void> {
if (params.length) {
console.info(arg, ...params);
} else {
console.info(arg);
}
}
override async trace(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (params.length) {
console.trace(arg, ...params);
} else {
console.trace(arg);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async warn(arg: string | Loggable, ...params: any[]): Promise<void> {
if (params.length) {
console.warn(arg, ...params);
} else {
console.warn(arg);
}
}
override async error(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
if (params.length) {
console.error(arg, ...params);
} else {
console.error(arg);
}
}
override async fatal(
arg: string | Loggable,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...params: any[]
): Promise<void> {
return this.error(arg, params);
}
}
@injectable()
class SilentArduinoDaemon extends ArduinoDaemonImpl {
protected override onData(): void {
@ -160,7 +73,7 @@ class SilentArduinoDaemon extends ArduinoDaemonImpl {
@injectable()
class TestBoardDiscovery extends BoardDiscovery {
mutableAvailablePorts: AvailablePorts = {};
mutableDetectedPorts: DetectedPorts = {};
override async start(): Promise<void> {
// NOOP
@ -168,8 +81,8 @@ class TestBoardDiscovery extends BoardDiscovery {
override async stop(): Promise<void> {
// NOOP
}
override get availablePorts(): AvailablePorts {
return this.mutableAvailablePorts;
override get detectedPorts(): DetectedPorts {
return this.mutableDetectedPorts;
}
}
@ -221,7 +134,7 @@ class TestNotificationServiceServer implements NotificationServiceServer {
notifyLibraryDidUninstall(event: { item: LibraryPackage }): void {
this.events.push(`notifyLibraryDidUninstall:${JSON.stringify(event)}`);
}
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void {
notifyDetectedPortsDidChange(event: { detectedPorts: DetectedPorts }): void {
this.events.push(`notifyAttachedBoardsDidChange:${JSON.stringify(event)}`);
}
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void {
@ -309,6 +222,7 @@ export async function createBaseContainer(
}
const container = new Container({ defaultScope: 'Singleton' });
const module = new ContainerModule((bind, unbind, isBound, rebind) => {
bindCommon(bind);
bind(CoreClientProvider).toSelf().inSingletonScope();
bind(CoreServiceImpl).toSelf().inSingletonScope();
bind(CoreService).toService(CoreServiceImpl);
@ -336,15 +250,10 @@ export async function createBaseContainer(
bind(SilentArduinoDaemon).toSelf().inSingletonScope();
bind(ArduinoDaemon).toService(SilentArduinoDaemon);
bind(ArduinoDaemonImpl).toService(SilentArduinoDaemon);
bind(ConsoleLogger).toSelf().inSingletonScope();
bind(ILogger).toService(ConsoleLogger);
bind(TestNotificationServiceServer).toSelf().inSingletonScope();
bind(NotificationServiceServer).toService(TestNotificationServiceServer);
bind(ConfigServiceImpl).toSelf().inSingletonScope();
bind(ConfigService).toService(ConfigServiceImpl);
bind(CommandRegistry).toSelf().inSingletonScope();
bind(CommandService).toService(CommandRegistry);
bindContributionProvider(bind, CommandContribution);
bind(TestBoardDiscovery).toSelf().inSingletonScope();
bind(BoardDiscovery).toService(TestBoardDiscovery);
bind(IsTempSketch).toSelf().inSingletonScope();

View File

@ -12,7 +12,7 @@ import { sync as rimrafSync } from 'rimraf';
import { Sketch, SketchesService } from '../../common/protocol';
import { SketchesServiceImpl } from '../../node/sketches-service-impl';
import { ErrnoException } from '../../node/utils/errors';
import { createBaseContainer, startDaemon } from './test-bindings';
import { createBaseContainer, startDaemon } from './node-test-bindings';
const testTimeout = 10_000;

View File

@ -18,24 +18,21 @@
"configDialog1": "Select both a Board and a Port if you want to upload a sketch.",
"configDialog2": "If you only select a Board you will be able to compile, but not to upload your sketch.",
"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?",
"disconnected": "Disconnected",
"editBoardsConfig": "Edit Board and Port...",
"getBoardInfo": "Get Board Info",
"inSketchbook": " (in Sketchbook)",
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
"noBoardsFound": "No boards found for \"{0}\"",
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
"noNativeSerialPort": "Native serial port, can't obtain info.",
"noPortsDiscovered": "No ports discovered",
"noPortsSelected": "No ports selected for board: '{0}'.",
"nonSerialPort": "Non-serial port, can't obtain info.",
"noneSelected": "No boards selected.",
"openBoardsConfig": "Select other board and port…",
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
"port": "Port{0}",
"portLabel": "Port: {0}",
"ports": "ports",
"programmer": "Programmer",
"reselectLater": "Reselect later",
"revertBoardsConfig": "Use '{0}' discovered on '{1}'",
"searchBoard": "Search board",
"selectBoard": "Select Board",
"selectPortForInfo": "Please select a port to obtain board info.",
@ -44,6 +41,7 @@
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
"typeOfPorts": "{0} ports",
"unconfirmedBoard": "Unconfirmed board",
"unknownBoard": "Unknown board"
},
"boardsManager": "Boards Manager",
@ -215,6 +213,11 @@
"optimizeForDebugging": "Optimize for Debugging",
"sketchIsNotCompiled": "Sketch '{0}' must be verified before starting a debug session. Please verify the sketch and start debugging again. Do you want to verify the sketch now?"
},
"developer": {
"clearBoardList": "Clear the Board List History",
"clearBoardsConfig": "Clear the Board and Port Selection",
"dumpBoardList": "Dump the Board List"
},
"dialog": {
"dontAskAgain": "Don't ask again"
},