Skip to content

Commit 18dc12a

Browse files
author
Akos Kitta
committed
fix: can open ide from sketch folder path
Signed-off-by: Akos Kitta <[email protected]>
1 parent e78db75 commit 18dc12a

File tree

1 file changed

+79
-32
lines changed

1 file changed

+79
-32
lines changed

arduino-ide-extension/src/electron-main/theia/electron-main-application.ts

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
} from '@theia/core/electron-shared/electron';
99
import { fork } from 'child_process';
1010
import { AddressInfo } from 'net';
11-
import { join, dirname } from 'path';
12-
import * as fs from 'fs-extra';
11+
import { join, dirname, isAbsolute, resolve } from 'path';
12+
import { promises as fs, Stats } from 'fs';
1313
import { MaybePromise } from '@theia/core/lib/common/types';
1414
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
1515
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
@@ -69,8 +69,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
6969
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
7070
// See: https://github.com/electron-userland/electron-builder/issues/2468
7171
// Regression in Theia: https://github.com/eclipse-theia/theia/issues/8701
72+
console.log(`${config.applicationName} ${app.getVersion()}`);
7273
app.on('ready', () => app.setName(config.applicationName));
73-
this.attachFileAssociations();
74+
const cwd = process.cwd();
75+
this.attachFileAssociations(cwd);
7476
this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native';
7577
this._config = config;
7678
this.hookApplicationEvents();
@@ -84,7 +86,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
8486
return this.launch({
8587
secondInstance: false,
8688
argv: this.processArgv.getProcessArgvWithoutBin(process.argv),
87-
cwd: process.cwd(),
89+
cwd
8890
});
8991
}
9092

@@ -119,7 +121,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
119121
let traceFile: string | undefined;
120122
if (appPath) {
121123
const tracesPath = join(appPath, 'traces');
122-
await fs.promises.mkdir(tracesPath, { recursive: true });
124+
await fs.mkdir(tracesPath, { recursive: true });
123125
traceFile = join(tracesPath, `trace-${new Date().toISOString()}.trace`);
124126
}
125127
console.log('>>> Content tracing has started...');
@@ -135,14 +137,16 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
135137
})();
136138
}
137139

