Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bc78f4e

Browse files
author
Akos Kitta
committed
fix: workaround for arduino/arduino-cli#1968
Do not try to parse the original `NotFound` error message, but look for a sketch somewhere in the requested path. Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent b6c4777 commit bc78f4e

File tree

3 files changed

+65
-93
lines changed

3 files changed

+65
-93
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export class OpenSketchFiles extends SketchContribution {
102102
): Promise<Sketch | undefined> {
103103
const { invalidMainSketchUri } = err.data;
104104
requestAnimationFrame(() => this.messageService.error(err.message));
105-
await wait(10); // let IDE2 toast the error message.
105+
await wait(250); // let IDE2 open the editor and toast the error message, then open the modal dialog
106106
const movedSketch = await promptMoveSketch(invalidMainSketchUri, {
107107
fileService: this.fileService,
108108
sketchService: this.sketchService,

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

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { fork } from 'child_process';
1010
import { AddressInfo } from 'net';
1111
import { join, isAbsolute, resolve } from 'path';
12-
import { promises as fs,Stats } from 'fs';
12+
import { promises as fs } 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';
@@ -29,6 +29,7 @@ import {
2929
} from '../../common/ipc-communication';
3030
import isValidPath = require('is-valid-path');
3131
import { ErrnoException } from '../../node/utils/errors';
32+
import { isAccessibleSketchPath } from '../../node/sketches-service-impl';
3233

3334
app.commandLine.appendSwitch('disable-http-cache');
3435

@@ -146,7 +147,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
146147
event.preventDefault();
147148
const resolvedPath = await this.resolvePath(path, cwd);
148149
if (resolvedPath) {
149-
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
150+
const sketchFolderPath = await isAccessibleSketchPath(
151+
resolvedPath,
152+
true
153+
);
150154
if (sketchFolderPath) {
151155
this.openFilePromise.reject(new InterruptWorkspaceRestoreError());
152156
await this.openSketch(sketchFolderPath);
@@ -159,49 +163,6 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
159163
}
160164
}
161165

162-
/**
163-
* The `path` argument is valid, if accessible and either pointing to a `.ino` file,
164-
* or it's a directory, and one of the files in the directory is an `.ino` file.
165-
*
166-
* If `undefined`, `path` was pointing to neither an accessible sketch file nor a sketch folder.
167-
*
168-
* The sketch folder name and sketch file name can be different. This method is not sketch folder name compliant.
169-
* The `path` must be an absolute, resolved path.
170-
*/
171-
private async isValidSketchPath(path: string): Promise<string | undefined> {
172-
let stats: Stats | undefined = undefined;
173-
try {
174-
stats = await fs.stat(path);
175-
} catch (err) {
176-
if (ErrnoException.isENOENT(err)) {
177-
return undefined;
178-
}
179-
throw err;
180-
}
181-
if (!stats) {
182-
return undefined;
183-
}
184-
if (stats.isFile()) {
185-
return path.endsWith('.ino') ? path : undefined;
186-
}
187-
try {
188-
const entries = await fs.readdir(path, { withFileTypes: true });
189-
const sketchFilename = entries
190-
.filter((entry) => entry.isFile() && entry.name.endsWith('.ino'))
191-
.map(({ name }) => name)
192-
.sort((left, right) => left.localeCompare(right))[0];
193-
if (sketchFilename) {
194-
return join(path, sketchFilename);
195-
}
196-
// If no sketches found in the folder, but the folder exists,
197-
// return with the path of the empty folder and let IDE2's frontend
198-
// figure out the workspace root.
199-
return path;
200-
} catch (err) {
201-
throw err;
202-
}
203-
}
204-
205166
private async resolvePath(
206167
maybePath: string,
207168
cwd: string
@@ -257,7 +218,10 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
257218
if (!resolvedPath) {
258219
continue;
259220
}
260-
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
221+
const sketchFolderPath = await isAccessibleSketchPath(
222+
resolvedPath,
223+
true
224+
);
261225
if (sketchFolderPath) {
262226
workspace.file = sketchFolderPath;
263227
if (this.isTempSketch.is(workspace.file)) {
@@ -288,7 +252,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
288252
if (!resolvedPath) {
289253
continue;
290254
}
291-
const sketchFolderPath = await this.isValidSketchPath(resolvedPath);
255+
const sketchFolderPath = await isAccessibleSketchPath(resolvedPath,true);
292256
if (sketchFolderPath) {
293257
path = sketchFolderPath;
294258
break;

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

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -733,60 +733,68 @@ function isNotFoundError(err: unknown): err is ServiceError {
733733

734734
/**
735735
* Tries to detect whether the error was caused by an invalid main sketch file name.
736-
* IDE2 should handle gracefully when there is an invalid sketch folder name. See the [spec](https://arduino.github.io/arduino-cli/latest/sketch-specification/#sketch-root-folder) for details.
737-
* The CLI does not have error codes (https://github.com/arduino/arduino-cli/issues/1762), so IDE2 parses the error message and tries to guess it.
736+
* IDE2 should handle gracefully when there is an invalid sketch folder name.
737+
* See the [spec](https://arduino.github.io/arduino-cli/latest/sketch-specification/#sketch-root-folder) for details.
738+
* The CLI does not have error codes (https://github.com/arduino/arduino-cli/issues/1762),
739+
* IDE2 cannot parse the error message (https://github.com/arduino/arduino-cli/issues/1968#issuecomment-1306936142)
740+
* so it checks if a sketch even if it's invalid can be discovered from the requested path.
738741
* Nothing guarantees that the invalid existing main sketch file still exits by the time client performs the sketch move.
739742
*/
740743
async function isInvalidSketchNameError(
741744
cliErr: unknown,
742745
requestSketchPath: string
743746
): Promise<string | undefined> {
744-
if (isNotFoundError(cliErr)) {
745-
const ino = requestSketchPath.endsWith('.ino');
746-
if (ino) {
747-
const sketchFolderPath = path.dirname(requestSketchPath);
748-
const sketchName = path.basename(sketchFolderPath);
749-
const pattern = `${invalidSketchNameErrorRegExpPrefix}${path.join(
750-
sketchFolderPath,
751-
`${sketchName}.ino`
752-
)}`.replace(/\\/g, '\\\\'); // make windows path separator with \\ to have a valid regexp.
753-
if (new RegExp(pattern, 'i').test(cliErr.details)) {
754-
try {
755-
await fs.access(requestSketchPath);
756-
return requestSketchPath;
757-
} catch {
758-
return undefined;
759-
}
760-
}
761-
} else {
762-
try {
763-
const resources = await fs.readdir(requestSketchPath, {
764-
withFileTypes: true,
765-
});
766-
return (
767-
resources
768-
.filter((resource) => resource.isFile())
769-
.filter((resource) => resource.name.endsWith('.ino'))
770-
// A folder might contain multiple sketches. It's OK to ick the first one as IDE2 cannot do much,
771-
// but ensure a deterministic behavior as `readdir(3)` does not guarantee an order. Sort them.
772-
.sort(({ name: left }, { name: right }) =>
773-
left.localeCompare(right)
774-
)
775-
.map(({ name }) => name)
776-
.map((name) => path.join(requestSketchPath, name))[0]
777-
);
778-
} catch (err) {
779-
if (ErrnoException.isENOENT(err) || ErrnoException.isENOTDIR(err)) {
780-
return undefined;
781-
}
782-
throw err;
783-
}
747+
return isNotFoundError(cliErr)
748+
? isAccessibleSketchPath(requestSketchPath)
749+
: undefined;
750+
}
751+
752+
/**
753+
* The `path` argument is valid, if accessible and either pointing to a `.ino` file,
754+
* or it's a directory, and one of the files in the directory is an `.ino` file.
755+
*
756+
* `undefined` if `path` was pointing to neither an accessible sketch file nor a sketch folder.
757+
*
758+
* The sketch folder name and sketch file name can be different. This method is not sketch folder name compliant.
759+
* The `path` must be an absolute, resolved path. This method does not handle EACCES (Permission denied) errors.
760+
*
761+
* When `fallbackToInvalidFolderPath` is `true`, and the `path` is an accessible folder without any sketch files,
762+
* this method returns with the `path` argument instead of `undefined`.
763+
*/
764+
export async function isAccessibleSketchPath(
765+
path: string,
766+
fallbackToInvalidFolderPath = false
767+
): Promise<string | undefined> {
768+
let stats: Stats | undefined = undefined;
769+
try {
770+
stats = await fs.stat(path);
771+
} catch (err) {
772+
if (ErrnoException.isENOENT(err)) {
773+
return undefined;
784774
}
775+
throw err;
776+
}
777+
if (!stats) {
778+
return undefined;
779+
}
780+
if (stats.isFile()) {
781+
return path.endsWith('.ino') ? path : undefined;
782+
}
783+
const entries = await fs.readdir(path, { withFileTypes: true });
784+
const sketchFilename = entries
785+
.filter((entry) => entry.isFile() && entry.name.endsWith('.ino'))
786+
.map(({ name }) => name)
787+
// A folder might contain multiple sketches. It's OK to pick the first one as IDE2 cannot do much,
788+
// but ensure a deterministic behavior as `readdir(3)` does not guarantee an order. Sort them.
789+
.sort((left, right) => left.localeCompare(right))[0];
790+
if (sketchFilename) {
791+
return join(path, sketchFilename);
785792
}
786-
return undefined;
793+
// If no sketches found in the folder, but the folder exists,
794+
// return with the path of the empty folder and let IDE2's frontend
795+
// figure out the workspace root.
796+
return fallbackToInvalidFolderPath ? path : undefined;
787797
}
788-
const invalidSketchNameErrorRegExpPrefix =
789-
'.*: main file missing from sketch: ';
790798

791799
/*
792800
* When a new sketch is created, add a suffix to distinguish it

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /