From 7a9a5e1601b684f309909342a2f65d42e6773776 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 4 Jul 2022 18:45:50 +0200 Subject: [PATCH 01/22] IDE2 falls back to new sketch if opening failed. Closes #1089 Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 57 +++++++++++- .../browser/arduino-ide-frontend-module.ts | 5 +- .../browser/boards/boards-service-provider.ts | 15 ++-- .../browser/contributions/notifications.ts | 52 +++++++++++ .../src/browser/contributions/sketchbook.ts | 40 ++++++--- .../theia/workspace/workspace-service.ts | 55 ++++++++++-- .../workspace-variable-contribution.ts | 24 +++-- .../src/browser/utils/window.ts | 7 ++ .../widgets/sketchbook/startup-task.ts | 88 +++++++++++++++---- .../protocol/sketches-service-client-impl.ts | 26 ++++-- .../src/common/protocol/sketches-service.ts | 16 ++++ .../src/node/sketches-service-impl.ts | 39 ++++---- 12 files changed, 343 insertions(+), 81 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/notifications.ts create mode 100644 arduino-ide-extension/src/browser/utils/window.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index f9a2f7810..aff62e456 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -11,6 +11,7 @@ import { ExecutableService, Sketch, ArduinoDaemon, + SketchesError, } from '../common/protocol'; import { Mutex } from 'async-mutex'; import { @@ -19,6 +20,7 @@ import { MenuModelRegistry, ILogger, DisposableCollection, + ApplicationError, } from '@theia/core'; import { Dialog, @@ -75,6 +77,8 @@ import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { IDEUpdater } from '../common/protocol/ide-updater'; import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { HostedPluginEvents } from './hosted-plugin-events'; +import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; +import { Notifications } from './contributions/notifications'; export const SKIP_IDE_VERSION = 'skipIDEVersion'; @@ -150,6 +154,9 @@ export class ArduinoFrontendContribution @inject(ArduinoDaemon) private readonly daemon: ArduinoDaemon; + @inject(WorkspaceService) + private readonly workspaceService: WorkspaceService; + protected invalidConfigPopup: | Promise | undefined; @@ -523,13 +530,55 @@ export class ArduinoFrontendContribution } }); } - } catch (e) { - console.error(e); - const message = e instanceof Error ? e.message : JSON.stringify(e); - this.messageService.error(message); + } catch (err) { + if (SketchesError.NotFound.is(err)) { + this.openFallbackSketch(err); + } else { + console.error(err); + const message = + err instanceof Error + ? err.message + : typeof err === 'string' + ? err + : String(err); + this.messageService.error(message); + } } } + private openFallbackSketch( + err: ApplicationError< + number, + { + uri: string; + } + > + ) { + this.sketchService.createNewSketch().then((sketch) => { + this.workspaceService.open( + new URI(sketch.uri), + Object.assign( + { + preserveWindow: true, + }, + { + tasks: [ + { + command: Notifications.Commands.NOTIFY.id, + args: [ + { + type: 'error', + message: err.message, + }, + ], + }, + ], + } + ) + ); + }); + } + protected async ensureOpened( uri: string, forceOpen = false, 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 231b6636a..22a94f6d1 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -301,7 +301,7 @@ import { CoreErrorHandler } from './contributions/core-error-handler'; import { CompilerErrors } from './contributions/compiler-errors'; import { WidgetManager } from './theia/core/widget-manager'; import { WidgetManager as TheiaWidgetManager } from '@theia/core/lib/browser/widget-manager'; -import { StartupTask } from './widgets/sketchbook/startup-task'; +import { StartupTasks } from './widgets/sketchbook/startup-task'; import { IndexesUpdateProgress } from './contributions/indexes-update-progress'; import { Daemon } from './contributions/daemon'; import { FirstStartupInstaller } from './contributions/first-startup-installer'; @@ -697,10 +697,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, PlotterFrontendContribution); Contribution.configure(bind, Format); Contribution.configure(bind, CompilerErrors); - Contribution.configure(bind, StartupTask); + Contribution.configure(bind, StartupTasks); Contribution.configure(bind, IndexesUpdateProgress); Contribution.configure(bind, Daemon); Contribution.configure(bind, FirstStartupInstaller); + Contribution.configure(bind, StartupTasks); // 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. diff --git a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts index d0d5fc353..345ecaafb 100644 --- a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts +++ b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts @@ -21,6 +21,7 @@ import { ArduinoCommands } from '../arduino-commands'; import { StorageWrapper } from '../storage-wrapper'; import { nls } from '@theia/core/lib/common'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; @injectable() export class BoardsServiceProvider implements FrontendApplicationContribution { @@ -39,6 +40,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { @inject(NotificationCenter) protected notificationCenter: NotificationCenter; + @inject(FrontendApplicationStateService) + private readonly appStateService: FrontendApplicationStateService; + protected readonly onBoardsConfigChangedEmitter = new Emitter(); protected readonly onAvailableBoardsChangedEmitter = new Emitter< @@ -87,11 +91,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { this.notifyPlatformUninstalled.bind(this) ); - Promise.all([ - this.boardsService.getAttachedBoards(), - this.boardsService.getAvailablePorts(), - this.loadState(), - ]).then(async ([attachedBoards, availablePorts]) => { + this.appStateService.reachedState('ready').then(async () => { + const [attachedBoards, availablePorts] = await Promise.all([ + this.boardsService.getAttachedBoards(), + this.boardsService.getAvailablePorts(), + this.loadState(), + ]); this._attachedBoards = attachedBoards; this._availablePorts = availablePorts; this.onAvailablePortsChangedEmitter.fire(this._availablePorts); diff --git a/arduino-ide-extension/src/browser/contributions/notifications.ts b/arduino-ide-extension/src/browser/contributions/notifications.ts new file mode 100644 index 000000000..70ed6c50e --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/notifications.ts @@ -0,0 +1,52 @@ +import { injectable } from '@theia/core/shared/inversify'; +import { Command, CommandRegistry, Contribution } from './contribution'; + +@injectable() +export class Notifications extends Contribution { + override registerCommands(registry: CommandRegistry): void { + registry.registerCommand(Notifications.Commands.NOTIFY, { + execute: (arg) => { + if (NotifyParams.is(arg)) { + switch (arg.type) { + case 'info': + return this.messageService.info(arg.message); + case 'warn': + return this.messageService.warn(arg.message); + case 'error': + return this.messageService.error(arg.message); + } + } + }, + }); + } +} +export namespace Notifications { + export namespace Commands { + export const NOTIFY: Command = { + id: 'arduino-notify', + }; + } +} +const TypeLiterals = ['info', 'warn', 'error'] as const; +export type Type = typeof TypeLiterals[number]; +interface NotifyParams { + readonly type: Type; + readonly message: string; +} +namespace NotifyParams { + export function is(arg: unknown): arg is NotifyParams { + if (typeof arg === 'object') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const object = arg as any; + return ( + 'message' in object && + 'type' in object && + typeof object['message'] === 'string' && + typeof object['type'] === 'string' && + // eslint-disable-next-line @typescript-eslint/no-explicit-any + TypeLiterals.includes(object['type'] as any) + ); + } + return false; + } +} diff --git a/arduino-ide-extension/src/browser/contributions/sketchbook.ts b/arduino-ide-extension/src/browser/contributions/sketchbook.ts index 80dc99065..fdac918b7 100644 --- a/arduino-ide-extension/src/browser/contributions/sketchbook.ts +++ b/arduino-ide-extension/src/browser/contributions/sketchbook.ts @@ -5,7 +5,11 @@ import { ArduinoMenus } from '../menu/arduino-menus'; import { MainMenuManager } from '../../common/main-menu-manager'; import { NotificationCenter } from '../notification-center'; import { Examples } from './examples'; -import { SketchContainer } from '../../common/protocol'; +import { + SketchContainer, + SketchesError, + SketchRef, +} from '../../common/protocol'; import { OpenSketch } from './open-sketch'; import { nls } from '@theia/core/lib/common'; @@ -24,15 +28,14 @@ export class Sketchbook extends Examples { protected readonly notificationCenter: NotificationCenter; override onStart(): void { - this.sketchServiceClient.onSketchbookDidChange(() => { - this.sketchService.getSketches({}).then((container) => { - this.register(container); - this.mainMenuManager.update(); - }); - }); + this.sketchServiceClient.onSketchbookDidChange(() => this.update()); } override async onReady(): Promise { + this.update(); + } + + private update() { this.sketchService.getSketches({}).then((container) => { this.register(container); this.mainMenuManager.update(); @@ -59,11 +62,24 @@ export class Sketchbook extends Examples { protected override createHandler(uri: string): CommandHandler { return { execute: async () => { - const sketch = await this.sketchService.loadSketch(uri); - return this.commandService.executeCommand( - OpenSketch.Commands.OPEN_SKETCH.id, - sketch - ); + let sketch: SketchRef | undefined = undefined; + try { + sketch = await this.sketchService.loadSketch(uri); + } catch (err) { + if (SketchesError.NotFound.is(err)) { + // To handle the following: + // Open IDE2, delete a sketch from sketchbook, click on File > Sketchbook > the deleted sketch. + // Filesystem watcher misses out delete events on macOS; hence IDE2 has no chance to update the menu items. + this.messageService.error(err.message); + this.update(); + } + } + if (sketch) { + await this.commandService.executeCommand( + OpenSketch.Commands.OPEN_SKETCH.id, + sketch + ); + } }, }; } diff --git a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts index 3ba955c7c..730c71201 100644 --- a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts +++ b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts @@ -17,11 +17,16 @@ import { ConfigService } from '../../../common/protocol/config-service'; import { SketchesService, Sketch, + SketchesError, } from '../../../common/protocol/sketches-service'; import { BoardsServiceProvider } from '../../boards/boards-service-provider'; import { BoardsConfig } from '../../boards/boards-config'; import { FileStat } from '@theia/filesystem/lib/common/files'; -import { StartupTask } from '../../widgets/sketchbook/startup-task'; +import { + StartupTask, + StartupTasks, +} from '../../widgets/sketchbook/startup-task'; +import { setURL } from '../../utils/window'; @injectable() export class WorkspaceService extends TheiaWorkspaceService { @@ -60,6 +65,35 @@ export class WorkspaceService extends TheiaWorkspaceService { this.onCurrentWidgetChange({ newValue, oldValue: null }); } + protected override async toFileStat( + uri: string | URI | undefined + ): Promise { + const stat = await super.toFileStat(uri); + if (!stat) { + return this.toFileStatWithNewSketchFallback(uri); + } + return stat; + } + + private async toFileStatWithNewSketchFallback( + uri: string | URI | undefined + ): Promise { + if (!uri) { + return; + } + try { + await this.sketchService.loadSketch( + uri instanceof URI ? uri.toString() : uri + ); + } catch (err) { + if (SketchesError.NotFound.is(err)) { + this.messageService.error(err.message); + } + } + const newSketchUri = await this.sketchService.createNewSketch(); + return this.toFileStat(newSketchUri.uri); + } + // Was copied from the Theia implementation. // Unlike the default behavior, IDE2 does not check the existence of the workspace before open. protected override async doGetDefaultWorkspaceUri(): Promise< @@ -78,6 +112,7 @@ export class WorkspaceService extends TheiaWorkspaceService { const wpPath = decodeURI(window.location.hash.substring(1)); const workspaceUri = new URI().withPath(wpPath).withScheme('file'); // ### Customization! Here, we do no check if the workspace exists. + // ### The error or missing sketch handling is done in the customized `toFileStat`. return workspaceUri.toString(); } else { // Else, ask the server for its suggested workspace (usually the one @@ -127,7 +162,7 @@ export class WorkspaceService extends TheiaWorkspaceService { protected override openWindow(uri: FileStat, options?: WorkspaceInput): void { const workspacePath = uri.resource.path.toString(); if (this.shouldPreserveWindow(options)) { - this.reloadWindow(); + this.reloadWindow(options); // Unlike Theia, IDE2 passes the `input` downstream. } else { try { this.openNewWindow(workspacePath, options); // Unlike Theia, IDE2 passes the `input` downstream. @@ -139,21 +174,25 @@ export class WorkspaceService extends TheiaWorkspaceService { } } + protected override reloadWindow(options?: WorkspaceInput): void { + if (StartupTasks.WorkspaceInput.is(options)) { + setURL(StartupTask.append(options.tasks, new URL(window.location.href))); + } + super.reloadWindow(); + } + protected override openNewWindow( workspacePath: string, options?: WorkspaceInput ): void { const { boardsConfig } = this.boardsServiceProvider; - const url = BoardsConfig.Config.setConfig( + let url = BoardsConfig.Config.setConfig( boardsConfig, new URL(window.location.href) ); // Set the current boards config for the new browser window. url.hash = workspacePath; - if (StartupTask.WorkspaceInput.is(options)) { - url.searchParams.set( - StartupTask.QUERY_STRING, - encodeURIComponent(JSON.stringify(options.tasks)) - ); + if (StartupTasks.WorkspaceInput.is(options)) { + url = StartupTask.append(options.tasks, url); } this.windowService.openNewWindow(url.toString()); diff --git a/arduino-ide-extension/src/browser/theia/workspace/workspace-variable-contribution.ts b/arduino-ide-extension/src/browser/theia/workspace/workspace-variable-contribution.ts index 1ca0b7b85..2f9c88800 100644 --- a/arduino-ide-extension/src/browser/theia/workspace/workspace-variable-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/workspace/workspace-variable-contribution.ts @@ -10,21 +10,31 @@ import { CurrentSketch, SketchesServiceClientImpl, } from '../../../common/protocol/sketches-service-client-impl'; +import { DisposableCollection } from '@theia/core/lib/common/disposable'; @injectable() export class WorkspaceVariableContribution extends TheiaWorkspaceVariableContribution { @inject(SketchesServiceClientImpl) - protected readonly sketchesServiceClient: SketchesServiceClientImpl; + private readonly sketchesServiceClient: SketchesServiceClientImpl; - protected currentSketch?: Sketch; + private currentSketch?: Sketch; @postConstruct() protected override init(): void { - this.sketchesServiceClient.currentSketch().then((sketch) => { - if (CurrentSketch.isValid(sketch)) { - this.currentSketch = sketch; - } - }); + const sketch = this.sketchesServiceClient.tryGetCurrentSketch(); + if (CurrentSketch.isValid(sketch)) { + this.currentSketch = sketch; + } else { + const toDispose = new DisposableCollection(); + toDispose.push( + this.sketchesServiceClient.onCurrentSketchDidChange((sketch) => { + if (CurrentSketch.isValid(sketch)) { + this.currentSketch = sketch; + } + toDispose.dispose(); + }) + ); + } } override getResourceUri(): URI | undefined { diff --git a/arduino-ide-extension/src/browser/utils/window.ts b/arduino-ide-extension/src/browser/utils/window.ts new file mode 100644 index 000000000..54e046724 --- /dev/null +++ b/arduino-ide-extension/src/browser/utils/window.ts @@ -0,0 +1,7 @@ +/** + * Changes the `window.location` without navigating away. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function setURL(url: URL, data: any = {}): void { + history.pushState(data, '', url); +} diff --git a/arduino-ide-extension/src/browser/widgets/sketchbook/startup-task.ts b/arduino-ide-extension/src/browser/widgets/sketchbook/startup-task.ts index 47ab60dbc..25cf33a3e 100644 --- a/arduino-ide-extension/src/browser/widgets/sketchbook/startup-task.ts +++ b/arduino-ide-extension/src/browser/widgets/sketchbook/startup-task.ts @@ -1,36 +1,86 @@ import { injectable } from '@theia/core/shared/inversify'; import { WorkspaceInput as TheiaWorkspaceInput } from '@theia/workspace/lib/browser'; import { Contribution } from '../../contributions/contribution'; +import { setURL } from '../../utils/window'; -export interface Task { +@injectable() +export class StartupTasks extends Contribution { + override onReady(): void { + const tasks = StartupTask.get(new URL(window.location.href)); + console.log(`Executing startup tasks: ${JSON.stringify(tasks)}`); + tasks.forEach(({ command, args = [] }) => + this.commandService + .executeCommand(command, ...args) + .catch((err) => + console.error( + `Error occurred when executing the startup task '${command}'${ + args?.length ? ` with args: '${JSON.stringify(args)}` : '' + }.`, + err + ) + ) + ); + if (tasks.length) { + // Remove the startup tasks after the execution. + // Otherwise, IDE2 executes them again on a window reload event. + setURL(StartupTask.set([], new URL(window.location.href))); + console.info(`Removed startup tasks from URL.`); + } + } +} + +export interface StartupTask { command: string; /** - * This must be JSON serializable. + * Must be JSON serializable. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any args?: any[]; } - -@injectable() -export class StartupTask extends Contribution { - override onReady(): void { - const params = new URLSearchParams(window.location.search); - const encoded = params.get(StartupTask.QUERY_STRING); - if (!encoded) return; - - const commands = JSON.parse(decodeURIComponent(encoded)); - - if (Array.isArray(commands)) { - commands.forEach(({ command, args }) => { - this.commandService.executeCommand(command, ...args); - }); +export namespace StartupTask { + const QUERY = 'startupTasks'; + export function is(arg: unknown): arg is StartupTasks { + if (typeof arg === 'object') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const object = arg as any; + return 'command' in object && typeof object['command'] === 'string'; + } + return false; + } + export function get(url: URL): StartupTask[] { + const { searchParams } = url; + const encodedTasks = searchParams.get(QUERY); + if (encodedTasks) { + const rawTasks = decodeURIComponent(encodedTasks); + const tasks = JSON.parse(rawTasks); + if (Array.isArray(tasks)) { + return tasks.filter((task) => { + if (StartupTask.is(task)) { + return true; + } + console.warn(`Was not a task: ${JSON.stringify(task)}. Ignoring.`); + return false; + }); + } else { + debugger; + console.warn(`Startup tasks was not an array: ${rawTasks}. Ignoring.`); + } } + return []; + } + export function set(tasks: StartupTask[], url: URL): URL { + const copy = new URL(url); + copy.searchParams.set(QUERY, encodeURIComponent(JSON.stringify(tasks))); + return copy; + } + export function append(tasks: StartupTask[], url: URL): URL { + return set([...get(url), ...tasks], url); } } -export namespace StartupTask { - export const QUERY_STRING = 'startupTasks'; + +export namespace StartupTasks { export interface WorkspaceInput extends TheiaWorkspaceInput { - tasks: Task[]; + tasks: StartupTask[]; } export namespace WorkspaceInput { export function is( diff --git a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts index 1e50729d6..58ac383cb 100644 --- a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts +++ b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts @@ -47,7 +47,6 @@ export class SketchesServiceClientImpl @inject(ConfigService) protected readonly configService: ConfigService; - protected toDispose = new DisposableCollection(); protected sketches = new Map(); // TODO: rename this + event to the `onBlabla` pattern protected sketchbookDidChangeEmitter = new Emitter<{ @@ -55,8 +54,16 @@ export class SketchesServiceClientImpl removed: SketchRef[]; }>(); readonly onSketchbookDidChange = this.sketchbookDidChangeEmitter.event; + protected currentSketchDidChangeEmitter = new Emitter(); + readonly onCurrentSketchDidChange = this.currentSketchDidChangeEmitter.event; - private _currentSketch = new Deferred(); + protected toDispose = new DisposableCollection( + this.sketchbookDidChangeEmitter, + this.currentSketchDidChangeEmitter + ); + + private _currentSketch: CurrentSketch | undefined; + private currentSketchLoaded = new Deferred(); onStart(): void { this.configService.getConfiguration().then(({ sketchDirUri }) => { @@ -110,9 +117,12 @@ export class SketchesServiceClientImpl ); }); }); - this.loadCurrentSketch().then((currentSketch) => - this._currentSketch.resolve(currentSketch) - ); + setTimeout(async () => { + const currentSketch = await this.loadCurrentSketch(); + this._currentSketch = currentSketch; + this.currentSketchDidChangeEmitter.fire(this._currentSketch); + this.currentSketchLoaded.resolve(this._currentSketch); + }, 1_000); } onStop(): void { @@ -143,7 +153,11 @@ export class SketchesServiceClientImpl } async currentSketch(): Promise { - return this._currentSketch.promise; + return this.currentSketchLoaded.promise; + } + + tryGetCurrentSketch(): CurrentSketch | undefined { + return this._currentSketch; } async currentSketchFile(): Promise { diff --git a/arduino-ide-extension/src/common/protocol/sketches-service.ts b/arduino-ide-extension/src/common/protocol/sketches-service.ts index eb07572d7..0394e6e94 100644 --- a/arduino-ide-extension/src/common/protocol/sketches-service.ts +++ b/arduino-ide-extension/src/common/protocol/sketches-service.ts @@ -1,5 +1,21 @@ +import { ApplicationError } from '@theia/core/lib/common/application-error'; import URI from '@theia/core/lib/common/uri'; +export namespace SketchesError { + export const Codes = { + NotFound: 5001, + }; + export const NotFound = ApplicationError.declare( + Codes.NotFound, + (message: string, uri: string) => { + return { + message, + data: { uri }, + }; + } + ); +} + export const SketchesServicePath = '/services/sketches-service'; export const SketchesService = Symbol('SketchesService'); export interface SketchesService { diff --git a/arduino-ide-extension/src/node/sketches-service-impl.ts b/arduino-ide-extension/src/node/sketches-service-impl.ts index c7c74724b..23b6e0f23 100644 --- a/arduino-ide-extension/src/node/sketches-service-impl.ts +++ b/arduino-ide-extension/src/node/sketches-service-impl.ts @@ -16,6 +16,7 @@ import { Sketch, SketchRef, SketchContainer, + SketchesError, } from '../common/protocol/sketches-service'; import { firstToLowerCase } from '../common/utils'; import { NotificationServiceServerImpl } from './notification-service-server'; @@ -28,6 +29,7 @@ import { import { duration } from '../common/decorators'; import * as glob from 'glob'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { ServiceError } from './service-error'; const WIN32_DRIVE_REGEXP = /^[a-zA-Z]:\\/; @@ -201,7 +203,11 @@ export class SketchesServiceImpl const sketch = await new Promise((resolve, reject) => { client.loadSketch(req, async (err, resp) => { if (err) { - reject(err); + reject( + isNotFoundError(err) + ? SketchesError.NotFound(err.details, uri) + : err + ); return; } const responseSketchPath = maybeNormalizeDrive(resp.getLocationPath()); @@ -448,26 +454,15 @@ void loop() { private async _isSketchFolder( uri: string ): Promise { - const fsPath = FileUri.fsPath(uri); - let stat: fs.Stats | undefined; try { - stat = await promisify(fs.lstat)(fsPath); - } catch {} - if (stat && stat.isDirectory()) { - const basename = path.basename(fsPath); - const files = await promisify(fs.readdir)(fsPath); - for (let i = 0; i < files.length; i++) { - if (files[i] === basename + '.ino' || files[i] === basename + '.pde') { - try { - const sketch = await this.loadSketch( - FileUri.create(fsPath).toString() - ); - return sketch; - } catch {} - } + const sketch = await this.loadSketch(uri); + return sketch; + } catch (err) { + if (SketchesError.NotFound.is(err)) { + return undefined; } + throw err; } - return undefined; } async isTemp(sketch: SketchRef): Promise { @@ -588,6 +583,14 @@ interface SketchWithDetails extends Sketch { readonly mtimeMs: number; } +function isNotFoundError(err: unknown): err is ServiceError { + return ( + ServiceError.is(err) && + err.code === 5 && + err.message.toLocaleLowerCase('en-US').includes('not_found') + ); +} + /** * If on Windows, will change the input `C:\\path\\to\\somewhere` to `c:\\path\\to\\somewhere`. */ From 2d589299f851b2c4b5869df748a60e8706d893df Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 08:46:49 +0200 Subject: [PATCH 02/22] no message check. Signed-off-by: Akos Kitta --- arduino-ide-extension/src/node/sketches-service-impl.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arduino-ide-extension/src/node/sketches-service-impl.ts b/arduino-ide-extension/src/node/sketches-service-impl.ts index 23b6e0f23..288641f16 100644 --- a/arduino-ide-extension/src/node/sketches-service-impl.ts +++ b/arduino-ide-extension/src/node/sketches-service-impl.ts @@ -584,11 +584,7 @@ interface SketchWithDetails extends Sketch { } function isNotFoundError(err: unknown): err is ServiceError { - return ( - ServiceError.is(err) && - err.code === 5 && - err.message.toLocaleLowerCase('en-US').includes('not_found') - ); + return ServiceError.is(err) && err.code === 5; // `NOT_FOUND` https://grpc.github.io/grpc/core/md_doc_statuscodes.html } /** From 13232b74dad9f4be8a57ef1386d41eb6b7c9eed6 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 08:47:16 +0200 Subject: [PATCH 03/22] open sketch files in dedicated contribution. Signed-off-by: Akos Kitta --- .../src/browser/arduino-commands.ts | 7 - .../browser/arduino-frontend-contribution.tsx | 140 ++---------------- .../browser/arduino-ide-frontend-module.ts | 4 + .../contributions/open-sketch-files.ts | 138 +++++++++++++++++ .../theia/core/frontend-application.ts | 4 +- 5 files changed, 159 insertions(+), 134 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/open-sketch-files.ts diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts index 12673d71b..cdd4cb762 100644 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ b/arduino-ide-extension/src/browser/arduino-commands.ts @@ -8,13 +8,6 @@ export namespace ArduinoCommands { id: 'arduino-toggle-compile-for-debug', }; - /** - * Unlike `OPEN_SKETCH`, it opens all files from a sketch folder. (ino, cpp, etc...) - */ - export const OPEN_SKETCH_FILES: Command = { - id: 'arduino-open-sketch-files', - }; - export const OPEN_BOARDS_DIALOG: Command = { id: 'arduino-open-boards-dialog', }; diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index aff62e456..09de73aa6 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -11,7 +11,6 @@ import { ExecutableService, Sketch, ArduinoDaemon, - SketchesError, } from '../common/protocol'; import { Mutex } from 'async-mutex'; import { @@ -20,7 +19,6 @@ import { MenuModelRegistry, ILogger, DisposableCollection, - ApplicationError, } from '@theia/core'; import { Dialog, @@ -46,12 +44,7 @@ import { } from '@theia/core/lib/common/command'; import { MessageService } from '@theia/core/lib/common/message-service'; import URI from '@theia/core/lib/common/uri'; -import { - EditorCommands, - EditorMainMenu, - EditorManager, - EditorOpenerOptions, -} from '@theia/editor/lib/browser'; +import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; @@ -77,8 +70,7 @@ import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { IDEUpdater } from '../common/protocol/ide-updater'; import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { HostedPluginEvents } from './hosted-plugin-events'; -import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; -import { Notifications } from './contributions/notifications'; +import { OpenSketchFiles } from './contributions/open-sketch-files'; export const SKIP_IDE_VERSION = 'skipIDEVersion'; @@ -103,9 +95,6 @@ export class ArduinoFrontendContribution @inject(BoardsServiceProvider) private readonly boardsServiceClientImpl: BoardsServiceProvider; - @inject(EditorManager) - private readonly editorManager: EditorManager; - @inject(FileService) private readonly fileService: FileService; @@ -154,12 +143,6 @@ export class ArduinoFrontendContribution @inject(ArduinoDaemon) private readonly daemon: ArduinoDaemon; - @inject(WorkspaceService) - private readonly workspaceService: WorkspaceService; - - protected invalidConfigPopup: - | Promise - | undefined; protected toDisposeOnStop = new DisposableCollection(); @postConstruct() @@ -221,9 +204,14 @@ export class ArduinoFrontendContribution sketch.uri ); if (Sketch.isInSketch(resource, reloadedSketch)) { - this.ensureOpened(resource.toString(), true, { - mode: 'open', - }); + this.commandRegistry.executeCommand( + OpenSketchFiles.Commands.ENSURE_OPENED.id, + resource.toString(), + true, + { + mode: 'open', + } + ); } } } @@ -299,6 +287,11 @@ export class ArduinoFrontendContribution } } }); + + // TODO: Verify this! If true IDE2 can start ~100ms faster. + // If the preferences is resolved, then the `ready` call will happen in the same tick + // and will do a `send_sync` request to the electron main to get the current window. + // Consider moving after app `ready`. this.arduinoPreferences.ready.then(() => { const webContents = remote.getCurrentWebContents(); const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel'); @@ -445,11 +438,6 @@ export class ArduinoFrontendContribution execute: () => this.editorMode.toggleCompileForDebug(), isToggled: () => this.editorMode.compileForDebug, }); - registry.registerCommand(ArduinoCommands.OPEN_SKETCH_FILES, { - execute: async (uri: URI) => { - this.openSketchFiles(uri); - }, - }); registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, { execute: async (query?: string | undefined) => { const boardsConfig = await this.boardsConfigDialog.open(query); @@ -501,104 +489,6 @@ export class ArduinoFrontendContribution }); } - protected async openSketchFiles(uri: URI): Promise { - try { - const sketch = await this.sketchService.loadSketch(uri.toString()); - const { mainFileUri, rootFolderFileUris } = sketch; - for (const uri of [mainFileUri, ...rootFolderFileUris]) { - await this.ensureOpened(uri); - } - if (mainFileUri.endsWith('.pde')) { - const message = nls.localize( - 'arduino/common/oldFormat', - "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", - 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.commandRegistry.executeCommand( - SaveAsSketch.Commands.SAVE_AS_SKETCH.id, - { - execOnlyIfTemp: false, - openAfterMove: true, - wipeOriginal: false, - } - ); - } - }); - } - } catch (err) { - if (SketchesError.NotFound.is(err)) { - this.openFallbackSketch(err); - } else { - console.error(err); - const message = - err instanceof Error - ? err.message - : typeof err === 'string' - ? err - : String(err); - this.messageService.error(message); - } - } - } - - private openFallbackSketch( - err: ApplicationError< - number, - { - uri: string; - } - > - ) { - this.sketchService.createNewSketch().then((sketch) => { - this.workspaceService.open( - new URI(sketch.uri), - Object.assign( - { - preserveWindow: true, - }, - { - tasks: [ - { - command: Notifications.Commands.NOTIFY.id, - args: [ - { - type: 'error', - message: err.message, - }, - ], - }, - ], - } - ) - ); - }); - } - - protected async ensureOpened( - uri: string, - forceOpen = false, - options?: EditorOpenerOptions | undefined - ): Promise { - const widget = this.editorManager.all.find( - (widget) => widget.editor.uri.toString() === uri - ); - if (!widget || forceOpen) { - return this.editorManager.open( - new URI(uri), - options ?? { - mode: 'reveal', - preview: false, - counter: 0, - } - ); - } - } - registerColors(colors: ColorRegistry): void { colors.register( { 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 22a94f6d1..dc9f02a4a 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -305,6 +305,8 @@ import { StartupTasks } from './widgets/sketchbook/startup-task'; import { IndexesUpdateProgress } from './contributions/indexes-update-progress'; import { Daemon } from './contributions/daemon'; import { FirstStartupInstaller } from './contributions/first-startup-installer'; +import { Notifications } from './contributions/notifications'; +import { OpenSketchFiles } from './contributions/open-sketch-files'; MonacoThemingService.register({ id: 'arduino-theme', @@ -702,6 +704,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, Daemon); Contribution.configure(bind, FirstStartupInstaller); Contribution.configure(bind, StartupTasks); + Contribution.configure(bind, Notifications); + Contribution.configure(bind, OpenSketchFiles); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts b/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts new file mode 100644 index 000000000..4c6f0453b --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts @@ -0,0 +1,138 @@ +import { ApplicationError } from '@theia/core/lib/common/application-error'; +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 { SketchesError } from '../../common/protocol'; +import { + Command, + CommandRegistry, + SketchContribution, + URI, +} from './contribution'; +import { Notifications } from './notifications'; +import { SaveAsSketch } from './save-as-sketch'; + +@injectable() +export class OpenSketchFiles extends SketchContribution { + override registerCommands(registry: CommandRegistry): void { + registry.registerCommand(OpenSketchFiles.Commands.OPEN_SKETCH_FILES, { + execute: (uri: URI) => this.openSketchFiles(uri), + }); + registry.registerCommand(OpenSketchFiles.Commands.ENSURE_OPENED, { + execute: ( + uri: string, + forceOpen?: boolean, + options?: EditorOpenerOptions + ) => { + this.ensureOpened(uri, forceOpen, options); + }, + }); + } + + private async openSketchFiles(uri: URI): Promise { + try { + const sketch = await this.sketchService.loadSketch(uri.toString()); + const { mainFileUri, rootFolderFileUris } = sketch; + for (const uri of [mainFileUri, ...rootFolderFileUris]) { + await this.ensureOpened(uri); + } + if (mainFileUri.endsWith('.pde')) { + const message = nls.localize( + 'arduino/common/oldFormat', + "The '{0}' still uses the old `.pde` format. Do you want to switch to the new `.ino` extension?", + 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, + } + ); + } + }); + } + } catch (err) { + if (SketchesError.NotFound.is(err)) { + this.openFallbackSketch(err); + } else { + console.error(err); + const message = + err instanceof Error + ? err.message + : typeof err === 'string' + ? err + : String(err); + this.messageService.error(message); + } + } + } + + private async openFallbackSketch( + err: ApplicationError< + number, + { + uri: string; + } + > + ): Promise { + const sketch = await this.sketchService.createNewSketch(); + this.workspaceService.open( + new URI(sketch.uri), + Object.assign( + { + preserveWindow: true, + }, + { + tasks: [ + { + command: Notifications.Commands.NOTIFY.id, + args: [ + { + type: 'error', + message: err.message, + }, + ], + }, + ], + } + ) + ); + } + + private async ensureOpened( + uri: string, + forceOpen = false, + options?: EditorOpenerOptions + ): Promise { + const widget = this.editorManager.all.find( + (widget) => widget.editor.uri.toString() === uri + ); + if (!widget || forceOpen) { + return this.editorManager.open( + new URI(uri), + options ?? { + mode: 'reveal', + preview: false, + counter: 0, + } + ); + } + } +} +export namespace OpenSketchFiles { + export namespace Commands { + export const OPEN_SKETCH_FILES: Command = { + id: 'arduino-open-sketch-files', + }; + export const ENSURE_OPENED: Command = { + id: 'arduino-ensure-opened', + }; + } +} diff --git a/arduino-ide-extension/src/browser/theia/core/frontend-application.ts b/arduino-ide-extension/src/browser/theia/core/frontend-application.ts index ba6b2f8bc..ba02d295e 100644 --- a/arduino-ide-extension/src/browser/theia/core/frontend-application.ts +++ b/arduino-ide-extension/src/browser/theia/core/frontend-application.ts @@ -4,7 +4,7 @@ import { CommandService } from '@theia/core/lib/common/command'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { FrontendApplication as TheiaFrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { SketchesService } from '../../../common/protocol'; -import { ArduinoCommands } from '../../arduino-commands'; +import { OpenSketchFiles } from '../../contributions/open-sketch-files'; @injectable() export class FrontendApplication extends TheiaFrontendApplication { @@ -25,7 +25,7 @@ export class FrontendApplication extends TheiaFrontendApplication { this.workspaceService.roots.then(async (roots) => { for (const root of roots) { await this.commandService.executeCommand( - ArduinoCommands.OPEN_SKETCH_FILES.id, + OpenSketchFiles.Commands.OPEN_SKETCH_FILES.id, root.resource ); this.sketchesService.markAsRecentlyOpened(root.resource.toString()); // no await, will get the notification later and rebuild the menu From bf1159721be5d36250354efacbf9edccd90fc31f Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 09:02:01 +0200 Subject: [PATCH 04/22] moved LS to its own contribution. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 175 ++---------------- .../browser/arduino-ide-frontend-module.ts | 2 + .../src/browser/contributions/ino-language.ts | 153 +++++++++++++++ 3 files changed, 173 insertions(+), 157 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/ino-language.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 09de73aa6..3c02cdf4d 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -1,24 +1,17 @@ +import * as remote from '@theia/core/electron-shared/@electron/remote'; import { inject, injectable, postConstruct, } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; -import * as remote from '@theia/core/electron-shared/@electron/remote'; -import { - BoardsService, - SketchesService, - ExecutableService, - Sketch, - ArduinoDaemon, -} from '../common/protocol'; -import { Mutex } from 'async-mutex'; +import { SketchesService, Sketch } from '../common/protocol'; + import { + DisposableCollection, MAIN_MENU_BAR, MenuContribution, MenuModelRegistry, - ILogger, - DisposableCollection, } from '@theia/core'; import { Dialog, @@ -30,14 +23,15 @@ import { StatusBar, StatusBarAlignment, } from '@theia/core/lib/browser'; -import { nls } from '@theia/core/lib/common'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { TabBarToolbarContribution, TabBarToolbarRegistry, } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { nls } from '@theia/core/lib/common'; import { CommandContribution, CommandRegistry, @@ -45,32 +39,30 @@ import { import { MessageService } from '@theia/core/lib/common/message-service'; import URI from '@theia/core/lib/common/uri'; import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser'; +import { FileChangeType } from '@theia/filesystem/lib/browser'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; -import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { FileChangeType } from '@theia/filesystem/lib/browser'; -import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; +import { IDEUpdater } from '../common/protocol/ide-updater'; +import { + CurrentSketch, + SketchesServiceClientImpl, +} from '../common/protocol/sketches-service-client-impl'; import { ArduinoCommands } from './arduino-commands'; +import { ArduinoPreferences } from './arduino-preferences'; import { BoardsConfig } from './boards/boards-config'; import { BoardsConfigDialog } from './boards/boards-config-dialog'; import { BoardsServiceProvider } from './boards/boards-service-provider'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; +import { OpenSketchFiles } from './contributions/open-sketch-files'; +import { SaveAsSketch } from './contributions/save-as-sketch'; +import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { EditorMode } from './editor-mode'; import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; -import { ArduinoPreferences } from './arduino-preferences'; -import { - CurrentSketch, - SketchesServiceClientImpl, -} from '../common/protocol/sketches-service-client-impl'; -import { SaveAsSketch } from './contributions/save-as-sketch'; -import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; -import { IDEUpdater } from '../common/protocol/ide-updater'; -import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; -import { HostedPluginEvents } from './hosted-plugin-events'; -import { OpenSketchFiles } from './contributions/open-sketch-files'; export const SKIP_IDE_VERSION = 'skipIDEVersion'; @@ -83,15 +75,9 @@ export class ArduinoFrontendContribution MenuContribution, ColorContribution { - @inject(ILogger) - private readonly logger: ILogger; - @inject(MessageService) private readonly messageService: MessageService; - @inject(BoardsService) - private readonly boardsService: BoardsService; - @inject(BoardsServiceProvider) private readonly boardsServiceClientImpl: BoardsServiceProvider; @@ -113,12 +99,6 @@ export class ArduinoFrontendContribution @inject(EditorMode) private readonly editorMode: EditorMode; - @inject(HostedPluginEvents) - private readonly hostedPluginEvents: HostedPluginEvents; - - @inject(ExecutableService) - private readonly executableService: ExecutableService; - @inject(ArduinoPreferences) private readonly arduinoPreferences: ArduinoPreferences; @@ -140,9 +120,6 @@ export class ArduinoFrontendContribution @inject(IDEUpdaterDialog) private readonly updaterDialog: IDEUpdaterDialog; - @inject(ArduinoDaemon) - private readonly daemon: ArduinoDaemon; - protected toDisposeOnStop = new DisposableCollection(); @postConstruct() @@ -246,31 +223,9 @@ export class ArduinoFrontendContribution ); }); - const start = async ( - { selectedBoard }: BoardsConfig.Config, - forceStart = false - ) => { - if (selectedBoard) { - const { name, fqbn } = selectedBoard; - if (fqbn) { - this.startLanguageServer(fqbn, name, forceStart); - } - } - }; - this.boardsServiceClientImpl.onBoardsConfigChanged(start); - this.hostedPluginEvents.onPluginsDidStart(() => - start(this.boardsServiceClientImpl.boardsConfig) - ); - this.hostedPluginEvents.onPluginsWillUnload( - () => (this.languageServerFqbn = undefined) - ); this.arduinoPreferences.onPreferenceChanged((event) => { if (event.newValue !== event.oldValue) { switch (event.preferenceName) { - case 'arduino.language.log': - case 'arduino.language.realTimeDiagnostics': - start(this.boardsServiceClientImpl.boardsConfig, true); - break; case 'arduino.window.zoomLevel': if (typeof event.newValue === 'number') { const webContents = remote.getCurrentWebContents(); @@ -318,100 +273,6 @@ export class ArduinoFrontendContribution this.toDisposeOnStop.dispose(); } - protected languageServerFqbn?: string; - protected languageServerStartMutex = new Mutex(); - protected async startLanguageServer( - fqbn: string, - name: string | undefined, - forceStart = false - ): Promise { - const port = await this.daemon.tryGetPort(); - if (!port) { - return; - } - const release = await this.languageServerStartMutex.acquire(); - try { - await this.hostedPluginEvents.didStart; - const details = await this.boardsService.getBoardDetails({ fqbn }); - if (!details) { - // Core is not installed for the selected board. - console.info( - `Could not start language server for ${fqbn}. The core is not installed for the board.` - ); - if (this.languageServerFqbn) { - try { - await this.commandRegistry.executeCommand( - 'arduino.languageserver.stop' - ); - console.info( - `Stopped language server process for ${this.languageServerFqbn}.` - ); - this.languageServerFqbn = undefined; - } catch (e) { - console.error( - `Failed to start language server process for ${this.languageServerFqbn}`, - e - ); - throw e; - } - } - return; - } - if (!forceStart && fqbn === this.languageServerFqbn) { - // NOOP - return; - } - this.logger.info(`Starting language server: ${fqbn}`); - const log = this.arduinoPreferences.get('arduino.language.log'); - const realTimeDiagnostics = this.arduinoPreferences.get( - 'arduino.language.realTimeDiagnostics' - ); - let currentSketchPath: string | undefined = undefined; - if (log) { - const currentSketch = await this.sketchServiceClient.currentSketch(); - if (CurrentSketch.isValid(currentSketch)) { - currentSketchPath = await this.fileService.fsPath( - new URI(currentSketch.uri) - ); - } - } - const { clangdUri, lsUri } = await this.executableService.list(); - const [clangdPath, lsPath] = await Promise.all([ - this.fileService.fsPath(new URI(clangdUri)), - this.fileService.fsPath(new URI(lsUri)), - ]); - - this.languageServerFqbn = await Promise.race([ - new Promise((_, reject) => - setTimeout( - () => reject(new Error(`Timeout after ${20_000} ms.`)), - 20_000 - ) - ), - this.commandRegistry.executeCommand( - 'arduino.languageserver.start', - { - lsPath, - cliDaemonAddr: `localhost:${port}`, - clangdPath, - log: currentSketchPath ? currentSketchPath : log, - cliDaemonInstance: '1', - realTimeDiagnostics, - board: { - fqbn, - name: name ? `"${name}"` : undefined, - }, - } - ), - ]); - } catch (e) { - console.log(`Failed to start language server for ${fqbn}`, e); - this.languageServerFqbn = undefined; - } finally { - release(); - } - } - registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem({ id: BoardsToolBarItem.TOOLBAR_ID, 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 dc9f02a4a..ebade1f27 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -307,6 +307,7 @@ import { Daemon } from './contributions/daemon'; import { FirstStartupInstaller } from './contributions/first-startup-installer'; import { Notifications } from './contributions/notifications'; import { OpenSketchFiles } from './contributions/open-sketch-files'; +import { InoLanguage } from './contributions/ino-language'; MonacoThemingService.register({ id: 'arduino-theme', @@ -706,6 +707,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, StartupTasks); Contribution.configure(bind, Notifications); Contribution.configure(bind, OpenSketchFiles); + Contribution.configure(bind, InoLanguage); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/ino-language.ts b/arduino-ide-extension/src/browser/contributions/ino-language.ts new file mode 100644 index 000000000..a2d33c60c --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/ino-language.ts @@ -0,0 +1,153 @@ +import { Mutex } from 'async-mutex'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { + ArduinoDaemon, + BoardsService, + ExecutableService, +} from '../../common/protocol'; +import { HostedPluginEvents } from '../hosted-plugin-events'; +import { SketchContribution, URI } from './contribution'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; +import { ArduinoPreferences } from '../arduino-preferences'; +import { BoardsConfig } from '../boards/boards-config'; +import { BoardsServiceProvider } from '../boards/boards-service-provider'; + +@injectable() +export class InoLanguage extends SketchContribution { + @inject(HostedPluginEvents) + private readonly hostedPluginEvents: HostedPluginEvents; + + @inject(ExecutableService) + private readonly executableService: ExecutableService; + + @inject(ArduinoDaemon) + private readonly daemon: ArduinoDaemon; + + @inject(BoardsService) + private readonly boardsService: BoardsService; + + @inject(BoardsServiceProvider) + private readonly boardsServiceProvider: BoardsServiceProvider; + + @inject(ArduinoPreferences) + private readonly arduinoPreferences: ArduinoPreferences; + + private languageServerFqbn?: string; + private languageServerStartMutex = new Mutex(); + + override onReady(): void { + const start = ({ selectedBoard }: BoardsConfig.Config) => { + if (selectedBoard) { + const { name, fqbn } = selectedBoard; + if (fqbn) { + this.startLanguageServer(fqbn, name); + } + } + }; + this.boardsServiceProvider.onBoardsConfigChanged(start); + this.hostedPluginEvents.onPluginsDidStart(() => + start(this.boardsServiceProvider.boardsConfig) + ); + this.hostedPluginEvents.onPluginsWillUnload( + () => (this.languageServerFqbn = undefined) + ); + this.arduinoPreferences.onPreferenceChanged( + ({ preferenceName, oldValue, newValue }) => { + if ( + preferenceName === 'arduino.language.log' && + newValue !== oldValue + ) { + start(this.boardsServiceProvider.boardsConfig); + } + } + ); + start(this.boardsServiceProvider.boardsConfig); + } + + private async startLanguageServer( + fqbn: string, + name: string | undefined + ): Promise { + const port = await this.daemon.tryGetPort(); + if (!port) { + return; + } + const release = await this.languageServerStartMutex.acquire(); + try { + await this.hostedPluginEvents.didStart; + const details = await this.boardsService.getBoardDetails({ fqbn }); + if (!details) { + // Core is not installed for the selected board. + console.info( + `Could not start language server for ${fqbn}. The core is not installed for the board.` + ); + if (this.languageServerFqbn) { + try { + await this.commandService.executeCommand( + 'arduino.languageserver.stop' + ); + console.info( + `Stopped language server process for ${this.languageServerFqbn}.` + ); + this.languageServerFqbn = undefined; + } catch (e) { + console.error( + `Failed to start language server process for ${this.languageServerFqbn}`, + e + ); + throw e; + } + } + return; + } + if (fqbn === this.languageServerFqbn) { + // NOOP + return; + } + this.logger.info(`Starting language server: ${fqbn}`); + const log = this.arduinoPreferences.get('arduino.language.log'); + let currentSketchPath: string | undefined = undefined; + if (log) { + const currentSketch = await this.sketchServiceClient.currentSketch(); + if (CurrentSketch.isValid(currentSketch)) { + currentSketchPath = await this.fileService.fsPath( + new URI(currentSketch.uri) + ); + } + } + const { clangdUri, lsUri } = await this.executableService.list(); + const [clangdPath, lsPath] = await Promise.all([ + this.fileService.fsPath(new URI(clangdUri)), + this.fileService.fsPath(new URI(lsUri)), + ]); + + this.languageServerFqbn = await Promise.race([ + new Promise((_, reject) => + setTimeout( + () => reject(new Error(`Timeout after ${20_000} ms.`)), + 20_000 + ) + ), + this.commandService.executeCommand( + 'arduino.languageserver.start', + { + lsPath, + cliDaemonAddr: `localhost:${port}`, + clangdPath, + log: currentSketchPath ? currentSketchPath : log, + cliDaemonInstance: '1', + board: { + fqbn, + name: name ? `"${name}"` : undefined, + }, + } + ), + ]); + } catch (e) { + console.log(`Failed to start language server for ${fqbn}`, e); + this.languageServerFqbn = undefined; + } finally { + release(); + } + } +} From baa9936b70f23e87698e66c4e25c3be1de611e7e Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 11:07:50 +0200 Subject: [PATCH 05/22] get rid of old editor mode. use a preference. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 16 ----- .../browser/arduino-ide-frontend-module.ts | 5 -- .../src/browser/arduino-preferences.ts | 9 +++ .../src/browser/contributions/contribution.ts | 4 -- .../src/browser/contributions/debug.ts | 67 +++++++++++++++---- .../src/browser/contributions/ino-language.ts | 8 +-- .../browser/contributions/upload-sketch.ts | 4 +- .../browser/contributions/verify-sketch.ts | 5 +- .../src/browser/editor-mode.ts | 37 ---------- 9 files changed, 71 insertions(+), 84 deletions(-) delete mode 100644 arduino-ide-extension/src/browser/editor-mode.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 3c02cdf4d..692ed92d2 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -59,7 +59,6 @@ import { BoardsToolBarItem } from './boards/boards-toolbar-item'; import { OpenSketchFiles } from './contributions/open-sketch-files'; import { SaveAsSketch } from './contributions/save-as-sketch'; import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; -import { EditorMode } from './editor-mode'; import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; @@ -96,9 +95,6 @@ export class ArduinoFrontendContribution @inject(StatusBar) private readonly statusBar: StatusBar; - @inject(EditorMode) - private readonly editorMode: EditorMode; - @inject(ArduinoPreferences) private readonly arduinoPreferences: ArduinoPreferences; @@ -295,10 +291,6 @@ export class ArduinoFrontendContribution } registerCommands(registry: CommandRegistry): void { - registry.registerCommand(ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG, { - execute: () => this.editorMode.toggleCompileForDebug(), - isToggled: () => this.editorMode.compileForDebug, - }); registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, { execute: async (query?: string | undefined) => { const boardsConfig = await this.boardsConfigDialog.open(query); @@ -340,14 +332,6 @@ export class ArduinoFrontendContribution ArduinoMenus.TOOLS, nls.localize('arduino/menu/tools', 'Tools') ); - registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { - commandId: ArduinoCommands.TOGGLE_COMPILE_FOR_DEBUG.id, - label: nls.localize( - 'arduino/debug/optimizeForDebugging', - 'Optimize for Debugging' - ), - order: '5', - }); } registerColors(colors: ColorRegistry): void { 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 ebade1f27..c6b7ed955 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -80,7 +80,6 @@ import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browse import { ProblemManager } from './theia/markers/problem-manager'; import { BoardsAutoInstaller } from './boards/boards-auto-installer'; import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer'; -import { EditorMode } from './editor-mode'; import { ListItemRenderer } from './widgets/component-list/list-item-renderer'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service'; @@ -489,10 +488,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { WorkspaceVariableContribution ); - // Customizing default Theia layout based on the editor mode: `pro-mode` or `classic`. - bind(EditorMode).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(EditorMode); - bind(SurveyNotificationService) .toDynamicValue((context) => { return ElectronIpcConnectionProvider.createProxy( diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index 398a4f2c2..f04a7bf7d 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -92,6 +92,14 @@ export const ArduinoConfigSchema: PreferenceSchema = { ), default: 'None', }, + 'arduino.compile.optimizeForDebug': { + type: 'boolean', + description: nls.localize( + 'arduino/preferences/compile.optimizeForDebug', + "Optimize compile output for debug, not for release. It's 'false' by default." + ), + default: false, + }, 'arduino.upload.verbose': { type: 'boolean', description: nls.localize( @@ -251,6 +259,7 @@ export interface ArduinoConfiguration { 'arduino.compile.experimental': boolean; 'arduino.compile.revealRange': ErrorRevealStrategy; 'arduino.compile.warnings': CompilerWarnings; + 'arduino.compile.optimizeForDebug': boolean; 'arduino.upload.verbose': boolean; 'arduino.upload.verify': boolean; 'arduino.window.autoScale': boolean; diff --git a/arduino-ide-extension/src/browser/contributions/contribution.ts b/arduino-ide-extension/src/browser/contributions/contribution.ts index fc51d5b65..7a999a6a3 100644 --- a/arduino-ide-extension/src/browser/contributions/contribution.ts +++ b/arduino-ide-extension/src/browser/contributions/contribution.ts @@ -37,7 +37,6 @@ import { CommandContribution, CommandService, } from '@theia/core/lib/common/command'; -import { EditorMode } from '../editor-mode'; import { SettingsService } from '../dialogs/settings/settings'; import { CurrentSketch, @@ -90,9 +89,6 @@ export abstract class Contribution @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - @inject(EditorMode) - protected readonly editorMode: EditorMode; - @inject(LabelProvider) protected readonly labelProvider: LabelProvider; diff --git a/arduino-ide-extension/src/browser/contributions/debug.ts b/arduino-ide-extension/src/browser/contributions/debug.ts index 8d94df4dd..54137b1e5 100644 --- a/arduino-ide-extension/src/browser/contributions/debug.ts +++ b/arduino-ide-extension/src/browser/contributions/debug.ts @@ -12,46 +12,54 @@ import { SketchContribution, TabBarToolbarRegistry, } from './contribution'; -import { MaybePromise, nls } from '@theia/core/lib/common'; +import { MaybePromise, MenuModelRegistry, nls } from '@theia/core/lib/common'; import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; +import { ArduinoMenus } from '../menu/arduino-menus'; +import { + PreferenceScope, + PreferenceService, +} from '@theia/core/lib/browser/preferences/preference-service'; @injectable() export class Debug extends SketchContribution { @inject(HostedPluginSupport) - protected hostedPluginSupport: HostedPluginSupport; + private readonly hostedPluginSupport: HostedPluginSupport; @inject(NotificationCenter) - protected readonly notificationCenter: NotificationCenter; + private readonly notificationCenter: NotificationCenter; @inject(ExecutableService) - protected readonly executableService: ExecutableService; + private readonly executableService: ExecutableService; @inject(BoardsService) - protected readonly boardService: BoardsService; + private readonly boardService: BoardsService; @inject(BoardsServiceProvider) - protected readonly boardsServiceProvider: BoardsServiceProvider; + private readonly boardsServiceProvider: BoardsServiceProvider; + + @inject(PreferenceService) + private readonly preferenceService: PreferenceService; /** * If `undefined`, debugging is enabled. Otherwise, the reason why it's disabled. */ - protected _disabledMessages?: string = nls.localize( + private _disabledMessages?: string = nls.localize( 'arduino/common/noBoardSelected', 'No board selected' ); // Initial pessimism. - protected disabledMessageDidChangeEmitter = new Emitter(); - protected onDisabledMessageDidChange = + private disabledMessageDidChangeEmitter = new Emitter(); + private onDisabledMessageDidChange = this.disabledMessageDidChangeEmitter.event; - protected get disabledMessage(): string | undefined { + private get disabledMessage(): string | undefined { return this._disabledMessages; } - protected set disabledMessage(message: string | undefined) { + private set disabledMessage(message: string | undefined) { this._disabledMessages = message; this.disabledMessageDidChangeEmitter.fire(this._disabledMessages); } - protected readonly debugToolbarItem = { + private readonly debugToolbarItem = { id: Debug.Commands.START_DEBUGGING.id, command: Debug.Commands.START_DEBUGGING.id, tooltip: `${ @@ -98,12 +106,24 @@ export class Debug extends SketchContribution { ArduinoToolbar.is(widget) && widget.side === 'left', isEnabled: () => !this.disabledMessage, }); + registry.registerCommand(Debug.Commands.OPTIMIZE_FOR_DEBUG, { + execute: () => this.toggleOptimizeForDebug(), + isToggled: () => this.isOptimizeForDebug(), + }); } override registerToolbarItems(registry: TabBarToolbarRegistry): void { registry.registerItem(this.debugToolbarItem); } + override registerMenus(registry: MenuModelRegistry): void { + registry.registerMenuAction(ArduinoMenus.SKETCH__MAIN_GROUP, { + commandId: Debug.Commands.OPTIMIZE_FOR_DEBUG.id, + label: Debug.Commands.OPTIMIZE_FOR_DEBUG.label, + order: '5', + }); + } + private async refreshState( board: Board | undefined = this.boardsServiceProvider.boardsConfig .selectedBoard @@ -145,7 +165,7 @@ export class Debug extends SketchContribution { } } - protected async startDebug( + private async startDebug( board: Board | undefined = this.boardsServiceProvider.boardsConfig .selectedBoard ): Promise { @@ -183,8 +203,19 @@ export class Debug extends SketchContribution { }; return this.commandService.executeCommand('arduino.debug.start', config); } -} + private isOptimizeForDebug(): boolean { + return this.preferences.get('arduino.compile.optimizeForDebug'); + } + + private async toggleOptimizeForDebug(): Promise { + return this.preferenceService.set( + 'arduino.compile.optimizeForDebug', + !this.isOptimizeForDebug(), + PreferenceScope.User + ); + } +} export namespace Debug { export namespace Commands { export const START_DEBUGGING = Command.toLocalizedCommand( @@ -195,5 +226,13 @@ export namespace Debug { }, 'vscode/debug.contribution/startDebuggingHelp' ); + export const OPTIMIZE_FOR_DEBUG = Command.toLocalizedCommand( + { + id: 'arduino-optimize-for-debug', + label: 'Optimize for Debugging', + category: 'Arduino', + }, + 'arduino/debug/optimizeForDebugging' + ); } } diff --git a/arduino-ide-extension/src/browser/contributions/ino-language.ts b/arduino-ide-extension/src/browser/contributions/ino-language.ts index a2d33c60c..610d1823c 100644 --- a/arduino-ide-extension/src/browser/contributions/ino-language.ts +++ b/arduino-ide-extension/src/browser/contributions/ino-language.ts @@ -8,7 +8,6 @@ import { import { HostedPluginEvents } from '../hosted-plugin-events'; import { SketchContribution, URI } from './contribution'; import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; -import { ArduinoPreferences } from '../arduino-preferences'; import { BoardsConfig } from '../boards/boards-config'; import { BoardsServiceProvider } from '../boards/boards-service-provider'; @@ -29,9 +28,6 @@ export class InoLanguage extends SketchContribution { @inject(BoardsServiceProvider) private readonly boardsServiceProvider: BoardsServiceProvider; - @inject(ArduinoPreferences) - private readonly arduinoPreferences: ArduinoPreferences; - private languageServerFqbn?: string; private languageServerStartMutex = new Mutex(); @@ -51,7 +47,7 @@ export class InoLanguage extends SketchContribution { this.hostedPluginEvents.onPluginsWillUnload( () => (this.languageServerFqbn = undefined) ); - this.arduinoPreferences.onPreferenceChanged( + this.preferences.onPreferenceChanged( ({ preferenceName, oldValue, newValue }) => { if ( preferenceName === 'arduino.language.log' && @@ -105,7 +101,7 @@ export class InoLanguage extends SketchContribution { return; } this.logger.info(`Starting language server: ${fqbn}`); - const log = this.arduinoPreferences.get('arduino.language.log'); + const log = this.preferences.get('arduino.language.log'); let currentSketchPath: string | undefined = undefined; if (log) { const currentSketch = await this.sketchServiceClient.currentSketch(); diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts index ebfd02c6d..0c16daf09 100644 --- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts @@ -227,7 +227,9 @@ export class UploadSketch extends CoreServiceContribution { fqbn, }; let options: CoreService.Upload.Options | undefined = undefined; - const optimizeForDebug = this.editorMode.compileForDebug; + const optimizeForDebug = this.preferences.get( + 'arduino.compile.optimizeForDebug' + ); const { selectedPort } = boardsConfig; const port = selectedPort; const userFields = diff --git a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts index b7f391bd5..638cbfa16 100644 --- a/arduino-ide-extension/src/browser/contributions/verify-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/verify-sketch.ts @@ -114,11 +114,14 @@ export class VerifySketch extends CoreServiceContribution { }; const verbose = this.preferences.get('arduino.compile.verbose'); const compilerWarnings = this.preferences.get('arduino.compile.warnings'); + const optimizeForDebug = this.preferences.get( + 'arduino.compile.optimizeForDebug' + ); this.outputChannelManager.getChannel('Arduino').clear(); await this.coreService.compile({ sketch, board, - optimizeForDebug: this.editorMode.compileForDebug, + optimizeForDebug, verbose, exportBinaries, sourceOverride, diff --git a/arduino-ide-extension/src/browser/editor-mode.ts b/arduino-ide-extension/src/browser/editor-mode.ts deleted file mode 100644 index 33ebef45f..000000000 --- a/arduino-ide-extension/src/browser/editor-mode.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { - FrontendApplicationContribution, - FrontendApplication, -} from '@theia/core/lib/browser'; -import { MainMenuManager } from '../common/main-menu-manager'; - -@injectable() -export class EditorMode implements FrontendApplicationContribution { - @inject(MainMenuManager) - protected readonly mainMenuManager: MainMenuManager; - - protected app: FrontendApplication; - - onStart(app: FrontendApplication): void { - this.app = app; - } - - get compileForDebug(): boolean { - const value = window.localStorage.getItem(EditorMode.COMPILE_FOR_DEBUG_KEY); - return value === 'true'; - } - - async toggleCompileForDebug(): Promise { - const oldState = this.compileForDebug; - const newState = !oldState; - window.localStorage.setItem( - EditorMode.COMPILE_FOR_DEBUG_KEY, - String(newState) - ); - this.mainMenuManager.update(); - } -} - -export namespace EditorMode { - export const COMPILE_FOR_DEBUG_KEY = 'arduino-compile-for-debug'; -} From ba3990923e84c919214896d8c0cb3c51ce03d978 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 11:58:28 +0200 Subject: [PATCH 06/22] moved selected board tracker to its module. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 37 +------------ .../browser/arduino-ide-frontend-module.ts | 2 + .../browser/contributions/selected-board.ts | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/selected-board.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 692ed92d2..d7842fb17 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -20,8 +20,6 @@ import { LocalStorageService, OnWillStopAction, SaveableWidget, - StatusBar, - StatusBarAlignment, } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; @@ -52,7 +50,6 @@ import { } from '../common/protocol/sketches-service-client-impl'; import { ArduinoCommands } from './arduino-commands'; import { ArduinoPreferences } from './arduino-preferences'; -import { BoardsConfig } from './boards/boards-config'; import { BoardsConfigDialog } from './boards/boards-config-dialog'; import { BoardsServiceProvider } from './boards/boards-service-provider'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; @@ -92,9 +89,6 @@ export class ArduinoFrontendContribution @inject(CommandRegistry) private readonly commandRegistry: CommandRegistry; - @inject(StatusBar) - private readonly statusBar: StatusBar; - @inject(ArduinoPreferences) private readonly arduinoPreferences: ArduinoPreferences; @@ -129,36 +123,6 @@ export class ArduinoFrontendContribution ) ); } - const updateStatusBar = ({ - selectedBoard, - selectedPort, - }: BoardsConfig.Config) => { - this.statusBar.setElement('arduino-selected-board', { - alignment: StatusBarAlignment.RIGHT, - text: selectedBoard - ? `$(microchip) ${selectedBoard.name}` - : `$(close) ${nls.localize( - 'arduino/common/noBoardSelected', - 'No board selected' - )}`, - className: 'arduino-selected-board', - }); - if (selectedBoard) { - this.statusBar.setElement('arduino-selected-port', { - alignment: StatusBarAlignment.RIGHT, - text: selectedPort - ? nls.localize( - 'arduino/common/selectedOn', - 'on {0}', - selectedPort.address - ) - : nls.localize('arduino/common/notConnected', '[not connected]'), - className: 'arduino-selected-port', - }); - } - }; - this.boardsServiceClientImpl.onBoardsConfigChanged(updateStatusBar); - updateStatusBar(this.boardsServiceClientImpl.boardsConfig); this.appStateService.reachedState('ready').then(async () => { const sketch = await this.sketchServiceClient.currentSketch(); if ( @@ -249,6 +213,7 @@ export class ArduinoFrontendContribution webContents.setZoomLevel(zoomLevel); }); + // Removes the _Settings_ (cog) icon from the left sidebar app.shell.leftPanelHandler.removeBottomMenu('settings-menu'); this.fileSystemFrontendContribution.onDidChangeEditorFile( 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 c6b7ed955..ee96dd7fe 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -307,6 +307,7 @@ import { FirstStartupInstaller } from './contributions/first-startup-installer'; import { Notifications } from './contributions/notifications'; import { OpenSketchFiles } from './contributions/open-sketch-files'; import { InoLanguage } from './contributions/ino-language'; +import { SelectedBoard } from './contributions/selected-board'; MonacoThemingService.register({ id: 'arduino-theme', @@ -703,6 +704,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, Notifications); Contribution.configure(bind, OpenSketchFiles); Contribution.configure(bind, InoLanguage); + Contribution.configure(bind, SelectedBoard); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/selected-board.ts b/arduino-ide-extension/src/browser/contributions/selected-board.ts new file mode 100644 index 000000000..bf8a84ae8 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/selected-board.ts @@ -0,0 +1,54 @@ +import { + StatusBar, + StatusBarAlignment, +} from '@theia/core/lib/browser/status-bar/status-bar'; +import { nls } from '@theia/core/lib/common/nls'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { BoardsConfig } from '../boards/boards-config'; +import { BoardsServiceProvider } from '../boards/boards-service-provider'; +import { Contribution } from './contribution'; + +@injectable() +export class SelectedBoard extends Contribution { + @inject(StatusBar) + private readonly statusBar: StatusBar; + + @inject(BoardsServiceProvider) + private readonly boardsServiceProvider: BoardsServiceProvider; + + override onStart(): void { + this.boardsServiceProvider.onBoardsConfigChanged((config) => + this.update(config) + ); + } + + override onReady(): void { + this.update(this.boardsServiceProvider.boardsConfig); + } + + private update({ selectedBoard, selectedPort }: BoardsConfig.Config): void { + this.statusBar.setElement('arduino-selected-board', { + alignment: StatusBarAlignment.RIGHT, + text: selectedBoard + ? `$(microchip) ${selectedBoard.name}` + : `$(close) ${nls.localize( + 'arduino/common/noBoardSelected', + 'No board selected' + )}`, + className: 'arduino-selected-board', + }); + if (selectedBoard) { + this.statusBar.setElement('arduino-selected-port', { + alignment: StatusBarAlignment.RIGHT, + text: selectedPort + ? nls.localize( + 'arduino/common/selectedOn', + 'on {0}', + selectedPort.address + ) + : nls.localize('arduino/common/notConnected', '[not connected]'), + className: 'arduino-selected-port', + }); + } + } +} From 123ccb127de7dd52cecbcff0e82c2e047b4854ee Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 11:58:41 +0200 Subject: [PATCH 07/22] Use the default Theia spinner. Signed-off-by: Akos Kitta --- .../src/browser/style/index.css | 18 --------------- .../theia/core/frontend-application.ts | 22 ------------------- electron/build/scripts/patch-theia-preload.js | 16 -------------- electron/build/template-package.json | 2 +- 4 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 electron/build/scripts/patch-theia-preload.js diff --git a/arduino-ide-extension/src/browser/style/index.css b/arduino-ide-extension/src/browser/style/index.css index 50b57cc7a..fe5d9753f 100644 --- a/arduino-ide-extension/src/browser/style/index.css +++ b/arduino-ide-extension/src/browser/style/index.css @@ -134,21 +134,3 @@ button.secondary[disabled], .theia-button.secondary[disabled] { .fa-reload { font-size: 14px; } - -/* restore the old Theia spinner */ -/* https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318 */ -.old-theia-preload { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 50000; - background: var(--theia-editor-background); - background-image: var(--theia-preloader); - background-size: 60px 60px; - background-repeat: no-repeat; - background-attachment: fixed; - background-position: center; - transition: opacity 0.8s; -} diff --git a/arduino-ide-extension/src/browser/theia/core/frontend-application.ts b/arduino-ide-extension/src/browser/theia/core/frontend-application.ts index ba02d295e..cb1a96206 100644 --- a/arduino-ide-extension/src/browser/theia/core/frontend-application.ts +++ b/arduino-ide-extension/src/browser/theia/core/frontend-application.ts @@ -32,26 +32,4 @@ export class FrontendApplication extends TheiaFrontendApplication { } }); } - - protected override getStartupIndicator( - host: HTMLElement - ): HTMLElement | undefined { - let startupElement = this.doGetStartupIndicator(host, 'old-theia-preload'); // https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318 - if (!startupElement) { - startupElement = this.doGetStartupIndicator(host, 'theia-preload'); // We show the new Theia spinner in dev mode. - } - return startupElement; - } - - private doGetStartupIndicator( - host: HTMLElement, - classNames: string - ): HTMLElement | undefined { - const elements = host.getElementsByClassName(classNames); - const first = elements[0]; - if (first instanceof HTMLElement) { - return first; - } - return undefined; - } } diff --git a/electron/build/scripts/patch-theia-preload.js b/electron/build/scripts/patch-theia-preload.js deleted file mode 100644 index e4b94d7c9..000000000 --- a/electron/build/scripts/patch-theia-preload.js +++ /dev/null @@ -1,16 +0,0 @@ -// Patch the Theia spinner: https://github.com/eclipse-theia/theia/pull/10761#issuecomment-1131476318 -// Replaces the `theia-preload` selector with `old-theia-preload` in the generated `index.html`. -let arg = process.argv.splice(2)[0] -if (!arg) { - console.error("The path to the index.html to patch is missing. Use 'node patch-theia-preload.js ./path/to/index.html'") - process.exit(1) -} -(async () => { - const { promises: fs } = require('fs') - const path = require('path') - const index = path.isAbsolute(arg) ? arg : path.join(process.cwd(), arg) - console.log(`>>> Patching 'theia-preload' with 'old-theia-preload' in ${index}.`) - const content = await fs.readFile(index, { encoding: 'utf-8' }) - await fs.writeFile(index, content.replace(/theia-preload/g, 'old-theia-preload'), { encoding: 'utf-8' }) - console.log(`<<< Successfully patched index.html.`) -})() \ No newline at end of file diff --git a/electron/build/template-package.json b/electron/build/template-package.json index 5a235f783..2c8224fff 100644 --- a/electron/build/template-package.json +++ b/electron/build/template-package.json @@ -23,7 +23,7 @@ "package": "cross-env DEBUG=* && electron-builder --publish=never", "package:publish": "cross-env DEBUG=* && electron-builder --publish=always", "download:plugins": "theia download:plugins", - "patch": "ncp ./patch/backend/main.js ./src-gen/backend/main.js && node ./scripts/patch-theia-preload.js ./lib/index.html" + "patch": "ncp ./patch/backend/main.js ./src-gen/backend/main.js" }, "engines": { "node": ">=14.0.0 <15" From a12b1c98bd3ede5195cd223b9485072104b83b35 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 13:12:56 +0200 Subject: [PATCH 08/22] missing translation for the new preference. Signed-off-by: Akos Kitta --- i18n/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/i18n/en.json b/i18n/en.json index 8654a9c97..42ac7ef2d 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -256,6 +256,7 @@ "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", "compile": "compile", "compile.experimental": "True if the IDE should handle multiple compiler errors. False by default", + "compile.optimizeForDebug": "Optimize compile output for debug, not for release. It's 'false' by default.", "compile.revealRange": "Adjusts how compiler errors are revealed in the editor after a failed verify/upload. Possible values: 'auto': Scroll vertically as necessary and reveal a line. 'center': Scroll vertically as necessary and reveal a line centered vertically. 'top': Scroll vertically as necessary and reveal a line close to the top of the viewport, optimized for viewing a code definition. 'centerIfOutsideViewport': Scroll vertically as necessary and reveal a line centered vertically only if it lies outside the viewport. The default value is '{0}'.", "compile.verbose": "True for verbose compile output. False by default", "compile.warnings": "Tells gcc which warning level to use. It's 'None' by default", From fe743f8470aba95929ddb926ac05a83ea5d343a7 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 13:32:52 +0200 Subject: [PATCH 09/22] check for updates in own module. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 39 ----------- .../browser/arduino-ide-frontend-module.ts | 2 + .../contributions/check-for-updates.ts | 64 +++++++++++++++++++ .../browser/contributions/compiler-errors.ts | 8 +-- .../src/browser/contributions/contribution.ts | 6 +- .../contributions/edit-contributions.ts | 8 +-- .../ide-updater/ide-updater-dialog.tsx | 2 +- .../src/common/protocol/ide-updater.ts | 2 + 8 files changed, 75 insertions(+), 56 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/check-for-updates.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index d7842fb17..e82da7b3a 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -43,7 +43,6 @@ import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/fi import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; -import { IDEUpdater } from '../common/protocol/ide-updater'; import { CurrentSketch, SketchesServiceClientImpl, @@ -55,7 +54,6 @@ import { BoardsServiceProvider } from './boards/boards-service-provider'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; import { OpenSketchFiles } from './contributions/open-sketch-files'; import { SaveAsSketch } from './contributions/save-as-sketch'; -import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; @@ -104,12 +102,6 @@ export class ArduinoFrontendContribution @inject(FileSystemFrontendContribution) private readonly fileSystemFrontendContribution: FileSystemFrontendContribution; - @inject(IDEUpdater) - private readonly updater: IDEUpdater; - - @inject(IDEUpdaterDialog) - private readonly updaterDialog: IDEUpdaterDialog; - protected toDisposeOnStop = new DisposableCollection(); @postConstruct() @@ -159,30 +151,6 @@ export class ArduinoFrontendContribution } async onStart(app: FrontendApplication): Promise { - this.updater - .init( - this.arduinoPreferences.get('arduino.ide.updateChannel'), - this.arduinoPreferences.get('arduino.ide.updateBaseUrl') - ) - .then(() => this.updater.checkForUpdates(true)) - .then(async (updateInfo) => { - if (!updateInfo) return; - const versionToSkip = await this.localStorageService.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 - ) - ); - }); - this.arduinoPreferences.onPreferenceChanged((event) => { if (event.newValue !== event.oldValue) { switch (event.preferenceName) { @@ -192,13 +160,6 @@ export class ArduinoFrontendContribution webContents.setZoomLevel(event.newValue || 0); } break; - case 'arduino.ide.updateChannel': - case 'arduino.ide.updateBaseUrl': - this.updater.init( - this.arduinoPreferences.get('arduino.ide.updateChannel'), - this.arduinoPreferences.get('arduino.ide.updateBaseUrl') - ); - break; } } }); 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 ee96dd7fe..030f060b2 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -308,6 +308,7 @@ import { Notifications } from './contributions/notifications'; 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'; MonacoThemingService.register({ id: 'arduino-theme', @@ -705,6 +706,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, OpenSketchFiles); Contribution.configure(bind, InoLanguage); Contribution.configure(bind, SelectedBoard); + 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. diff --git a/arduino-ide-extension/src/browser/contributions/check-for-updates.ts b/arduino-ide-extension/src/browser/contributions/check-for-updates.ts new file mode 100644 index 000000000..16db7a845 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/check-for-updates.ts @@ -0,0 +1,64 @@ +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 CheckForUpdates 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 { + 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/compiler-errors.ts b/arduino-ide-extension/src/browser/contributions/compiler-errors.ts index 0356d40c8..b3946bfd2 100644 --- a/arduino-ide-extension/src/browser/contributions/compiler-errors.ts +++ b/arduino-ide-extension/src/browser/contributions/compiler-errors.ts @@ -29,10 +29,7 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter'; import { CoreError } from '../../common/protocol/core-service'; -import { - ArduinoPreferences, - ErrorRevealStrategy, -} from '../arduino-preferences'; +import { ErrorRevealStrategy } from '../arduino-preferences'; import { InoSelector } from '../ino-selectors'; import { fullRange } from '../utils/monaco'; import { Contribution } from './contribution'; @@ -127,9 +124,6 @@ export class CompilerErrors @inject(CoreErrorHandler) private readonly coreErrorHandler: CoreErrorHandler; - @inject(ArduinoPreferences) - private readonly preferences: ArduinoPreferences; - private readonly errors: ErrorDecoration[] = []; private readonly onDidChangeEmitter = new monaco.Emitter(); private readonly currentErrorDidChangEmitter = new Emitter(); diff --git a/arduino-ide-extension/src/browser/contributions/contribution.ts b/arduino-ide-extension/src/browser/contributions/contribution.ts index 7a999a6a3..096071047 100644 --- a/arduino-ide-extension/src/browser/contributions/contribution.ts +++ b/arduino-ide-extension/src/browser/contributions/contribution.ts @@ -95,6 +95,9 @@ export abstract class Contribution @inject(SettingsService) protected readonly settingsService: SettingsService; + @inject(ArduinoPreferences) + protected readonly preferences: ArduinoPreferences; + @inject(FrontendApplicationStateService) protected readonly appStateService: FrontendApplicationStateService; @@ -142,9 +145,6 @@ export abstract class SketchContribution extends Contribution { @inject(SketchesServiceClientImpl) protected readonly sketchServiceClient: SketchesServiceClientImpl; - @inject(ArduinoPreferences) - protected readonly preferences: ArduinoPreferences; - @inject(EditorManager) protected readonly editorManager: EditorManager; diff --git a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts index 6b77d5163..c66b41865 100644 --- a/arduino-ide-extension/src/browser/contributions/edit-contributions.ts +++ b/arduino-ide-extension/src/browser/contributions/edit-contributions.ts @@ -1,7 +1,6 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { CommonCommands } from '@theia/core/lib/browser/common-frontend-contribution'; import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; -import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; import { Contribution, @@ -20,13 +19,10 @@ import type { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/edit @injectable() export class EditContributions extends Contribution { @inject(MonacoEditorService) - protected readonly codeEditorService: MonacoEditorService; + private readonly codeEditorService: MonacoEditorService; @inject(ClipboardService) - protected readonly clipboardService: ClipboardService; - - @inject(PreferenceService) - protected readonly preferences: PreferenceService; + private readonly clipboardService: ClipboardService; override registerCommands(registry: CommandRegistry): void { registry.registerCommand(EditContributions.Commands.GO_TO_LINE, { diff --git a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx index 2b0b952bd..231687873 100644 --- a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx +++ b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx @@ -12,10 +12,10 @@ import { IDEUpdater, IDEUpdaterClient, ProgressInfo, + SKIP_IDE_VERSION, UpdateInfo, } from '../../../common/protocol/ide-updater'; import { LocalStorageService } from '@theia/core/lib/browser'; -import { SKIP_IDE_VERSION } from '../../arduino-frontend-contribution'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; @injectable() diff --git a/arduino-ide-extension/src/common/protocol/ide-updater.ts b/arduino-ide-extension/src/common/protocol/ide-updater.ts index 7af4f7cb7..e1c79b188 100644 --- a/arduino-ide-extension/src/common/protocol/ide-updater.ts +++ b/arduino-ide-extension/src/common/protocol/ide-updater.ts @@ -69,3 +69,5 @@ export interface IDEUpdaterClient { notifyDownloadProgressChanged(message: ProgressInfo): void; notifyDownloadFinished(message: UpdateInfo): void; } + +export const SKIP_IDE_VERSION = 'skipIDEVersion'; From 198d52b924e05fd09894bdbced776d41bf3b1faa Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 13:41:10 +0200 Subject: [PATCH 10/22] added todo. Signed-off-by: Akos Kitta --- .../src/browser/arduino-frontend-contribution.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index e82da7b3a..53dbeb256 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -409,6 +409,7 @@ export class ArduinoFrontendContribution ); } + // TODO: should be handled by `Close` contribution. https://github.com/arduino/arduino-ide/issues/1016 onWillStop(): OnWillStopAction { return { reason: 'temp-sketch', From b71e3ef8cfcd9aa20b139cfc22f9d93b7c122eec Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 13:42:48 +0200 Subject: [PATCH 11/22] Fixed the typo in the preference key. Closes #1067. Signed-off-by: Akos Kitta --- arduino-ide-extension/src/browser/arduino-preferences.ts | 6 +++--- arduino-ide-extension/src/browser/create/create-api.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index f04a7bf7d..3db30a72f 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -193,10 +193,10 @@ export const ArduinoConfigSchema: PreferenceSchema = { ), default: true, }, - 'arduino.cloud.sketchSyncEnpoint': { + 'arduino.cloud.sketchSyncEndpoint': { type: 'string', description: nls.localize( - 'arduino/preferences/cloud.sketchSyncEnpoint', + 'arduino/preferences/cloud.sketchSyncEndpoint', 'The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.' ), default: 'https://api2.arduino.cc/create', @@ -272,7 +272,7 @@ export interface ArduinoConfiguration { 'arduino.cloud.pull.warn': boolean; 'arduino.cloud.push.warn': boolean; 'arduino.cloud.pushpublic.warn': boolean; - 'arduino.cloud.sketchSyncEnpoint': string; + 'arduino.cloud.sketchSyncEndpoint': string; 'arduino.auth.clientID': string; 'arduino.auth.domain': string; 'arduino.auth.audience': string; diff --git a/arduino-ide-extension/src/browser/create/create-api.ts b/arduino-ide-extension/src/browser/create/create-api.ts index 1e8740a96..1faf05754 100644 --- a/arduino-ide-extension/src/browser/create/create-api.ts +++ b/arduino-ide-extension/src/browser/create/create-api.ts @@ -507,7 +507,8 @@ export class CreateApi { } private domain(apiVersion = 'v2'): string { - const endpoint = this.arduinoPreferences['arduino.cloud.sketchSyncEnpoint']; + const endpoint = + this.arduinoPreferences['arduino.cloud.sketchSyncEndpoint']; return `${endpoint}/${apiVersion}`; } From 626863421a9cf1d43c88ab9e7383d1ff3b4f70d7 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 13:55:37 +0200 Subject: [PATCH 12/22] Fixed the translations. Signed-off-by: Akos Kitta #1067 --- i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/en.json b/i18n/en.json index 42ac7ef2d..b323b1d94 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -253,7 +253,7 @@ "cloud.pull.warn": "True if users should be warned before pulling a cloud sketch. Defaults to true.", "cloud.push.warn": "True if users should be warned before pushing a cloud sketch. Defaults to true.", "cloud.pushpublic.warn": "True if users should be warned before pushing a public sketch to the cloud. Defaults to true.", - "cloud.sketchSyncEnpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", + "cloud.sketchSyncEndpoint": "The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.", "compile": "compile", "compile.experimental": "True if the IDE should handle multiple compiler errors. False by default", "compile.optimizeForDebug": "Optimize compile output for debug, not for release. It's 'false' by default.", From cba1802a14640813d4daa81bfa1d8247d8a7c27b Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 14:36:13 +0200 Subject: [PATCH 13/22] open boards config in own contribution. Signed-off-by: Akos Kitta --- .../src/browser/arduino-commands.ts | 14 ----- .../browser/arduino-frontend-contribution.tsx | 18 +----- .../browser/arduino-ide-frontend-module.ts | 2 + .../browser/boards/boards-service-provider.ts | 3 +- .../browser/boards/boards-toolbar-item.tsx | 55 ++++++++++--------- .../contributions/open-boards-config.ts | 32 +++++++++++ 6 files changed, 66 insertions(+), 58 deletions(-) delete mode 100644 arduino-ide-extension/src/browser/arduino-commands.ts create mode 100644 arduino-ide-extension/src/browser/contributions/open-boards-config.ts diff --git a/arduino-ide-extension/src/browser/arduino-commands.ts b/arduino-ide-extension/src/browser/arduino-commands.ts deleted file mode 100644 index cdd4cb762..000000000 --- a/arduino-ide-extension/src/browser/arduino-commands.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Command } from '@theia/core/lib/common/command'; - -/** - * @deprecated all these commands should go under contributions and have their command, menu, keybinding, and toolbar contributions. - */ -export namespace ArduinoCommands { - export const TOGGLE_COMPILE_FOR_DEBUG: Command = { - id: 'arduino-toggle-compile-for-debug', - }; - - export const OPEN_BOARDS_DIALOG: Command = { - id: 'arduino-open-boards-dialog', - }; -} diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 53dbeb256..4c137d599 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -47,9 +47,7 @@ import { CurrentSketch, SketchesServiceClientImpl, } from '../common/protocol/sketches-service-client-impl'; -import { ArduinoCommands } from './arduino-commands'; import { ArduinoPreferences } from './arduino-preferences'; -import { BoardsConfigDialog } from './boards/boards-config-dialog'; import { BoardsServiceProvider } from './boards/boards-service-provider'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; import { OpenSketchFiles } from './contributions/open-sketch-files'; @@ -73,7 +71,7 @@ export class ArduinoFrontendContribution private readonly messageService: MessageService; @inject(BoardsServiceProvider) - private readonly boardsServiceClientImpl: BoardsServiceProvider; + private readonly boardsServiceProvider: BoardsServiceProvider; @inject(FileService) private readonly fileService: FileService; @@ -81,9 +79,6 @@ export class ArduinoFrontendContribution @inject(SketchesService) private readonly sketchService: SketchesService; - @inject(BoardsConfigDialog) - private readonly boardsConfigDialog: BoardsConfigDialog; - @inject(CommandRegistry) private readonly commandRegistry: CommandRegistry; @@ -202,7 +197,7 @@ export class ArduinoFrontendContribution ), isVisible: (widget) => @@ -217,15 +212,6 @@ export class ArduinoFrontendContribution } registerCommands(registry: CommandRegistry): void { - registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, { - execute: async (query?: string | undefined) => { - const boardsConfig = await this.boardsConfigDialog.open(query); - if (boardsConfig) { - this.boardsServiceClientImpl.boardsConfig = boardsConfig; - } - }, - }); - for (const command of [ EditorCommands.SPLIT_EDITOR_DOWN, EditorCommands.SPLIT_EDITOR_LEFT, 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 030f060b2..21291c589 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -309,6 +309,7 @@ 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 { OpenBoardsConfig } from './contributions/open-boards-config'; MonacoThemingService.register({ id: 'arduino-theme', @@ -707,6 +708,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, InoLanguage); Contribution.configure(bind, SelectedBoard); Contribution.configure(bind, CheckForUpdates); + Contribution.configure(bind, OpenBoardsConfig); // 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. diff --git a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts index 345ecaafb..12ad3ec19 100644 --- a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts +++ b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts @@ -17,7 +17,6 @@ import { import { BoardsConfig } from './boards-config'; import { naturalCompare } from '../../common/utils'; import { NotificationCenter } from '../notification-center'; -import { ArduinoCommands } from '../arduino-commands'; import { StorageWrapper } from '../storage-wrapper'; import { nls } from '@theia/core/lib/common'; import { Deferred } from '@theia/core/lib/common/promise-util'; @@ -171,7 +170,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution { .then(async (answer) => { if (answer === yes) { this.commandService.executeCommand( - ArduinoCommands.OPEN_BOARDS_DIALOG.id, + 'arduino-open-boards-dialog', selectedBoard.name ); } diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx index ed4814370..263882f8d 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -3,13 +3,14 @@ import * as ReactDOM from '@theia/core/shared/react-dom'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { Port } from '../../common/protocol'; -import { ArduinoCommands } from '../arduino-commands'; +import { BoardsConfig } from './boards-config'; import { BoardsServiceProvider, AvailableBoard, } from './boards-service-provider'; import { nls } from '@theia/core/lib/common'; import classNames from 'classnames'; +import { OpenBoardsConfig } from '../contributions/open-boards-config'; export interface BoardsDropDownListCoords { readonly top: number; @@ -155,7 +156,7 @@ export class BoardsToolBarItem extends React.Component< constructor(props: BoardsToolBarItem.Props) { super(props); - const { availableBoards } = props.boardsServiceClient; + const { availableBoards } = props.boardsServiceProvider; this.state = { availableBoards, coords: 'hidden', @@ -167,8 +168,8 @@ export class BoardsToolBarItem extends React.Component< } override componentDidMount(): void { - this.props.boardsServiceClient.onAvailableBoardsChanged((availableBoards) => - this.setState({ availableBoards }) + this.props.boardsServiceProvider.onAvailableBoardsChanged( + (availableBoards) => this.setState({ availableBoards }) ); } @@ -199,24 +200,23 @@ export class BoardsToolBarItem extends React.Component< override render(): React.ReactNode { const { coords, availableBoards } = this.state; - const selectedBoard = availableBoards.find(({ selected }) => selected); - - const boardLabel = - selectedBoard?.name || - nls.localize('arduino/board/selectBoard', 'Select Board'); - const selectedPortLabel = portLabel(selectedBoard?.port?.address); - - const isConnected = Boolean( - selectedBoard && AvailableBoard.hasPort(selectedBoard) - ); - const protocolIcon = isConnected - ? iconNameFromProtocol(selectedBoard?.port?.protocol || '') - : null; - const procolIconClassNames = classNames( - 'arduino-boards-toolbar-item--protocol', - 'fa', - protocolIcon - ); + const boardsConfig = this.props.boardsServiceProvider.boardsConfig; + const title = BoardsConfig.Config.toString(boardsConfig, { + default: nls.localize( + 'arduino/common/noBoardSelected', + 'No board selected' + ), + }); + const decorator = (() => { + const selectedBoard = availableBoards.find(({ selected }) => selected); + if (!selectedBoard || !selectedBoard.port) { + return 'fa fa-times notAttached'; + } + if (selectedBoard.state === AvailableBoard.State.guessed) { + return 'fa fa-exclamation-triangle guessed'; + } + return ''; + })(); return ( @@ -245,12 +245,12 @@ export class BoardsToolBarItem extends React.Component< ...board, onClick: () => { if (board.state === AvailableBoard.State.incomplete) { - this.props.boardsServiceClient.boardsConfig = { + this.props.boardsServiceProvider.boardsConfig = { selectedPort: board.port, }; this.openDialog(); } else { - this.props.boardsServiceClient.boardsConfig = { + this.props.boardsServiceProvider.boardsConfig = { selectedBoard: board, selectedPort: board.port, }; @@ -265,12 +265,15 @@ export class BoardsToolBarItem extends React.Component< } protected openDialog = () => { - this.props.commands.executeCommand(ArduinoCommands.OPEN_BOARDS_DIALOG.id); + this.props.commands.executeCommand( + OpenBoardsConfig.Commands.OPEN_DIALOG.id + ); + this.setState({ coords: 'hidden' }); }; } export namespace BoardsToolBarItem { export interface Props { - readonly boardsServiceClient: BoardsServiceProvider; + readonly boardsServiceProvider: BoardsServiceProvider; readonly commands: CommandRegistry; } diff --git a/arduino-ide-extension/src/browser/contributions/open-boards-config.ts b/arduino-ide-extension/src/browser/contributions/open-boards-config.ts new file mode 100644 index 000000000..3b8e1294e --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/open-boards-config.ts @@ -0,0 +1,32 @@ +import { CommandRegistry } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { BoardsConfigDialog } from '../boards/boards-config-dialog'; +import { BoardsServiceProvider } from '../boards/boards-service-provider'; +import { Contribution, Command } from './contribution'; + +@injectable() +export class OpenBoardsConfig extends Contribution { + @inject(BoardsServiceProvider) + private readonly boardsServiceProvider: BoardsServiceProvider; + + @inject(BoardsConfigDialog) + private readonly boardsConfigDialog: BoardsConfigDialog; + + override registerCommands(registry: CommandRegistry): void { + registry.registerCommand(OpenBoardsConfig.Commands.OPEN_DIALOG, { + execute: async (query?: string | undefined) => { + const boardsConfig = await this.boardsConfigDialog.open(query); + if (boardsConfig) { + this.boardsServiceProvider.boardsConfig = boardsConfig; + } + }, + }); + } +} +export namespace OpenBoardsConfig { + export namespace Commands { + export const OPEN_DIALOG: Command = { + id: 'arduino-open-boards-dialog', + }; + } +} From f5a1f9e98502c0299fce91522e3bfa15c676674a Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 14:57:06 +0200 Subject: [PATCH 14/22] tracking the sketch files has its own module. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 75 +------------------ .../browser/arduino-ide-frontend-module.ts | 2 + .../contributions/sketch-files-tracker.ts | 69 +++++++++++++++++ 3 files changed, 72 insertions(+), 74 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 4c137d599..8f2db85a7 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -5,10 +5,9 @@ import { postConstruct, } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; -import { SketchesService, Sketch } from '../common/protocol'; +import { SketchesService } from '../common/protocol'; import { - DisposableCollection, MAIN_MENU_BAR, MenuContribution, MenuModelRegistry, @@ -17,14 +16,11 @@ import { Dialog, FrontendApplication, FrontendApplicationContribution, - LocalStorageService, OnWillStopAction, - SaveableWidget, } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; -import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { TabBarToolbarContribution, TabBarToolbarRegistry, @@ -35,11 +31,7 @@ import { CommandRegistry, } from '@theia/core/lib/common/command'; import { MessageService } from '@theia/core/lib/common/message-service'; -import URI from '@theia/core/lib/common/uri'; import { EditorCommands, EditorMainMenu } from '@theia/editor/lib/browser'; -import { FileChangeType } from '@theia/filesystem/lib/browser'; -import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/navigator-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; @@ -50,7 +42,6 @@ import { import { ArduinoPreferences } from './arduino-preferences'; import { BoardsServiceProvider } from './boards/boards-service-provider'; import { BoardsToolBarItem } from './boards/boards-toolbar-item'; -import { OpenSketchFiles } from './contributions/open-sketch-files'; import { SaveAsSketch } from './contributions/save-as-sketch'; import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; @@ -73,9 +64,6 @@ export class ArduinoFrontendContribution @inject(BoardsServiceProvider) private readonly boardsServiceProvider: BoardsServiceProvider; - @inject(FileService) - private readonly fileService: FileService; - @inject(SketchesService) private readonly sketchService: SketchesService; @@ -88,17 +76,6 @@ export class ArduinoFrontendContribution @inject(SketchesServiceClientImpl) private readonly sketchServiceClient: SketchesServiceClientImpl; - @inject(FrontendApplicationStateService) - private readonly appStateService: FrontendApplicationStateService; - - @inject(LocalStorageService) - private readonly localStorageService: LocalStorageService; - - @inject(FileSystemFrontendContribution) - private readonly fileSystemFrontendContribution: FileSystemFrontendContribution; - - protected toDisposeOnStop = new DisposableCollection(); - @postConstruct() protected async init(): Promise { if (!window.navigator.onLine) { @@ -110,39 +87,6 @@ export class ArduinoFrontendContribution ) ); } - this.appStateService.reachedState('ready').then(async () => { - const sketch = await this.sketchServiceClient.currentSketch(); - if ( - CurrentSketch.isValid(sketch) && - !(await this.sketchService.isTemp(sketch)) - ) { - this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); - this.toDisposeOnStop.push( - this.fileService.onDidFilesChange(async (event) => { - for (const { type, resource } of event.changes) { - if ( - type === FileChangeType.ADDED && - resource.parent.toString() === sketch.uri - ) { - const reloadedSketch = await this.sketchService.loadSketch( - sketch.uri - ); - if (Sketch.isInSketch(resource, reloadedSketch)) { - this.commandRegistry.executeCommand( - OpenSketchFiles.Commands.ENSURE_OPENED.id, - resource.toString(), - true, - { - mode: 'open', - } - ); - } - } - } - }) - ); - } - }); } async onStart(app: FrontendApplication): Promise { @@ -171,23 +115,6 @@ export class ArduinoFrontendContribution // Removes the _Settings_ (cog) icon from the left sidebar app.shell.leftPanelHandler.removeBottomMenu('settings-menu'); - - this.fileSystemFrontendContribution.onDidChangeEditorFile( - ({ type, editor }) => { - if (type === FileChangeType.DELETED) { - const editorWidget = editor; - if (SaveableWidget.is(editorWidget)) { - editorWidget.closeWithoutSaving(); - } else { - editorWidget.close(); - } - } - } - ); - } - - onStop(): void { - this.toDisposeOnStop.dispose(); } registerToolbarItems(registry: TabBarToolbarRegistry): void { 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 21291c589..d038b2011 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -310,6 +310,7 @@ import { InoLanguage } from './contributions/ino-language'; import { SelectedBoard } from './contributions/selected-board'; import { CheckForUpdates } from './contributions/check-for-updates'; import { OpenBoardsConfig } from './contributions/open-boards-config'; +import { SketchFilesTracker } from './contributions/sketch-files-tracker'; MonacoThemingService.register({ id: 'arduino-theme', @@ -709,6 +710,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, SelectedBoard); Contribution.configure(bind, CheckForUpdates); Contribution.configure(bind, OpenBoardsConfig); + Contribution.configure(bind, SketchFilesTracker); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts b/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts new file mode 100644 index 000000000..ad7704adb --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/sketch-files-tracker.ts @@ -0,0 +1,69 @@ +import { SaveableWidget } from '@theia/core/lib/browser/saveable'; +import { DisposableCollection } from '@theia/core/lib/common/disposable'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FileSystemFrontendContribution } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; +import { FileChangeType } from '@theia/filesystem/lib/common/files'; +import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl'; +import { Sketch, SketchContribution, URI } from './contribution'; +import { OpenSketchFiles } from './open-sketch-files'; + +@injectable() +export class SketchFilesTracker extends SketchContribution { + @inject(FileSystemFrontendContribution) + private readonly fileSystemFrontendContribution: FileSystemFrontendContribution; + private readonly toDisposeOnStop = new DisposableCollection(); + + override onStart(): void { + this.fileSystemFrontendContribution.onDidChangeEditorFile( + ({ type, editor }) => { + if (type === FileChangeType.DELETED) { + const editorWidget = editor; + if (SaveableWidget.is(editorWidget)) { + editorWidget.closeWithoutSaving(); + } else { + editorWidget.close(); + } + } + } + ); + } + + override onReady(): void { + this.sketchServiceClient.currentSketch().then(async (sketch) => { + if ( + CurrentSketch.isValid(sketch) && + !(await this.sketchService.isTemp(sketch)) + ) { + this.toDisposeOnStop.push(this.fileService.watch(new URI(sketch.uri))); + this.toDisposeOnStop.push( + this.fileService.onDidFilesChange(async (event) => { + for (const { type, resource } of event.changes) { + if ( + type === FileChangeType.ADDED && + resource.parent.toString() === sketch.uri + ) { + const reloadedSketch = await this.sketchService.loadSketch( + sketch.uri + ); + if (Sketch.isInSketch(resource, reloadedSketch)) { + this.commandService.executeCommand( + OpenSketchFiles.Commands.ENSURE_OPENED.id, + resource.toString(), + true, + { + mode: 'open', + } + ); + } + } + } + }) + ); + } + }); + } + + onStop(): void { + this.toDisposeOnStop.dispose(); + } +} From 8f93cf4352bf52d59c09db00ce7ad8c3b5b080af Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 15:15:17 +0200 Subject: [PATCH 15/22] set zoom level after layout initialization. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 8f2db85a7..78a3f37a9 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -6,7 +6,6 @@ import { } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; import { SketchesService } from '../common/protocol'; - import { MAIN_MENU_BAR, MenuContribution, @@ -46,6 +45,7 @@ import { SaveAsSketch } from './contributions/save-as-sketch'; import { ArduinoMenus } from './menu/arduino-menus'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { ArduinoToolbar } from './toolbar/arduino-toolbar'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; export const SKIP_IDE_VERSION = 'skipIDEVersion'; @@ -76,6 +76,9 @@ export class ArduinoFrontendContribution @inject(SketchesServiceClientImpl) private readonly sketchServiceClient: SketchesServiceClientImpl; + @inject(FrontendApplicationStateService) + private readonly appStateService: FrontendApplicationStateService; + @postConstruct() protected async init(): Promise { if (!window.navigator.onLine) { @@ -102,17 +105,15 @@ export class ArduinoFrontendContribution } } }); - - // TODO: Verify this! If true IDE2 can start ~100ms faster. - // If the preferences is resolved, then the `ready` call will happen in the same tick - // and will do a `send_sync` request to the electron main to get the current window. - // Consider moving after app `ready`. - this.arduinoPreferences.ready.then(() => { - const webContents = remote.getCurrentWebContents(); - const zoomLevel = this.arduinoPreferences.get('arduino.window.zoomLevel'); - webContents.setZoomLevel(zoomLevel); - }); - + this.appStateService.reachedState('initialized_layout').then(() => + this.arduinoPreferences.ready.then(() => { + const webContents = remote.getCurrentWebContents(); + const zoomLevel = this.arduinoPreferences.get( + 'arduino.window.zoomLevel' + ); + webContents.setZoomLevel(zoomLevel); + }) + ); // Removes the _Settings_ (cog) icon from the left sidebar app.shell.leftPanelHandler.removeBottomMenu('settings-menu'); } From 8ac8211e451df0d8e04309b6fe19afa0631f937d Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 8 Jul 2022 15:30:59 +0200 Subject: [PATCH 16/22] first start installer has its own contribution. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 2 - .../browser/arduino-ide-frontend-module.ts | 2 + .../contributions/first-start-installer.ts | 45 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/first-start-installer.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 78a3f37a9..c3f7e6534 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -47,8 +47,6 @@ import { MonitorViewContribution } from './serial/monitor/monitor-view-contribut import { ArduinoToolbar } from './toolbar/arduino-toolbar'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; -export const SKIP_IDE_VERSION = 'skipIDEVersion'; - @injectable() export class ArduinoFrontendContribution implements 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 d038b2011..241020db7 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -311,6 +311,7 @@ import { SelectedBoard } from './contributions/selected-board'; import { CheckForUpdates } from './contributions/check-for-updates'; import { OpenBoardsConfig } from './contributions/open-boards-config'; import { SketchFilesTracker } from './contributions/sketch-files-tracker'; +import { FirstStartInstaller } from './contributions/first-start-installer'; MonacoThemingService.register({ id: 'arduino-theme', @@ -711,6 +712,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, CheckForUpdates); Contribution.configure(bind, OpenBoardsConfig); Contribution.configure(bind, SketchFilesTracker); + Contribution.configure(bind, FirstStartInstaller); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/first-start-installer.ts b/arduino-ide-extension/src/browser/contributions/first-start-installer.ts new file mode 100644 index 000000000..37e14a234 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/first-start-installer.ts @@ -0,0 +1,45 @@ +import { LocalStorageService } from '@theia/core/lib/browser/storage-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { BoardsService, LibraryService } from '../../common/protocol'; +import { Contribution } from './contribution'; + +const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; + +@injectable() +export class FirstStartInstaller extends Contribution { + @inject(LocalStorageService) + private readonly localStorageService: LocalStorageService; + + @inject(BoardsService) + private readonly boardsService: BoardsService; + + @inject(LibraryService) + private readonly libraryService: LibraryService; + + override onReady(): void { + this.localStorageService + .getData(INIT_LIBS_AND_PACKAGES) + .then(async (value) => { + const isFirstStartup = !value; + if (isFirstStartup) { + await this.localStorageService.setData(INIT_LIBS_AND_PACKAGES, true); + const avrPackage = await this.boardsService.getBoardPackage({ + id: 'arduino:avr', + }); + const builtInLibrary = ( + await this.libraryService.search({ + query: 'Arduino_BuiltIn', + }) + )[0]; + + !!avrPackage && + (await this.boardsService.install({ item: avrPackage })); + !!builtInLibrary && + (await this.libraryService.install({ + item: builtInLibrary, + installDependencies: true, + })); + } + }); + } +} From eb89d8594378c27348512c3815d54e274b2ed151 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 14 Jul 2022 14:44:57 +0200 Subject: [PATCH 17/22] Fix the incorrect error message from the CLI. Signed-off-by: Akos Kitta --- .../src/node/sketches-service-impl.ts | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/arduino-ide-extension/src/node/sketches-service-impl.ts b/arduino-ide-extension/src/node/sketches-service-impl.ts index 288641f16..f346f5261 100644 --- a/arduino-ide-extension/src/node/sketches-service-impl.ts +++ b/arduino-ide-extension/src/node/sketches-service-impl.ts @@ -10,7 +10,7 @@ import { promisify } from 'util'; import URI from '@theia/core/lib/common/uri'; import { FileUri } from '@theia/core/lib/node'; import { isWindows, isOSX } from '@theia/core/lib/common/os'; -import { ConfigService } from '../common/protocol/config-service'; +import { ConfigServiceImpl } from './config-service-impl'; import { SketchesService, Sketch, @@ -50,8 +50,8 @@ export class SketchesServiceImpl ? tempDir : maybeNormalizeDrive(fs.realpathSync.native(tempDir)); - @inject(ConfigService) - protected readonly configService: ConfigService; + @inject(ConfigServiceImpl) + protected readonly configService: ConfigServiceImpl; @inject(NotificationServiceServerImpl) protected readonly notificationService: NotificationServiceServerImpl; @@ -205,7 +205,14 @@ export class SketchesServiceImpl if (err) { reject( isNotFoundError(err) - ? SketchesError.NotFound(err.details, uri) + ? SketchesError.NotFound( + fixErrorMessage( + err, + requestSketchPath, + this.configService.cliConfiguration?.directories.user + ), + uri + ) : err ); return; @@ -583,6 +590,36 @@ interface SketchWithDetails extends Sketch { readonly mtimeMs: number; } +// https://github.com/arduino/arduino-cli/issues/1797 +function fixErrorMessage( + err: ServiceError, + sketchPath: string, + sketchbookPath: string | undefined +): string { + if (!sketchbookPath) { + return err.details; // No way to repair the error message. The current sketchbook path is not available. + } + // Original: `Can't open sketch: no valid sketch found in /Users/a.kitta/Documents/Arduino: missing /Users/a.kitta/Documents/Arduino/Arduino.ino` + // Fixed: `Can't open sketch: no valid sketch found in /Users/a.kitta/Documents/Arduino: missing $sketchPath` + const message = err.details; + const incorrectMessageSuffix = path.join(sketchbookPath, 'Arduino.ino'); + if ( + message.startsWith("Can't open sketch: no valid sketch found in") && + message.endsWith(`${incorrectMessageSuffix}`) + ) { + const sketchName = path.basename(sketchPath); + const correctMessagePrefix = message.substring( + 0, + message.length - incorrectMessageSuffix.length + ); + return `${correctMessagePrefix}${path.join( + sketchPath, + `${sketchName}.ino` + )}`; + } + return err.details; +} + function isNotFoundError(err: unknown): err is ServiceError { return ServiceError.is(err) && err.code === 5; // `NOT_FOUND` https://grpc.github.io/grpc/core/md_doc_statuscodes.html } From 95d28628a2871a4a7b547c3f7aea80b9cf8b8149 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 14 Jul 2022 14:56:33 +0200 Subject: [PATCH 18/22] no setTimeout Signed-off-by: Akos Kitta --- .../protocol/sketches-service-client-impl.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts index 58ac383cb..0787fe1ab 100644 --- a/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts +++ b/arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts @@ -17,6 +17,7 @@ import { } from '../../browser/utils/constants'; import * as monaco from '@theia/monaco-editor-core'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; const READ_ONLY_FILES = ['sketch.json']; const READ_ONLY_FILES_REMOTE = ['thingProperties.h', 'thingsProperties.h']; @@ -47,6 +48,9 @@ export class SketchesServiceClientImpl @inject(ConfigService) protected readonly configService: ConfigService; + @inject(FrontendApplicationStateService) + private readonly appStateService: FrontendApplicationStateService; + protected sketches = new Map(); // TODO: rename this + event to the `onBlabla` pattern protected sketchbookDidChangeEmitter = new Emitter<{ @@ -117,12 +121,14 @@ export class SketchesServiceClientImpl ); }); }); - setTimeout(async () => { - const currentSketch = await this.loadCurrentSketch(); - this._currentSketch = currentSketch; - this.currentSketchDidChangeEmitter.fire(this._currentSketch); - this.currentSketchLoaded.resolve(this._currentSketch); - }, 1_000); + this.appStateService + .reachedState('started_contributions') + .then(async () => { + const currentSketch = await this.loadCurrentSketch(); + this._currentSketch = currentSketch; + this.currentSketchDidChangeEmitter.fire(this._currentSketch); + this.currentSketchLoaded.resolve(this._currentSketch); + }); } onStop(): void { From fa7f2e8583d470ed826fd6a2375e9b5d6e5c8c80 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 15 Jul 2022 10:18:38 +0200 Subject: [PATCH 19/22] do not show error notification on sketch restore Signed-off-by: Akos Kitta --- .../browser/arduino-ide-frontend-module.ts | 2 - .../browser/boards/boards-toolbar-item.tsx | 45 ++++++++-------- .../browser/contributions/notifications.ts | 52 ------------------- .../contributions/open-sketch-files.ts | 35 ++----------- .../theia/workspace/workspace-service.ts | 23 +------- 5 files changed, 27 insertions(+), 130 deletions(-) delete mode 100644 arduino-ide-extension/src/browser/contributions/notifications.ts 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 241020db7..0f9155f07 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -304,7 +304,6 @@ import { StartupTasks } from './widgets/sketchbook/startup-task'; import { IndexesUpdateProgress } from './contributions/indexes-update-progress'; import { Daemon } from './contributions/daemon'; import { FirstStartupInstaller } from './contributions/first-startup-installer'; -import { Notifications } from './contributions/notifications'; import { OpenSketchFiles } from './contributions/open-sketch-files'; import { InoLanguage } from './contributions/ino-language'; import { SelectedBoard } from './contributions/selected-board'; @@ -705,7 +704,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, Daemon); Contribution.configure(bind, FirstStartupInstaller); Contribution.configure(bind, StartupTasks); - Contribution.configure(bind, Notifications); Contribution.configure(bind, OpenSketchFiles); Contribution.configure(bind, InoLanguage); Contribution.configure(bind, SelectedBoard); diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx index 263882f8d..3cbf1d96f 100644 --- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx +++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx @@ -3,14 +3,13 @@ import * as ReactDOM from '@theia/core/shared/react-dom'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; import { Port } from '../../common/protocol'; -import { BoardsConfig } from './boards-config'; +import { OpenBoardsConfig } from '../contributions/open-boards-config'; import { BoardsServiceProvider, AvailableBoard, } from './boards-service-provider'; import { nls } from '@theia/core/lib/common'; import classNames from 'classnames'; -import { OpenBoardsConfig } from '../contributions/open-boards-config'; export interface BoardsDropDownListCoords { readonly top: number; @@ -177,7 +176,7 @@ export class BoardsToolBarItem extends React.Component< this.toDispose.dispose(); } - protected readonly show = (event: React.MouseEvent) => { + protected readonly show = (event: React.MouseEvent): void => { const { currentTarget: element } = event; if (element instanceof HTMLElement) { if (this.state.coords === 'hidden') { @@ -200,23 +199,24 @@ export class BoardsToolBarItem extends React.Component< override render(): React.ReactNode { const { coords, availableBoards } = this.state; - const boardsConfig = this.props.boardsServiceProvider.boardsConfig; - const title = BoardsConfig.Config.toString(boardsConfig, { - default: nls.localize( - 'arduino/common/noBoardSelected', - 'No board selected' - ), - }); - const decorator = (() => { - const selectedBoard = availableBoards.find(({ selected }) => selected); - if (!selectedBoard || !selectedBoard.port) { - return 'fa fa-times notAttached'; - } - if (selectedBoard.state === AvailableBoard.State.guessed) { - return 'fa fa-exclamation-triangle guessed'; - } - return ''; - })(); + const selectedBoard = availableBoards.find(({ selected }) => selected); + + const boardLabel = + selectedBoard?.name || + nls.localize('arduino/board/selectBoard', 'Select Board'); + const selectedPortLabel = portLabel(selectedBoard?.port?.address); + + const isConnected = Boolean( + selectedBoard && AvailableBoard.hasPort(selectedBoard) + ); + const protocolIcon = isConnected + ? iconNameFromProtocol(selectedBoard?.port?.protocol || '') + : null; + const protocolIconClassNames = classNames( + 'arduino-boards-toolbar-item--protocol', + 'fa', + protocolIcon + ); return ( @@ -225,7 +225,7 @@ export class BoardsToolBarItem extends React.Component< title={selectedPortLabel} onClick={this.show} > - {protocolIcon &&
} + {protocolIcon &&
}
{ + protected openDialog = (): void => { this.props.commands.executeCommand( OpenBoardsConfig.Commands.OPEN_DIALOG.id ); - this.setState({ coords: 'hidden' }); }; } export namespace BoardsToolBarItem { diff --git a/arduino-ide-extension/src/browser/contributions/notifications.ts b/arduino-ide-extension/src/browser/contributions/notifications.ts deleted file mode 100644 index 70ed6c50e..000000000 --- a/arduino-ide-extension/src/browser/contributions/notifications.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { injectable } from '@theia/core/shared/inversify'; -import { Command, CommandRegistry, Contribution } from './contribution'; - -@injectable() -export class Notifications extends Contribution { - override registerCommands(registry: CommandRegistry): void { - registry.registerCommand(Notifications.Commands.NOTIFY, { - execute: (arg) => { - if (NotifyParams.is(arg)) { - switch (arg.type) { - case 'info': - return this.messageService.info(arg.message); - case 'warn': - return this.messageService.warn(arg.message); - case 'error': - return this.messageService.error(arg.message); - } - } - }, - }); - } -} -export namespace Notifications { - export namespace Commands { - export const NOTIFY: Command = { - id: 'arduino-notify', - }; - } -} -const TypeLiterals = ['info', 'warn', 'error'] as const; -export type Type = typeof TypeLiterals[number]; -interface NotifyParams { - readonly type: Type; - readonly message: string; -} -namespace NotifyParams { - export function is(arg: unknown): arg is NotifyParams { - if (typeof arg === 'object') { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const object = arg as any; - return ( - 'message' in object && - 'type' in object && - typeof object['message'] === 'string' && - typeof object['type'] === 'string' && - // eslint-disable-next-line @typescript-eslint/no-explicit-any - TypeLiterals.includes(object['type'] as any) - ); - } - return false; - } -} 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 4c6f0453b..8fbea21ae 100644 --- a/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts +++ b/arduino-ide-extension/src/browser/contributions/open-sketch-files.ts @@ -1,4 +1,3 @@ -import { ApplicationError } from '@theia/core/lib/common/application-error'; 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'; @@ -9,7 +8,6 @@ import { SketchContribution, URI, } from './contribution'; -import { Notifications } from './notifications'; import { SaveAsSketch } from './save-as-sketch'; @injectable() @@ -60,7 +58,7 @@ export class OpenSketchFiles extends SketchContribution { } } catch (err) { if (SketchesError.NotFound.is(err)) { - this.openFallbackSketch(err); + this.openFallbackSketch(); } else { console.error(err); const message = @@ -74,36 +72,9 @@ export class OpenSketchFiles extends SketchContribution { } } - private async openFallbackSketch( - err: ApplicationError< - number, - { - uri: string; - } - > - ): Promise { + private async openFallbackSketch(): Promise { const sketch = await this.sketchService.createNewSketch(); - this.workspaceService.open( - new URI(sketch.uri), - Object.assign( - { - preserveWindow: true, - }, - { - tasks: [ - { - command: Notifications.Commands.NOTIFY.id, - args: [ - { - type: 'error', - message: err.message, - }, - ], - }, - ], - } - ) - ); + this.workspaceService.open(new URI(sketch.uri), { preserveWindow: true }); } private async ensureOpened( diff --git a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts index 730c71201..38f15c9d1 100644 --- a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts +++ b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts @@ -17,7 +17,6 @@ import { ConfigService } from '../../../common/protocol/config-service'; import { SketchesService, Sketch, - SketchesError, } from '../../../common/protocol/sketches-service'; import { BoardsServiceProvider } from '../../boards/boards-service-provider'; import { BoardsConfig } from '../../boards/boards-config'; @@ -70,30 +69,12 @@ export class WorkspaceService extends TheiaWorkspaceService { ): Promise { const stat = await super.toFileStat(uri); if (!stat) { - return this.toFileStatWithNewSketchFallback(uri); + const newSketchUri = await this.sketchService.createNewSketch(); + return this.toFileStat(newSketchUri.uri); } return stat; } - private async toFileStatWithNewSketchFallback( - uri: string | URI | undefined - ): Promise { - if (!uri) { - return; - } - try { - await this.sketchService.loadSketch( - uri instanceof URI ? uri.toString() : uri - ); - } catch (err) { - if (SketchesError.NotFound.is(err)) { - this.messageService.error(err.message); - } - } - const newSketchUri = await this.sketchService.createNewSketch(); - return this.toFileStat(newSketchUri.uri); - } - // Was copied from the Theia implementation. // Unlike the default behavior, IDE2 does not check the existence of the workspace before open. protected override async doGetDefaultWorkspaceUri(): Promise< From e6c4d5784524c159246b872a3aaf1b562a78d2f3 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 18 Jul 2022 09:26:01 +0200 Subject: [PATCH 20/22] removed duplicate Signed-off-by: Akos Kitta --- .../browser/arduino-ide-frontend-module.ts | 2 - .../contributions/first-start-installer.ts | 45 ------------------- 2 files changed, 47 deletions(-) delete mode 100644 arduino-ide-extension/src/browser/contributions/first-start-installer.ts 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 0f9155f07..806de77b8 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -310,7 +310,6 @@ import { SelectedBoard } from './contributions/selected-board'; import { CheckForUpdates } from './contributions/check-for-updates'; import { OpenBoardsConfig } from './contributions/open-boards-config'; import { SketchFilesTracker } from './contributions/sketch-files-tracker'; -import { FirstStartInstaller } from './contributions/first-start-installer'; MonacoThemingService.register({ id: 'arduino-theme', @@ -710,7 +709,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, CheckForUpdates); Contribution.configure(bind, OpenBoardsConfig); Contribution.configure(bind, SketchFilesTracker); - Contribution.configure(bind, FirstStartInstaller); // 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. diff --git a/arduino-ide-extension/src/browser/contributions/first-start-installer.ts b/arduino-ide-extension/src/browser/contributions/first-start-installer.ts deleted file mode 100644 index 37e14a234..000000000 --- a/arduino-ide-extension/src/browser/contributions/first-start-installer.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { LocalStorageService } from '@theia/core/lib/browser/storage-service'; -import { inject, injectable } from '@theia/core/shared/inversify'; -import { BoardsService, LibraryService } from '../../common/protocol'; -import { Contribution } from './contribution'; - -const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; - -@injectable() -export class FirstStartInstaller extends Contribution { - @inject(LocalStorageService) - private readonly localStorageService: LocalStorageService; - - @inject(BoardsService) - private readonly boardsService: BoardsService; - - @inject(LibraryService) - private readonly libraryService: LibraryService; - - override onReady(): void { - this.localStorageService - .getData(INIT_LIBS_AND_PACKAGES) - .then(async (value) => { - const isFirstStartup = !value; - if (isFirstStartup) { - await this.localStorageService.setData(INIT_LIBS_AND_PACKAGES, true); - const avrPackage = await this.boardsService.getBoardPackage({ - id: 'arduino:avr', - }); - const builtInLibrary = ( - await this.libraryService.search({ - query: 'Arduino_BuiltIn', - }) - )[0]; - - !!avrPackage && - (await this.boardsService.install({ item: avrPackage })); - !!builtInLibrary && - (await this.libraryService.install({ - item: builtInLibrary, - installDependencies: true, - })); - } - }); - } -} From 824e55c436a606e238e0585a67ecf4de5b1a00be Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 18 Jul 2022 09:42:40 +0200 Subject: [PATCH 21/22] removed duplicate contribution Signed-off-by: Akos Kitta --- arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts | 1 - 1 file changed, 1 deletion(-) 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 806de77b8..59f2cef86 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -702,7 +702,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, IndexesUpdateProgress); Contribution.configure(bind, Daemon); Contribution.configure(bind, FirstStartupInstaller); - Contribution.configure(bind, StartupTasks); Contribution.configure(bind, OpenSketchFiles); Contribution.configure(bind, InoLanguage); Contribution.configure(bind, SelectedBoard); From 676b255ffefd0c48bd9bcca8f7421a3551e359ea Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Mon, 18 Jul 2022 10:36:54 +0200 Subject: [PATCH 22/22] merged in `realTimeDiagnostics` support. Signed-off-by: Akos Kitta --- .../src/browser/contributions/ino-language.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/arduino-ide-extension/src/browser/contributions/ino-language.ts b/arduino-ide-extension/src/browser/contributions/ino-language.ts index 610d1823c..653d59577 100644 --- a/arduino-ide-extension/src/browser/contributions/ino-language.ts +++ b/arduino-ide-extension/src/browser/contributions/ino-language.ts @@ -32,11 +32,14 @@ export class InoLanguage extends SketchContribution { private languageServerStartMutex = new Mutex(); override onReady(): void { - const start = ({ selectedBoard }: BoardsConfig.Config) => { + const start = ( + { selectedBoard }: BoardsConfig.Config, + forceStart = false + ) => { if (selectedBoard) { const { name, fqbn } = selectedBoard; if (fqbn) { - this.startLanguageServer(fqbn, name); + this.startLanguageServer(fqbn, name, forceStart); } } }; @@ -49,11 +52,12 @@ export class InoLanguage extends SketchContribution { ); this.preferences.onPreferenceChanged( ({ preferenceName, oldValue, newValue }) => { - if ( - preferenceName === 'arduino.language.log' && - newValue !== oldValue - ) { - start(this.boardsServiceProvider.boardsConfig); + if (oldValue !== newValue) { + switch (preferenceName) { + case 'arduino.language.log': + case 'arduino.language.realTimeDiagnostics': + start(this.boardsServiceProvider.boardsConfig, true); + } } } ); @@ -62,7 +66,8 @@ export class InoLanguage extends SketchContribution { private async startLanguageServer( fqbn: string, - name: string | undefined + name: string | undefined, + forceStart = false ): Promise { const port = await this.daemon.tryGetPort(); if (!port) { @@ -96,7 +101,7 @@ export class InoLanguage extends SketchContribution { } return; } - if (fqbn === this.languageServerFqbn) { + if (!forceStart && fqbn === this.languageServerFqbn) { // NOOP return; }