138-
private attachFileAssociations(): void {
140+
private attachFileAssociations(cwd: string): void {
139141
// OSX: register open-file event
140142
if (os.isOSX) {
141-
app.on('open-file', async (event, uri) => {
143+
app.on('open-file', async (event, path) => {
142144
event.preventDefault();
143-
if (uri.endsWith('.ino') && (await fs.pathExists(uri))) {
145+
const resolvedPath = await this.resolvePath(path, cwd);
146+
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
147+
if (sketchFolderPath) {
144148
this.openFilePromise.reject(new InterruptWorkspaceRestoreError());
145-
await this.openSketch(dirname(uri));
149+
await this.openSketch(sketchFolderPath);
146150
}
147151
});
148152
setTimeout(() => this.openFilePromise.resolve(), 500);
@@ -151,8 +155,49 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
151155
}
152156
}
153157

154-
private async isValidSketchPath(uri: string): Promise<boolean | undefined> {
155-
return typeof uri === 'string' && (await fs.pathExists(uri));
158+
/**
159+
* The `path` argument is valid, if accessible and either pointing to a `.ino` file,
160+
* or it's a directory, and one of the files in the directory is an `.ino` file.
161+
* The resolved filesystem path is pointing to the sketch folder to open in IDE2.
162+
* If `undefined`, `path` was pointing to neither an accessible sketch file nor a sketch folder.
163+
*
164+
* The sketch folder name and sketch file name can be different. This method is not that strict.
165+
* The `path` must be an absolute path.
166+
*/
167+
private async isValidSketchPath(path: string): Promise<string | undefined> {
168+
let stats: Stats | undefined = undefined;
169+
try {
170+
stats = await fs.stat(path);
171+
} catch (err) {
172+
if ('code' in err && err.code === 'ENOENT') {
173+
return undefined;
174+
}
175+
throw err;
176+
}
177+
if (!stats) {
178+
return undefined;
179+
}
180+
if (stats.isFile() && path.endsWith('.ino')) {
181+
return dirname(path);
182+
}
183+
try {
184+
const entries = await fs.readdir(path, { withFileTypes: true });
185+
if (
186+
entries.some((entry) => entry.isFile() && entry.name.endsWith('.ino'))
187+
) {
188+
return path;
189+
}
190+
} catch (err) {
191+
throw err;
192+
}
193+
return undefined;
194+
}
195+
196+
private async resolvePath(path: string, cwd: string): Promise<string> {
197+
if (isAbsolute(path)) {
198+
return path;
199+
}
200+
return fs.realpath(resolve(cwd, path));
156201
}
157202

158203
protected override async launch(
@@ -171,7 +216,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
171216
throw err;
172217
}
173218

174-
if (!os.isOSX && (await this.launchFromArgs(params))) {
219+
if (await this.launchFromArgs(params)) {
175220
// Application has received a file in its arguments and will skip the default application launch
176221
return;
177222
}
@@ -185,7 +230,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
185230
`Restoring workspace roots: ${workspaces.map(({ file }) => file)}`
186231
);
187232
for (const workspace of workspaces) {
188-
if (await this.isValidSketchPath(workspace.file)) {
233+
const resolvedPath = await this.resolvePath(workspace.file, params.cwd);
234+
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
235+
if (sketchFolderPath) {
236+
workspace.file = sketchFolderPath;
189237
if (this.isTempSketch.is(workspace.file)) {
190238
console.info(
191239
`Skipped opening sketch. The sketch was detected as temporary. Workspace path: ${workspace.file}.`
@@ -208,38 +256,37 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
208256
): Promise<boolean> {
209257
// Copy to prevent manipulation of original array
210258
const argCopy = [...params.argv];
211-
let uri: string | undefined;
212-
for (const possibleUri of argCopy) {
213-
if (
214-
possibleUri.endsWith('.ino') &&
215-
(await this.isValidSketchPath(possibleUri))
216-
) {
217-
uri = possibleUri;
259+
let path: string | undefined;
260+
for (const maybePath of argCopy) {
261+
const resolvedPath = await this.resolvePath(maybePath, params.cwd);
262+
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
263+
if (sketchFolderPath) {
264+
path = sketchFolderPath;
218265
break;
219266
}
220267
}
221-
if (uri) {
222-
await this.openSketch(dirname(uri));
268+
if (path) {
269+
await this.openSketch(path);
223270
return true;
224271
}
225272
return false;
226273
}
227274

228275
private async openSketch(
229-
workspace: WorkspaceOptions | string
276+
workspaceOrPath: WorkspaceOptions | string
230277
): Promise<BrowserWindow> {
231278
const options = await this.getLastWindowOptions();
232279
let file: string;
233-
if (typeof workspace === 'object') {
234-
options.x = workspace.x;
235-
options.y = workspace.y;
236-
options.width = workspace.width;
237-
options.height = workspace.height;
238-
options.isMaximized = workspace.isMaximized;
239-
options.isFullScreen = workspace.isFullScreen;
240-
file = workspace.file;
280+
if (typeof workspaceOrPath === 'object') {
281+
options.x = workspaceOrPath.x;
282+
options.y = workspaceOrPath.y;
283+
options.width = workspaceOrPath.width;
284+
options.height = workspaceOrPath.height;
285+
options.isMaximized = workspaceOrPath.isMaximized;
286+
options.isFullScreen = workspaceOrPath.isFullScreen;
287+
file = workspaceOrPath.file;
241288
} else {
242-
file = workspace;
289+
file = workspaceOrPath;
243290
}
244291
const [uri, electronWindow] = await Promise.all([
245292
this.createWindowUri(),

0 commit comments

Comments
 (0)