mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-08 09:58:34 +00:00
- Updated `@theia/*` to `1.37.0`. - Fixed all `yarn audit` security vulnerabilities. - Updated to `electron@23.2.4`: - `contextIsolation` is `true`, - `nodeIntegration` is `false`, and the - `webpack` target is moved from `electron-renderer` to `web`. - Updated to `typescript@4.9.3`. - Updated the `eslint` plugins. - Added the new `Light High Contrast` theme to the IDE2. - High contrast themes use Theia APIs for style adjustments. - Support for ESM modules: `"moduleResolution": "node16"`. - Node.js >= 16.14 is required. - VISX langage packs were bumped to `1.70.0`. - Removed undesired editor context menu items. (Closes #1394) Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
231 lines
8.1 KiB
TypeScript
231 lines
8.1 KiB
TypeScript
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 { Later } from '../../common/nls';
|
|
import { Sketch, SketchesError } from '../../common/protocol';
|
|
import {
|
|
Command,
|
|
CommandRegistry,
|
|
SketchContribution,
|
|
URI,
|
|
} from './contribution';
|
|
import { SaveAsSketch } from './save-as-sketch';
|
|
import { promptMoveSketch } from './open-sketch';
|
|
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
|
import { Deferred, wait } from '@theia/core/lib/common/promise-util';
|
|
import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
|
|
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
|
|
@injectable()
|
|
export class OpenSketchFiles extends SketchContribution {
|
|
override registerCommands(registry: CommandRegistry): void {
|
|
registry.registerCommand(OpenSketchFiles.Commands.OPEN_SKETCH_FILES, {
|
|
execute: (uri: URI, focusMainSketchFile) =>
|
|
this.openSketchFiles(uri, focusMainSketchFile),
|
|
});
|
|
registry.registerCommand(OpenSketchFiles.Commands.ENSURE_OPENED, {
|
|
execute: (
|
|
uri: string,
|
|
forceOpen?: boolean,
|
|
options?: EditorOpenerOptions
|
|
) => {
|
|
this.ensureOpened(uri, forceOpen, options);
|
|
},
|
|
});
|
|
}
|
|
|
|
private async openSketchFiles(
|
|
uri: URI,
|
|
focusMainSketchFile = false
|
|
): Promise<void> {
|
|
try {
|
|
const sketch = await this.sketchesService.loadSketch(uri.toString());
|
|
const { mainFileUri, rootFolderFileUris } = sketch;
|
|
for (const uri of [mainFileUri, ...rootFolderFileUris]) {
|
|
await this.ensureOpened(uri);
|
|
}
|
|
if (focusMainSketchFile) {
|
|
await this.ensureOpened(mainFileUri, true, {
|
|
mode: 'activate',
|
|
preview: false,
|
|
counter: 0,
|
|
});
|
|
}
|
|
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, Later, yes).then((answer) => {
|
|
if (answer === yes) {
|
|
this.commandService.executeCommand(
|
|
SaveAsSketch.Commands.SAVE_AS_SKETCH.id,
|
|
{
|
|
execOnlyIfTemp: false,
|
|
openAfterMove: true,
|
|
wipeOriginal: false,
|
|
}
|
|
);
|
|
}
|
|
});
|
|
}
|
|
const { workspaceError } = this.workspaceService;
|
|
// This happens when the IDE2 has been started (from either a terminal or clicking on an `ino` file) with a /path/to/invalid/sketch. (#964)
|
|
if (SketchesError.InvalidName.is(workspaceError)) {
|
|
await this.promptMove(workspaceError);
|
|
}
|
|
} catch (err) {
|
|
// This happens when the user gracefully closed IDE2, all went well
|
|
// but the main sketch file was renamed outside of IDE2 and when the user restarts the IDE2
|
|
// the workspace path still exists, but the sketch path is not valid anymore. (#964)
|
|
if (SketchesError.InvalidName.is(err)) {
|
|
const movedSketch = await this.promptMove(err);
|
|
if (!movedSketch) {
|
|
// If user did not accept the move, or move was not possible, force reload with a fallback.
|
|
return this.openFallbackSketch();
|
|
}
|
|
}
|
|
|
|
if (SketchesError.NotFound.is(err)) {
|
|
return this.openFallbackSketch();
|
|
} else {
|
|
console.error(err);
|
|
const message =
|
|
err instanceof Error
|
|
? err.message
|
|
: typeof err === 'string'
|
|
? err
|
|
: String(err);
|
|
this.messageService.error(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async promptMove(
|
|
err: ApplicationError<
|
|
number,
|
|
{
|
|
invalidMainSketchUri: string;
|
|
}
|
|
>
|
|
): Promise<Sketch | undefined> {
|
|
const { invalidMainSketchUri } = err.data;
|
|
requestAnimationFrame(() => this.messageService.error(err.message));
|
|
await wait(250); // let IDE2 open the editor and toast the error message, then open the modal dialog
|
|
const movedSketch = await promptMoveSketch(invalidMainSketchUri, {
|
|
fileService: this.fileService,
|
|
sketchesService: this.sketchesService,
|
|
labelProvider: this.labelProvider,
|
|
dialogService: this.dialogService,
|
|
});
|
|
if (movedSketch) {
|
|
this.workspaceService.open(new URI(movedSketch.uri), {
|
|
preserveWindow: true,
|
|
});
|
|
return movedSketch;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
private async openFallbackSketch(): Promise<void> {
|
|
const sketch = await this.sketchesService.createNewSketch();
|
|
this.workspaceService.open(new URI(sketch.uri), { preserveWindow: true });
|
|
}
|
|
|
|
private async ensureOpened(
|
|
uri: string,
|
|
forceOpen = false,
|
|
options?: EditorOpenerOptions
|
|
): Promise<EditorWidget | undefined> {
|
|
const widget = this.editorManager.all.find(
|
|
(widget) => widget.editor.uri.toString() === uri
|
|
);
|
|
if (widget && !forceOpen) {
|
|
return widget;
|
|
}
|
|
|
|
const disposables = new DisposableCollection();
|
|
const deferred = new Deferred<EditorWidget>();
|
|
// An editor can be in two primary states:
|
|
// - The editor is not yet opened. The `widget` is `undefined`. With `editorManager#open`, Theia will create an editor and fire an `editorManager#onCreated` event.
|
|
// - The editor is opened. Can be active, current, or open.
|
|
// - If the editor has the focus (the cursor blinks in the editor): it's the active editor.
|
|
// - If the editor does not have the focus (the focus is on a different widget or the context menu is opened in the editor): it's the current editor.
|
|
// - If the editor is not the top editor in the main area, it's opened.
|
|
if (!widget) {
|
|
// If the widget is `undefined`, IDE2 expects one `onCreate` event. Subscribe to the `onCreated` event
|
|
// and resolve the promise with the editor only when the new editor's visibility changes.
|
|
disposables.push(
|
|
this.editorManager.onCreated((editor) => {
|
|
if (editor.editor.uri.toString() === uri) {
|
|
if (editor.isAttached && editor.isVisible) {
|
|
deferred.resolve(editor);
|
|
} else {
|
|
disposables.push(
|
|
editor.onDidChangeVisibility((visible) => {
|
|
if (visible) {
|
|
// wait an animation frame. although the visible and attached props are true the editor is not there.
|
|
// let the browser render the widget
|
|
setTimeout(
|
|
() =>
|
|
requestAnimationFrame(() => deferred.resolve(editor)),
|
|
0
|
|
);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
this.editorManager
|
|
.open(
|
|
new URI(uri),
|
|
options ?? {
|
|
mode: 'reveal',
|
|
preview: false,
|
|
counter: 0,
|
|
}
|
|
)
|
|
.then((editorWidget) => {
|
|
// If the widget was defined, it was already opened.
|
|
// The editor is expected to be attached to the shell and visible in the UI.
|
|
// The deferred promise does not have to wait for the `editorManager#onCreated` event.
|
|
// It can resolve earlier.
|
|
if (widget) {
|
|
deferred.resolve(editorWidget);
|
|
}
|
|
});
|
|
|
|
const timeout = 5_000; // number of ms IDE2 waits for the editor to show up in the UI
|
|
const result: EditorWidget | undefined | 'timeout' = await Promise.race([
|
|
deferred.promise,
|
|
wait(timeout).then(() => {
|
|
disposables.dispose();
|
|
return 'timeout' as const;
|
|
}),
|
|
]);
|
|
if (result === 'timeout') {
|
|
console.warn(
|
|
`Timeout after ${timeout} millis. The editor has not shown up in time. URI: ${uri}`
|
|
);
|
|
return undefined;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
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',
|
|
};
|
|
}
|
|
}
|