Skip to content

Commit 58aac23

Browse files
committed
Allow leading underscore in sketch filenames
The Arduino Sketch Specification defines the allowed format of sketch folder names and sketch code filenames. Arduino IDE enforces compliance with the specification in order to ensure sketches created with Arduino IDE can be used with any other Arduino development tool. The Arduino Sketch Specification has been changed to allow a leading underscore in sketch folder names and sketch code filenames so IDE's sketch name validation must be updated accordingly.
1 parent ec24b68 commit 58aac23

File tree

4 files changed

+27
-39
lines changed

4 files changed

+27
-39
lines changed

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

+6-14
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,6 @@ export namespace Sketch {
157157
// (non-API) exported for the tests
158158
export const defaultSketchFolderName = 'sketch';
159159
// (non-API) exported for the tests
160-
export const defaultFallbackFirstChar = '0';
161-
// (non-API) exported for the tests
162160
export const defaultFallbackChar = '_';
163161
// (non-API) exported for the tests
164162
export function reservedFilename(name: string): string {
@@ -176,11 +174,11 @@ export namespace Sketch {
176174
// (non-API) exported for the tests
177175
export const invalidSketchFolderNameMessage = nls.localize(
178176
'arduino/sketch/invalidSketchName',
179-
'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.'
177+
'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.'
180178
);
181179
const invalidCloudSketchFolderNameMessage = nls.localize(
182180
'arduino/sketch/invalidCloudSketchName',
183-
'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.'
181+
'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.'
184182
);
185183
/**
186184
* `undefined` if the candidate sketch folder name is valid. Otherwise, the validation error message.
@@ -193,7 +191,7 @@ export namespace Sketch {
193191
if (validFilenameError) {
194192
return validFilenameError;
195193
}
196-
return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate)
194+
return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate)
197195
? undefined
198196
: invalidSketchFolderNameMessage;
199197
}
@@ -208,7 +206,7 @@ export namespace Sketch {
208206
if (validFilenameError) {
209207
return validFilenameError;
210208
}
211-
return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate)
209+
return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate)
212210
? undefined
213211
: invalidCloudSketchFolderNameMessage;
214212
}
@@ -252,10 +250,7 @@ export namespace Sketch {
252250
return defaultSketchFolderName;
253251
}
254252
const validName = candidate
255-
? candidate
256-
.replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar)
257-
.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar)
258-
.slice(0, 63)
253+
? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 63)
259254
: defaultSketchFolderName;
260255
if (appendTimestampSuffix) {
261256
return `${validName.slice(0, 63 - timestampSuffixLength)}${
@@ -283,10 +278,7 @@ export namespace Sketch {
283278
return defaultSketchFolderName;
284279
}
285280
return candidate
286-
? candidate
287-
.replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar)
288-
.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar)
289-
.slice(0, 36)
281+
? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 36)
290282
: defaultSketchFolderName;
291283
}
292284

arduino-ide-extension/src/test/browser/workspace-commands.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,22 @@ describe('workspace-commands', () => {
171171
});
172172

173173
it('code files cannot start with number (no extension)', async () => {
174-
const actual = await testMe('_invalid');
174+
const actual = await testMe('-invalid');
175175
expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage);
176176
});
177177

178178
it('code files cannot start with number (trailing dot)', async () => {
179-
const actual = await testMe('_invalid.');
179+
const actual = await testMe('-invalid.');
180180
expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage);
181181
});
182182

183183
it('code files cannot start with number (trailing dot)', async () => {
184-
const actual = await testMe('_invalid.cpp');
184+
const actual = await testMe('-invalid.cpp');
185185
expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage);
186186
});
187187

188188
it('should warn about invalid extension first', async () => {
189-
const actual = await testMe('_invalid.xxx');
189+
const actual = await testMe('-invalid.xxx');
190190
expect(actual).to.be.equal(invalidExtensionMessage('.xxx'));
191191
});
192192

@@ -196,7 +196,7 @@ describe('workspace-commands', () => {
196196
});
197197

198198
it('should ignore non-code filename validation from the spec', async () => {
199-
const actual = await testMe('_invalid.json');
199+
const actual = await testMe('-invalid.json');
200200
expect(actual).to.be.empty;
201201
});
202202

arduino-ide-extension/src/test/common/sketches-service.test.ts

+14-18
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('sketch', () => {
4343
['trailing.dots...', false],
4444
['no.trailing.dots.._', true],
4545
['No Spaces', false],
46-
['_invalidToStartWithUnderscore', false],
46+
['_validToStartWithUnderscore', true],
4747
['Invalid+Char.ino', false],
4848
['', false],
4949
['/', false],
@@ -81,7 +81,7 @@ describe('sketch', () => {
8181
['can.contain.dots', true],
8282
['-cannot-start-with-dash', false],
8383
['.cannot.start.with.dash', false],
84-
['_cannot_start_with_underscore', false],
84+
['_can_start_with_underscore', true],
8585
['No Spaces', false],
8686
['Invalid+Char.ino', false],
8787
['', false],
@@ -108,14 +108,14 @@ describe('sketch', () => {
108108
describe('toValidSketchFolderName', () => {
109109
[
110110
['', Sketch.defaultSketchFolderName],
111-
[' ', Sketch.defaultFallbackFirstChar],
112-
[' ', Sketch.defaultFallbackFirstChar + Sketch.defaultFallbackChar],
111+
[' ', Sketch.defaultFallbackChar],
112+
[' ', Sketch.defaultFallbackChar + Sketch.defaultFallbackChar],
113113
[
114114
'0123456789012345678901234567890123456789012345678901234567890123',
115115
'012345678901234567890123456789012345678901234567890123456789012',
116116
],
117117
['foo bar', 'foo_bar'],
118-
['_foobar', '0foobar'],
118+
['-foobar', '_foobar'],
119119
['vAlid', 'vAlid'],
120120
['COM1', Sketch.defaultSketchFolderName],
121121
['COM1.', 'COM1_'],
@@ -130,20 +130,18 @@ describe('sketch', () => {
130130
const epochSuffix = Sketch.timestampSuffix(epoch);
131131
[
132132
['', Sketch.defaultSketchFolderName + epochSuffix],
133-
[' ', Sketch.defaultFallbackFirstChar + epochSuffix],
133+
[' ', Sketch.defaultFallbackChar + epochSuffix],
134134
[
135135
' ',
136-
Sketch.defaultFallbackFirstChar +
137-
Sketch.defaultFallbackChar +
138-
epochSuffix,
136+
Sketch.defaultFallbackChar + Sketch.defaultFallbackChar + epochSuffix,
139137
],
140138
[
141139
'0123456789012345678901234567890123456789012345678901234567890123',
142140
'0123456789012345678901234567890123456789012' + epochSuffix,
143141
],
144142
['foo bar', 'foo_bar' + epochSuffix],
145-
['.foobar', '0foobar' + epochSuffix],
146-
['-fooBar', '0fooBar' + epochSuffix],
143+
['.foobar', '_foobar' + epochSuffix],
144+
['-fooBar', '_fooBar' + epochSuffix],
147145
['foobar.', 'foobar_' + epochSuffix],
148146
['fooBar-', 'fooBar_' + epochSuffix],
149147
['fooBar+', 'fooBar_' + epochSuffix],
@@ -164,19 +162,17 @@ describe('sketch', () => {
164162
['only_underscore-is+ok.ino', 'only_underscore_is_ok_ino'],
165163
['regex++', 'regex__'],
166164
['dots...', 'dots___'],
167-
['.dots...', '0dots___'],
168-
['-dashes---', '0dashes___'],
169-
['_underscore___', '0underscore___'],
165+
['.dots...', '_dots___'],
166+
['-dashes---', '_dashes___'],
170167
['No Spaces', 'No_Spaces'],
171-
['_startsWithUnderscore', '0startsWithUnderscore'],
172168
['Invalid+Char.ino', 'Invalid_Char_ino'],
173169
['', 'sketch'],
174-
['/', '0'],
170+
['/', '_'],
175171
[
176172
'/-1////////////////////+//////////////-/',
177-
'0_1_________________________________',
173+
'__1_________________________________',
178174
],
179-
['//trash/', '0_trash_'],
175+
['//trash/', '__trash_'],
180176
[
181177
'63Length_012345678901234567890123456789012345678901234567890123',
182178
'63Length_012345678901234567890123456',

i18n/en.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -419,11 +419,11 @@
419419
"editInvalidSketchFolderLocationQuestion": "Do you want to try saving the sketch to a different location?",
420420
"editInvalidSketchFolderQuestion": "Do you want to try saving the sketch with a different name?",
421421
"exportBinary": "Export Compiled Binary",
422-
"invalidCloudSketchName": "The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.",
422+
"invalidCloudSketchName": "The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.",
423423
"invalidSketchFolderLocationDetails": "You cannot save a sketch into a folder inside itself.",
424424
"invalidSketchFolderLocationMessage": "Invalid sketch folder location: '{0}'",
425425
"invalidSketchFolderNameMessage": "Invalid sketch folder name: '{0}'",
426-
"invalidSketchName": "The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.",
426+
"invalidSketchName": "The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.",
427427
"moving": "Moving",
428428
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named \"{1}\".\nCreate this folder, move the file, and continue?",
429429
"new": "New Sketch",

0 commit comments

Comments
 (0)