diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index c421cb211..55ea00a93 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -124,7 +124,11 @@ ], "arduino": { "cli": { - "version": "0.16.0" + "version": { + "owner": "arduino", + "repo": "arduino-cli", + "commitish": "scerza/lib-install-deps" + } } } } 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 7c72a3c4c..96efce4e3 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -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); diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index 42e6cedf8..919cd8e5b 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -1,11 +1,6 @@ import { interfaces } from 'inversify'; -import { - createPreferenceProxy, - PreferenceProxy, - PreferenceService, - PreferenceContribution, - PreferenceSchema -} from '@theia/core/lib/browser/preferences'; +import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser/preferences'; +import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol'; export const ArduinoConfigSchema: PreferenceSchema = { 'type': 'object', @@ -20,6 +15,11 @@ export const ArduinoConfigSchema: PreferenceSchema = { 'description': 'True for verbose compile output. False by default', 'default': false }, + 'arduino.compile.warnings': { + 'enum': [...CompilerWarningLiterals], + 'description': "Tells gcc which warning level to use. It's 'None' by default", + 'default': 'None' + }, 'arduino.upload.verbose': { 'type': 'boolean', 'description': 'True for verbose upload output. False by default.', @@ -50,6 +50,7 @@ export const ArduinoConfigSchema: PreferenceSchema = { export interface ArduinoConfiguration { 'arduino.language.log': boolean; 'arduino.compile.verbose': boolean; + 'arduino.compile.warnings': CompilerWarnings; 'arduino.upload.verbose': boolean; 'arduino.upload.verify': boolean; 'arduino.window.autoScale': boolean; diff --git a/arduino-ide-extension/src/browser/contributions/add-zip-library.ts b/arduino-ide-extension/src/browser/contributions/add-zip-library.ts new file mode 100644 index 000000000..508a02d44 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/add-zip-library.ts @@ -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 { + 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' + }; + } +} diff --git a/arduino-ide-extension/src/browser/contributions/contribution.ts b/arduino-ide-extension/src/browser/contributions/contribution.ts index 06d3335a5..af4a5d47c 100644 --- a/arduino-ide-extension/src/browser/contributions/contribution.ts +++ b/arduino-ide-extension/src/browser/contributions/contribution.ts @@ -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> { const override: Record = {}; const sketch = await this.sketchServiceClient.currentSketch(); diff --git a/arduino-ide-extension/src/browser/contributions/include-library.ts b/arduino-ide-extension/src/browser/contributions/include-library.ts index 079e6b2ac..df42e3f50 100644 --- a/arduino-ide-extension/src/browser/contributions/include-library.ts +++ b/arduino-ide-extension/src/browser/contributions/include-library.ts @@ -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 diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index fdd85f941..4f0109aa8 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -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() diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index d01869fa1..fe9b3e074 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -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() @@ -80,6 +76,7 @@ export class VerifySketch extends SketchContribution { this.sourceOverride() ]); const verbose = this.preferences.get('arduino.compile.verbose'); + const compilerWarnings = this.preferences.get('arduino.compile.warnings'); this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.compile({ sketchUri: sketch.uri, @@ -87,7 +84,8 @@ export class VerifySketch extends SketchContribution { optimizeForDebug: this.editorMode.compileForDebug, verbose, exportBinaries, - sourceOverride + sourceOverride, + compilerWarnings }); this.messageService.info('Done compiling.', { timeout: 1000 }); } catch (e) { diff --git a/arduino-ide-extension/src/browser/data/arduino.color-theme.json b/arduino-ide-extension/src/browser/data/arduino.color-theme.json index 6b3b37517..355b2e694 100644 --- a/arduino-ide-extension/src/browser/data/arduino.color-theme.json +++ b/arduino-ide-extension/src/browser/data/arduino.color-theme.json @@ -80,22 +80,22 @@ } ], "colors": { - "list.highlightForeground": "#006468", - "list.activeSelectionBackground": "#006468", + "list.highlightForeground": "#005c5f", + "list.activeSelectionBackground": "#005c5f", "editor.background": "#ffffff", "editorCursor.foreground": "#434f54", "editor.foreground": "#434f54", "editorWhitespace.foreground": "#bfbfbf", "editor.lineHighlightBackground": "#434f5410", "editor.selectionBackground": "#ffcb00", - "focusBorder": "#4db7bb99", + "focusBorder": "#7fcbcd99", "menubar.selectionBackground": "#ffffff", "menubar.selectionForeground": "#212121", "menu.selectionBackground": "#dae3e3", "menu.selectionForeground": "#212121", "editorGroupHeader.tabsBackground": "#f7f9f9", - "button.background": "#4db7bb", - "titleBar.activeBackground": "#006468", + "button.background": "#7fcbcd", + "titleBar.activeBackground": "#005c5f", "titleBar.activeForeground": "#ffffff", "terminal.background": "#000000", "terminal.foreground": "#e0e0e0", @@ -103,7 +103,7 @@ "dropdown.background": "#ececec", "activityBar.background": "#ececec", "activityBar.foreground": "#616161", - "statusBar.background": "#006468", + "statusBar.background": "#005c5f", "secondaryButton.background": "#b5c8c9", "secondaryButton.hoverBackground": "#dae3e3", "arduino.branding.primary": "#00979d", 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 60871e4e7..73a437cdb 100644 --- a/arduino-ide-extension/src/browser/library/library-list-widget.ts +++ b/arduino-ide-extension/src/browser/library/library-list-widget.ts @@ -1,6 +1,10 @@ import { injectable, postConstruct, inject } from 'inversify'; +import { Message } from '@phosphor/messaging'; +import { addEventListener } from '@theia/core/lib/browser/widgets/widget'; +import { AbstractDialog, DialogProps } from '@theia/core/lib/browser/dialogs'; import { LibraryPackage, 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'; @injectable() @@ -33,4 +37,110 @@ export class LibraryListWidget extends ListWidget { ]); } + protected async install({ item, version }: { item: LibraryPackage, version: Installable.Version }): Promise { + const dependencies = await this.service.listDependencies({ item, version, filterSelf: true }); + let installDependencies: boolean | undefined = undefined; + if (dependencies.length) { + const message = document.createElement('div'); + message.innerHTML = `The library ${item.name}:${version} needs ${dependencies.length === 1 ? 'another dependency' : 'some other dependencies'} currently not installed:`; + const listContainer = document.createElement('div'); + listContainer.style.maxHeight = '300px'; + listContainer.style.overflowY = 'auto'; + const list = document.createElement('ul'); + list.style.listStyleType = 'none'; + for (const { name } of dependencies) { + const listItem = document.createElement('li'); + listItem.textContent = ` - ${name}`; + listItem.style.fontWeight = 'bold'; + list.appendChild(listItem); + } + listContainer.appendChild(list); + message.appendChild(listContainer); + const question = document.createElement('div'); + question.textContent = `Would you like to install ${dependencies.length === 1 ? 'the missing dependency' : 'all the missing dependencies'}?`; + message.appendChild(question); + const result = await new MessageBoxDialog({ + title: `Dependencies for library ${item.name}:${version}`, + message, + buttons: [ + 'Install all', + `Install ${item.name} only`, + 'Cancel' + ], + maxWidth: 740 // Aligned with `settings-dialog.css`. + }).open(); + + if (result) { + const { response } = result; + if (response === 0) { // All + installDependencies = true; + } else if (response === 1) { // Current only + installDependencies = false; + } + } + } + + if (typeof installDependencies === 'boolean') { + return this.service.install({ item, version, installDependencies }); + } + } + +} + +class MessageBoxDialog extends AbstractDialog { + + protected response: number; + + constructor(protected readonly options: MessageBoxDialog.Options) { + super(options); + this.contentNode.appendChild(this.createMessageNode(this.options.message)); + (options.buttons || ['OK']).forEach((text, index) => { + const button = this.createButton(text); + button.classList.add(index === 0 ? 'main' : 'secondary'); + this.controlPanel.appendChild(button); + this.toDisposeOnDetach.push(addEventListener(button, 'click', () => { + this.response = index; + this.accept(); + })); + }); + } + + protected onCloseRequest(message: Message): void { + super.onCloseRequest(message); + this.accept(); + } + + get value(): MessageBoxDialog.Result { + return { response: this.response }; + } + + protected createMessageNode(message: string | HTMLElement): HTMLElement { + if (typeof message === 'string') { + const messageNode = document.createElement('div'); + messageNode.textContent = message; + return messageNode; + } + return message; + } + + protected handleEnter(event: KeyboardEvent): boolean | void { + this.response = 0; + super.handleEnter(event); + } + +} +export namespace MessageBoxDialog { + export interface Options extends DialogProps { + /** + * When empty, `['OK']` will be inferred. + */ + buttons?: string[]; + message: string | HTMLElement; + } + export interface Result { + /** + * The index of `buttons` that was clicked. + */ + readonly response: number; + } } diff --git a/arduino-ide-extension/src/browser/settings.tsx b/arduino-ide-extension/src/browser/settings.tsx index b856ea930..650868406 100644 --- a/arduino-ide-extension/src/browser/settings.tsx +++ b/arduino-ide-extension/src/browser/settings.tsx @@ -18,7 +18,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { AbstractDialog, DialogProps, PreferenceService, PreferenceScope, DialogError, ReactWidget } from '@theia/core/lib/browser'; import { Index } from '../common/types'; -import { ConfigService, FileSystemExt, Network, ProxySettings } from '../common/protocol'; +import { CompilerWarnings, CompilerWarningLiterals, ConfigService, FileSystemExt, Network, ProxySettings } from '../common/protocol'; export interface Settings extends Index { editorFontSize: number; // `editor.fontSize` @@ -29,6 +29,7 @@ export interface Settings extends Index { interfaceScale: number; // `arduino.window.zoomLevel` https://github.com/eclipse-theia/theia/issues/8751 checkForUpdates?: boolean; // `arduino.ide.autoUpdate` verboseOnCompile: boolean; // `arduino.compile.verbose` + compilerWarnings: CompilerWarnings; // `arduino.compile.warnings` verboseOnUpload: boolean; // `arduino.upload.verbose` verifyAfterUpload: boolean; // `arduino.upload.verify` enableLsLogs: boolean; // `arduino.language.log` @@ -87,6 +88,7 @@ export class SettingsService { interfaceScale, // checkForUpdates, verboseOnCompile, + compilerWarnings, verboseOnUpload, verifyAfterUpload, enableLsLogs, @@ -99,6 +101,7 @@ export class SettingsService { this.preferenceService.get('arduino.window.zoomLevel', 0), // this.preferenceService.get('arduino.ide.autoUpdate', true), this.preferenceService.get('arduino.compile.verbose', true), + this.preferenceService.get('arduino.compile.warnings', 'None'), this.preferenceService.get('arduino.upload.verbose', true), this.preferenceService.get('arduino.upload.verify', true), this.preferenceService.get('arduino.language.log', true), @@ -114,6 +117,7 @@ export class SettingsService { interfaceScale, // checkForUpdates, verboseOnCompile, + compilerWarnings, verboseOnUpload, verifyAfterUpload, enableLsLogs, @@ -175,6 +179,7 @@ export class SettingsService { interfaceScale, // checkForUpdates, verboseOnCompile, + compilerWarnings, verboseOnUpload, verifyAfterUpload, enableLsLogs, @@ -198,6 +203,7 @@ export class SettingsService { this.preferenceService.set('arduino.window.zoomLevel', interfaceScale, PreferenceScope.User), // this.preferenceService.set('arduino.ide.autoUpdate', checkForUpdates, PreferenceScope.User), this.preferenceService.set('arduino.compile.verbose', verboseOnCompile, PreferenceScope.User), + this.preferenceService.set('arduino.compile.warnings', compilerWarnings, PreferenceScope.User), this.preferenceService.set('arduino.upload.verbose', verboseOnUpload, PreferenceScope.User), this.preferenceService.set('arduino.upload.verify', verifyAfterUpload, PreferenceScope.User), this.preferenceService.set('arduino.language.log', enableLsLogs, PreferenceScope.User), @@ -267,6 +273,7 @@ export class SettingsComponent extends React.ComponentInterface scale:
Theme:
Show verbose output during:
+
Compiler warnings:
@@ -321,6 +328,14 @@ export class SettingsComponent extends React.Component
+
+ +