From 5cb9166c839e7b7be19502bf28b45852127e84e4 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Sat, 27 Aug 2022 12:04:35 +0200 Subject: [PATCH] Implemented filter and update all for libs/boards. Closes #177 Closes #1188 Co-authored-by: Francesco Spissu Co-authored-by: Per Tillisch Co-authored-by: Akos Kitta Signed-off-by: Akos Kitta --- arduino-ide-extension/package.json | 2 +- .../browser/arduino-ide-frontend-module.ts | 28 +- .../src/browser/arduino-preferences.ts | 9 + .../browser/boards/boards-auto-installer.ts | 12 +- .../src/browser/boards/boards-list-widget.ts | 12 +- .../boards-widget-frontend-contribution.ts | 10 +- .../contributions/check-for-ide-updates.ts | 68 +++ .../contributions/check-for-updates.ts | 255 ++++++++-- .../contributions/open-sketch-files.ts | 27 +- .../ide-updater/ide-updater-commands.ts | 4 +- .../browser/library/library-list-widget.ts | 25 +- .../src/browser/style/list-widget.css | 49 +- .../preferences/preference-editor-widget.ts | 19 + .../component-list/component-list-item.tsx | 45 +- .../widgets/component-list/component-list.tsx | 137 +++++- .../component-list/filter-renderer.tsx | 121 +++++ .../filterable-list-container.tsx | 81 ++-- .../component-list/list-item-renderer.tsx | 14 +- .../list-widget-frontend-contribution.ts | 13 +- .../widgets/component-list/list-widget.tsx | 47 +- arduino-ide-extension/src/common/nls.ts | 18 + .../src/common/protocol/arduino-component.ts | 6 +- .../src/common/protocol/boards-service.ts | 38 +- .../src/common/protocol/installable.ts | 27 ++ .../src/common/protocol/library-service.ts | 97 +++- .../src/common/protocol/progressible.ts | 2 +- .../src/common/protocol/searchable.ts | 4 +- .../src/node/boards-service-impl.ts | 28 +- .../cli/commands/v1/commands_grpc_pb.d.ts | 15 + .../cli/commands/v1/commands_grpc_pb.js | 34 ++ .../cc/arduino/cli/commands/v1/common_pb.d.ts | 6 + .../cc/arduino/cli/commands/v1/common_pb.js | 53 +- .../cc/arduino/cli/commands/v1/lib_pb.d.ts | 63 +++ .../cc/arduino/cli/commands/v1/lib_pb.js | 457 ++++++++++++++++++ .../src/node/library-service-impl.ts | 45 +- .../src/test/browser/fixtures/boards.ts | 1 + i18n/en.json | 44 +- 37 files changed, 1696 insertions(+), 220 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/check-for-ide-updates.ts create mode 100644 arduino-ide-extension/src/browser/theia/preferences/preference-editor-widget.ts create mode 100644 arduino-ide-extension/src/browser/widgets/component-list/filter-renderer.tsx diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 93b258d67..b5f024d1d 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -163,7 +163,7 @@ "version": { "owner": "arduino", "repo": "arduino-cli", - "commitish": "63f1e18" + "commitish": "05d1446" } }, "fwuploader": { diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index 5c6c2bdea..3f158f65a 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -314,7 +314,7 @@ import { FirstStartupInstaller } from './contributions/first-startup-installer'; import { OpenSketchFiles } from './contributions/open-sketch-files'; import { InoLanguage } from './contributions/ino-language'; import { SelectedBoard } from './contributions/selected-board'; -import { CheckForUpdates } from './contributions/check-for-updates'; +import { CheckForIDEUpdates } from './contributions/check-for-ide-updates'; import { OpenBoardsConfig } from './contributions/open-boards-config'; import { SketchFilesTracker } from './contributions/sketch-files-tracker'; import { MonacoThemeServiceIsReady } from './utils/window'; @@ -323,6 +323,15 @@ import { StatusBarImpl } from './theia/core/status-bar'; import { StatusBarImpl as TheiaStatusBarImpl } from '@theia/core/lib/browser'; import { EditorMenuContribution } from './theia/editor/editor-file'; import { EditorMenuContribution as TheiaEditorMenuContribution } from '@theia/editor/lib/browser/editor-menu'; +import { PreferencesEditorWidget as TheiaPreferencesEditorWidget } from '@theia/preferences/lib/browser/views/preference-editor-widget'; +import { PreferencesEditorWidget } from './theia/preferences/preference-editor-widget'; +import { PreferencesWidget } from '@theia/preferences/lib/browser/views/preference-widget'; +import { createPreferencesWidgetContainer } from '@theia/preferences/lib/browser/views/preference-widget-bindings'; +import { + BoardsFilterRenderer, + LibraryFilterRenderer, +} from './widgets/component-list/filter-renderer'; +import { CheckForUpdates } from './contributions/check-for-updates'; const registerArduinoThemes = () => { const themes: MonacoThemeJson[] = [ @@ -364,6 +373,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // Renderer for both the library and the core widgets. bind(ListItemRenderer).toSelf().inSingletonScope(); + bind(LibraryFilterRenderer).toSelf().inSingletonScope(); + bind(BoardsFilterRenderer).toSelf().inSingletonScope(); // Library service bind(LibraryService) @@ -737,9 +748,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, OpenSketchFiles); Contribution.configure(bind, InoLanguage); Contribution.configure(bind, SelectedBoard); - Contribution.configure(bind, CheckForUpdates); + Contribution.configure(bind, CheckForIDEUpdates); Contribution.configure(bind, OpenBoardsConfig); Contribution.configure(bind, SketchFilesTracker); + Contribution.configure(bind, CheckForUpdates); // Disabled the quick-pick customization from Theia when multiple formatters are available. // Use the default VS Code behavior, and pick the first one. In the IDE2, clang-format has `exclusive` selectors. @@ -845,6 +857,18 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(DockPanelRenderer).toSelf(); rebind(TheiaDockPanelRenderer).toService(DockPanelRenderer); + // Avoid running the "reset scroll" interval tasks until the preference editor opens. + rebind(PreferencesWidget) + .toDynamicValue(({ container }) => { + const child = createPreferencesWidgetContainer(container); + child.bind(PreferencesEditorWidget).toSelf().inSingletonScope(); + child + .rebind(TheiaPreferencesEditorWidget) + .toService(PreferencesEditorWidget); + return child.get(PreferencesWidget); + }) + .inSingletonScope(); + // Preferences bindArduinoPreferences(bind); diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index f9875a6fc..5fef59072 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -241,6 +241,14 @@ export const ArduinoConfigSchema: PreferenceSchema = { ), default: false, }, + 'arduino.checkForUpdates': { + type: 'boolean', + description: nls.localize( + 'arduino/preferences/checkForUpdate', + "Receive notifications of available updates for the IDE, boards, and libraries. Requires an IDE restart after change. It's true by default." + ), + default: true, + }, }, }; @@ -270,6 +278,7 @@ export interface ArduinoConfiguration { 'arduino.auth.registerUri': string; 'arduino.survey.notification': boolean; 'arduino.cli.daemon.debug': boolean; + 'arduino.checkForUpdates': boolean; } export const ArduinoPreferences = Symbol('ArduinoPreferences'); diff --git a/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts b/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts index 845f97b59..f516258f6 100644 --- a/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts +++ b/arduino-ide-extension/src/browser/boards/boards-auto-installer.ts @@ -12,6 +12,7 @@ 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 { InstallManually } from '../../common/nls'; interface AutoInstallPromptAction { // isAcceptance, whether or not the action indicates acceptance of auto-install proposal @@ -231,19 +232,18 @@ export class BoardsAutoInstaller implements FrontendApplicationContribution { candidate: BoardsPackage ): AutoInstallPromptActions { const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes'); - const manualInstall = nls.localize( - 'arduino/board/installManually', - 'Install Manually' - ); const actions: AutoInstallPromptActions = [ { - key: manualInstall, + key: InstallManually, handler: () => { this.boardsManagerFrontendContribution .openView({ reveal: true }) .then((widget) => - widget.refresh(candidate.name.toLocaleLowerCase()) + widget.refresh({ + query: candidate.name.toLocaleLowerCase(), + type: 'All', + }) ); }, }, diff --git a/arduino-ide-extension/src/browser/boards/boards-list-widget.ts b/arduino-ide-extension/src/browser/boards/boards-list-widget.ts index f199063f8..8b7626720 100644 --- a/arduino-ide-extension/src/browser/boards/boards-list-widget.ts +++ b/arduino-ide-extension/src/browser/boards/boards-list-widget.ts @@ -4,22 +4,24 @@ import { postConstruct, } from '@theia/core/shared/inversify'; import { + BoardSearch, BoardsPackage, BoardsService, } from '../../common/protocol/boards-service'; import { ListWidget } from '../widgets/component-list/list-widget'; import { ListItemRenderer } from '../widgets/component-list/list-item-renderer'; import { nls } from '@theia/core/lib/common'; +import { BoardsFilterRenderer } from '../widgets/component-list/filter-renderer'; @injectable() -export class BoardsListWidget extends ListWidget { +export class BoardsListWidget extends ListWidget { static WIDGET_ID = 'boards-list-widget'; static WIDGET_LABEL = nls.localize('arduino/boardsManager', 'Boards Manager'); constructor( - @inject(BoardsService) protected service: BoardsService, - @inject(ListItemRenderer) - protected itemRenderer: ListItemRenderer + @inject(BoardsService) service: BoardsService, + @inject(ListItemRenderer) itemRenderer: ListItemRenderer, + @inject(BoardsFilterRenderer) filterRenderer: BoardsFilterRenderer ) { super({ id: BoardsListWidget.WIDGET_ID, @@ -30,6 +32,8 @@ export class BoardsListWidget extends ListWidget { itemLabel: (item: BoardsPackage) => item.name, itemDeprecated: (item: BoardsPackage) => item.deprecated, itemRenderer, + filterRenderer, + defaultSearchOptions: { query: '', type: 'All' }, }); } diff --git a/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts b/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts index af31aff6e..a6e535d6f 100644 --- a/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/boards/boards-widget-frontend-contribution.ts @@ -1,10 +1,16 @@ import { injectable } from '@theia/core/shared/inversify'; import { BoardsListWidget } from './boards-list-widget'; -import { BoardsPackage } from '../../common/protocol/boards-service'; +import type { + BoardSearch, + BoardsPackage, +} from '../../common/protocol/boards-service'; import { ListWidgetFrontendContribution } from '../widgets/component-list/list-widget-frontend-contribution'; @injectable() -export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution { +export class BoardsListWidgetFrontendContribution extends ListWidgetFrontendContribution< + BoardsPackage, + BoardSearch +> { constructor() { super({ widgetId: BoardsListWidget.WIDGET_ID, diff --git a/arduino-ide-extension/src/browser/contributions/check-for-ide-updates.ts b/arduino-ide-extension/src/browser/contributions/check-for-ide-updates.ts new file mode 100644 index 000000000..14ba623bf --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/check-for-ide-updates.ts @@ -0,0 +1,68 @@ +import { nls } from '@theia/core/lib/common/nls'; +import { LocalStorageService } from '@theia/core/lib/browser/storage-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { + IDEUpdater, + SKIP_IDE_VERSION, +} from '../../common/protocol/ide-updater'; +import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog'; +import { Contribution } from './contribution'; + +@injectable() +export class CheckForIDEUpdates extends Contribution { + @inject(IDEUpdater) + private readonly updater: IDEUpdater; + + @inject(IDEUpdaterDialog) + private readonly updaterDialog: IDEUpdaterDialog; + + @inject(LocalStorageService) + private readonly localStorage: LocalStorageService; + + override onStart(): void { + this.preferences.onPreferenceChanged( + ({ preferenceName, newValue, oldValue }) => { + if (newValue !== oldValue) { + switch (preferenceName) { + case 'arduino.ide.updateChannel': + case 'arduino.ide.updateBaseUrl': + this.updater.init( + this.preferences.get('arduino.ide.updateChannel'), + this.preferences.get('arduino.ide.updateBaseUrl') + ); + } + } + } + ); + } + + override onReady(): void { + const checkForUpdates = this.preferences['arduino.checkForUpdates']; + if (!checkForUpdates) { + return; + } + this.updater + .init( + this.preferences.get('arduino.ide.updateChannel'), + this.preferences.get('arduino.ide.updateBaseUrl') + ) + .then(() => this.updater.checkForUpdates(true)) + .then(async (updateInfo) => { + if (!updateInfo) return; + const versionToSkip = await this.localStorage.getData( + SKIP_IDE_VERSION + ); + if (versionToSkip === updateInfo.version) return; + this.updaterDialog.open(updateInfo); + }) + .catch((e) => { + this.messageService.error( + nls.localize( + 'arduino/ide-updater/errorCheckingForUpdates', + 'Error while checking for Arduino IDE updates.\n{0}', + e.message + ) + ); + }); + } +} diff --git a/arduino-ide-extension/src/browser/contributions/check-for-updates.ts b/arduino-ide-extension/src/browser/contributions/check-for-updates.ts index 16db7a845..d305f9db2 100644 --- a/arduino-ide-extension/src/browser/contributions/check-for-updates.ts +++ b/arduino-ide-extension/src/browser/contributions/check-for-updates.ts @@ -1,64 +1,221 @@ +import type { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { nls } from '@theia/core/lib/common/nls'; -import { LocalStorageService } from '@theia/core/lib/browser/storage-service'; import { inject, injectable } from '@theia/core/shared/inversify'; +import { InstallManually, Later } from '../../common/nls'; import { - IDEUpdater, - SKIP_IDE_VERSION, -} from '../../common/protocol/ide-updater'; -import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog'; -import { Contribution } from './contribution'; + ArduinoComponent, + BoardsPackage, + BoardsService, + LibraryPackage, + LibraryService, + ResponseServiceClient, + Searchable, +} from '../../common/protocol'; +import { Installable } from '../../common/protocol/installable'; +import { ExecuteWithProgress } from '../../common/protocol/progressible'; +import { BoardsListWidgetFrontendContribution } from '../boards/boards-widget-frontend-contribution'; +import { LibraryListWidgetFrontendContribution } from '../library/library-widget-frontend-contribution'; +import { WindowServiceExt } from '../theia/core/window-service-ext'; +import type { ListWidget } from '../widgets/component-list/list-widget'; +import { Command, CommandRegistry, Contribution } from './contribution'; + +const NoUpdates = nls.localize( + 'arduino/checkForUpdates/noUpdates', + 'There are no recent updates available.' +); +const PromptUpdateBoards = nls.localize( + 'arduino/checkForUpdates/promptUpdateBoards', + 'Updates are available for some of your boards.' +); +const PromptUpdateLibraries = nls.localize( + 'arduino/checkForUpdates/promptUpdateLibraries', + 'Updates are available for some of your libraries.' +); +const UpdatingBoards = nls.localize( + 'arduino/checkForUpdates/updatingBoards', + 'Updating boards...' +); +const UpdatingLibraries = nls.localize( + 'arduino/checkForUpdates/updatingLibraries', + 'Updating libraries...' +); +const InstallAll = nls.localize( + 'arduino/checkForUpdates/installAll', + 'Install All' +); + +interface Task { + readonly run: () => Promise; + readonly item: T; +} + +const Updatable = { type: 'Updatable' } as const; @injectable() export class CheckForUpdates extends Contribution { - @inject(IDEUpdater) - private readonly updater: IDEUpdater; + @inject(WindowServiceExt) + private readonly windowService: WindowServiceExt; + @inject(ResponseServiceClient) + private readonly responseService: ResponseServiceClient; + @inject(BoardsService) + private readonly boardsService: BoardsService; + @inject(LibraryService) + private readonly libraryService: LibraryService; + @inject(BoardsListWidgetFrontendContribution) + private readonly boardsContribution: BoardsListWidgetFrontendContribution; + @inject(LibraryListWidgetFrontendContribution) + private readonly librariesContribution: LibraryListWidgetFrontendContribution; + + override registerCommands(register: CommandRegistry): void { + register.registerCommand(CheckForUpdates.Commands.CHECK_FOR_UPDATES, { + execute: () => this.checkForUpdates(false), + }); + } + + override async onReady(): Promise { + const checkForUpdates = this.preferences['arduino.checkForUpdates']; + if (checkForUpdates) { + this.windowService.isFirstWindow().then((firstWindow) => { + if (firstWindow) { + this.checkForUpdates(); + } + }); + } + } + + private async checkForUpdates(silent = true) { + const [boardsPackages, libraryPackages] = await Promise.all([ + this.boardsService.search(Updatable), + this.libraryService.search(Updatable), + ]); + this.promptUpdateBoards(boardsPackages); + this.promptUpdateLibraries(libraryPackages); + if (!libraryPackages.length && !boardsPackages.length && !silent) { + this.messageService.info(NoUpdates); + } + } - @inject(IDEUpdaterDialog) - private readonly updaterDialog: IDEUpdaterDialog; + private promptUpdateBoards(items: BoardsPackage[]): void { + this.prompt({ + items, + installable: this.boardsService, + viewContribution: this.boardsContribution, + viewSearchOptions: { query: '', ...Updatable }, + promptMessage: PromptUpdateBoards, + updatingMessage: UpdatingBoards, + }); + } + + private promptUpdateLibraries(items: LibraryPackage[]): void { + this.prompt({ + items, + installable: this.libraryService, + viewContribution: this.librariesContribution, + viewSearchOptions: { query: '', topic: 'All', ...Updatable }, + promptMessage: PromptUpdateLibraries, + updatingMessage: UpdatingLibraries, + }); + } - @inject(LocalStorageService) - private readonly localStorage: LocalStorageService; + private prompt< + T extends ArduinoComponent, + S extends Searchable.Options + >(options: { + items: T[]; + installable: Installable; + viewContribution: AbstractViewContribution>; + viewSearchOptions: S; + promptMessage: string; + updatingMessage: string; + }): void { + const { + items, + installable, + viewContribution, + promptMessage: message, + viewSearchOptions, + updatingMessage, + } = options; + + if (!items.length) { + return; + } + this.messageService + .info(message, Later, InstallManually, InstallAll) + .then((answer) => { + if (answer === InstallAll) { + const tasks = items.map((item) => + this.createInstallTask(item, installable) + ); + this.executeTasks(updatingMessage, tasks); + } else if (answer === InstallManually) { + viewContribution + .openView({ reveal: true }) + .then((widget) => widget.refresh(viewSearchOptions)); + } + }); + } - override onStart(): void { - this.preferences.onPreferenceChanged( - ({ preferenceName, newValue, oldValue }) => { - if (newValue !== oldValue) { - switch (preferenceName) { - case 'arduino.ide.updateChannel': - case 'arduino.ide.updateBaseUrl': - this.updater.init( - this.preferences.get('arduino.ide.updateChannel'), - this.preferences.get('arduino.ide.updateBaseUrl') - ); + private async executeTasks( + message: string, + tasks: Task[] + ): Promise { + if (tasks.length) { + return ExecuteWithProgress.withProgress( + message, + this.messageService, + async (progress) => { + try { + const total = tasks.length; + let count = 0; + for (const { run, item } of tasks) { + try { + await run(); // runs update sequentially. // TODO: is parallel update desired? + } catch (err) { + console.error(err); + this.messageService.error( + `Failed to update ${item.name}. ${err}` + ); + } finally { + progress.report({ work: { total, done: ++count } }); + } + } + } finally { + progress.cancel(); } } - } - ); + ); + } } - override onReady(): void { - this.updater - .init( - this.preferences.get('arduino.ide.updateChannel'), - this.preferences.get('arduino.ide.updateBaseUrl') - ) - .then(() => this.updater.checkForUpdates(true)) - .then(async (updateInfo) => { - if (!updateInfo) return; - const versionToSkip = await this.localStorage.getData( - SKIP_IDE_VERSION - ); - if (versionToSkip === updateInfo.version) return; - this.updaterDialog.open(updateInfo); - }) - .catch((e) => { - this.messageService.error( - nls.localize( - 'arduino/ide-updater/errorCheckingForUpdates', - 'Error while checking for Arduino IDE updates.\n{0}', - e.message - ) - ); - }); + private createInstallTask( + item: T, + installable: Installable + ): Task { + const latestVersion = item.availableVersions[0]; + return { + item, + run: () => + Installable.installWithProgress({ + installable, + item, + version: latestVersion, + messageService: this.messageService, + responseService: this.responseService, + keepOutput: true, + }), + }; + } +} +export namespace CheckForUpdates { + export namespace Commands { + export const CHECK_FOR_UPDATES: Command = Command.toLocalizedCommand( + { + id: 'arduino-check-for-updates', + label: 'Check for Arduino Updates', + category: 'Arduino', + }, + 'arduino/checkForUpdates/checkForUpdates' + ); } } diff --git a/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts b/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts index 8fbea21ae..63cea8ca4 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts @@ -1,6 +1,7 @@ import { nls } from '@theia/core/lib/common/nls'; import { injectable } from '@theia/core/shared/inversify'; import type { EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager'; +import { Later } from '../../common/nls'; import { SketchesError } from '../../common/protocol'; import { Command, @@ -41,20 +42,18 @@ export class OpenSketchFiles extends SketchContribution { sketch.name ); const yes = nls.localize('vscode/extensionsUtils/yes', 'Yes'); - this.messageService - .info(message, nls.localize('arduino/common/later', 'Later'), yes) - .then(async (answer) => { - if (answer === yes) { - this.commandService.executeCommand( - SaveAsSketch.Commands.SAVE_AS_SKETCH.id, - { - execOnlyIfTemp: false, - openAfterMove: true, - wipeOriginal: false, - } - ); - } - }); + this.messageService.info(message, Later, yes).then((answer) => { + if (answer === yes) { + this.commandService.executeCommand( + SaveAsSketch.Commands.SAVE_AS_SKETCH.id, + { + execOnlyIfTemp: false, + openAfterMove: true, + wipeOriginal: false, + } + ); + } + }); } } catch (err) { if (SketchesError.NotFound.is(err)) { diff --git a/arduino-ide-extension/src/browser/ide-updater/ide-updater-commands.ts b/arduino-ide-extension/src/browser/ide-updater/ide-updater-commands.ts index 2ab2b754a..167c83120 100644 --- a/arduino-ide-extension/src/browser/ide-updater/ide-updater-commands.ts +++ b/arduino-ide-extension/src/browser/ide-updater/ide-updater-commands.ts @@ -54,8 +54,8 @@ export class IDEUpdaterCommands implements CommandContribution { export namespace IDEUpdaterCommands { export const CHECK_FOR_UPDATES: Command = Command.toLocalizedCommand( { - id: 'arduino-ide-check-for-updates', - label: 'Check for Arduino IDE updates', + id: 'arduino-check-for-ide-updates', + label: 'Check for Arduino IDE Updates', category: 'Arduino', }, 'arduino/ide-updater/checkForUpdates' diff --git a/arduino-ide-extension/src/browser/library/library-list-widget.ts b/arduino-ide-extension/src/browser/library/library-list-widget.ts index eaeacc520..5c654b615 100644 --- a/arduino-ide-extension/src/browser/library/library-list-widget.ts +++ b/arduino-ide-extension/src/browser/library/library-list-widget.ts @@ -1,19 +1,28 @@ -import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; +import { + injectable, + postConstruct, + inject, +} from '@theia/core/shared/inversify'; import { Message } from '@theia/core/shared/@phosphor/messaging'; import { addEventListener } from '@theia/core/lib/browser/widgets/widget'; import { DialogProps } from '@theia/core/lib/browser/dialogs'; import { AbstractDialog } from '../theia/dialogs/dialogs'; import { LibraryPackage, + LibrarySearch, LibraryService, } from '../../common/protocol/library-service'; import { ListWidget } from '../widgets/component-list/list-widget'; import { Installable } from '../../common/protocol'; import { ListItemRenderer } from '../widgets/component-list/list-item-renderer'; import { nls } from '@theia/core/lib/common'; +import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer'; @injectable() -export class LibraryListWidget extends ListWidget { +export class LibraryListWidget extends ListWidget< + LibraryPackage, + LibrarySearch +> { static WIDGET_ID = 'library-list-widget'; static WIDGET_LABEL = nls.localize( 'arduino/library/title', @@ -21,9 +30,9 @@ export class LibraryListWidget extends ListWidget { ); constructor( - @inject(LibraryService) protected service: LibraryService, - @inject(ListItemRenderer) - protected itemRenderer: ListItemRenderer + @inject(LibraryService) private service: LibraryService, + @inject(ListItemRenderer) itemRenderer: ListItemRenderer, + @inject(LibraryFilterRenderer) filterRenderer: LibraryFilterRenderer ) { super({ id: LibraryListWidget.WIDGET_ID, @@ -34,6 +43,8 @@ export class LibraryListWidget extends ListWidget { itemLabel: (item: LibraryPackage) => item.name, itemDeprecated: (item: LibraryPackage) => item.deprecated, itemRenderer, + filterRenderer, + defaultSearchOptions: { query: '', type: 'All', topic: 'All' }, }); } @@ -41,7 +52,9 @@ export class LibraryListWidget extends ListWidget { protected override init(): void { super.init(); this.toDispose.pushAll([ - this.notificationCenter.onLibraryDidInstall(() => this.refresh(undefined)), + this.notificationCenter.onLibraryDidInstall(() => + this.refresh(undefined) + ), this.notificationCenter.onLibraryDidUninstall(() => this.refresh(undefined) ), diff --git a/arduino-ide-extension/src/browser/style/list-widget.css b/arduino-ide-extension/src/browser/style/list-widget.css index 5d6babf9b..ae5168bf5 100644 --- a/arduino-ide-extension/src/browser/style/list-widget.css +++ b/arduino-ide-extension/src/browser/style/list-widget.css @@ -8,13 +8,35 @@ } .arduino-list-widget .search-bar { - margin: 0px 10px 10px 15px; + margin: 0px 10px 5px 15px; } .arduino-list-widget .search-bar:focus { border-color: var(--theia-focusBorder); } +.arduino-list-widget .filter-bar { + margin: 0px 10px 5px 15px; +} + +.arduino-list-widget .filter-bar > * { + padding: 5px 5px 0px 0px; +} + +.arduino-list-widget .filter-bar .filter { + display: flex; + align-items: center; +} + +.arduino-list-widget .filter-bar .filter > select { + width: 120px; +} + +.arduino-list-widget .filter-bar .filter-label { + display: flex; + width: 50px; +} + .filterable-list-container { display: flex; flex-direction: column; @@ -23,33 +45,24 @@ } .filterable-list-container .items-container { - height: 100%; /* This has to be propagated down from the widget. */ - position: relative; /* To fix the `top` of the vertical toolbar. */ + padding-bottom: calc(2 * var(--theia-statusBar-height)); } -.filterable-list-container .items-container > div:nth-child(odd) { +.filterable-list-container .items-container > div > div:nth-child(odd) { background-color: var(--theia-sideBar-background); filter: contrast(105%); } -.filterable-list-container .items-container > div:nth-child(even) { +.filterable-list-container .items-container > div > div:nth-child(even) { background-color: var(--theia-sideBar-background); filter: contrast(95%); } -.filterable-list-container .items-container > div:hover { +.filterable-list-container .items-container > div > div:hover { background-color: var(--theia-sideBar-background); filter: contrast(90%); } -/* Perfect scrollbar does not like if we explicitly set the `background-color` of the contained elements. -See above: `.filterable-list-container .items-container > div:nth-child(odd|event)`. -We have to increase `z-index` of the scroll-bar thumb. Otherwise, the thumb is not visible. -https://github.com/arduino/arduino-pro-ide/issues/82 */ -.arduino-list-widget .filterable-list-container .items-container .ps__rail-y { - z-index: 1; -} - .component-list-item { padding: 10px 10px 10px 15px; font-size: var(--theia-ui-font-size1); @@ -113,7 +126,7 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */ .component-list-item[min-width~="170px"] .footer { padding: 5px 5px 0px 0px; - min-height: 30px; + min-height: 35px; display: flex; flex-direction: row-reverse; } @@ -122,10 +135,6 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */ flex-direction: column-reverse; } -.component-list-item .footer > * { - display: none -} - .component-list-item:hover .footer > * { display: inline-block; margin: 5px 0px 0px 10px; @@ -155,4 +164,4 @@ https://github.com/arduino/arduino-pro-ide/issues/82 */ .hc-black.hc-theia.theia-hc .component-list-item .header .installed:before { border: 1px solid var(--theia-button-border); -} +} \ No newline at end of file diff --git a/arduino-ide-extension/src/browser/theia/preferences/preference-editor-widget.ts b/arduino-ide-extension/src/browser/theia/preferences/preference-editor-widget.ts new file mode 100644 index 000000000..bcac5c084 --- /dev/null +++ b/arduino-ide-extension/src/browser/theia/preferences/preference-editor-widget.ts @@ -0,0 +1,19 @@ +import { injectable } from '@theia/core/shared/inversify'; +import { PreferencesEditorWidget as TheiaPreferencesEditorWidget } from '@theia/preferences/lib/browser/views/preference-editor-widget'; + +@injectable() +export class PreferencesEditorWidget extends TheiaPreferencesEditorWidget { + protected override resetScroll( + nodeIDToScrollTo?: string, + filterWasCleared = false + ): void { + if (this.scrollBar) { + // Absent on widget creation + this.doResetScroll(nodeIDToScrollTo, filterWasCleared); + } else { + // NOOP + // Unlike Theia, IDE2 does not start multiple tasks to check if the scrollbar is ready to reset it. + // If the "scroll reset" request arrived before the existence of the scrollbar, what to reset? + } + } +} diff --git a/arduino-ide-extension/src/browser/widgets/component-list/component-list-item.tsx b/arduino-ide-extension/src/browser/widgets/component-list/component-list-item.tsx index 39c0a2ce0..950df64bd 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/component-list-item.tsx +++ b/arduino-ide-extension/src/browser/widgets/component-list/component-list-item.tsx @@ -14,11 +14,38 @@ export class ComponentListItem< )[0]; this.state = { selectedVersion: version, + focus: false, }; } } - protected async install(item: T): Promise { + override componentDidUpdate( + prevProps: ComponentListItem.Props, + prevState: ComponentListItem.State + ): void { + if (this.state.focus !== prevState.focus) { + this.props.onFocusDidChange(); + } + } + + override render(): React.ReactNode { + const { item, itemRenderer } = this.props; + return ( +
this.setState({ focus: true })} + onMouseLeave={() => this.setState({ focus: false })} + > + {itemRenderer.renderItem( + Object.assign(this.state, { item }), + this.install.bind(this), + this.uninstall.bind(this), + this.onVersionChange.bind(this) + )} +
+ ); + } + + private async install(item: T): Promise { const toInstall = this.state.selectedVersion; const version = this.props.item.availableVersions.filter( (version) => version !== this.state.selectedVersion @@ -35,23 +62,13 @@ export class ComponentListItem< } } - protected async uninstall(item: T): Promise { + private async uninstall(item: T): Promise { await this.props.uninstall(item); } - protected onVersionChange(version: Installable.Version) { + private onVersionChange(version: Installable.Version): void { this.setState({ selectedVersion: version }); } - - override render(): React.ReactNode { - const { item, itemRenderer } = this.props; - return itemRenderer.renderItem( - Object.assign(this.state, { item }), - this.install.bind(this), - this.uninstall.bind(this), - this.onVersionChange.bind(this) - ); - } } export namespace ComponentListItem { @@ -60,9 +77,11 @@ export namespace ComponentListItem { readonly install: (item: T, version?: Installable.Version) => Promise; readonly uninstall: (item: T) => Promise; readonly itemRenderer: ListItemRenderer; + readonly onFocusDidChange: () => void; } export interface State { selectedVersion?: Installable.Version; + focus: boolean; } } diff --git a/arduino-ide-extension/src/browser/widgets/component-list/component-list.tsx b/arduino-ide-extension/src/browser/widgets/component-list/component-list.tsx index 42dce70b8..e142de629 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/component-list.tsx +++ b/arduino-ide-extension/src/browser/widgets/component-list/component-list.tsx @@ -1,43 +1,141 @@ import * as React from '@theia/core/shared/react'; -import { Installable } from '../../../common/protocol/installable'; +import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; +import { + CellMeasurer, + CellMeasurerCache, +} from 'react-virtualized/dist/commonjs/CellMeasurer'; +import type { + ListRowProps, + ListRowRenderer, +} from 'react-virtualized/dist/commonjs/List'; +import List from 'react-virtualized/dist/commonjs/List'; import { ArduinoComponent } from '../../../common/protocol/arduino-component'; +import { Installable } from '../../../common/protocol/installable'; import { ComponentListItem } from './component-list-item'; import { ListItemRenderer } from './list-item-renderer'; +function sameAs(left: T[], right: T[], key: (item: T) => string): boolean { + if (left === right) { + return true; + } + const leftLength = left.length; + if (leftLength !== right.length) { + return false; + } + for (let i = 0; i < leftLength; i++) { + const leftKey = key(left[i]); + const rightKey = key(right[i]); + if (leftKey !== rightKey) { + return false; + } + } + return true; +} + export class ComponentList extends React.Component< ComponentList.Props > { - protected container?: HTMLElement; + private readonly cache: CellMeasurerCache; + private resizeAllFlag: boolean; + private list: List | undefined; + private mostRecentWidth: number | undefined; + + constructor(props: ComponentList.Props) { + super(props); + this.cache = new CellMeasurerCache({ + defaultHeight: 300, + fixedWidth: true, + }); + } override render(): React.ReactNode { return ( -
- {this.props.items.map((item) => this.createItem(item))} -
+ + {({ width, height }) => { + if (this.mostRecentWidth && this.mostRecentWidth !== width) { + this.resizeAllFlag = true; + setTimeout(() => this.clearAll(), 0); + } + this.mostRecentWidth = width; + return ( + + ); + }} + ); } - override componentDidMount(): void { - if (this.container && this.props.resolveContainer) { - this.props.resolveContainer(this.container); + override componentDidUpdate(prevProps: ComponentList.Props): void { + if ( + this.resizeAllFlag || + !sameAs(this.props.items, prevProps.items, this.props.itemLabel) + ) { + this.clearAll(true); } } - protected setRef = (element: HTMLElement | null) => { - this.container = element || undefined; + private setListRef = (ref: List | null): void => { + this.list = ref || undefined; }; - protected createItem(item: T): React.ReactNode { + private clearAll(scrollToTop = false): void { + this.resizeAllFlag = false; + this.cache.clearAll(); + if (this.list) { + this.list.recomputeRowHeights(); + if (scrollToTop) { + this.list.scrollToPosition(0); + } + } + } + + private clear(index: number): void { + this.cache.clear(index, 0); + this.list?.recomputeRowHeights(index); + // Update the last item if the if the one before was updated + if (index === this.props.items.length - 2) { + this.cache.clear(index + 1, 0); + this.list?.recomputeRowHeights(index + 1); + } + } + + private createItem: ListRowRenderer = ({ + index, + parent, + key, + style, + }: ListRowProps): React.ReactNode => { + const item = this.props.items[index]; return ( - - key={this.props.itemLabel(item)} - item={item} - itemRenderer={this.props.itemRenderer} - install={this.props.install} - uninstall={this.props.uninstall} - /> + +
+ + key={this.props.itemLabel(item)} + item={item} + itemRenderer={this.props.itemRenderer} + install={this.props.install} + uninstall={this.props.uninstall} + onFocusDidChange={() => this.clear(index)} + /> +
+
); - } + }; } export namespace ComponentList { @@ -48,6 +146,5 @@ export namespace ComponentList { readonly itemRenderer: ListItemRenderer; readonly install: (item: T, version?: Installable.Version) => Promise; readonly uninstall: (item: T) => Promise; - readonly resolveContainer: (element: HTMLElement) => void; } } diff --git a/arduino-ide-extension/src/browser/widgets/component-list/filter-renderer.tsx b/arduino-ide-extension/src/browser/widgets/component-list/filter-renderer.tsx new file mode 100644 index 000000000..9f4a9cffb --- /dev/null +++ b/arduino-ide-extension/src/browser/widgets/component-list/filter-renderer.tsx @@ -0,0 +1,121 @@ +import { injectable } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { + BoardSearch, + LibrarySearch, + Searchable, +} from '../../../common/protocol'; + +@injectable() +export abstract class FilterRenderer { + render( + options: S, + handlePropChange: (prop: keyof S, value: S[keyof S]) => void + ): React.ReactNode { + const props = this.props(); + return ( +
+ {Object.entries(options) + .filter(([prop]) => props.includes(prop as keyof S)) + .map(([prop, value]) => ( +
+
+ {`${this.propertyLabel(prop as keyof S)}:`} +
+ +
+ ))} +
+ ); + } + protected abstract props(): (keyof S)[]; + protected abstract options(prop: keyof S): string[]; + protected abstract valueLabel(prop: keyof S, key: string): string; + protected abstract propertyLabel(prop: keyof S): string; +} + +@injectable() +export class BoardsFilterRenderer extends FilterRenderer { + protected props(): (keyof BoardSearch)[] { + return ['type']; + } + protected options(prop: keyof BoardSearch): string[] { + switch (prop) { + case 'type': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return BoardSearch.TypeLiterals as any; + default: + throw new Error(`Unexpected prop: ${prop}`); + } + } + protected valueLabel(prop: keyof BoardSearch, key: string): string { + switch (prop) { + case 'type': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (BoardSearch.TypeLabels as any)[key]; + default: + throw new Error(`Unexpected key: ${prop}`); + } + } + protected propertyLabel(prop: keyof BoardSearch): string { + switch (prop) { + case 'type': + return BoardSearch.PropertyLabels[prop]; + default: + throw new Error(`Unexpected key: ${prop}`); + } + } +} + +@injectable() +export class LibraryFilterRenderer extends FilterRenderer { + protected props(): (keyof LibrarySearch)[] { + return ['type', 'topic']; + } + protected options(prop: keyof LibrarySearch): string[] { + switch (prop) { + case 'type': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return LibrarySearch.TypeLiterals as any; + case 'topic': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return LibrarySearch.TopicLiterals as any; + default: + throw new Error(`Unexpected prop: ${prop}`); + } + } + protected propertyLabel(prop: keyof LibrarySearch): string { + switch (prop) { + case 'type': + case 'topic': + return LibrarySearch.PropertyLabels[prop]; + default: + throw new Error(`Unexpected key: ${prop}`); + } + } + protected valueLabel(prop: keyof LibrarySearch, key: string): string { + switch (prop) { + case 'type': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (LibrarySearch.TypeLabels as any)[key] as any; + case 'topic': + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (LibrarySearch.TopicLabels as any)[key] as any; + default: + throw new Error(`Unexpected prop: ${prop}`); + } + } +} diff --git a/arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx b/arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx index ce132bc5f..70d4321d2 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx +++ b/arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx @@ -14,25 +14,30 @@ import { ComponentList } from './component-list'; import { ListItemRenderer } from './list-item-renderer'; import { ResponseServiceClient } from '../../../common/protocol'; import { nls } from '@theia/core/lib/common'; +import { FilterRenderer } from './filter-renderer'; export class FilterableListContainer< - T extends ArduinoComponent + T extends ArduinoComponent, + S extends Searchable.Options > extends React.Component< - FilterableListContainer.Props, - FilterableListContainer.State + FilterableListContainer.Props, + FilterableListContainer.State > { - constructor(props: Readonly>) { + constructor(props: Readonly>) { super(props); this.state = { - filterText: '', + searchOptions: props.defaultSearchOptions, items: [], }; } override componentDidMount(): void { this.search = debounce(this.search, 500); - this.handleFilterTextChange(''); - this.props.filterTextChangeEvent(this.handleFilterTextChange.bind(this)); + this.search(this.state.searchOptions); + this.props.searchOptionsDidChange((newSearchOptions) => { + const { searchOptions } = this.state; + this.setSearchOptionsAndUpdate({ ...searchOptions, ...newSearchOptions }); + }); } override componentDidUpdate(): void { @@ -44,30 +49,38 @@ export class FilterableListContainer< override render(): React.ReactNode { return (
- {this.renderSearchFilter()} {this.renderSearchBar()} + {this.renderSearchFilter()} {this.renderComponentList()}
); } protected renderSearchFilter(): React.ReactNode { - return undefined; + return ( + <> + {this.props.filterRenderer.render( + this.state.searchOptions, + this.handlePropChange.bind(this) + )} + + ); } protected renderSearchBar(): React.ReactNode { return ( + this.handlePropChange('query', query as S['query']) + } /> ); } protected renderComponentList(): React.ReactNode { - const { itemLabel, itemDeprecated, resolveContainer, itemRenderer } = - this.props; + const { itemLabel, itemDeprecated, itemRenderer } = this.props; return ( items={this.state.items} @@ -76,22 +89,26 @@ export class FilterableListContainer< itemRenderer={itemRenderer} install={this.install.bind(this)} uninstall={this.uninstall.bind(this)} - resolveContainer={resolveContainer} /> ); } - protected handleFilterTextChange = ( - filterText: string = this.state.filterText - ) => { - this.setState({ filterText }); - this.search(filterText); + protected handlePropChange = (prop: keyof S, value: S[keyof S]): void => { + const searchOptions = { + ...this.state.searchOptions, + [prop]: value, + }; + this.setSearchOptionsAndUpdate(searchOptions); }; - protected search(query: string): void { + private setSearchOptionsAndUpdate(searchOptions: S) { + this.setState({ searchOptions }, () => this.search(searchOptions)); + } + + protected search(searchOptions: S): void { const { searchable } = this.props; searchable - .search({ query: query.trim() }) + .search(searchOptions) .then((items) => this.setState({ items: this.sort(items) })); } @@ -119,7 +136,7 @@ export class FilterableListContainer< ` ${item.name}:${version}`, run: ({ progressId }) => install({ item, progressId, version }), }); - const items = await searchable.search({ query: this.state.filterText }); + const items = await searchable.search(this.state.searchOptions); this.setState({ items: this.sort(items) }); } @@ -147,21 +164,25 @@ export class FilterableListContainer< }`, run: ({ progressId }) => uninstall({ item, progressId }), }); - const items = await searchable.search({ query: this.state.filterText }); + const items = await searchable.search(this.state.searchOptions); this.setState({ items: this.sort(items) }); } } export namespace FilterableListContainer { - export interface Props { - readonly container: ListWidget; - readonly searchable: Searchable; + export interface Props< + T extends ArduinoComponent, + S extends Searchable.Options + > { + readonly defaultSearchOptions: S; + readonly container: ListWidget; + readonly searchable: Searchable; readonly itemLabel: (item: T) => string; readonly itemDeprecated: (item: T) => boolean; readonly itemRenderer: ListItemRenderer; - readonly resolveContainer: (element: HTMLElement) => void; + readonly filterRenderer: FilterRenderer; readonly resolveFocus: (element: HTMLElement | undefined) => void; - readonly filterTextChangeEvent: Event; + readonly searchOptionsDidChange: Event | undefined>; readonly messageService: MessageService; readonly responseService: ResponseServiceClient; readonly install: ({ @@ -183,8 +204,8 @@ export namespace FilterableListContainer { readonly commandService: CommandService; } - export interface State { - filterText: string; + export interface State { + searchOptions: S; items: T[]; } } diff --git a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx index 62e1f3e1c..537f4d415 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx +++ b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx @@ -14,7 +14,7 @@ export class ListItemRenderer { protected onMoreInfoClick = ( event: React.SyntheticEvent - ) => { + ): void => { const { target } = event.nativeEvent; if (target instanceof HTMLAnchorElement) { this.windowService.openNewWindow(target.href, { external: true }); @@ -28,7 +28,7 @@ export class ListItemRenderer { uninstall: (item: T) => Promise, onVersionChange: (version: Installable.Version) => void ): React.ReactNode { - const { item } = input; + const { item, focus } = input; let nameAndAuthor: JSX.Element; if (item.name && item.author) { const name = {item.name}; @@ -120,10 +120,12 @@ export class ListItemRenderer { {description}
{moreInfo}
-
- {versions} - {installButton} -
+ {focus && ( +
+ {versions} + {installButton} +
+ )} ); } diff --git a/arduino-ide-extension/src/browser/widgets/component-list/list-widget-frontend-contribution.ts b/arduino-ide-extension/src/browser/widgets/component-list/list-widget-frontend-contribution.ts index ed9827919..6ec22ddfd 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/list-widget-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/widgets/component-list/list-widget-frontend-contribution.ts @@ -3,13 +3,20 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser/fronten import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { ArduinoComponent } from '../../../common/protocol/arduino-component'; import { ListWidget } from './list-widget'; +import { Searchable } from '../../../common/protocol'; @injectable() -export abstract class ListWidgetFrontendContribution - extends AbstractViewContribution> +export abstract class ListWidgetFrontendContribution< + T extends ArduinoComponent, + S extends Searchable.Options + > + extends AbstractViewContribution> implements FrontendApplicationContribution { - async initializeLayout(): Promise {} + async initializeLayout(): Promise { + // TS requires at least one method from `FrontendApplicationContribution`. + // Expected to be empty. + } override registerMenus(): void { // NOOP diff --git a/arduino-ide-extension/src/browser/widgets/component-list/list-widget.tsx b/arduino-ide-extension/src/browser/widgets/component-list/list-widget.tsx index 3e562c140..3e388cce2 100644 --- a/arduino-ide-extension/src/browser/widgets/component-list/list-widget.tsx +++ b/arduino-ide-extension/src/browser/widgets/component-list/list-widget.tsx @@ -6,9 +6,7 @@ import { } from '@theia/core/shared/inversify'; import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Message } from '@theia/core/shared/@phosphor/messaging'; -import { Deferred } from '@theia/core/lib/common/promise-util'; import { Emitter } from '@theia/core/lib/common/event'; -import { MaybePromise } from '@theia/core/lib/common/types'; import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { CommandService } from '@theia/core/lib/common/command'; import { MessageService } from '@theia/core/lib/common/message-service'; @@ -21,10 +19,12 @@ import { import { FilterableListContainer } from './filterable-list-container'; import { ListItemRenderer } from './list-item-renderer'; import { NotificationCenter } from '../../notification-center'; +import { FilterRenderer } from './filter-renderer'; @injectable() export abstract class ListWidget< - T extends ArduinoComponent + T extends ArduinoComponent, + S extends Searchable.Options > extends ReactWidget { @inject(MessageService) protected readonly messageService: MessageService; @@ -42,9 +42,8 @@ export abstract class ListWidget< * Do not touch or use it. It is for setting the focus on the `input` after the widget activation. */ protected focusNode: HTMLElement | undefined; - protected readonly deferredContainer = new Deferred(); - protected readonly filterTextChangeEmitter = new Emitter< - string | undefined + protected readonly searchOptionsChangeEmitter = new Emitter< + Partial | undefined >(); /** * Instead of running an `update` from the `postConstruct` `init` method, @@ -52,7 +51,7 @@ export abstract class ListWidget< */ protected firstActivate = true; - constructor(protected options: ListWidget.Options) { + constructor(protected options: ListWidget.Options) { super(); const { id, label, iconClass } = options; this.id = id; @@ -62,10 +61,8 @@ export abstract class ListWidget< this.title.closable = true; this.addClass('arduino-list-widget'); this.node.tabIndex = 0; // To be able to set the focus on the widget. - this.scrollOptions = { - suppressScrollX: true, - }; - this.toDispose.push(this.filterTextChangeEmitter); + this.scrollOptions = undefined; + this.toDispose.push(this.searchOptionsChangeEmitter); } @postConstruct() @@ -77,10 +74,6 @@ export abstract class ListWidget< ]); } - protected override getScrollContainer(): MaybePromise { - return this.deferredContainer.promise; - } - protected override onAfterShow(message: Message): void { this.maybeUpdateOnFirstRender(); super.onAfterShow(message); @@ -109,7 +102,7 @@ export abstract class ListWidget< this.updateScrollBar(); } - protected onFocusResolved = (element: HTMLElement | undefined) => { + protected onFocusResolved = (element: HTMLElement | undefined): void => { this.focusNode = element; }; @@ -137,9 +130,9 @@ export abstract class ListWidget< render(): React.ReactNode { return ( - + + defaultSearchOptions={this.options.defaultSearchOptions} container={this} - resolveContainer={this.deferredContainer.resolve} resolveFocus={this.onFocusResolved} searchable={this.options.searchable} install={this.install.bind(this)} @@ -147,7 +140,8 @@ export abstract class ListWidget< itemLabel={this.options.itemLabel} itemDeprecated={this.options.itemDeprecated} itemRenderer={this.options.itemRenderer} - filterTextChangeEvent={this.filterTextChangeEmitter.event} + filterRenderer={this.options.filterRenderer} + searchOptionsDidChange={this.searchOptionsChangeEmitter.event} messageService={this.messageService} commandService={this.commandService} responseService={this.responseService} @@ -159,10 +153,8 @@ export abstract class ListWidget< * If `filterText` is defined, sets the filter text to the argument. * If it is `undefined`, updates the view state by re-running the search with the current `filterText` term. */ - refresh(filterText: string | undefined): void { - this.deferredContainer.promise.then(() => - this.filterTextChangeEmitter.fire(filterText) - ); + refresh(searchOptions: Partial | undefined): void { + this.searchOptionsChangeEmitter.fire(searchOptions); } updateScrollBar(): void { @@ -173,14 +165,19 @@ export abstract class ListWidget< } export namespace ListWidget { - export interface Options { + export interface Options< + T extends ArduinoComponent, + S extends Searchable.Options + > { readonly id: string; readonly label: string; readonly iconClass: string; readonly installable: Installable; - readonly searchable: Searchable; + readonly searchable: Searchable; readonly itemLabel: (item: T) => string; readonly itemDeprecated: (item: T) => boolean; readonly itemRenderer: ListItemRenderer; + readonly filterRenderer: FilterRenderer; + readonly defaultSearchOptions: S; } } diff --git a/arduino-ide-extension/src/common/nls.ts b/arduino-ide-extension/src/common/nls.ts index 2435ff3b4..a2e58b86a 100644 --- a/arduino-ide-extension/src/common/nls.ts +++ b/arduino-ide-extension/src/common/nls.ts @@ -1,3 +1,21 @@ import { nls } from '@theia/core/lib/common/nls'; 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'); +export const All = nls.localize('arduino/common/all', 'All'); +export const Type = nls.localize('arduino/common/type', 'Type'); +export const Partner = nls.localize('arduino/common/partner', 'Partner'); +export const Contributed = nls.localize( + 'arduino/common/contributed', + 'Contributed' +); +export const Recommended = nls.localize( + 'arduino/common/recommended', + 'Recommended' +); +export const Retired = nls.localize('arduino/common/retired', 'Retired'); +export const InstallManually = nls.localize( + 'arduino/common/installManually', + 'Install Manually' +); diff --git a/arduino-ide-extension/src/common/protocol/arduino-component.ts b/arduino-ide-extension/src/common/protocol/arduino-component.ts index 4a32d869a..2cdfe38a2 100644 --- a/arduino-ide-extension/src/common/protocol/arduino-component.ts +++ b/arduino-ide-extension/src/common/protocol/arduino-component.ts @@ -7,11 +7,13 @@ export interface ArduinoComponent { readonly summary: string; readonly description: string; readonly moreInfoLink?: string; - readonly availableVersions: Installable.Version[]; readonly installable: boolean; - readonly installedVersion?: Installable.Version; + /** + * This is the `Type` in IDE (1.x) UI. + */ + readonly types: string[]; } export namespace ArduinoComponent { export function is(arg: any): arg is ArduinoComponent { diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts index 6ff6e440f..9e84f127f 100644 --- a/arduino-ide-extension/src/common/protocol/boards-service.ts +++ b/arduino-ide-extension/src/common/protocol/boards-service.ts @@ -2,6 +2,8 @@ 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 { All, Contributed, Partner, Type, Updatable } from '../nls'; export type AvailablePorts = Record]>; export namespace AvailablePorts { @@ -131,7 +133,7 @@ export const BoardsServicePath = '/services/boards-service'; export const BoardsService = Symbol('BoardsService'); export interface BoardsService extends Installable, - Searchable { + Searchable { getState(): Promise; getBoardDetails(options: { fqbn: string }): Promise; getBoardPackage(options: { id: string }): Promise; @@ -145,6 +147,40 @@ export interface BoardsService }): Promise; } +export interface BoardSearch extends Searchable.Options { + readonly type?: BoardSearch.Type; +} +export namespace BoardSearch { + export const TypeLiterals = [ + 'All', + 'Updatable', + 'Arduino', + 'Contributed', + 'Arduino Certified', + 'Partner', + 'Arduino@Heart', + ] as const; + export type Type = typeof TypeLiterals[number]; + export const TypeLabels: Record = { + All: All, + Updatable: Updatable, + Arduino: 'Arduino', + Contributed: Contributed, + 'Arduino Certified': nls.localize( + 'arduino/boardsType/arduinoCertified', + 'Arduino Certified' + ), + Partner: Partner, + 'Arduino@Heart': 'Arduino@Heart', + }; + export const PropertyLabels: Record< + keyof Omit, + string + > = { + type: Type, + }; +} + export interface Port { readonly address: string; readonly addressLabel: string; diff --git a/arduino-ide-extension/src/common/protocol/installable.ts b/arduino-ide-extension/src/common/protocol/installable.ts index 527805697..096934ff5 100644 --- a/arduino-ide-extension/src/common/protocol/installable.ts +++ b/arduino-ide-extension/src/common/protocol/installable.ts @@ -36,6 +36,31 @@ export namespace Installable { }; } + export const Installed = ({ + installedVersion, + }: T): boolean => { + return !!installedVersion; + }; + + export const Updateable = (item: T): boolean => { + const { installedVersion } = item; + if (!installedVersion) { + return false; + } + const latestVersion = item.availableVersions[0]; + if (!latestVersion) { + console.warn( + `Installed version ${installedVersion} is available for ${item.name}, but no available versions were available. Skipping.` + ); + return false; + } + const result = Installable.Version.COMPARATOR( + latestVersion, + installedVersion + ); + return result > 0; + }; + export async function installWithProgress< T extends ArduinoComponent >(options: { @@ -44,6 +69,7 @@ export namespace Installable { responseService: ResponseServiceClient; item: T; version: Installable.Version; + keepOutput?: boolean; }): Promise { const { item, version } = options; return ExecuteWithProgress.doWithProgress({ @@ -65,6 +91,7 @@ export namespace Installable { messageService: MessageService; responseService: ResponseServiceClient; item: T; + keepOutput?: boolean; }): Promise { const { item } = options; return ExecuteWithProgress.doWithProgress({ diff --git a/arduino-ide-extension/src/common/protocol/library-service.ts b/arduino-ide-extension/src/common/protocol/library-service.ts index 235ca9ef6..14f6f0d04 100644 --- a/arduino-ide-extension/src/common/protocol/library-service.ts +++ b/arduino-ide-extension/src/common/protocol/library-service.ts @@ -1,13 +1,24 @@ import { Searchable } from './searchable'; import { Installable } from './installable'; import { ArduinoComponent } from './arduino-component'; +import { nls } from '@theia/core/lib/common/nls'; +import { + All, + Contributed, + Partner, + Recommended, + Retired, + Type, + Updatable, +} from '../nls'; export const LibraryServicePath = '/services/library-service'; export const LibraryService = Symbol('LibraryService'); export interface LibraryService extends Installable, - Searchable { + Searchable { list(options: LibraryService.List.Options): Promise; + search(options: LibrarySearch): Promise; /** * When `installDependencies` is not set, it is `true` by default. If you want to skip the installation of required dependencies, set it to `false`. */ @@ -38,6 +49,86 @@ export interface LibraryService }): Promise; } +export interface LibrarySearch extends Searchable.Options { + readonly type?: LibrarySearch.Type; + readonly topic?: LibrarySearch.Topic; +} +export namespace LibrarySearch { + export const TypeLiterals = [ + 'All', + 'Updatable', + 'Installed', + 'Arduino', + 'Partner', + 'Recommended', + 'Contributed', + 'Retired', + ] as const; + export type Type = typeof TypeLiterals[number]; + export const TypeLabels: Record = { + All: All, + Updatable: Updatable, + Installed: nls.localize('arduino/libraryType/installed', 'Installed'), + Arduino: 'Arduino', + Partner: Partner, + Recommended: Recommended, + Contributed: Contributed, + Retired: Retired, + }; + export const TopicLiterals = [ + 'All', + 'Communication', + 'Data Processing', + 'Data Storage', + 'Device Control', + 'Display', + 'Other', + 'Sensors', + 'Signal Input/Output', + 'Timing', + 'Uncategorized', + ] as const; + export type Topic = typeof TopicLiterals[number]; + export const TopicLabels: Record = { + All: All, + Communication: nls.localize( + 'arduino/libraryTopic/communication', + 'Communication' + ), + 'Data Processing': nls.localize( + 'arduino/libraryTopic/dataProcessing', + 'Data Processing' + ), + 'Data Storage': nls.localize( + 'arduino/libraryTopic/dataStorage', + 'Data Storage' + ), + 'Device Control': nls.localize( + 'arduino/libraryTopic/deviceControl', + 'Device Control' + ), + Display: nls.localize('arduino/libraryTopic/display', 'Display'), + Other: nls.localize('arduino/libraryTopic/other', 'Other'), + Sensors: nls.localize('arduino/libraryTopic/sensors', 'Sensors'), + 'Signal Input/Output': nls.localize( + 'arduino/libraryTopic/signalInputOutput', + 'Signal Input/Output' + ), + Timing: nls.localize('arduino/libraryTopic/timing', 'Timing'), + Uncategorized: nls.localize( + 'arduino/libraryTopic/uncategorized', + 'Uncategorized' + ), + }; + export const PropertyLabels: Record< + keyof Omit, + string + > = { + topic: nls.localize('arduino/librarySearchProperty/topic', 'Topic'), + type: Type, + }; +} + export namespace LibraryService { export namespace List { export interface Options { @@ -85,6 +176,10 @@ export interface LibraryPackage extends ArduinoComponent { readonly exampleUris: string[]; readonly location: LibraryLocation; readonly installDirUri?: string; + /** + * This is the `Topic` in the IDE (1.x) UI. + */ + readonly category: string; } export namespace LibraryPackage { export function is(arg: any): arg is LibraryPackage { diff --git a/arduino-ide-extension/src/common/protocol/progressible.ts b/arduino-ide-extension/src/common/protocol/progressible.ts index 03e5141ef..c27737ccc 100644 --- a/arduino-ide-extension/src/common/protocol/progressible.ts +++ b/arduino-ide-extension/src/common/protocol/progressible.ts @@ -39,7 +39,7 @@ export namespace ExecuteWithProgress { ); } - async function withProgress( + export async function withProgress( text: string, messageService: MessageService, cb: (progress: Progress, token: CancellationToken) => Promise diff --git a/arduino-ide-extension/src/common/protocol/searchable.ts b/arduino-ide-extension/src/common/protocol/searchable.ts index 30a056773..af6a2c02e 100644 --- a/arduino-ide-extension/src/common/protocol/searchable.ts +++ b/arduino-ide-extension/src/common/protocol/searchable.ts @@ -1,5 +1,5 @@ -export interface Searchable { - search(options: Searchable.Options): Promise; +export interface Searchable { + search(options: O): Promise; } export namespace Searchable { export interface Options { diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index 927a10e46..12c3fe354 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -16,6 +16,7 @@ import { AvailablePorts, BoardWithPackage, BoardUserField, + BoardSearch, } from '../common/protocol'; import { PlatformInstallRequest, @@ -264,7 +265,7 @@ export class BoardsServiceImpl })); } - async search(options: { query?: string }): Promise { + async search(options: BoardSearch): Promise { const coreClient = await this.coreClient; const { client, instance } = coreClient; @@ -310,6 +311,7 @@ export class BoardsServiceImpl .map((b) => b.getName()) .join(', '), installable: true, + types: platform.getTypeList(), deprecated: platform.getDeprecated(), summary: nls.localize( 'arduino/component/boardsIncluded', @@ -380,7 +382,29 @@ export class BoardsServiceImpl } } - return [...packages.values()]; + const filter = this.typePredicate(options); + return [...packages.values()].filter(filter); + } + + private typePredicate( + options: BoardSearch + ): (item: BoardsPackage) => boolean { + const { type } = options; + if (!type || type === 'All') { + return () => true; + } + switch (options.type) { + case 'Updatable': + return Installable.Updateable; + case 'Arduino': + case 'Partner': + case 'Arduino@Heart': + case 'Contributed': + case 'Arduino Certified': + return ({ types }: BoardsPackage) => !!types && types?.includes(type); + default: + throw new Error(`Unhandled type: ${options.type}`); + } } async install(options: { diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.d.ts b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.d.ts index 9f34ff24f..65af18363 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.d.ts +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.d.ts @@ -49,6 +49,7 @@ interface IArduinoCoreServiceService extends grpc.ServiceDefinition; responseDeserialize: grpc.deserialize; } +interface IArduinoCoreServiceService_ILibraryUpgrade extends grpc.MethodDefinition { + path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUpgrade"; + requestStream: false; + responseStream: true; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} interface IArduinoCoreServiceService_IZipLibraryInstall extends grpc.MethodDefinition { path: "/cc.arduino.cli.commands.v1.ArduinoCoreService/ZipLibraryInstall"; requestStream: false; @@ -465,6 +475,7 @@ export interface IArduinoCoreServiceServer { platformList: grpc.handleUnaryCall; libraryDownload: grpc.handleServerStreamingCall; libraryInstall: grpc.handleServerStreamingCall; + libraryUpgrade: grpc.handleServerStreamingCall; zipLibraryInstall: grpc.handleServerStreamingCall; gitLibraryInstall: grpc.handleServerStreamingCall; libraryUninstall: grpc.handleServerStreamingCall; @@ -557,6 +568,8 @@ export interface IArduinoCoreServiceClient { libraryDownload(request: cc_arduino_cli_commands_v1_lib_pb.LibraryDownloadRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; libraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.LibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; libraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.LibraryInstallRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; + libraryUpgrade(request: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest, options?: Partial): grpc.ClientReadableStream; + libraryUpgrade(request: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; zipLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.ZipLibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; zipLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.ZipLibraryInstallRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; gitLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.GitLibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; @@ -663,6 +676,8 @@ export class ArduinoCoreServiceClient extends grpc.Client implements IArduinoCor public libraryDownload(request: cc_arduino_cli_commands_v1_lib_pb.LibraryDownloadRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; public libraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.LibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; public libraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.LibraryInstallRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; + public libraryUpgrade(request: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest, options?: Partial): grpc.ClientReadableStream; + public libraryUpgrade(request: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; public zipLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.ZipLibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; public zipLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.ZipLibraryInstallRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; public gitLibraryInstall(request: cc_arduino_cli_commands_v1_lib_pb.GitLibraryInstallRequest, options?: Partial): grpc.ClientReadableStream; diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.js b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.js index 8358c89e2..2251692b1 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.js +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb.js @@ -489,6 +489,28 @@ function deserialize_cc_arduino_cli_commands_v1_LibraryUpgradeAllResponse(buffer return cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeAllResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_cc_arduino_cli_commands_v1_LibraryUpgradeRequest(arg) { + if (!(arg instanceof cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest)) { + throw new Error('Expected argument of type cc.arduino.cli.commands.v1.LibraryUpgradeRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_cc_arduino_cli_commands_v1_LibraryUpgradeRequest(buffer_arg) { + return cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_cc_arduino_cli_commands_v1_LibraryUpgradeResponse(arg) { + if (!(arg instanceof cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeResponse)) { + throw new Error('Expected argument of type cc.arduino.cli.commands.v1.LibraryUpgradeResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_cc_arduino_cli_commands_v1_LibraryUpgradeResponse(buffer_arg) { + return cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_cc_arduino_cli_commands_v1_ListProgrammersAvailableForUploadRequest(arg) { if (!(arg instanceof cc_arduino_cli_commands_v1_upload_pb.ListProgrammersAvailableForUploadRequest)) { throw new Error('Expected argument of type cc.arduino.cli.commands.v1.ListProgrammersAvailableForUploadRequest'); @@ -1325,6 +1347,18 @@ libraryInstall: { responseSerialize: serialize_cc_arduino_cli_commands_v1_LibraryInstallResponse, responseDeserialize: deserialize_cc_arduino_cli_commands_v1_LibraryInstallResponse, }, + // Upgrade a library to the newest version available. +libraryUpgrade: { + path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/LibraryUpgrade', + requestStream: false, + responseStream: true, + requestType: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeRequest, + responseType: cc_arduino_cli_commands_v1_lib_pb.LibraryUpgradeResponse, + requestSerialize: serialize_cc_arduino_cli_commands_v1_LibraryUpgradeRequest, + requestDeserialize: deserialize_cc_arduino_cli_commands_v1_LibraryUpgradeRequest, + responseSerialize: serialize_cc_arduino_cli_commands_v1_LibraryUpgradeResponse, + responseDeserialize: deserialize_cc_arduino_cli_commands_v1_LibraryUpgradeResponse, + }, // Install a library from a Zip File zipLibraryInstall: { path: '/cc.arduino.cli.commands.v1.ArduinoCoreService/ZipLibraryInstall', diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.d.ts b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.d.ts index a7bd5e06c..f79eb14c0 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.d.ts +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.d.ts @@ -159,6 +159,11 @@ export class Platform extends jspb.Message { getDeprecated(): boolean; setDeprecated(value: boolean): Platform; + clearTypeList(): void; + getTypeList(): Array; + setTypeList(value: Array): Platform; + addType(value: string, index?: number): string; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): Platform.AsObject; @@ -182,6 +187,7 @@ export namespace Platform { boardsList: Array, manuallyInstalled: boolean, deprecated: boolean, + typeList: Array, } } diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.js b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.js index 8b3916047..4ffbe5d22 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.js +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/common_pb.js @@ -987,7 +987,7 @@ proto.cc.arduino.cli.commands.v1.Programmer.prototype.setName = function(value) * @private {!Array} * @const */ -proto.cc.arduino.cli.commands.v1.Platform.repeatedFields_ = [8]; +proto.cc.arduino.cli.commands.v1.Platform.repeatedFields_ = [8,11]; @@ -1030,7 +1030,8 @@ proto.cc.arduino.cli.commands.v1.Platform.toObject = function(includeInstance, m boardsList: jspb.Message.toObjectList(msg.getBoardsList(), proto.cc.arduino.cli.commands.v1.Board.toObject, includeInstance), manuallyInstalled: jspb.Message.getBooleanFieldWithDefault(msg, 9, false), - deprecated: jspb.Message.getBooleanFieldWithDefault(msg, 10, false) + deprecated: jspb.Message.getBooleanFieldWithDefault(msg, 10, false), + typeList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f }; if (includeInstance) { @@ -1108,6 +1109,10 @@ proto.cc.arduino.cli.commands.v1.Platform.deserializeBinaryFromReader = function var value = /** @type {boolean} */ (reader.readBool()); msg.setDeprecated(value); break; + case 11: + var value = /** @type {string} */ (reader.readString()); + msg.addType(value); + break; default: reader.skipField(); break; @@ -1208,6 +1213,13 @@ proto.cc.arduino.cli.commands.v1.Platform.serializeBinaryToWriter = function(mes f ); } + f = message.getTypeList(); + if (f.length > 0) { + writer.writeRepeatedString( + 11, + f + ); + } }; @@ -1411,6 +1423,43 @@ proto.cc.arduino.cli.commands.v1.Platform.prototype.setDeprecated = function(val }; +/** + * repeated string type = 11; + * @return {!Array} + */ +proto.cc.arduino.cli.commands.v1.Platform.prototype.getTypeList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 11)); +}; + + +/** + * @param {!Array} value + * @return {!proto.cc.arduino.cli.commands.v1.Platform} returns this + */ +proto.cc.arduino.cli.commands.v1.Platform.prototype.setTypeList = function(value) { + return jspb.Message.setField(this, 11, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.cc.arduino.cli.commands.v1.Platform} returns this + */ +proto.cc.arduino.cli.commands.v1.Platform.prototype.addType = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 11, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.cc.arduino.cli.commands.v1.Platform} returns this + */ +proto.cc.arduino.cli.commands.v1.Platform.prototype.clearTypeList = function() { + return this.setTypeList([]); +}; + + diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.d.ts b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.d.ts index d8f4dc9cd..c6573e28e 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.d.ts +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.d.ts @@ -134,6 +134,69 @@ export namespace LibraryInstallResponse { } } +export class LibraryUpgradeRequest 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): 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; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: LibraryUpgradeRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): LibraryUpgradeRequest; + static deserializeBinaryFromReader(message: LibraryUpgradeRequest, reader: jspb.BinaryReader): LibraryUpgradeRequest; +} + +export namespace LibraryUpgradeRequest { + export type AsObject = { + instance?: cc_arduino_cli_commands_v1_common_pb.Instance.AsObject, + name: string, + noDeps: boolean, + } +} + +export class LibraryUpgradeResponse extends jspb.Message { + + hasProgress(): boolean; + clearProgress(): void; + 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; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: LibraryUpgradeResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): LibraryUpgradeResponse; + static deserializeBinaryFromReader(message: LibraryUpgradeResponse, reader: jspb.BinaryReader): LibraryUpgradeResponse; +} + +export namespace LibraryUpgradeResponse { + export type AsObject = { + progress?: cc_arduino_cli_commands_v1_common_pb.DownloadProgress.AsObject, + taskProgress?: cc_arduino_cli_commands_v1_common_pb.TaskProgress.AsObject, + } +} + export class LibraryUninstallRequest extends jspb.Message { hasInstance(): boolean; diff --git a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.js b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.js index be2398106..7aef8b4e8 100644 --- a/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.js +++ b/arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/lib_pb.js @@ -42,6 +42,8 @@ goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUninstallRequest', nu goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUninstallResponse', null, global); goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUpgradeAllRequest', null, global); goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUpgradeAllResponse', null, global); +goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest', null, global); +goog.exportSymbol('proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse', null, global); goog.exportSymbol('proto.cc.arduino.cli.commands.v1.SearchedLibrary', null, global); goog.exportSymbol('proto.cc.arduino.cli.commands.v1.ZipLibraryInstallRequest', null, global); goog.exportSymbol('proto.cc.arduino.cli.commands.v1.ZipLibraryInstallResponse', null, global); @@ -129,6 +131,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.cc.arduino.cli.commands.v1.LibraryInstallResponse.displayName = 'proto.cc.arduino.cli.commands.v1.LibraryInstallResponse'; } +/** + * 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.LibraryUpgradeRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.displayName = 'proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest'; +} +/** + * 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.LibraryUpgradeResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.displayName = 'proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -1408,6 +1452,419 @@ proto.cc.arduino.cli.commands.v1.LibraryInstallResponse.prototype.hasTaskProgres +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_, 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.LibraryUpgradeRequest.prototype.toObject = function(opt_includeInstance) { + return proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.LibraryUpgradeRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.toObject = function(includeInstance, msg) { + var f, obj = { + instance: (f = msg.getInstance()) && cc_arduino_cli_commands_v1_common_pb.Instance.toObject(includeInstance, f), + name: jspb.Message.getFieldWithDefault(msg, 2, ""), + noDeps: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) + }; + + 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.LibraryUpgradeRequest} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest; + return proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.LibraryUpgradeRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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_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.setName(value); + break; + case 3: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setNoDeps(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.LibraryUpgradeRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.LibraryUpgradeRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.getName(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getNoDeps(); + if (f) { + writer.writeBool( + 3, + f + ); + } +}; + + +/** + * optional Instance instance = 1; + * @return {?proto.cc.arduino.cli.commands.v1.Instance} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.LibraryUpgradeRequest} returns this +*/ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.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.LibraryUpgradeRequest} returns this + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.clearInstance = function() { + return this.setInstance(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.hasInstance = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional string name = 2; + * @return {string} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest} returns this + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bool no_deps = 3; + * @return {boolean} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.getNoDeps = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest} returns this + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeRequest.prototype.setNoDeps = function(value) { + return jspb.Message.setProto3BooleanField(this, 3, 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_, 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.LibraryUpgradeResponse.prototype.toObject = function(opt_includeInstance) { + return proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.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.LibraryUpgradeResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.toObject = function(includeInstance, msg) { + var f, obj = { + progress: (f = msg.getProgress()) && cc_arduino_cli_commands_v1_common_pb.DownloadProgress.toObject(includeInstance, f), + taskProgress: (f = msg.getTaskProgress()) && cc_arduino_cli_commands_v1_common_pb.TaskProgress.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.LibraryUpgradeResponse} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse; + return proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.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.LibraryUpgradeResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.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_common_pb.DownloadProgress; + reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.DownloadProgress.deserializeBinaryFromReader); + msg.setProgress(value); + break; + case 2: + var value = new cc_arduino_cli_commands_v1_common_pb.TaskProgress; + reader.readMessage(value,cc_arduino_cli_commands_v1_common_pb.TaskProgress.deserializeBinaryFromReader); + msg.setTaskProgress(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.LibraryUpgradeResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.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.LibraryUpgradeResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getProgress(); + if (f != null) { + writer.writeMessage( + 1, + f, + cc_arduino_cli_commands_v1_common_pb.DownloadProgress.serializeBinaryToWriter + ); + } + f = message.getTaskProgress(); + if (f != null) { + writer.writeMessage( + 2, + f, + cc_arduino_cli_commands_v1_common_pb.TaskProgress.serializeBinaryToWriter + ); + } +}; + + +/** + * optional DownloadProgress progress = 1; + * @return {?proto.cc.arduino.cli.commands.v1.DownloadProgress} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.getProgress = function() { + return /** @type{?proto.cc.arduino.cli.commands.v1.DownloadProgress} */ ( + jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.DownloadProgress, 1)); +}; + + +/** + * @param {?proto.cc.arduino.cli.commands.v1.DownloadProgress|undefined} value + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse} returns this +*/ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.setProgress = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse} returns this + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.clearProgress = function() { + return this.setProgress(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.hasProgress = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional TaskProgress task_progress = 2; + * @return {?proto.cc.arduino.cli.commands.v1.TaskProgress} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.getTaskProgress = function() { + return /** @type{?proto.cc.arduino.cli.commands.v1.TaskProgress} */ ( + jspb.Message.getWrapperField(this, cc_arduino_cli_commands_v1_common_pb.TaskProgress, 2)); +}; + + +/** + * @param {?proto.cc.arduino.cli.commands.v1.TaskProgress|undefined} value + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse} returns this +*/ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.setTaskProgress = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse} returns this + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.clearTaskProgress = function() { + return this.setTaskProgress(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.cc.arduino.cli.commands.v1.LibraryUpgradeResponse.prototype.hasTaskProgress = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto. diff --git a/arduino-ide-extension/src/node/library-service-impl.ts b/arduino-ide-extension/src/node/library-service-impl.ts index c205826b5..073e4ae76 100644 --- a/arduino-ide-extension/src/node/library-service-impl.ts +++ b/arduino-ide-extension/src/node/library-service-impl.ts @@ -3,6 +3,7 @@ import { LibraryDependency, LibraryLocation, LibraryPackage, + LibrarySearch, LibraryService, } from '../common/protocol/library-service'; import { CoreClientAware } from './core-client-provider'; @@ -26,6 +27,7 @@ import { ILogger, notEmpty } from '@theia/core'; import { FileUri } from '@theia/core/lib/node'; import { ResponseService, NotificationServiceServer } from '../common/protocol'; import { ExecuteWithProgress } from './grpc-progressible'; +import { duration } from '../common/decorators'; @injectable() export class LibraryServiceImpl @@ -44,7 +46,8 @@ export class LibraryServiceImpl @inject(NotificationServiceServer) protected readonly notificationServer: NotificationServiceServer; - async search(options: { query?: string }): Promise { + @duration() + async search(options: LibrarySearch): Promise { const coreClient = await this.coreClient; const { client, instance } = coreClient; @@ -78,7 +81,6 @@ export class LibraryServiceImpl const items = resp .getLibrariesList() .filter((item) => !!item.getLatest()) - .slice(0, 50) .map((item) => { // TODO: This seems to contain only the latest item instead of all of the items. const availableVersions = item @@ -103,7 +105,42 @@ export class LibraryServiceImpl ); }); - return items; + const typePredicate = this.typePredicate(options); + const topicPredicate = this.topicPredicate(options); + return items.filter((item) => typePredicate(item) && topicPredicate(item)); + } + + private typePredicate( + options: LibrarySearch + ): (item: LibraryPackage) => boolean { + const { type } = options; + if (!type || type === 'All') { + return () => true; + } + switch (options.type) { + case 'Installed': + return Installable.Installed; + case 'Updatable': + return Installable.Updateable; + case 'Arduino': + case 'Partner': + case 'Recommended': + case 'Contributed': + case 'Retired': + return ({ types }: LibraryPackage) => !!types && types.includes(type); + default: + throw new Error(`Unhandled type: ${options.type}`); + } + } + + private topicPredicate( + options: LibrarySearch + ): (item: LibraryPackage) => boolean { + const { topic } = options; + if (!topic || topic === 'All') { + return () => true; + } + return (item: LibraryPackage) => item.category === topic; } async list({ @@ -408,5 +445,7 @@ function toLibrary( description: lib.getSentence(), moreInfoLink: lib.getWebsite(), summary: lib.getParagraph(), + category: lib.getCategory(), + types: lib.getTypesList(), }; } diff --git a/arduino-ide-extension/src/test/browser/fixtures/boards.ts b/arduino-ide-extension/src/test/browser/fixtures/boards.ts index d7ddc3126..0cedb5b77 100644 --- a/arduino-ide-extension/src/test/browser/fixtures/boards.ts +++ b/arduino-ide-extension/src/test/browser/fixtures/boards.ts @@ -53,6 +53,7 @@ export const aPackage: BoardsPackage = { moreInfoLink: 'http://www.some-url.lol/', name: 'Some Arduino Package', summary: 'Boards included in this package:', + types: ['Arduino'], }; export const anInstalledPackage: BoardsPackage = { diff --git a/i18n/en.json b/i18n/en.json index 65b02b5c6..af2a8730b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,7 +14,6 @@ "disconnected": "Disconnected", "getBoardInfo": "Get Board Info", "inSketchbook": " (in Sketchbook)", - "installManually": "Install Manually", "installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?", "noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?", "noPortsDiscovered": "No ports discovered", @@ -36,6 +35,9 @@ "succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}" }, "boardsManager": "Boards Manager", + "boardsType": { + "arduinoCertified": "Arduino Certified" + }, "bootloader": { "burnBootloader": "Burn Bootloader", "burningBootloader": "Burning bootloader...", @@ -61,6 +63,15 @@ "uploadRootCertificates": "Upload SSL Root Certificates", "uploadingCertificates": "Uploading certificates." }, + "checkForUpdates": { + "checkForUpdates": "Check for Arduino Updates", + "installAll": "Install All", + "noUpdates": "There are no recent updates available.", + "promptUpdateBoards": "Updates are available for some of your boards.", + "promptUpdateLibraries": "Updates are available for some of your libraries.", + "updatingBoards": "Updating boards...", + "updatingLibraries": "Updating libraries..." + }, "cli-error-parser": { "keyboardError": "'Keyboard' not found. Does your sketch include the line '#include '?", "mouseError": "'Mouse' not found. Does your sketch include the line '#include '?" @@ -103,15 +114,23 @@ "visitArduinoCloud": "Visit Arduino Cloud to create Cloud Sketches." }, "common": { + "all": "All", + "contributed": "Contributed", + "installManually": "Install Manually", "later": "Later", "noBoardSelected": "No board selected", "notConnected": "[not connected]", "offlineIndicator": "You appear to be offline. Without an Internet connection, the Arduino CLI might not be able to download the required resources and could cause malfunction. Please connect to the Internet and restart the application.", "oldFormat": "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + "partner": "Partner", "processing": "Processing", + "recommended": "Recommended", + "retired": "Retired", "selectedOn": "on {0}", "serialMonitor": "Serial Monitor", - "unknown": "Unknown" + "type": "Type", + "unknown": "Unknown", + "updateable": "Updatable" }, "compile": { "error": "Compilation error: {0}" @@ -197,7 +216,7 @@ "visit": "Visit Arduino.cc" }, "ide-updater": { - "checkForUpdates": "Check for Arduino IDE updates", + "checkForUpdates": "Check for Arduino IDE Updates", "closeAndInstallButton": "Close and Install", "closeToInstallNotice": "Close the software and install the update on your machine.", "downloadButton": "Download", @@ -235,6 +254,24 @@ "uninstalledSuccessfully": "Successfully uninstalled library {0}:{1}", "zipLibrary": "Library" }, + "librarySearchProperty": { + "topic": "Topic" + }, + "libraryTopic": { + "communication": "Communication", + "dataProcessing": "Data Processing", + "dataStorage": "Data Storage", + "deviceControl": "Device Control", + "display": "Display", + "other": "Other", + "sensors": "Sensors", + "signalInputOutput": "Signal Input/Output", + "timing": "Timing", + "uncategorized": "Uncategorized" + }, + "libraryType": { + "installed": "Installed" + }, "menu": { "advanced": "Advanced", "sketch": "Sketch", @@ -253,6 +290,7 @@ "automatic": "Automatic", "board.certificates": "List of certificates that can be uploaded to boards", "browse": "Browse", + "checkForUpdate": "Receive notifications of available updates for the IDE, boards, and libraries. Requires an IDE restart after change. It's true by default.", "choose": "Choose", "cli.daemonDebug": "Enable debug logging of the gRPC calls to the Arduino CLI. A restart of the IDE is needed for this setting to take effect. It's false by default.", "cloud.enabled": "True if the sketch sync functions are enabled. Defaults to true.",