Skip to content

Commit e755a1c

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Aligned the electron app to the latest Theia APIs.
Signed-off-by: Akos Kitta <[email protected]>
1 parent def93ea commit e755a1c

File tree

11 files changed

+123
-329
lines changed

11 files changed

+123
-329
lines changed

arduino-ide-extension/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@
113113
},
114114
{
115115
"frontend": "lib/browser/boards/quick-open/boards-quick-open-module"
116+
},
117+
{
118+
"electronMain": "lib/electron-main/arduino-electron-main-module"
116119
}
117120
]
118121
}

arduino-ide-extension/scripts/download-examples.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// @ts-check
22

3+
// The version to use.
4+
const version = '1.9.0';
5+
36
(async () => {
47

58
const os = require('os');
@@ -10,14 +13,21 @@
1013
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
1114
if (shell.mkdir('-p', repository).code !== 0) {
1215
shell.exit(1);
16+
process.exit(1);
17+
}
18+
19+
if (shell.exec(`git clone https://github.com/arduino/arduino-examples.git ${repository}`).code !== 0) {
20+
shell.exit(1);
21+
process.exit(1);
1322
}
1423

15-
if (shell.exec(`git clone https://github.com/arduino/arduino.git --depth 1 ${repository}`).code !== 0) {
24+
if (shell.exec(`git -C ${repository} checkout tags/${version} -b ${version}`).code !== 0) {
1625
shell.exit(1);
26+
process.exit(1);
1727
}
1828

1929
const destination = path.join(__dirname, '..', 'Examples');
2030
shell.mkdir('-p', destination);
21-
shell.cp('-fR', path.join(repository, 'build', 'shared', 'examples', '*'), destination);
31+
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);
2232

2333
})();

arduino-ide-extension/src/browser/contributions/contribution.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { inject, injectable, interfaces } from 'inversify';
22
import URI from '@theia/core/lib/common/uri';
33
import { ILogger } from '@theia/core/lib/common/logger';
4-
import { FileSystem } from '@theia/filesystem/lib/common';
4+
import { FileService } from '@theia/filesystem/lib/browser/file-service';
55
import { MaybePromise } from '@theia/core/lib/common/types';
66
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
77
import { MessageService } from '@theia/core/lib/common/message-service';
@@ -59,8 +59,8 @@ export abstract class Contribution implements CommandContribution, MenuContribut
5959
@injectable()
6060
export abstract class SketchContribution extends Contribution {
6161

62-
@inject(FileSystem)
63-
protected readonly fileSystem: FileSystem;
62+
@inject(FileService)
63+
protected readonly fileService: FileService;
6464

6565
@inject(FileSystemExt)
6666
protected readonly fileSystemExt: FileSystemExt;

arduino-ide-extension/src/browser/contributions/open-sketch-external.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { injectable } from 'inversify';
22
import { remote } from 'electron';
3+
import URI from '@theia/core/lib/common/uri';
34
import { ArduinoMenus } from '../menu/arduino-menus';
45
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry, KeybindingRegistry } from './contribution';
56

