diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index 5fef59072..ecd45735d 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -249,6 +249,14 @@ export const ArduinoConfigSchema: PreferenceSchema = { ), default: true, }, + 'arduino.sketch.inoBlueprint': { + type: 'string', + markdownDescription: nls.localize( + 'arduino/preferences/sketch/inoBlueprint', + 'Absolute filesystem path to the default `.ino` blueprint file. If specified, the content of the blueprint file will be used for every new sketch created by the IDE. The sketches will be generated with the default Arduino content if not specified. Unaccessible blueprint files are ignored. **A restart of the IDE is needed** for this setting to take effect.' + ), + default: undefined, + }, }, }; @@ -278,6 +286,7 @@ export interface ArduinoConfiguration { 'arduino.auth.registerUri': string; 'arduino.survey.notification': boolean; 'arduino.cli.daemon.debug': boolean; + 'arduino.sketch.inoBlueprint': string; 'arduino.checkForUpdates': boolean; } diff --git a/arduino-ide-extension/src/node/sketches-service-impl.ts b/arduino-ide-extension/src/node/sketches-service-impl.ts index 06c7931b6..e6793e39a 100644 --- a/arduino-ide-extension/src/node/sketches-service-impl.ts +++ b/arduino-ide-extension/src/node/sketches-service-impl.ts @@ -32,8 +32,19 @@ import { maybeNormalizeDrive, TempSketchPrefix, } from './is-temp-sketch'; +import { join } from 'path'; const RecentSketches = 'recent-sketches.json'; +const DefaultIno = `void setup() { + // put your setup code here, to run once: + +} + +void loop() { + // put your main code here, to run repeatedly: + +} +`; @injectable() export class SketchesServiceImpl @@ -47,6 +58,7 @@ export class SketchesServiceImpl autoStart: true, concurrency: 1, }); + private inoContent: Deferred | undefined; @inject(ILogger) @named('sketches-service') @@ -446,21 +458,11 @@ export class SketchesServiceImpl const sketchDir = path.join(parentPath, sketchName); const sketchFile = path.join(sketchDir, `${sketchName}.ino`); - await fs.mkdir(sketchDir, { recursive: true }); - await fs.writeFile( - sketchFile, - `void setup() { - // put your setup code here, to run once: - -} - -void loop() { - // put your main code here, to run repeatedly: - -} -`, - { encoding: 'utf8' } - ); + const [inoContent] = await Promise.all([ + this.loadInoContent(), + fs.mkdir(sketchDir, { recursive: true }), + ]); + await fs.writeFile(sketchFile, inoContent, { encoding: 'utf8' }); return this.loadSketch(FileUri.create(sketchDir).toString()); } @@ -637,6 +639,61 @@ void loop() { return false; } } + + // Returns the default.ino from the settings or from default folder. + private async readSettings(): Promise | undefined> { + const configDirUri = await this.envVariableServer.getConfigDirUri(); + const configDirPath = FileUri.fsPath(configDirUri); + + try { + const raw = await fs.readFile(join(configDirPath, 'settings.json'), { + encoding: 'utf8', + }); + + return this.tryParse(raw); + } catch (err) { + if ('code' in err && err.code === 'ENOENT') { + return undefined; + } + throw err; + } + } + + private tryParse(raw: string): Record | undefined { + try { + return JSON.parse(raw); + } catch { + return undefined; + } + } + + // Returns the default.ino from the settings or from default folder. + private async loadInoContent(): Promise { + if (!this.inoContent) { + this.inoContent = new Deferred(); + const settings = await this.readSettings(); + if (settings) { + const inoBlueprintPath = settings['arduino.sketch.inoBlueprint']; + if (inoBlueprintPath && typeof inoBlueprintPath === 'string') { + try { + const inoContent = await fs.readFile(inoBlueprintPath, { + encoding: 'utf8', + }); + this.inoContent.resolve(inoContent); + } catch (err) { + if ('code' in err && err.code === 'ENOENT') { + // Ignored. The custom `.ino` blueprint file is optional. + } else { + throw err; + } + } + } + } + this.inoContent.resolve(DefaultIno); + } + + return this.inoContent.promise; + } } interface SketchWithDetails extends Sketch { diff --git a/i18n/en.json b/i18n/en.json index 3480a2215..165f34fb0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -348,6 +348,9 @@ "username": "Username" }, "showVerbose": "Show verbose output during", + "sketch": { + "inoBlueprint": "Absolute filesystem path to the default `.ino` blueprint file. If specified, the content of the blueprint file will be used for every new sketch created by the IDE. The sketches will be generated with the default Arduino content if not specified. Unaccessible blueprint files are ignored. **A restart of the IDE is needed** for this setting to take effect." + }, "sketchbook.location": "Sketchbook location", "sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.", "survey.notification": "True if users should be notified if a survey is available. True by default.",