ATL-1054: Support for Add .ZIP LIbrary...

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2021-02-24 14:44:23 +01:00 committed by Akos Kitta
parent 86be874bb0
commit e1b36c6c56
8 changed files with 127 additions and 18 deletions

View File

@ -142,6 +142,7 @@ import { AddFile } from './contributions/add-file';
import { ArchiveSketch } from './contributions/archive-sketch';
import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
import { OutputToolbarContribution } from './theia/output/output-toolbar-contribution';
import { AddZipLibrary } from './contributions/add-zip-library';
const ElementQueries = require('css-element-queries/src/ElementQueries');
@ -354,6 +355,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, Help);
Contribution.configure(bind, AddFile);
Contribution.configure(bind, ArchiveSketch);
Contribution.configure(bind, AddZipLibrary);
bind(OutputServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, outputService) => {
WebSocketConnectionProvider.createProxy(container, OutputServicePath, outputService);

View File

@ -0,0 +1,73 @@
import { inject, injectable } from 'inversify';
import { remote } from 'electron';
import { ArduinoMenus } from '../menu/arduino-menus';
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import URI from '@theia/core/lib/common/uri';
import { InstallationProgressDialog } from '../widgets/progress-dialog';
import { LibraryService } from '../../common/protocol';
@injectable()
export class AddZipLibrary extends SketchContribution {
@inject(EnvVariablesServer)
protected readonly envVariableServer: EnvVariablesServer;
@inject(LibraryService)
protected readonly libraryService: LibraryService;
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, {
execute: () => this.addZipLibrary()
});
}
registerMenus(registry: MenuModelRegistry): void {
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
// TODO: do we need it? calling `registerSubmenu` multiple times is noop, so it does not hurt.
registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
registry.registerMenuAction([...includeLibMenuPath, '1_install'], {
commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id,
label: 'Add .ZIP Library...',
order: '1'
});
}
async addZipLibrary(): Promise<void> {
const homeUri = await this.envVariableServer.getHomeDirUri();
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
const { canceled, filePaths } = await remote.dialog.showOpenDialog({
title: "Select a zip file containing the library you'd like to add",
defaultPath,
properties: ['openFile'],
filters: [
{
name: 'Library',
extensions: ['zip']
}
]
});
if (!canceled && filePaths.length) {
const zipUri = await this.fileSystemExt.getUri(filePaths[0]);
const dialog = new InstallationProgressDialog('Installing library', 'zip');
try {
this.outputChannelManager.getChannel('Arduino').clear();
dialog.open();
await this.libraryService.installZip({ zipUri });
} catch (e) {
this.messageService.error(e.toString());
} finally {
dialog.close();
}
}
}
}
export namespace AddZipLibrary {
export namespace Commands {
export const ADD_ZIP_LIBRARY: Command = {
id: 'arduino-add-zip-library'
};
}
}

View File