@@ -30,9 +31,9 @@ export class OpenSketchExternal extends SketchContribution {
3031
protected async openExternal(): Promise<void> {
3132
const uri = await this.sketchServiceClient.currentSketchFile();
3233
if (uri) {
33-
const exists = this.fileSystem.exists(uri);
34+
const exists = this.fileService.exists(new URI(uri));
3435
if (exists) {
35-
const fsPath = await this.fileSystem.getFsPath(uri);
36+
const fsPath = await this.fileService.fsPath(new URI(uri));
3637
if (fsPath) {
3738
remote.shell.showItemInFolder(fsPath);
3839
}

arduino-ide-extension/src/browser/contributions/open-sketch.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export class OpenSketch extends SketchContribution {
115115

116116
protected async selectSketch(): Promise<Sketch | undefined> {
117117
const config = await this.configService.getConfiguration();
118-
const defaultPath = await this.fileSystem.getFsPath(config.sketchDirUri);
118+
const defaultPath = await this.fileService.fsPath(new URI(config.sketchDirUri));
119119
const { filePaths } = await remote.dialog.showOpenDialog({
120120
defaultPath,
121121
properties: ['createDirectory', 'openFile'],
@@ -149,7 +149,7 @@ export class OpenSketch extends SketchContribution {
149149
});
150150
if (response === 1) { // OK
151151
const newSketchUri = new URI(sketchFileUri).parent.resolve(name);
152-
const exists = await this.fileSystem.exists(newSketchUri.toString());
152+
const exists = await this.fileService.exists(newSketchUri);
153153
if (exists) {
154154
await remote.dialog.showMessageBox({
155155
type: 'error',
@@ -158,8 +158,8 @@ export class OpenSketch extends SketchContribution {
158158
});
159159
return undefined;
160160
}
161-
await this.fileSystem.createFolder(newSketchUri.toString());
162-
await this.fileSystem.move(sketchFileUri, newSketchUri.resolve(nameWithExt).toString());
161+
await this.fileService.createFolder(newSketchUri);
162+
await this.fileService.move(new URI(sketchFileUri), new URI(newSketchUri.resolve(nameWithExt).toString()));
163163
return this.sketchService.getSketchFolder(newSketchUri.toString());
164164
}
165165
}

arduino-ide-extension/src/browser/contributions/save-as-sketch.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ export class SaveAsSketch extends SketchContribution {
4545
// If target does not exist, propose a `directories.user`/${sketch.name} path
4646
// If target exists, propose `directories.user`/${sketch.name}_copy_${yyyymmddHHMMss}
4747
const sketchDirUri = new URI((await this.configService.getConfiguration()).sketchDirUri);
48-
const exists = await this.fileSystem.exists(sketchDirUri.resolve(sketch.name).toString());
48+
const exists = await this.fileService.exists(sketchDirUri.resolve(sketch.name));
4949
const defaultUri = exists
5050
? sketchDirUri.resolve(sketchDirUri.resolve(`${sketch.name}_copy_${dateFormat(new Date(), 'yyyymmddHHMMss')}`).toString())
5151
: sketchDirUri.resolve(sketch.name);
52-
const defaultPath = await this.fileSystem.getFsPath(defaultUri.toString())!;
52+
const defaultPath = await this.fileService.fsPath(defaultUri);
5353
const { filePath, canceled } = await remote.dialog.showSaveDialog({ title: 'Save sketch folder as...', defaultPath });
5454
if (!filePath || canceled) {
5555
return false;
@@ -61,7 +61,7 @@ export class SaveAsSketch extends SketchContribution {
6161
const workspaceUri = await this.sketchService.copy(sketch, { destinationUri });
6262
if (workspaceUri && openAfterMove) {
6363
if (wipeOriginal) {
64-
await this.fileSystem.delete(sketch.uri);
64+
await this.fileService.delete(new URI(sketch.uri));
6565
}
6666
this.workspaceService.open(new URI(workspaceUri), { preserveWindow: true });
6767
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ContainerModule } from 'inversify';
2+
import { ElectronMainApplication as TheiaElectronMainApplication } from '@theia/core/lib/electron-main/electron-main-application';
3+
import { ElectronMainApplication } from './theia/electron-main-application';
4+
5+
export default new ContainerModule((bind, unbind, isBound, rebind) => {
6+
bind(ElectronMainApplication).toSelf().inSingletonScope();
7+
rebind(TheiaElectronMainApplication).toService(ElectronMainApplication);
8+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { injectable } from 'inversify';
2+
import { app } from 'electron';
3+
import { fork } from 'child_process';
4+
import { AddressInfo } from 'net';
5+
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
6+
import { ElectronMainApplication as TheiaElectronMainApplication, TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/electron-main-application';
7+
8+
@injectable()
9+
export class ElectronMainApplication extends TheiaElectronMainApplication {
10+
11+
protected async getDefaultBrowserWindowOptions(): Promise<TheiaBrowserWindowOptions> {
12+
const options = await super.getDefaultBrowserWindowOptions();
13+
return {
14+
...options,
15+
// Set and use a custom minimum window size: https://github.com/arduino/arduino-pro-ide/issues/337#issuecomment-687017281
16+
minWidth: 900,
17+
minHeight: 800
18+
};
19+
}
20+
21+
protected async startBackend(): Promise<number> {
22+
// Check if we should run everything as one process.
23+
const noBackendFork = process.argv.indexOf('--no-cluster') !== -1;
24+
// We cannot use the `process.cwd()` as the application project path (the location of the `package.json` in other words)
25+
// in a bundled electron application because it depends on the way we start it. For instance, on OS X, these are a differences:
26+
// https://github.com/eclipse-theia/theia/issues/3297#issuecomment-439172274
27+
process.env.THEIA_APP_PROJECT_PATH = this.globals.THEIA_APP_PROJECT_PATH;
28+
// Set the electron version for both the dev and the production mode. (https://github.com/eclipse-theia/theia/issues/3254)
29+
// Otherwise, the forked backend processes will not know that they're serving the electron frontend.
30+
process.env.THEIA_ELECTRON_VERSION = process.versions.electron;
31+
if (noBackendFork) {
32+
process.env[ElectronSecurityToken] = JSON.stringify(this.electronSecurityToken);
33+
// The backend server main file is supposed to export a promise resolving with the port used by the http(s) server.
34+
const address: AddressInfo = await require(this.globals.THEIA_BACKEND_MAIN_PATH);
35+
return address.port;
36+
} else {
37+
let args = this.processArgv.getProcessArgvWithoutBin();
38+
// https://github.com/eclipse-theia/theia/issues/8227
39+
if (process.platform === 'darwin') {
40+
// https://github.com/electron/electron/issues/3657
41+
// https://stackoverflow.com/questions/10242115/os-x-strange-psn-command-line-parameter-when-launched-from-finder#comment102377986_10242200
42+
// macOS appends an extra `-psn_0_someNumber` arg if a file is opened from Finder after downloading from the Internet.
43+
// "AppName" is an app downloaded from the Internet. Are you sure you want to open it?
44+
args = args.filter(arg => !arg.startsWith('-psn'));
45+
}
46+
const backendProcess = fork(
47+
this.globals.THEIA_BACKEND_MAIN_PATH,
48+
args,
49+
await this.getForkOptions(),
50+
);
51+
return new Promise((resolve, reject) => {
52+
// The backend server main file is also supposed to send the resolved http(s) server port via IPC.
53+
backendProcess.on('message', (address: AddressInfo) => {
54+
resolve(address.port);
55+
});
56+
backendProcess.on('error', error => {
57+
reject(error);
58+
});
59+
app.on('quit', () => {
60+
try {
61+
// If we forked the process for the clusters, we need to manually terminate it.
62+
// See: https://github.com/eclipse-theia/theia/issues/835
63+
process.kill(backendProcess.pid);
64+
} catch (e) {
65+
if (e.code === 'ESRCH') {
66+
console.log('Could not terminate the backend process. It was not running.');
67+
return;
68+
}
69+
throw e;
70+
}
71+
});
72+
});
73+
}
74+
}
75+
76+
}

arduino-ide-extension/src/node/sketches-service-impl.ts

+10-13
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { ncp } from 'ncp';
66
import { Stats } from 'fs';
77
import * as fs from './fs-extra';
88
import URI from '@theia/core/lib/common/uri';
9+
import { FileUri } from '@theia/core/lib/node';
910
import { isWindows } from '@theia/core/lib/common/os';
10-
import { FileUri, BackendApplicationContribution } from '@theia/core/lib/node';
1111
import { ConfigService } from '../common/protocol/config-service';
1212
import { SketchesService, Sketch } from '../common/protocol/sketches-service';
1313
import { firstToLowerCase } from '../common/utils';
@@ -23,17 +23,11 @@ const prefix = '.arduinoProIDE-unsaved';
2323

2424
// TODO: `fs`: use async API
2525
@injectable()
26-
export class SketchesServiceImpl implements SketchesService, BackendApplicationContribution {
27-
28-
protected readonly temp = temp.track();
26+
export class SketchesServiceImpl implements SketchesService {
2927

3028
@inject(ConfigService)
3129
protected readonly configService: ConfigService;
3230

33-
onStop(): void {
34-
this.temp.cleanupSync();
35-
}
36-
3731
async getSketches(uri?: string): Promise<Sketch[]> {
3832
const sketches: Array<Sketch & { mtimeMs: number }> = [];
3933
let fsPath: undefined | string;
@@ -210,7 +204,7 @@ export class SketchesServiceImpl implements SketchesService, BackendApplicationC
210204
async cloneExample(uri: string): Promise<Sketch> {
211205
const sketch = await this.loadSketch(uri);
212206
const parentPath = await new Promise<string>((resolve, reject) => {
213-
this.temp.mkdir({ prefix }, (err, dirPath) => {
207+
temp.mkdir({ prefix }, (err, dirPath) => {
214208
if (err) {
215209
reject(err);
216210
return;
@@ -277,7 +271,7 @@ export class SketchesServiceImpl implements SketchesService, BackendApplicationC
277271
const monthNames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
278272
const today = new Date();
279273
const parentPath = await new Promise<string>((resolve, reject) => {
280-
this.temp.mkdir({ prefix }, (err, dirPath) => {
274+
temp.mkdir({ prefix }, (err, dirPath) => {
281275
if (err) {
282276
reject(err);
283277
return;
@@ -373,6 +367,11 @@ void loop() {
373367
if (!exists) {
374368
throw new Error(`Sketch does not exist: ${sketch}`);
375369
}
370+
// Nothing to do when source and destination are the same.
371+
if (sketch.uri === destinationUri) {
372+
await this.loadSketch(sketch.uri); // Sanity check.
373+
return sketch.uri;
374+
}
376375
const destination = FileUri.fsPath(destinationUri);
377376
await new Promise<void>((resolve, reject) => {
378377
ncp.ncp(source, destination, async error => {
@@ -384,9 +383,7 @@ void loop() {
384383
try {
385384
const oldPath = path.join(destination, new URI(sketch.mainFileUri).path.base);
386385
const newPath = path.join(destination, `${newName}.ino`);
387-
if (oldPath !== newPath) {
388-
await fs.rename(oldPath, newPath);
389-
}
386+
await fs.rename(oldPath, newPath);
390387
await this.loadSketch(destinationUri); // Sanity check.
391388
resolve();
392389
} catch (e) {

0 commit comments

Comments
 (0)