Skip to content

Commit e1b36c6

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-1054: Support for Add .ZIP LIbrary...
Signed-off-by: Akos Kitta <[email protected]>
1 parent 86be874 commit e1b36c6

File tree

8 files changed

+127
-18
lines changed

8 files changed

+127
-18
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ import { AddFile } from './contributions/add-file';
142142
import { ArchiveSketch } from './contributions/archive-sketch';
143143
import { OutputToolbarContribution as TheiaOutputToolbarContribution } from '@theia/output/lib/browser/output-toolbar-contribution';
144144
import { OutputToolbarContribution } from './theia/output/output-toolbar-contribution';
145+
import { AddZipLibrary } from './contributions/add-zip-library';
145146

146147
const ElementQueries = require('css-element-queries/src/ElementQueries');
147148

@@ -354,6 +355,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
354355
Contribution.configure(bind, Help);
355356
Contribution.configure(bind, AddFile);
356357
Contribution.configure(bind, ArchiveSketch);
358+
Contribution.configure(bind, AddZipLibrary);
357359

358360
bind(OutputServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, outputService) => {
359361
WebSocketConnectionProvider.createProxy(container, OutputServicePath, outputService);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { inject, injectable } from 'inversify';
2+
import { remote } from 'electron';
3+
import { ArduinoMenus } from '../menu/arduino-menus';
4+
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution';
5+
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
6+
import URI from '@theia/core/lib/common/uri';
7+
import { InstallationProgressDialog } from '../widgets/progress-dialog';
8+
import { LibraryService } from '../../common/protocol';
9+
10+
@injectable()
11+
export class AddZipLibrary extends SketchContribution {
12+
13+
@inject(EnvVariablesServer)
14+
protected readonly envVariableServer: EnvVariablesServer;
15+
16+
@inject(LibraryService)
17+
protected readonly libraryService: LibraryService;
18+
19+
registerCommands(registry: CommandRegistry): void {
20+
registry.registerCommand(AddZipLibrary.Commands.ADD_ZIP_LIBRARY, {
21+
execute: () => this.addZipLibrary()
22+
});
23+
}
24+
25+
registerMenus(registry: MenuModelRegistry): void {
26+
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
27+
// TODO: do we need it? calling `registerSubmenu` multiple times is noop, so it does not hurt.
28+
registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
29+
registry.registerMenuAction([...includeLibMenuPath, '1_install'], {
30+
commandId: AddZipLibrary.Commands.ADD_ZIP_LIBRARY.id,
31+
label: 'Add .ZIP Library...',
32+
order: '1'
33+
});
34+
}
35+
36+
async addZipLibrary(): Promise<void> {
37+
const homeUri = await this.envVariableServer.getHomeDirUri();
38+
const defaultPath = await this.fileService.fsPath(new URI(homeUri));
39+
const { canceled, filePaths } = await remote.dialog.showOpenDialog({
40+
title: "Select a zip file containing the library you'd like to add",
41+
defaultPath,
42+
properties: ['openFile'],
43+
filters: [
44+
{
45+
name: 'Library',
46+
extensions: ['zip']
47+
}
48+
]
49+
});
50+
if (!canceled && filePaths.length) {
51+
const zipUri = await this.fileSystemExt.getUri(filePaths[0]);
52+
const dialog = new InstallationProgressDialog('Installing library', 'zip');
53+
try {
54+
this.outputChannelManager.getChannel('Arduino').clear();
55+
dialog.open();
56+
await this.libraryService.installZip({ zipUri });
57+
} catch (e) {
58+
this.messageService.error(e.toString());
59+
} finally {
60+
dialog.close();
61+
}
62+
}
63+
}
64+
65+
}
66+
67+
export namespace AddZipLibrary {
68+
export namespace Commands {
69+
export const ADD_ZIP_LIBRARY: Command = {
70+
id: 'arduino-add-zip-library'
71+
};
72+
}
73+
}

Diff for: arduino-ide-extension/src/browser/contributions/contribution.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
99
import { MessageService } from '@theia/core/lib/common/message-service';
1010
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
1111
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
12+
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
1213
import { MenuModelRegistry, MenuContribution } from '@theia/core/lib/common/menu';
1314
import { KeybindingRegistry, KeybindingContribution } from '@theia/core/lib/browser/keybinding';
1415
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
@@ -90,6 +91,9 @@ export abstract class SketchContribution extends Contribution {
9091
@inject(EditorManager)
9192
protected readonly editorManager: EditorManager;
9293

94+
@inject(OutputChannelManager)
95+
protected readonly outputChannelManager: OutputChannelManager;
96+
9397
protected async sourceOverride(): Promise<Record<string, string>> {
9498
const override: Record<string, string> = {};
9599
const sketch = await this.sketchServiceClient.currentSketch();

Diff for: arduino-ide-extension/src/browser/contributions/include-library.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ export class IncludeLibrary extends SketchContribution {
4747
this.notificationCenter.onLibraryUninstalled(() => this.updateMenuActions());
4848
}
4949

50+
registerMenus(registry: MenuModelRegistry): void {
51+
// `Include Library` submenu
52+
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
53+
registry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
54+
// `Manage Libraries...` group.
55+
registry.registerMenuAction([...includeLibMenuPath, '0_manage'], {
56+
commandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
57+
label: 'Manage Libraries...'
58+
});
59+
}
60+
5061
registerCommands(registry: CommandRegistry): void {
5162
registry.registerCommand(IncludeLibrary.Commands.INCLUDE_LIBRARY, {
5263
execute: async arg => {
@@ -68,16 +79,7 @@ export class IncludeLibrary extends SketchContribution {
6879
libraries.push(...await this.libraryService.list({ fqbn }));
6980
}
7081

71-
// `Include Library` submenu
7282
const includeLibMenuPath = [...ArduinoMenus.SKETCH__UTILS_GROUP, '0_include'];
73-
this.menuRegistry.registerSubmenu(includeLibMenuPath, 'Include Library', { order: '1' });
74-
// `Manage Libraries...` group.
75-
this.menuRegistry.registerMenuAction([...includeLibMenuPath, '0_manage'], {
76-
commandId: `${LibraryListWidget.WIDGET_ID}:toggle`,
77-
label: 'Manage Libraries...'
78-
});
79-
this.toDispose.push(Disposable.create(() => this.menuRegistry.unregisterMenuAction({ commandId: `${LibraryListWidget.WIDGET_ID}:toggle` })));
80-
8183
// `Add .ZIP Library...`
8284
// TODO: implement it
8385

Diff for: arduino-ide-extension/src/browser/contributions/upload-sketch.ts

-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { inject, injectable } from 'inversify';
2-
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
32
import { CoreService } from '../../common/protocol';
43
import { ArduinoMenus } from '../menu/arduino-menus';
54
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@@ -23,9 +22,6 @@ export class UploadSketch extends SketchContribution {
2322
@inject(BoardsServiceProvider)
2423
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
2524

26-
@inject(OutputChannelManager)
27-
protected readonly outputChannelManager: OutputChannelManager;
28-
2925
registerCommands(registry: CommandRegistry): void {
3026
registry.registerCommand(UploadSketch.Commands.UPLOAD_SKETCH, {
3127
execute: () => this.uploadSketch()

Diff for: arduino-ide-extension/src/browser/contributions/verify-sketch.ts

-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { inject, injectable } from 'inversify';
2-
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
32
import { CoreService } from '../../common/protocol';
43
import { ArduinoMenus } from '../menu/arduino-menus';
54
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
@@ -19,9 +18,6 @@ export class VerifySketch extends SketchContribution {
1918
@inject(BoardsServiceProvider)
2019
protected readonly boardsServiceClientImpl: BoardsServiceProvider;
2120

22-
@inject(OutputChannelManager)
23-
protected readonly outputChannelManager: OutputChannelManager;
24-
2521
registerCommands(registry: CommandRegistry): void {
2622
registry.registerCommand(VerifySketch.Commands.VERIFY_SKETCH, {
2723
execute: () => this.verifySketch()

Diff for: arduino-ide-extension/src/common/protocol/library-service.ts

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

Diff for: arduino-ide-extension/src/node/library-service-server-impl.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { injectable, inject } from 'inversify';
2+
import * as path from 'path';
23
import { LibraryDependency, LibraryPackage, LibraryService } from '../common/protocol/library-service';
34
import { CoreClientAware } from './core-client-provider';
45
import {
@@ -13,13 +14,16 @@ import {
1314
LibraryUninstallReq,
1415
LibraryUninstallResp,
1516
Library,
16-
LibraryResolveDependenciesReq
17+
LibraryResolveDependenciesReq,
18+
ZipLibraryInstallReq,
19+
ZipLibraryInstallResp
1720
} from './cli-protocol/commands/lib_pb';
1821
import { Installable } from '../common/protocol/installable';
1922
import { ILogger, notEmpty } from '@theia/core';
2023
import { FileUri } from '@theia/core/lib/node';
2124
import { OutputService, NotificationServiceServer } from '../common/protocol';
2225

26+
2327
@injectable()
2428
export class LibraryServiceImpl extends CoreClientAware implements LibraryService {
2529

@@ -188,6 +192,37 @@ export class LibraryServiceImpl extends CoreClientAware implements LibraryServic
188192
console.info('<<< Library package installation done.', item);
189193
}
190194

195+
async installZip({ zipUri }: { zipUri: string }): Promise<void> {
196+
const coreClient = await this.coreClient();
197+
const { client, instance } = coreClient;
198+
const req = new ZipLibraryInstallReq();
199+
req.setPath(FileUri.fsPath(zipUri));
200+
req.setInstance(instance);
201+
const resp = client.zipLibraryInstall(req);
202+
resp.on('data', (r: ZipLibraryInstallResp) => {
203+
const task = r.getTaskProgress();
204+
if (task && task.getMessage()) {
205+
this.outputService.append({ chunk: task.getMessage() });
206+
}
207+
});
208+
await new Promise<void>((resolve, reject) => {
209+
resp.on('end', resolve);
210+
resp.on('error', error => {
211+
// This is a hack to have better error messages for the user. We try to get the name of the library from this:
212+
// Request installZip failed with error: 2 UNKNOWN: copying library: destination /path/to/lib already exists
213+
const match = error.message.match(/destination (.*?) already exists/);
214+
if (match && match.length >= 2) {
215+
const name = path.basename(match[1].trim());
216+
if (name) {
217+
reject(new Error(`A library named ${name} already exists.`));
218+
return;
219+
}
220+
}
221+
reject(error);
222+
});
223+
});
224+
}
225+
191226
async uninstall(options: { item: LibraryPackage }): Promise<void> {
192227
const item = options.item;
193228
const coreClient = await this.coreClient();

0 commit comments

Comments
 (0)