mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-10-22 17:48:33 +00:00

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>
230 lines
8.2 KiB
TypeScript
230 lines
8.2 KiB
TypeScript
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
|
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';
|
|
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 `BoardsConfigChangeEvent`s, if a board is selected which does not
|
|
* have the corresponding core installed, it proposes the user to install the core.
|
|
*/
|
|
@injectable()
|
|
export class BoardsAutoInstaller implements FrontendApplicationContribution {
|
|
@inject(NotificationCenter)
|
|
private readonly notificationCenter: NotificationCenter;
|
|
@inject(MessageService)
|
|
private readonly messageService: MessageService;
|
|
@inject(NotificationManager)
|
|
private readonly notificationManager: NotificationManager;
|
|
@inject(BoardsService)
|
|
private readonly boardsService: BoardsService;
|
|
@inject(BoardsServiceProvider)
|
|
private readonly boardsServiceProvider: BoardsServiceProvider;
|
|
@inject(ResponseServiceClient)
|
|
private readonly responseService: ResponseServiceClient;
|
|
@inject(BoardsListWidgetFrontendContribution)
|
|
private readonly boardsManagerWidgetContribution: BoardsListWidgetFrontendContribution;
|
|
|
|
// Workaround for https://github.com/eclipse-theia/theia/issues/9349
|
|
private readonly installNotificationInfos: Readonly<{
|
|
boardName: string;
|
|
platformId: string;
|
|
notificationId: string;
|
|
}>[] = [];
|
|
private readonly toDispose = new DisposableCollection();
|
|
|
|
onStart(): void {
|
|
this.toDispose.pushAll([
|
|
this.boardsServiceProvider.onBoardsConfigDidChange((event) => {
|
|
if (isBoardIdentifierChangeEvent(event)) {
|
|
this.ensureCoreExists(event.selectedBoard);
|
|
}
|
|
}),
|
|
this.notificationCenter.onPlatformDidInstall((event) =>
|
|
this.clearAllNotificationForPlatform(event.item.id)
|
|
),
|
|
]);
|
|
this.boardsServiceProvider.ready.then(() => {
|
|
const { selectedBoard } = this.boardsServiceProvider.boardsConfig;
|
|
this.ensureCoreExists(selectedBoard);
|
|
});
|
|
}
|
|
|
|
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 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 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);
|
|
|
|
const version = candidate.availableVersions[0]
|
|
? `[v ${candidate.availableVersions[0]}]`
|
|
: '';
|
|
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 notificationId = this.notificationId(message, InstallManually, yes);
|
|
this.installNotificationInfos.push({
|
|
boardName: selectedBoardName,
|
|
platformId: platformIdToInstall,
|
|
notificationId,
|
|
});
|
|
const answer = await this.messageService.info(
|
|
message,
|
|
InstallManually,
|
|
yes
|
|
);
|
|
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,
|
|
});
|
|
}
|
|
}
|