Skip to content

Commit

Permalink
IDE2 falls back to new sketch if opening failed.
Browse files Browse the repository at this point in the history
Closes #1089

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Jul 8, 2022
1 parent a36524e commit 855dcd1
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Sketch,
LibraryService,
ArduinoDaemon,
SketchesError,
} from '../common/protocol';
import { Mutex } from 'async-mutex';
import {
Expand All @@ -20,6 +21,7 @@ import {
MenuModelRegistry,
ILogger,
DisposableCollection,
ApplicationError,
} from '@theia/core';
import {
Dialog,
Expand Down Expand Up @@ -76,6 +78,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';

const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages';
export const SKIP_IDE_VERSION = 'skipIDEVersion';
Expand Down Expand Up @@ -155,6 +159,9 @@ export class ArduinoFrontendContribution
@inject(ArduinoDaemon)
private readonly daemon: ArduinoDaemon;

@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService;

protected invalidConfigPopup:
| Promise<void | 'No' | 'Yes' | undefined>
| undefined;
Expand Down Expand Up @@ -540,13 +547,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,10 @@ 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 { Notifications } from './contributions/notifications';

MonacoThemingService.register({
id: 'arduino-theme',
Expand Down Expand Up @@ -696,9 +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, StartupTasks);
Contribution.configure(bind, Notifications);

// 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<BoardsConfig.Config>();
protected readonly onAvailableBoardsChangedEmitter = new Emitter<
Expand Down Expand Up @@ -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);
Expand Down
52 changes: 52 additions & 0 deletions arduino-ide-extension/src/browser/contributions/notifications.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
40 changes: 28 additions & 12 deletions arduino-ide-extension/src/browser/contributions/sketchbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<void> {
this.update();
}

private update() {
this.sketchService.getSketches({}).then((container) => {
this.register(container);
this.mainMenuManager.update();
Expand All @@ -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
);
}
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -60,6 +65,35 @@ export class WorkspaceService extends TheiaWorkspaceService {
this.onCurrentWidgetChange({ newValue, oldValue: null });
}

protected override async toFileStat(
uri: string | URI | undefined
): Promise<FileStat | undefined> {
const stat = await super.toFileStat(uri);
if (!stat) {
return this.toFileStatWithNewSketchFallback(uri);
}
return stat;
}

private async toFileStatWithNewSketchFallback(
uri: string | URI | undefined
): Promise<FileStat | undefined> {
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<
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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());
Expand Down
Loading

0 comments on commit 855dcd1

Please sign in to comment.