@ -9,6 +9,7 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { MessageService } from '@theia/core/lib/common/message-service';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
import { MenuModelRegistry, MenuContribution } from '@theia/core/lib/common/menu';
import { KeybindingRegistry, KeybindingContribution } from '@theia/core/lib/browser/keybinding';
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
@ -90,6 +91,9 @@ export abstract class SketchContribution extends Contribution {
@inject(EditorManager)
protected readonly editorManager: EditorManager;
@inject(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
protected async sourceOverride(): Promise<Record<string, string>> {
const override: Record<string, string> = {};
const sketch = await this.sketchServiceClient.currentSketch();

View File

@ -47,6 +47,17 @@ export class IncludeLibrary extends SketchContribution {
this.notificationCenter.onLibraryUninstalled(() => this.updateMenuActions());
}
registerMenus(registry: MenuModelRegistry): void {
// `Include Library` submenu
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
// `Manage Libraries...` group.
registry.registerMenuAction([...includeLibMenuPath, '0_manage'], {
commandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
label: 'Manage Libraries...'
});
}
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, {
execute: async arg => {
@ -68,16 +79,7 @@ export class IncludeLibrary extends SketchContribution {
libraries.push(...await this.libraryService.list({ fqbn }));
}
// `Include Library` submenu
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
this.menuRegistry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
// `Manage Libraries...` group.
this.menuRegistry.registerMenuAction([...includeLibMenuPath, '0_manage'], {
commandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
label: 'Manage Libraries...'
});
this.toDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction({ commandId: `${LibraryListWidget.WIDGET_ID}:toggle` })));
// `Add .ZIP Library...`
// TODO: implement it

View File

@ -1,5 +1,4 @@
import { inject, injectable } from 'inversify';
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@ -23,9 +22,6 @@ export class UploadSketch extends SketchContribution {
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
@inject(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
execute: () => this.uploadSketch()

View File

@ -1,5 +1,4 @@
import { inject, injectable } from 'inversify';
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@ -19,9 +18,6 @@ export class VerifySketch extends SketchContribution {
@inject(BoardsServiceProvider)
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
@inject(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
execute: () => this.verifySketch()

View File

@ -10,6 +10,7 @@ export interface LibraryService extends Installable<LibraryPackage>, Searchable<
* When `installDependencies` is not set, it is `true` by default. If you want to skip the installation of required dependencies, set it to `false`.
*/
install(options: { item: LibraryPackage, version?: Installable.Version, installDependencies?: boolean }): Promise<void>;
installZip(options: { zipUri: string }): Promise<void>;
/**
* Set `filterSelf` to `true` if you want to avoid having `item` in the result set.
* Note: as of today (22.02.2021), the CLI works like this: `./arduino-cli lib deps Adaino@0.1.0 ✕ Adaino 0.1.0 must be installed.`.

View File

@ -1,4 +1,5 @@
import { injectable, inject } from 'inversify';
import * as path from 'path';
import { LibraryDependency, LibraryPackage, LibraryService } from '../common/protocol/library-service';
import { CoreClientAware } from './core-client-provider';
import {
@ -13,13 +14,16 @@ import {
LibraryUninstallReq,
LibraryUninstallResp,
Library,
LibraryResolveDependenciesReq
LibraryResolveDependenciesReq,
ZipLibraryInstallReq,
ZipLibraryInstallResp
} from './cli-protocol/commands/lib_pb';
import { Installable } from '../common/protocol/installable';
import { ILogger, notEmpty } from '@theia/core';
import { FileUri } from '@theia/core/lib/node';
import { OutputService, NotificationServiceServer } from '../common/protocol';
@injectable()
export class LibraryServiceImpl extends CoreClientAware implements LibraryService {
@ -188,6 +192,37 @@ export class LibraryServiceImpl extends CoreClientAware implements LibraryServic
console.info('<<< Library package installation done.', item);
}
async installZip({ zipUri }: { zipUri: string }): Promise<void> {
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const req = new ZipLibraryInstallReq();
req.setPath(FileUri.fsPath(zipUri));
req.setInstance(instance);
const resp = client.zipLibraryInstall(req);
resp.on('data', (r: ZipLibraryInstallResp) => {
const task = r.getTaskProgress();
if (task && task.getMessage()) {
this.outputService.append({ chunk: task.getMessage() });
}
});
await new Promise<void>((resolve, reject) => {
resp.on('end', resolve);
resp.on('error', error => {
// This is a hack to have better error messages for the user. We try to get the name of the library from this:
// Request installZip failed with error: 2 UNKNOWN: copying library: destination /path/to/lib already exists
const match = error.message.match(/destination (.*?) already exists/);
if (match && match.length >= 2) {
const name = path.basename(match[1].trim());
if (name) {
reject(new Error(`A library named ${name} already exists.`));
return;
}
}
reject(error);
});
});
}
async uninstall(options: { item: LibraryPackage }): Promise<void> {
const item = options.item;
const coreClient = await this.coreClient();