diff --git a/arduino-ide-extension/src/common/protocol/sketches-service.ts b/arduino-ide-extension/src/common/protocol/sketches-service.ts index df25a34f3..7953be720 100644 --- a/arduino-ide-extension/src/common/protocol/sketches-service.ts +++ b/arduino-ide-extension/src/common/protocol/sketches-service.ts @@ -157,8 +157,6 @@ export namespace Sketch { // (non-API) exported for the tests export const defaultSketchFolderName = 'sketch'; // (non-API) exported for the tests - export const defaultFallbackFirstChar = '0'; - // (non-API) exported for the tests export const defaultFallbackChar = '_'; // (non-API) exported for the tests export function reservedFilename(name: string): string { @@ -176,11 +174,11 @@ export namespace Sketch { // (non-API) exported for the tests export const invalidSketchFolderNameMessage = nls.localize( 'arduino/sketch/invalidSketchName', - 'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.' + 'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.' ); const invalidCloudSketchFolderNameMessage = nls.localize( 'arduino/sketch/invalidCloudSketchName', - 'The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.' + 'The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.' ); /** * `undefined` if the candidate sketch folder name is valid. Otherwise, the validation error message. @@ -193,7 +191,7 @@ export namespace Sketch { if (validFilenameError) { return validFilenameError; } - return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate) + return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,62}$/.test(candidate) ? undefined : invalidSketchFolderNameMessage; } @@ -208,7 +206,7 @@ export namespace Sketch { if (validFilenameError) { return validFilenameError; } - return /^[0-9a-zA-Z]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate) + return /^[0-9a-zA-Z_]{1}[0-9a-zA-Z_\.-]{0,35}$/.test(candidate) ? undefined : invalidCloudSketchFolderNameMessage; } @@ -252,10 +250,7 @@ export namespace Sketch { return defaultSketchFolderName; } const validName = candidate - ? candidate - .replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar) - .replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar) - .slice(0, 63) + ? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 63) : defaultSketchFolderName; if (appendTimestampSuffix) { return `${validName.slice(0, 63 - timestampSuffixLength)}${ @@ -283,10 +278,7 @@ export namespace Sketch { return defaultSketchFolderName; } return candidate - ? candidate - .replace(/^[^0-9a-zA-Z]{1}/g, defaultFallbackFirstChar) - .replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar) - .slice(0, 36) + ? candidate.replace(/[^0-9a-zA-Z_]/g, defaultFallbackChar).slice(0, 36) : defaultSketchFolderName; } diff --git a/arduino-ide-extension/src/test/browser/workspace-commands.test.ts b/arduino-ide-extension/src/test/browser/workspace-commands.test.ts index 234515e76..0676ed77a 100644 --- a/arduino-ide-extension/src/test/browser/workspace-commands.test.ts +++ b/arduino-ide-extension/src/test/browser/workspace-commands.test.ts @@ -171,22 +171,22 @@ describe('workspace-commands', () => { }); it('code files cannot start with number (no extension)', async () => { - const actual = await testMe('_invalid'); + const actual = await testMe('-invalid'); expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage); }); it('code files cannot start with number (trailing dot)', async () => { - const actual = await testMe('_invalid.'); + const actual = await testMe('-invalid.'); expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage); }); it('code files cannot start with number (trailing dot)', async () => { - const actual = await testMe('_invalid.cpp'); + const actual = await testMe('-invalid.cpp'); expect(actual).to.be.equal(Sketch.invalidSketchFolderNameMessage); }); it('should warn about invalid extension first', async () => { - const actual = await testMe('_invalid.xxx'); + const actual = await testMe('-invalid.xxx'); expect(actual).to.be.equal(invalidExtensionMessage('.xxx')); }); @@ -196,7 +196,7 @@ describe('workspace-commands', () => { }); it('should ignore non-code filename validation from the spec', async () => { - const actual = await testMe('_invalid.json'); + const actual = await testMe('-invalid.json'); expect(actual).to.be.empty; }); diff --git a/arduino-ide-extension/src/test/common/sketches-service.test.ts b/arduino-ide-extension/src/test/common/sketches-service.test.ts index 917155e58..ae9f2481c 100644 --- a/arduino-ide-extension/src/test/common/sketches-service.test.ts +++ b/arduino-ide-extension/src/test/common/sketches-service.test.ts @@ -43,7 +43,7 @@ describe('sketch', () => { ['trailing.dots...', false], ['no.trailing.dots.._', true], ['No Spaces', false], - ['_invalidToStartWithUnderscore', false], + ['_validToStartWithUnderscore', true], ['Invalid+Char.ino', false], ['', false], ['/', false], @@ -81,7 +81,7 @@ describe('sketch', () => { ['can.contain.dots', true], ['-cannot-start-with-dash', false], ['.cannot.start.with.dash', false], - ['_cannot_start_with_underscore', false], + ['_can_start_with_underscore', true], ['No Spaces', false], ['Invalid+Char.ino', false], ['', false], @@ -108,14 +108,14 @@ describe('sketch', () => { describe('toValidSketchFolderName', () => { [ ['', Sketch.defaultSketchFolderName], - [' ', Sketch.defaultFallbackFirstChar], - [' ', Sketch.defaultFallbackFirstChar + Sketch.defaultFallbackChar], + [' ', Sketch.defaultFallbackChar], + [' ', Sketch.defaultFallbackChar + Sketch.defaultFallbackChar], [ '0123456789012345678901234567890123456789012345678901234567890123', '012345678901234567890123456789012345678901234567890123456789012', ], ['foo bar', 'foo_bar'], - ['_foobar', '0foobar'], + ['-foobar', '_foobar'], ['vAlid', 'vAlid'], ['COM1', Sketch.defaultSketchFolderName], ['COM1.', 'COM1_'], @@ -130,20 +130,18 @@ describe('sketch', () => { const epochSuffix = Sketch.timestampSuffix(epoch); [ ['', Sketch.defaultSketchFolderName + epochSuffix], - [' ', Sketch.defaultFallbackFirstChar + epochSuffix], + [' ', Sketch.defaultFallbackChar + epochSuffix], [ ' ', - Sketch.defaultFallbackFirstChar + - Sketch.defaultFallbackChar + - epochSuffix, + Sketch.defaultFallbackChar + Sketch.defaultFallbackChar + epochSuffix, ], [ '0123456789012345678901234567890123456789012345678901234567890123', '0123456789012345678901234567890123456789012' + epochSuffix, ], ['foo bar', 'foo_bar' + epochSuffix], - ['.foobar', '0foobar' + epochSuffix], - ['-fooBar', '0fooBar' + epochSuffix], + ['.foobar', '_foobar' + epochSuffix], + ['-fooBar', '_fooBar' + epochSuffix], ['foobar.', 'foobar_' + epochSuffix], ['fooBar-', 'fooBar_' + epochSuffix], ['fooBar+', 'fooBar_' + epochSuffix], @@ -164,19 +162,17 @@ describe('sketch', () => { ['only_underscore-is+ok.ino', 'only_underscore_is_ok_ino'], ['regex++', 'regex__'], ['dots...', 'dots___'], - ['.dots...', '0dots___'], - ['-dashes---', '0dashes___'], - ['_underscore___', '0underscore___'], + ['.dots...', '_dots___'], + ['-dashes---', '_dashes___'], ['No Spaces', 'No_Spaces'], - ['_startsWithUnderscore', '0startsWithUnderscore'], ['Invalid+Char.ino', 'Invalid_Char_ino'], ['', 'sketch'], - ['/', '0'], + ['/', '_'], [ '/-1////////////////////+//////////////-/', - '0_1_________________________________', + '__1_________________________________', ], - ['//trash/', '0_trash_'], + ['//trash/', '__trash_'], [ '63Length_012345678901234567890123456789012345678901234567890123', '63Length_012345678901234567890123456', diff --git a/i18n/en.json b/i18n/en.json index e02f7eeb4..b24a89cd4 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -419,11 +419,11 @@ "editInvalidSketchFolderLocationQuestion": "Do you want to try saving the sketch to a different location?", "editInvalidSketchFolderQuestion": "Do you want to try saving the sketch with a different name?", "exportBinary": "Export Compiled Binary", - "invalidCloudSketchName": "The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.", + "invalidCloudSketchName": "The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 36 characters.", "invalidSketchFolderLocationDetails": "You cannot save a sketch into a folder inside itself.", "invalidSketchFolderLocationMessage": "Invalid sketch folder location: '{0}'", "invalidSketchFolderNameMessage": "Invalid sketch folder name: '{0}'", - "invalidSketchName": "The name must start with a letter or number, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.", + "invalidSketchName": "The name must start with a letter, number, or underscore, followed by letters, numbers, dashes, dots and underscores. Maximum length is 63 characters.", "moving": "Moving", "movingMsg": "The file \"{0}\" needs to be inside a sketch folder named \"{1}\".\nCreate this folder, move the file, and continue?", "new": "New Sketch",