Skip to content

Commit bba8c99

Browse files
author
Akos Kitta
committed
Init
Signed-off-by: Akos Kitta <a.kitta@arduino.cc> #964
1 parent 99b1094 commit bba8c99

File tree

4 files changed

+133
-44
lines changed

4 files changed

+133
-44
lines changed

Diff for: arduino-ide-extension/src/browser/contributions/open-sketch-files.ts

+21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
URI,
1111
} from './contribution';
1212
import { SaveAsSketch } from './save-as-sketch';
13+
import { promptMoveSketch } from './open-sketch';
1314

1415
@injectable()
1516
export class OpenSketchFiles extends SketchContribution {
@@ -56,6 +57,26 @@ export class OpenSketchFiles extends SketchContribution {
5657
});
5758
}
5859
} catch (err) {
60+
if (SketchesError.InvalidName.is(err)) {
61+
const { uri: invalidNameUri } = err.data;
62+
if (
63+
invalidNameUri === uri.toString() &&
64+
(await this.fileService.exists(uri))
65+
) {
66+
// If the main sketch file exists, but it's not in an appropriately named folder, user can decide to move the sketch and open that.
67+
const movedSketch = await promptMoveSketch(uri, {
68+
fileService: this.fileService,
69+
sketchService: this.sketchService,
70+
labelProvider: this.labelProvider,
71+
});
72+
if (movedSketch) {
73+
this.workspaceService.open(new URI(movedSketch.uri), {
74+
preserveWindow: true,
75+
});
76+
}
77+
}
78+
}
79+
5980
if (SketchesError.NotFound.is(err)) {
6081
this.openFallbackSketch();
6182
} else {

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

+63-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import * as remote from '@theia/core/electron-shared/@electron/remote';
2+
import { LabelProvider } from '@theia/core/lib/browser';
23
import { nls } from '@theia/core/lib/common/nls';
34
import { injectable } from '@theia/core/shared/inversify';
4-
import { SketchesError, SketchRef } from '../../common/protocol';
5+
import { FileService } from '@theia/filesystem/lib/browser/file-service';
6+
import {
7+
SketchesError,
8+
SketchesService,
9+
SketchRef,
10+
} from '../../common/protocol';
511
import { ArduinoMenus } from '../menu/arduino-menus';
612
import {
713
Command,
@@ -108,45 +114,11 @@ export class OpenSketch extends SketchContribution {
108114
return sketch;
109115
}
110116
if (Sketch.isSketchFile(sketchFileUri)) {
111-
const name = new URI(sketchFileUri).path.name;
112-
const nameWithExt = this.labelProvider.getName(new URI(sketchFileUri));
113-
const { response } = await remote.dialog.showMessageBox({
114-
title: nls.localize('arduino/sketch/moving', 'Moving'),
115-
type: 'question',
116-
buttons: [
117-
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
118-
nls.localize('vscode/issueMainService/ok', 'OK'),
119-
],
120-
message: nls.localize(
121-
'arduino/sketch/movingMsg',
122-
'The file "{0}" needs to be inside a sketch folder named "{1}".\nCreate this folder, move the file, and continue?',
123-
nameWithExt,
124-
name
125-
),
117+
return promptMoveSketch(sketchFileUri, {
118+
fileService: this.fileService,
119+
sketchService: this.sketchService,
120+
labelProvider: this.labelProvider,
126121
});
127-
if (response === 1) {
128-
// OK
129-
const newSketchUri = new URI(sketchFileUri).parent.resolve(name);
130-
const exists = await this.fileService.exists(newSketchUri);
131-
if (exists) {
132-
await remote.dialog.showMessageBox({
133-
type: 'error',
134-
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
135-
message: nls.localize(
136-
'arduino/sketch/cantOpen',
137-
'A folder named "{0}" already exists. Can\'t open sketch.',
138-
name
139-
),
140-
});
141-
return undefined;
142-
}
143-
await this.fileService.createFolder(newSketchUri);
144-
await this.fileService.move(
145-
new URI(sketchFileUri),
146-
new URI(newSketchUri.resolve(nameWithExt).toString())
147-
);
148-
return this.sketchService.getSketchFolder(newSketchUri.toString());
149-
}
150122
}
151123
}
152124
}
@@ -158,3 +130,55 @@ export namespace OpenSketch {
158130
};
159131
}
160132
}
133+
134+
export async function promptMoveSketch(
135+
sketchFileUri: string | URI,
136+
options: {
137+
fileService: FileService;
138+
sketchService: SketchesService;
139+
labelProvider: LabelProvider;
140+
}
141+
): Promise<Sketch | undefined> {
142+
const { fileService, sketchService, labelProvider } = options;
143+
const uri =
144+
sketchFileUri instanceof URI ? sketchFileUri : new URI(sketchFileUri);
145+
const name = uri.path.name;
146+
const nameWithExt = labelProvider.getName(uri);
147+
const { response } = await remote.dialog.showMessageBox({
148+
title: nls.localize('arduino/sketch/moving', 'Moving'),
149+
type: 'question',
150+
buttons: [
151+
nls.localize('vscode/issueMainService/cancel', 'Cancel'),
152+
nls.localize('vscode/issueMainService/ok', 'OK'),
153+
],
154+
message: nls.localize(
155+
'arduino/sketch/movingMsg',
156+
'The file "{0}" needs to be inside a sketch folder named "{1}".\nCreate this folder, move the file, and continue?',
157+
nameWithExt,
158+
name
159+
),
160+
});
161+
if (response === 1) {
162+
// OK
163+
const newSketchUri = uri.parent.resolve(name);
164+
const exists = await fileService.exists(newSketchUri);
165+
if (exists) {
166+
await remote.dialog.showMessageBox({
167+
type: 'error',
168+
title: nls.localize('vscode/dialog/dialogErrorMessage', 'Error'),
169+
message: nls.localize(
170+
'arduino/sketch/cantOpen',
171+
'A folder named "{0}" already exists. Can\'t open sketch.',
172+
name
173+
),
174+
});
175+
return undefined;
176+
}
177+
await fileService.createFolder(newSketchUri);
178+
await fileService.move(
179+
uri,
180+
new URI(newSketchUri.resolve(nameWithExt).toString())
181+
);
182+
return sketchService.getSketchFolder(newSketchUri.toString());
183+
}
184+
}

Diff for: arduino-ide-extension/src/common/protocol/sketches-service.ts

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import URI from '@theia/core/lib/common/uri';
44
export namespace SketchesError {
55
export const Codes = {
66
NotFound: 5001,
7+
InvalidName: 5002,
78
};
89
export const NotFound = ApplicationError.declare(
910
Codes.NotFound,
@@ -14,6 +15,15 @@ export namespace SketchesError {
1415
};
1516
}
1617
);
18+
export const InvalidName = ApplicationError.declare(
19+
Codes.InvalidName,
20+
(message: string, uri: string) => {
21+
return {
22+
message,
23+
data: { uri },
24+
};
25+
}
26+
);
1727
}
1828

1929
export const SketchesServicePath = '/services/sketches-service';

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

+39-5
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,15 @@ export class SketchesServiceImpl
187187
const sketch = await new Promise<SketchWithDetails>((resolve, reject) => {
188188
client.loadSketch(req, async (err, resp) => {
189189
if (err) {
190-
reject(
191-
isNotFoundError(err)
192-
? SketchesError.NotFound(err.details, uri)
193-
: err
194-
);
190+
let rejectWith: unknown = err;
191+
if (isNotFoundError(err)) {
192+
if (isInvalidSketchNameError(err, requestSketchPath)) {
193+
rejectWith = SketchesError.InvalidName(err.details, uri);
194+
} else {
195+
rejectWith = SketchesError.NotFound(err.details, uri);
196+
}
197+
}
198+
reject(rejectWith);
195199
return;
196200
}
197201
const responseSketchPath = maybeNormalizeDrive(resp.getLocationPath());
@@ -647,6 +651,36 @@ function isNotFoundError(err: unknown): err is ServiceError {
647651
return ServiceError.is(err) && err.code === 5; // `NOT_FOUND` https://grpc.github.io/grpc/core/md_doc_statuscodes.html
648652
}
649653

654+
/**
655+
* Tries to detect whether the error was caused by an invalid main sketch file name.
656+
* IDE2 should handle gracefully when there is an invalid sketch name. See the [spec](https://arduino.github.io/arduino-cli/latest/sketch-specification/#sketch-root-folder) for details.
657+
* 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.
658+
* This method does not check whether `requestSketchPath` exists. It's the responsibility of the caller who wishes to handle this error.
659+
*/
660+
function isInvalidSketchNameError(
661+
err: unknown,
662+
requestSketchPath: string
663+
): boolean {
664+
// When trying to open `/Users/a.kitta/Desktop/untitled folder/sketch.ino`.
665+
// CLI will throw `Can't open sketch: main file missing from sketch: /Users/a.kitta/Desktop/untitled folder/untitled folder.ino`
666+
if (isNotFoundError(err)) {
667+
const ino = requestSketchPath.endsWith('.ino');
668+
const pde = requestSketchPath.endsWith('.pde');
669+
if (ino || pde) {
670+
const dir = path.dirname(requestSketchPath);
671+
const sketchName = path.basename(dir);
672+
const regex = new RegExp(
673+
`Can't open sketch: main file missing from sketch: ${path.join(
674+
dir,
675+
`${sketchName}.${ino ? 'ino' : 'pde'}`
676+
)}`
677+
);
678+
return regex.test(err.details);
679+
}
680+
}
681+
return false;
682+
}
683+
650684
/*
651685
* When a new sketch is created, add a suffix to distinguish it
652686
* from other new sketches I created today.

0 commit comments

Comments
 (0)