From ae0ebd43ee4eab1bd8be1043c352197522edb2bf Mon Sep 17 00:00:00 2001 From: Giulio Date: Tue, 8 Dec 2020 00:41:05 +0100 Subject: [PATCH 001/142] Option to use arduino-cli instead of Arduino IDE (#1017) * Hardcoded Arduino-CLI commands for build and upload * Added menu checkbox for Arduino CLI - Upload with programmer is still not supported - Must be created an arduino symlink pointing to arduino-cli * If Arduino-CLI, check for arduino-cli instead of arduino. Not yet supported on Windows or MacOS * Typo * Fixed CI requests * Fixed CI requests * Update src/common/sys/darwin.ts MacOS patch for arduino-cli Co-authored-by: Marc Lage-Vianna * Update src/common/sys/win32.ts Windows patch for arduino-cli Co-authored-by: Marc Lage-Vianna * Trigger * add cli option in commandPath for win32 * add cli support to board and lib managers * rename isArduinoCli to useArduinoCli * adds support for uploading using programmer * simplify getProgrammer * add CLI upload * Update src/arduino/arduino.ts Co-authored-by: Jason Tranchida * refactor uploadUsingProgrammer * fix output path for CLI upload * Update package.json * update cli option text, thanks @maddogjt * update tests Co-authored-by: giuliof Co-authored-by: Marc Lage-Vianna Co-authored-by: Adi Azulay Co-authored-by: Adi Azulay Co-authored-by: Jason Tranchida --- package.json | 13 +++ src/arduino/arduino.ts | 161 +++++++++++++++---------------- src/arduino/arduinoSettings.ts | 10 +- src/arduino/programmerManager.ts | 34 ++++--- src/arduino/vscodeSettings.ts | 6 ++ src/common/platform.ts | 4 +- src/common/sys/darwin.ts | 5 +- src/common/sys/linux.ts | 4 +- src/common/sys/win32.ts | 5 +- src/common/util.ts | 7 ++ src/extension.ts | 37 ++++++- test/extension.test.ts | 2 + test/librarymanager.test.ts | 2 +- 13 files changed, 184 insertions(+), 106 deletions(-) diff --git a/package.json b/package.json index 0d526c16..20cf48a1 100644 --- a/package.json +++ b/package.json @@ -93,10 +93,18 @@ "light": "images/ArduinoUpload_16.svg" } }, + { + "command": "arduino.cliUpload", + "title": "Arduino CLI: Upload" + }, { "command": "arduino.uploadUsingProgrammer", "title": "Arduino: Upload Using Programmer" }, + { + "command": "arduino.cliUploadUsingProgrammer", + "title": "Arduino CLI: Upload Using Programmer" + }, { "command": "arduino.selectProgrammer", "title": "Arduino: Select Programmer" @@ -442,6 +450,11 @@ "type": "object", "title": "Arduino configuration", "properties": { + "arduino.useArduinoCli": { + "type": "boolean", + "default": false, + "markdownDescription": "Use Arduino CLI installed instead of Arduino IDE. `#arduino.path#` must be set, as there is no default path for 'arduino-cli'. (Requires a restart after change)" + }, "arduino.path": { "type": "string", "default": "", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 263a5069..5a59ca19 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -93,13 +93,23 @@ export class ArduinoApp { } } - public async upload() { + /** + * Upload code to selected board + * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload + * @param {bool} [useProgrammer=false] - Indicate whether a specific programmer should be used + */ + public async upload(compile: boolean = true, useProgrammer: boolean = false) { const dc = DeviceContext.getInstance(); const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return; } + const selectProgrammer = useProgrammer ? this.getProgrammerString() : null; + if (useProgrammer && !selectProgrammer) { + return; + } + if (!ArduinoWorkspace.rootPath) { vscode.window.showWarningMessage("Cannot find the sketch file."); return; @@ -140,8 +150,25 @@ export class ArduinoApp { } } + if (!compile && !this.useArduinoCli()) { + arduinoChannel.error("This command is only availble when using the Arduino CLI"); + return; + } + const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--upload", "--board", boardDescriptor]; + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + const args = (!compile && this.useArduinoCli()) ? ["upload", "-b", boardDescriptor] : + this.useArduinoCli() ? ["compile", "--upload", "-b", boardDescriptor] : + ["--upload", "--board", boardDescriptor]; + + if (useProgrammer) { + if (this.useArduinoCli()) { + args.push("--programmer", selectProgrammer) + } else { + args.push("--useprogrammer", "--pref", "programmer=" + selectProgrammer) + } + } + if (dc.port) { args.push("--port", dc.port); } @@ -149,7 +176,7 @@ export class ArduinoApp { if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); } - if (dc.output) { + if (dc.output && compile) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { @@ -157,77 +184,13 @@ export class ArduinoApp { return; } - args.push("--pref", `build.path=${outputPath}`); - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); - } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; - arduinoChannel.warning(msg); - } - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { - UsbDetector.getInstance().resumeListening(); - if (needRestore) { - await serialMonitor.openSerialMonitor(); - } - arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); - }, (reason) => { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); - }); - } - - public async uploadUsingProgrammer() { - const dc = DeviceContext.getInstance(); - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return; - } - - const selectProgrammer = this.getProgrammerString(); - if (!selectProgrammer) { - return; - } + if (this.useArduinoCli()) { + args.push("--build-path", outputPath); - if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); - return; - } - - if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); - } - if (!dc.port) { - const choice = await vscode.window.showInformationMessage( - "Serial port is not specified. Do you want to select a serial port for uploading?", - "Yes", "No"); - if (choice === "Yes") { - vscode.commands.executeCommand("arduino.selectSerialPort"); - } - return; - } - - arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); - - const serialMonitor = SerialMonitor.getInstance(); - - const needRestore = await serialMonitor.closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); - await vscode.workspace.saveAll(false); - - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--upload", "--board", boardDescriptor, "--port", dc.port, "--useprogrammer", - "--pref", "programmer=" + selectProgrammer, appPath]; - if (VscodeSettings.getInstance().logLevel === "verbose") { - args.push("--verbose"); - } - if (dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); - const dirPath = path.dirname(outputPath); - if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; + } else { + args.push("--pref", `build.path=${outputPath}`); } - args.push("--pref", `build.path=${outputPath}`); arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; @@ -277,7 +240,7 @@ export class ArduinoApp { } const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = ["--verify", "--board", boardDescriptor, appPath]; + const args = this.useArduinoCli() ? ["compile", "-b", boardDescriptor, appPath] : ["--verify", "--board", boardDescriptor, appPath]; if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); } @@ -289,7 +252,13 @@ export class ArduinoApp { return; } - args.push("--pref", `build.path=${outputPath}`); + if (this.useArduinoCli()) { + args.push("--build-path", outputPath); + + } else { + args.push("--pref", `build.path=${outputPath}`); + } + arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); } else { const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; @@ -495,9 +464,14 @@ export class ArduinoApp { } } - /** - * Install arduino board package based on package name and platform hardware architecture. - */ + /** + * Installs arduino board package. + * (If using the aduino CLI this installs the corrosponding core.) + * @param {string} packageName - board vendor + * @param {string} arch - board architecture + * @param {string} version - version of board package or core to download + * @param {boolean} [showOutput=true] - show raw output from command + */ public async installBoard(packageName: string, arch: string = "", version: string = "", showOutput: boolean = true) { arduinoChannel.show(); const updatingIndex = packageName === "dummy" && !arch && !version; @@ -505,23 +479,28 @@ export class ArduinoApp { arduinoChannel.start(`Update package index files...`); } else { try { - const packagePath = path.join(this._settings.packagePath, "packages", packageName); + const packagePath = path.join(this._settings.packagePath, "packages", packageName, arch); if (util.directoryExistsSync(packagePath)) { util.rmdirRecursivelySync(packagePath); } arduinoChannel.start(`Install package - ${packageName}...`); } catch (error) { arduinoChannel.start(`Install package - ${packageName} failed under directory : ${error.path}${os.EOL} -Please make sure the folder is not occupied by other procedures .`); + Please make sure the folder is not occupied by other procedures .`); arduinoChannel.error(`Error message - ${error.message}${os.EOL}`); arduinoChannel.error(`Exit with code=${error.code}${os.EOL}`); return; } } + arduinoChannel.info(`${packageName}${arch && ":" + arch}${version && ":" + version}`); try { - await util.spawn(this._settings.commandPath, - showOutput ? arduinoChannel.channel : null, - ["--install-boards", `${packageName}${arch && ":" + arch}${version && ":" + version}`]); + this.useArduinoCli() ? + await util.spawn(this._settings.commandPath, + showOutput ? arduinoChannel.channel : null, + ["core", "install", `${packageName}${arch && ":" + arch}${version && "@" + version}`]) : + await util.spawn(this._settings.commandPath, + showOutput ? arduinoChannel.channel : null, + ["--install-boards", `${packageName}${arch && ":" + arch}${version && ":" + version}`]); if (updatingIndex) { arduinoChannel.end("Updated package index files."); @@ -548,6 +527,13 @@ Please make sure the folder is not occupied by other procedures .`); arduinoChannel.end(`Uninstalled board package - ${boardName}${os.EOL}`); } + /** + * Downloads or updates a library + * @param {string} libName - name of the library to download + * @param {string} version - version of library to download + * @param {boolean} [showOutput=true] - show raw output from command + */ + public async installLibrary(libName: string, version: string = "", showOutput: boolean = true) { arduinoChannel.show(); const updatingIndex = (libName === "dummy" && !version); @@ -557,6 +543,10 @@ Please make sure the folder is not occupied by other procedures .`); arduinoChannel.start(`Install library - ${libName}`); } try { + this.useArduinoCli() ? + await util.spawn(this._settings.commandPath, + showOutput ? arduinoChannel.channel : null, + ["lib", "install", `${libName}${version && "@" + version}`]) : await util.spawn(this._settings.commandPath, showOutput ? arduinoChannel.channel : null, ["--install-library", `${libName}${version && ":" + version}`]); @@ -769,6 +759,15 @@ Please make sure the folder is not occupied by other procedures .`); this._programmerManager = value; } + /** + * Checks if the arduino cli is being used + * @returns {bool} - true if arduino cli is being use + */ + private useArduinoCli() { + return this._settings.useArduinoCli; + // return VscodeSettings.getInstance().useArduinoCli; + } + private getProgrammerString(): string { const selectProgrammer = this.programmerManager.currentProgrammer; if (!selectProgrammer) { diff --git a/src/arduino/arduinoSettings.ts b/src/arduino/arduinoSettings.ts index 58419519..8d994d81 100644 --- a/src/arduino/arduinoSettings.ts +++ b/src/arduino/arduinoSettings.ts @@ -22,6 +22,7 @@ export interface IArduinoSettings { preferencePath: string; defaultBaudRate: number; preferences: Map; + useArduinoCli: boolean; reloadPreferences(): void; } @@ -38,18 +39,21 @@ export class ArduinoSettings implements IArduinoSettings { private _preferences: Map; + private _useArduinoCli: boolean; + public constructor() { } public async initialize() { const platform = os.platform(); this._commandPath = VscodeSettings.getInstance().commandPath; + this._useArduinoCli = VscodeSettings.getInstance().useArduinoCli; await this.tryResolveArduinoPath(); await this.tryGetDefaultBaudRate(); if (platform === "win32") { await this.updateWindowsPath(); if (this._commandPath === "") { - this._commandPath = "arduino_debug.exe"; + this._useArduinoCli ? this._commandPath = "arduino-cli.exe" : this._commandPath = "arduino_debug.exe"; } } else if (platform === "linux") { if (util.directoryExistsSync(path.join(this._arduinoPath, "portable"))) { @@ -150,6 +154,10 @@ export class ArduinoSettings implements IArduinoSettings { return this._preferences; } + public get useArduinoCli() { + return this._useArduinoCli; + } + public get defaultBaudRate() { return this._defaultBaudRate; } diff --git a/src/arduino/programmerManager.ts b/src/arduino/programmerManager.ts index a060f211..eee12725 100644 --- a/src/arduino/programmerManager.ts +++ b/src/arduino/programmerManager.ts @@ -58,46 +58,54 @@ export class ProgrammerManager { dc.programmer = chosen; } + /** + * Gets a specific programmer from the programmers list. + * If using the Arduino IDE, adds prefix "adruino:" + * @param {ProgrammerList} newProgrammer - a list of the available programmers + */ public getProgrammer(newProgrammer: ProgrammerList) { + let prefix = ""; + if (!this._settings.useArduinoCli) { + prefix = "arduino:"}; switch (newProgrammer) { case ProgrammerList["AVR ISP"]: - this._programmervalue = "arduino:avrisp"; + this._programmervalue = prefix + "avrisp"; break; case ProgrammerList["AVRISP mkII"]: - this._programmervalue = "arduino:avrispmkii"; + this._programmervalue = prefix + "avrispmkii"; break; case ProgrammerList.USBtinyISP: - this._programmervalue = "arduino:usbtinyisp"; + this._programmervalue = prefix + "usbtinyisp"; break; case ProgrammerList.ArduinoISP: - this._programmervalue = "arduino:arduinoisp"; + this._programmervalue = prefix + "arduinoisp"; break; case ProgrammerList.USBasp: - this._programmervalue = "arduino:usbasp"; + this._programmervalue = prefix + "usbasp"; break; case ProgrammerList["Parallel Programmer"]: - this._programmervalue = "arduino:parallel"; + this._programmervalue = prefix + "parallel"; break; case ProgrammerList["Arduino as ISP"]: - this._programmervalue = "arduino:arduinoasisp"; + this._programmervalue = prefix + "arduinoasisp"; break; case ProgrammerList["Arduino Gemma"]: - this._programmervalue = "arduino:usbGemma"; + this._programmervalue = prefix + "usbGemma"; break; case ProgrammerList["BusPirate as ISP"]: - this._programmervalue = "arduino:buspirate"; + this._programmervalue = prefix + "buspirate"; break; case ProgrammerList["Atmel STK500 development board"]: - this._programmervalue = "arduino:stk500"; + this._programmervalue = prefix + "stk500"; break; case ProgrammerList["Atmel JTAGICE3 (ISP mode)"]: - this._programmervalue = "arduino:jtag3isp"; + this._programmervalue = prefix + "jtag3isp"; break; case ProgrammerList["Atmel JTAGICE3 (JTAG mode)"]: - this._programmervalue = "arduino:jtag3"; + this._programmervalue = prefix + "jtag3"; break; case ProgrammerList["Atmel-ICE (AVR)"]: - this._programmervalue = "arduino:atmel_ice"; + this._programmervalue = prefix + "atmel_ice"; break; default: break; diff --git a/src/arduino/vscodeSettings.ts b/src/arduino/vscodeSettings.ts index f66e268b..14726cd8 100644 --- a/src/arduino/vscodeSettings.ts +++ b/src/arduino/vscodeSettings.ts @@ -15,6 +15,7 @@ const configKeys = { IGNORE_BOARDS: "arduino.ignoreBoards", SKIP_HEADER_PROVIDER: "arduino.skipHeaderProvider", DEFAULT_BAUD_RATE: "arduino.defaultBaudRate", + USE_ARDUINO_CLI: "arduino.useArduinoCli", }; export interface IVscodeSettings { @@ -28,6 +29,7 @@ export interface IVscodeSettings { ignoreBoards: string[]; skipHeaderProvider: boolean; defaultBaudRate: number; + useArduinoCli: boolean; updateAdditionalUrls(urls: string | string[]): void; } @@ -83,6 +85,10 @@ export class VscodeSettings implements IVscodeSettings { return this.getConfigValue(configKeys.DEFAULT_BAUD_RATE); } + public get useArduinoCli(): boolean { + return this.getConfigValue(configKeys.USE_ARDUINO_CLI); + } + public get skipHeaderProvider(): boolean { return this.getConfigValue(configKeys.SKIP_HEADER_PROVIDER); } diff --git a/src/common/platform.ts b/src/common/platform.ts index aa17cadf..1675c8b0 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -14,8 +14,8 @@ export function resolveArduinoPath(): string { return internalSysLib.resolveArduinoPath(); } -export function validateArduinoPath(arduinoPath: string): boolean { - return internalSysLib.validateArduinoPath(arduinoPath); +export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean { + return internalSysLib.validateArduinoPath(arduinoPath, useArduinoCli); } export function findFile(fileName: string, cwd: string): string { diff --git a/src/common/sys/darwin.ts b/src/common/sys/darwin.ts index 235aaf38..4d5c7e98 100644 --- a/src/common/sys/darwin.ts +++ b/src/common/sys/darwin.ts @@ -18,8 +18,9 @@ export function resolveArduinoPath(): string { return result || ""; } -export function validateArduinoPath(arduinoPath: string): boolean { - return fileExistsSync(path.join(resolveMacArduinoAppPath(arduinoPath), "/Contents/MacOS/Arduino")); +export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean { + return fileExistsSync(path.join(resolveMacArduinoAppPath(arduinoPath), useArduinoCli ? "arduino-cli" : "/Contents/MacOS/Arduino")); + } export function findFile(fileName: string, cwd: string): string { diff --git a/src/common/sys/linux.ts b/src/common/sys/linux.ts index 6c9189d7..fbb904b4 100644 --- a/src/common/sys/linux.ts +++ b/src/common/sys/linux.ts @@ -20,8 +20,8 @@ export function resolveArduinoPath(): string { return pathString || ""; } -export function validateArduinoPath(arduinoPath: string): boolean { - return fileExistsSync(path.join(arduinoPath, "arduino")); +export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean { + return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli" : "arduino")); } export function findFile(fileName: string, cwd: string): string { diff --git a/src/common/sys/win32.ts b/src/common/sys/win32.ts index dbbbaaac..ad237e27 100644 --- a/src/common/sys/win32.ts +++ b/src/common/sys/win32.ts @@ -27,8 +27,9 @@ export async function resolveArduinoPath() { return pathString; } -export function validateArduinoPath(arduinoPath: string): boolean { - return fileExistsSync(path.join(arduinoPath, "arduino_debug.exe")); +export function validateArduinoPath(arduinoPath: string, useArduinoCli = false): boolean { + return fileExistsSync(path.join(arduinoPath, useArduinoCli ? "arduino-cli.exe" : "arduino_debug.exe")); + } export function findFile(fileName: string, cwd: string): string { diff --git a/src/common/util.ts b/src/common/util.ts index 2af4b83b..696c99ea 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -200,6 +200,13 @@ export function isArduinoFile(filePath): boolean { return fileExistsSync(filePath) && (path.extname(filePath) === ".ino" || path.extname(filePath) === ".pde"); } +/** + * Send a command to arduino + * @param {string} command - base command path (either Arduino IDE or CLI) + * @param {vscode.OutputChannel} outputChannel - output display channel + * @param {string[]} [args=[]] - arguments to pass to the command + * @param {any} [options={}] - options and flags for the arguments + */ export function spawn(command: string, outputChannel: vscode.OutputChannel, args: string[] = [], options: any = {}): Thenable { return new Promise((resolve, reject) => { const stdout = ""; diff --git a/src/extension.ts b/src/extension.ts index c4112bd7..b6cf7a78 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -91,7 +91,9 @@ export async function activate(context: vscode.ExtensionContext) { const arduinoPath = arduinoContextModule.default.arduinoApp.settings.arduinoPath; const commandPath = arduinoContextModule.default.arduinoApp.settings.commandPath; - if (!arduinoPath || !validateArduinoPath(arduinoPath)) { // Pop up vscode User Settings page when cannot resolve arduino path. + const useArduinoCli = arduinoContextModule.default.arduinoApp.settings.useArduinoCli; + // Pop up vscode User Settings page when cannot resolve arduino path. + if (!arduinoPath || !validateArduinoPath(arduinoPath, useArduinoCli)) { Logger.notifyUserError("InvalidArduinoPath", new Error(constants.messages.INVALID_ARDUINO_PATH)); vscode.commands.executeCommand("workbench.action.openGlobalSettings"); } else if (!commandPath || !util.fileExistsSync(commandPath)) { @@ -152,6 +154,24 @@ export async function activate(context: vscode.ExtensionContext) { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); + registerArduinoCommand("arduino.cliUpload", async () => { + if (!status.compile) { + status.compile = "cliUpload"; + try { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Using CLI to upload...", + }, async () => { + await arduinoContextModule.default.arduinoApp.upload(false); + }); + } catch (ex) { + } + delete status.compile; + } + }, () => { + return { board: arduinoContextModule.default.boardManager.currentBoard.name }; + }); + registerArduinoCommand("arduino.setSketchFile", async () => { const sketchFileName = deviceContext.sketch; const newSketchFileName = await vscode.window.showInputBox({ @@ -177,7 +197,20 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "upload"; try { - await arduinoContextModule.default.arduinoApp.uploadUsingProgrammer(); + await arduinoContextModule.default.arduinoApp.upload(true, true); + } catch (ex) { + } + delete status.compile; + } + }, () => { + return { board: arduinoContextModule.default.boardManager.currentBoard.name }; + }); + + registerArduinoCommand("arduino.cliUploadUsingProgrammer", async () => { + if (!status.compile) { + status.compile = "cliUpload"; + try { + await arduinoContextModule.default.arduinoApp.upload(false, true); } catch (ex) { } delete status.compile; diff --git a/test/extension.test.ts b/test/extension.test.ts index e1a8de12..9c56fc41 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -55,6 +55,8 @@ suite("Arduino: Extension Tests", () => { "arduino.loadPackages", "arduino.installBoard", "arduino.setSketchFile", + "arduino.cliUpload", + "arduino.cliUploadUsingProgrammer", ]; const foundArduinoCommands = commands.filter((value) => { diff --git a/test/librarymanager.test.ts b/test/librarymanager.test.ts index 7e43af26..5ff5cc84 100644 --- a/test/librarymanager.test.ts +++ b/test/librarymanager.test.ts @@ -79,7 +79,7 @@ suite("Arduino: Library Manager.", () => { if (util.directoryExistsSync(libPath)) { done(); } else { - done(new Error("AzureIoTHub library install failure, can't find library path :" + libPath)); + done(new Error("AzureIoTHub library install failure, can't find library path: " + libPath)); } }); From d6459d0b8cbfbbaabe55f85c18178344b232ed9b Mon Sep 17 00:00:00 2001 From: Jason Tranchida Date: Tue, 8 Dec 2020 13:19:45 -0800 Subject: [PATCH 002/142] Improved handling of programmer selection (#1118) * Improved handling of programmer selection - Selected programmer is now saved to and loaded from the arduino.json file - Arduino.json is monitored for changes, and changing file will update selected programmer & ui - Programmer selection UI now shows both the friendly name of the programmer, as well as the arduino name - Minor fix to deviceContexts to fire change events after all states are modified - Layed groundwork to support querying list of programmers for the current board from arduino toolchain * fix dtr on serial open * fix dtr on serial open * fix linting * pre release v0.3.4 * Add RTS signal on serial open, Add baudrates up to 2000000 (#1142) * add buad rates up to 2M * add rts signal on serial open and buad rate change * add missing space in baud rate array * Quick fix for Intellisense (#1144) * add hardware tool path * intellisense quick fix * add hardware tool path * intellisense quick fix * fix typo * pre release v0.3.4-rc2 * bump to v0.3.4 * Option to use arduino-cli instead of Arduino IDE (#1017) * Hardcoded Arduino-CLI commands for build and upload * Added menu checkbox for Arduino CLI - Upload with programmer is still not supported - Must be created an arduino symlink pointing to arduino-cli * If Arduino-CLI, check for arduino-cli instead of arduino. Not yet supported on Windows or MacOS * Typo * Fixed CI requests * Fixed CI requests * Update src/common/sys/darwin.ts MacOS patch for arduino-cli Co-authored-by: Marc Lage-Vianna * Update src/common/sys/win32.ts Windows patch for arduino-cli Co-authored-by: Marc Lage-Vianna * Trigger * add cli option in commandPath for win32 * add cli support to board and lib managers * rename isArduinoCli to useArduinoCli * adds support for uploading using programmer * simplify getProgrammer * add CLI upload * Update src/arduino/arduino.ts Co-authored-by: Jason Tranchida * refactor uploadUsingProgrammer * fix output path for CLI upload * Update package.json * update cli option text, thanks @maddogjt * update tests Co-authored-by: giuliof Co-authored-by: Marc Lage-Vianna Co-authored-by: Adi Azulay Co-authored-by: Adi Azulay Co-authored-by: Jason Tranchida * Improved handling of programmer selection - Selected programmer is now saved to and loaded from the arduino.json file - Arduino.json is monitored for changes, and changing file will update selected programmer & ui - Programmer selection UI now shows both the friendly name of the programmer, as well as the arduino name - Minor fix to deviceContexts to fire change events after all states are modified - Layed groundwork to support querying list of programmers for the current board from arduino toolchain * add cli suppport for programmers * add documentation Co-authored-by: Marc Goodner Co-authored-by: Adi Azulay Co-authored-by: Giulio Co-authored-by: giuliof Co-authored-by: Marc Lage-Vianna Co-authored-by: Adi Azulay --- package-lock.json | 2 +- src/arduino/arduino.ts | 2 +- src/arduino/programmerManager.ts | 135 ++++++++++++------------------- src/deviceContext.ts | 6 +- 4 files changed, 56 insertions(+), 89 deletions(-) diff --git a/package-lock.json b/package-lock.json index d72659ed..a7a49f3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-arduino", - "version": "0.3.3", + "version": "0.3.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 5a59ca19..a4b9b1aa 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -165,7 +165,7 @@ export class ArduinoApp { if (this.useArduinoCli()) { args.push("--programmer", selectProgrammer) } else { - args.push("--useprogrammer", "--pref", "programmer=" + selectProgrammer) + args.push("--useprogrammer", "--pref", "programmer=arduino:" + selectProgrammer) } } diff --git a/src/arduino/programmerManager.ts b/src/arduino/programmerManager.ts index eee12725..08d3a425 100644 --- a/src/arduino/programmerManager.ts +++ b/src/arduino/programmerManager.ts @@ -4,111 +4,78 @@ import { DeviceContext } from "../deviceContext"; import { ArduinoApp } from "./arduino"; import { IArduinoSettings } from "./arduinoSettings"; -export enum ProgrammerList { - "AVR ISP", - "AVRISP mkII", - "USBtinyISP", - "ArduinoISP", - "ArduinoISP.org", - "USBasp", - "Parallel Programmer", - "Arduino as ISP", - "Arduino Gemma", - "BusPirate as ISP", - "Atmel STK500 development board", - "Atmel JTAGICE3 (ISP mode)", - "Atmel JTAGICE3 (JTAG mode)", - "Atmel-ICE (AVR)", -} - export class ProgrammerManager { - - private static _programmerManager: ProgrammerManager = null; - - private _currentprogrammer: ProgrammerList; - private _programmervalue: string; private _programmerStatusBar: vscode.StatusBarItem; + // Static list of 'available' programmers. This should be repopulated by the currently selected board type. + private _availableProgrammers = { + avrisp: "AVR ISP", + avrispmkii: "AVRISP mkII", + usbtinyisp: "USBtinyISP", + arduinoisp: "ArduinoISP", + usbasp: "USBasp", + parallel: "Parallel Programmer", + arduinoasisp: "Arduino as ISP", + usbGemma: "Arduino Gemma", + buspirate: "BusPirate as ISP", + stk500: "Atmel STK500 development board", + jtag3isp: "Atmel JTAGICE3 (ISP mode)", + jtag3: "Atmel JTAGICE3 (JTAG mode)", + atmel_ice: "Atmel-ICE (AVR)", + }; + constructor(private _settings: IArduinoSettings, private _arduinoApp: ArduinoApp) { - this._programmerStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, constants.statusBarPriority.PROGRAMMER); + this._programmerStatusBar = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + constants.statusBarPriority.PROGRAMMER, + ); this._programmerStatusBar.command = "arduino.selectProgrammer"; this._programmerStatusBar.tooltip = "Select Programmer"; - this._programmerStatusBar.text = ""; + } + + private getFriendlyName(programmer: string): string { + const friendlyName = this._availableProgrammers[programmer]; + return friendlyName ? friendlyName : programmer; } } diff --git a/src/deviceContext.ts b/src/deviceContext.ts index 2e7f18e3..5e24aea1 100644 --- a/src/deviceContext.ts +++ b/src/deviceContext.ts @@ -151,9 +151,9 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = deviceConfigJson.configuration; this._output = deviceConfigJson.output; this._debugger = deviceConfigJson["debugger"]; - this._onDidChange.fire(); this._prebuild = deviceConfigJson.prebuild; this._programmer = deviceConfigJson.programmer; + this._onDidChange.fire(); } else { Logger.notifyUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); } @@ -164,9 +164,9 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = null; this._output = null; this._debugger = null; - this._onDidChange.fire(); this._prebuild = null; this._programmer = null; + this._onDidChange.fire(); } return this; }, (reason) => { @@ -182,9 +182,9 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = null; this._output = null; this._debugger = null; - this._onDidChange.fire(); this._prebuild = null; this._programmer = null; + this._onDidChange.fire(); return this; }); From ddb804feec82d53e456832543517676893b1ac29 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 13:05:30 +0100 Subject: [PATCH 003/142] Fixed misspelled function name --- webpack.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 24ccd7b3..b18b814a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -19,7 +19,7 @@ function getEntry() { fs.copySync(p, 'out/node_modules/' + mod); } - const list = getDependeciesFromNpm(mod); + const list = getDependenciesFromNpm(mod); const moduleList = list.filter((value, index, self) => { return self.indexOf(value) === index && unbundledModule.indexOf(value) === -1 && !/^@types\//.test(value); }); @@ -31,7 +31,7 @@ function getEntry() { return entry; } -function getDependeciesFromNpm(mod) { +function getDependenciesFromNpm(mod) { let list = []; const deps = mod.dependencies; if (!deps) { @@ -39,7 +39,7 @@ function getDependeciesFromNpm(mod) { } for (const m of Object.keys(deps)) { list.push(m); - list = list.concat(getDependeciesFromNpm(deps[m])); + list = list.concat(getDependenciesFromNpm(deps[m])); } return list; } From 2e490b5f4353f202d86f42bfc8e6a395666e7f96 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 7 Feb 2020 02:08:35 +0100 Subject: [PATCH 004/142] Fixed spelling. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20cf48a1..dd27fa3f 100644 --- a/package.json +++ b/package.json @@ -243,7 +243,7 @@ }, "targetArchitecture": { "type": "string", - "description": "The architecture of the debuggee.", + "description": "The architecture of the debugger.", "default": "arm" }, "cwd": { From 57f4aa4caf97c2f28348bc9df9990597e59363f5 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 22 Feb 2020 14:25:22 +0100 Subject: [PATCH 005/142] Fix @property --- src/arduino/package.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arduino/package.ts b/src/arduino/package.ts index 09e1144c..2966e68a 100644 --- a/src/arduino/package.ts +++ b/src/arduino/package.ts @@ -179,7 +179,7 @@ export interface IBoard { /** * Reference to the platform that contains this board. - * @prop {IPlatform} + * @property {IPlatform} */ platform: IPlatform; From 5daa2b1fed43722970328344aa1f7eafbb191015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 18:13:55 +0100 Subject: [PATCH 006/142] Add missing return values --- src/arduino/arduino.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index a4b9b1aa..8053a2d5 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -211,12 +211,12 @@ export class ArduinoApp { const dc = DeviceContext.getInstance(); const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { - return; + return false; } if (!ArduinoWorkspace.rootPath) { vscode.window.showWarningMessage("Cannot find the sketch file."); - return; + return false; } if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { @@ -249,7 +249,7 @@ export class ArduinoApp { const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; + return false; } if (this.useArduinoCli()) { From 48418e20193bdd20c694a287c3873981d7a90df0 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 23 Feb 2020 20:14:28 +0100 Subject: [PATCH 007/142] Removed unused import --- src/arduino/arduinoContentProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arduino/arduinoContentProvider.ts b/src/arduino/arduinoContentProvider.ts index 6c893eb2..af0a0b1b 100644 --- a/src/arduino/arduinoContentProvider.ts +++ b/src/arduino/arduinoContentProvider.ts @@ -10,7 +10,6 @@ import * as Constants from "../common/constants"; import * as JSONHelper from "../common/cycle"; import * as Logger from "../logger/logger"; import LocalWebServer from "./localWebServer"; -import { VscodeSettings } from "./vscodeSettings"; export class ArduinoContentProvider implements vscode.TextDocumentContentProvider { private _webserver: LocalWebServer; From 0291850c9b31b339e50faad8edbfb344157b5c3e Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 5 Feb 2020 16:28:45 +0100 Subject: [PATCH 008/142] Branch for the implementation of the automagical IntelliSense configuration --- BRANCHNOTES.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 BRANCHNOTES.md diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md new file mode 100644 index 00000000..eb7080ca --- /dev/null +++ b/BRANCHNOTES.md @@ -0,0 +1,41 @@ +# IntelliSense Autoconfiguration Branch +## Problem +This branch more or less adresses [these](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) issues. + +It implements a parser which parses the output from Arduino's build process to generate a very precise `c_cpp_properties.json` which in turn hopefully renders any user interaction with this file obsolete + +## Branch Goals +### Build Output Parser +The parser which parses the relevant includes, defines, compiler paths and flags from Arduino's build output +### `c_cpp_properties.json` Generator +The generator takes the parser's output and transforms it into a valid `c_cpp_properties.json` file. + +### Configuration Flags +Provide a configuration flag which allows the user to turn this feature off - this is useful for the cases in which this magic fails or the user has a very specific setup. Although this branch tries to eliminate most of the latter cases. + +### Status +| | Tasks | +|-----:|:--------| +| **Build output parser** | :heavy_check_mark: Basic parser working (not committed yet) | +| | :white_check_mark: Support for different boards | +| | :white_check_mark: X-platform support | +| **`c_cpp_properties.json` generator** | :white_check_mark: | +| **Configuration flags** | :white_check_mark: | +| **Unit tests** | :white_check_mark: Basic parser | + +## Motivation +I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives. The old and dysfunctional Arduino CDT extension for eclipse somehow stalled (even if it was promising), Sloeber could be an option but the maintainer is disillusioned and the project is more or less dead. Platform IO IDE's license is very [restrictive](https://community.platformio.org/t/what-part-of-platformio-is-open-source-licenced/1447/2). + +Then remains vscode-arduino. It seems that it isn't completely dead - but almost. Most of the core functionality seems to work (I used it a few days now). But the biggest show stopper is the bad IntelliSense support. + +## Beer Money +You can chip in some beer money to keep me motivated - this is really appreciated. + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PVLCSRZHBJ28G&source=url) + + + +## Useful Links +* [IntelliSense issues on vscode-arduino](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) +* [`c_cpp_properties.json` reference](https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference) +* [Interactive regex debugger](https://regex101.com/) From 5debfb429d17ff16995440d14fcf544faa13d896 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 5 Feb 2020 21:33:16 +0100 Subject: [PATCH 009/142] Update documentation * More documentation on this project * Tried to identify most of the tasks * Added beer money support option --- BRANCHNOTES.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index eb7080ca..b16fda5e 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -13,29 +13,92 @@ The generator takes the parser's output and transforms it into a valid `c_cpp_pr ### Configuration Flags Provide a configuration flag which allows the user to turn this feature off - this is useful for the cases in which this magic fails or the user has a very specific setup. Although this branch tries to eliminate most of the latter cases. +### Global Tasks in vscode-arduino +Places where `c_cpp_properties.json` gets altered (list in progress) +``` +src/extension.ts + 260, 53: arduinoContextModule.default.arduinoApp.tryToUpdateIncludePaths(); +src/arduino/arduino.ts + 328, 12: public tryToUpdateIncludePaths() { + +``` + ### Status + | | Tasks | |-----:|:--------| -| **Build output parser** | :heavy_check_mark: Basic parser working (not committed yet) | -| | :white_check_mark: Support for different boards | -| | :white_check_mark: X-platform support | -| **`c_cpp_properties.json` generator** | :white_check_mark: | +| **Build output parser** | :heavy_check_mark: Basic parser working* | +| | :white_check_mark: Support for different boards | +| | :white_check_mark: X-platform support | +| **`c_cpp_properties.json` generator** | :heavy_check_mark: Basic objects* | +| | :heavy_check_mark: Basic setting of parsing result* | +| | :heavy_check_mark: Basic file input* | +| | :heavy_check_mark: Basic file output* | +| | :white_check_mark: Merging of parsing result and existing file content | | **Configuration flags** | :white_check_mark: | | **Unit tests** | :white_check_mark: Basic parser | +| | :white_check_mark: JSON input | +| | :white_check_mark: JSON output | +| | :white_check_mark: Configuration merging | +| **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | +* not committed to branch yet ## Motivation I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives. The old and dysfunctional Arduino CDT extension for eclipse somehow stalled (even if it was promising), Sloeber could be an option but the maintainer is disillusioned and the project is more or less dead. Platform IO IDE's license is very [restrictive](https://community.platformio.org/t/what-part-of-platformio-is-open-source-licenced/1447/2). Then remains vscode-arduino. It seems that it isn't completely dead - but almost. Most of the core functionality seems to work (I used it a few days now). But the biggest show stopper is the bad IntelliSense support. -## Beer Money +## Beer Money :beers: You can chip in some beer money to keep me motivated - this is really appreciated. [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PVLCSRZHBJ28G&source=url) +I will list every supporter here, thanks! + +### Supporters +5$ -> 1 :beer: +1h coding -> 20$ -> 4 :beers: +2020-02-04 Elektronik Workshop: 32 :beers: (8h coding) +2020-02-05 Elektronik Workshop: 40 :beers: (10h coding) + + ## Useful Links * [IntelliSense issues on vscode-arduino](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) * [`c_cpp_properties.json` reference](https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference) * [Interactive regex debugger](https://regex101.com/) +* [Git branch management](https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/) + +## Future Work +* Proper interactive serial terminal (this is the second major show stopper in my opinion) +* Lots of redundant code + * e.g. "upload is a superset of "verify" + * general lack of modularity - the above is the result +* It seems that this extension is pretty chaotic. Most probably some refactoring is necessary. + +---- + +## Implementation + +### Settings +Global user settings, on linux under `~/.config/Code/User/settings.json`, for instance: +```json +{ + "arduino.additionalUrls": "", + "arduino.logLevel": "verbose", + "C_Cpp.default.cppStandard": "c++11", + "C_Cpp.default.cStandard": "c11", + "arduino.disableTestingOpen": true, + "workbench.editor.enablePreview": false +} +``` +Project settings in `.vscode/arduino.json` +``` +{ + "board": "arduino:avr:nano", + "configuration": "cpu=atmega328old", + "sketch": "examples/lcdpong-butenc/lcdpong-butenc.ino", + "port": "/dev/ttyUSB0" +} +``` From 82513e4b60899f43b966787aa24ff7dbde17d5ad Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Thu, 6 Feb 2020 01:06:03 +0100 Subject: [PATCH 010/142] More documentation update * More investigations and documentation, especially on intrinsic/built-in compiler header include paths --- BRANCHNOTES.md | 48 ++++++++++++++++++- .../compilerinfo/avr-gcc_built_in_specs.txt | 33 +++++++++++++ .../xtensa-esp32-elf-gcc_built_in_specs.txt | 32 +++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt create mode 100644 doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index b16fda5e..b6afa023 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -1,6 +1,6 @@ # IntelliSense Autoconfiguration Branch ## Problem -This branch more or less adresses [these](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) issues. +This branch more or less addresses [these](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) issues (about seven). It implements a parser which parses the output from Arduino's build process to generate a very precise `c_cpp_properties.json` which in turn hopefully renders any user interaction with this file obsolete @@ -24,6 +24,7 @@ src/arduino/arduino.ts ``` ### Status +**2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) | | Tasks | |-----:|:--------| @@ -35,6 +36,7 @@ src/arduino/arduino.ts | | :heavy_check_mark: Basic file input* | | | :heavy_check_mark: Basic file output* | | | :white_check_mark: Merging of parsing result and existing file content | +| | :white_check_mark: Getting intrinsic gcc include paths (partly done)| | **Configuration flags** | :white_check_mark: | | **Unit tests** | :white_check_mark: Basic parser | | | :white_check_mark: JSON input | @@ -81,6 +83,50 @@ I will list every supporter here, thanks! ## Implementation +### `c_cpp_properties.json` Generator +#### Intrinsic Include Paths +Some include paths are built into gcc and don't have to be specified on the command line. This requires that we have to get them from the compiler. + +Just searching the compiler installation directory with something like +```bash +find ~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/ -name "include*" +``` +won't do since not all include directories are named `include`. Fortunately gcc can be queried about its configuration ([source](https://stackoverflow.com/a/6666338)): +```bash +# generally for C++ +gcc -xc++ -E -v - +# for esp32 +~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc -xc++ -E -v - < /dev/null 2>&1 | tee xtensa-esp32-elf-gcc_built_in_specs.txt +# avr +~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-gcc -xc++ -E -v - < /dev/null 2>&1 | tee avr-gcc_built_in_specs.txt +``` +The result can be inspected here: +* [xtensa-esp32-elf-gcc_built_in_specs.txt](doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt) +* [avr-gcc_built_in_specs.txt](doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt) +To show the most interesting section in the output of the above commands, for ESP32: +``` +#include "..." search starts here: +#include <...> search starts here: + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0 + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/backward + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/usr/include +End of search list. +``` +for AVR: +``` +#include "..." search starts here: +#include <...> search starts here: + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include-fixed + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include +End of search list. +``` +As one can see with the ESP32-gcc not all include directories are named `include`. Parsing of this output is pretty trivial though. + ### Settings Global user settings, on linux under `~/.config/Code/User/settings.json`, for instance: ```json diff --git a/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt b/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt new file mode 100644 index 00000000..950f0c06 --- /dev/null +++ b/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt @@ -0,0 +1,33 @@ +Using built-in specs. +Reading specs from /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/device-specs/specs-avr2 +COLLECT_GCC=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-gcc +Target: avr +Configured with: ../gcc/configure --enable-fixed-point --enable-languages=c,c++ --prefix=/home/jenkins/workspace/avr-gcc-staging/label/debian7-x86_64/objdir --disable-nls --disable-libssp --disable-libada --disable-shared --with-avrlibc=yes --with-dwarf2 --disable-doc --target=avr +Thread model: single +gcc version 7.3.0 (GCC) +COLLECT_GCC_OPTIONS='-E' '-v' '-specs=device-specs/specs-avr2' + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/avr/7.3.0/cc1plus -E -quiet -v -iprefix /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/ - -mn-flash=6 -mskip-bug -mn-flash=6 -mskip-bug -fno-rtti -fno-enforce-eh-specs -fno-exceptions +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/avr" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/backward" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/sys-include" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/avr" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/backward" +ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include" +ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include-fixed" +ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/sys-include" +ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include" +#include "..." search starts here: +#include <...> search starts here: + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include-fixed + /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include +End of search list. +# 1 "" +# 1 "" +# 1 "" +# 1 "" +COMPILER_PATH=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/avr/7.3.0/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ +LIBRARY_PATH=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/ +COLLECT_GCC_OPTIONS='-E' '-v' '-specs=device-specs/specs-avr2' diff --git a/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt b/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt new file mode 100644 index 00000000..7e39fe2e --- /dev/null +++ b/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt @@ -0,0 +1,32 @@ +Using built-in specs. +COLLECT_GCC=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc +Target: xtensa-esp32-elf +Configured with: /builds/idf/crosstool-NG/.build/src/gcc-5.2.0/configure --build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu --target=xtensa-esp32-elf --prefix=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf --with-local-prefix=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot --with-sysroot=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot --with-newlib --enable-threads=no --disable-shared --with-pkgversion='crosstool-NG crosstool-ng-1.22.0-80-g6c4433a' --disable-__cxa_atexit --enable-cxx-flags='-fno-rtti -ffunction-sections' --with-gmp=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-mpfr=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-mpc=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-isl=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-cloog=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-libelf=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --enable-lto --enable-target-optspace --without-long-double-128 --disable-libgomp --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libquadmath-support --disable-nls --enable-languages=c,c++ --disable-libstdcxx-verbose --enable-threads=posix --enable-gcov-custom-rtio +Thread model: posix +gcc version 5.2.0 (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) +COLLECT_GCC_OPTIONS='-E' '-v' + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/xtensa-esp32-elf/5.2.0/cc1plus -E -quiet -v -iprefix /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/ -isysroot /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot - +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0" +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf" +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/backward" +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/include" +ignoring nonexistent directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot/include" +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed" +ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include" +#include "..." search starts here: +#include <...> search starts here: + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0 + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/backward + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include + /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/usr/include +End of search list. +# 1 "" +# 1 "" +# 1 "" +# 1 "" +COMPILER_PATH=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/xtensa-esp32-elf/5.2.0/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ +LIBRARY_PATH=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/lib/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/lib/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/usr/lib/ +COLLECT_GCC_OPTIONS='-E' '-v' From 16e861dff92570fcaca13690b7962b3e2a46397b Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Thu, 6 Feb 2020 21:48:34 +0100 Subject: [PATCH 011/142] Added IntelliSense compiler parsing engine code * Added IntelliSense compiler parsing engine code * First injection of the compiler command parser and IntelliSense auto-configuration. Currently injected into "verify" only. * Updated branch documentation to reflect the current state of this project --- BRANCHNOTES.md | 36 ++-- src/arduino/arduino.ts | 24 ++- src/arduino/intellisense.ts | 354 ++++++++++++++++++++++++++++++++++++ src/common/util.ts | 18 +- 4 files changed, 416 insertions(+), 16 deletions(-) create mode 100644 src/arduino/intellisense.ts diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index b6afa023..7c0c283f 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -25,25 +25,31 @@ src/arduino/arduino.ts ### Status **2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) +**2020-02-06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. | | Tasks | |-----:|:--------| -| **Build output parser** | :heavy_check_mark: Basic parser working* | -| | :white_check_mark: Support for different boards | +| **Build output parser** | :heavy_check_mark: Basic parser working | +| | :heavy_check_mark: Support for different boards (done for AVR, ESP32, ESP8266) -- The code has been designed such that it is easy to write/add new parser engines (for non gcc compilers for instance) | +| | :heavy_check_mark: Getting intrinsic gcc include paths | +| | :heavy_check_mark: Handling quoted arguments | | | :white_check_mark: X-platform support | -| **`c_cpp_properties.json` generator** | :heavy_check_mark: Basic objects* | -| | :heavy_check_mark: Basic setting of parsing result* | -| | :heavy_check_mark: Basic file input* | -| | :heavy_check_mark: Basic file output* | +| **`c_cpp_properties.json` generator** | :heavy_check_mark: Basic objects | +| | :heavy_check_mark: Basic setting of parsing result | +| | :heavy_check_mark: Basic file input | +| | :heavy_check_mark: Basic file output | | | :white_check_mark: Merging of parsing result and existing file content | -| | :white_check_mark: Getting intrinsic gcc include paths (partly done)| +| | :white_check_mark: Handling inexistent files and folders | | **Configuration flags** | :white_check_mark: | -| **Unit tests** | :white_check_mark: Basic parser | +| **Unit tests** | :white_check_mark: Basic parser (known boards, match/no match)| +| | :white_check_mark: Querying of compiler built-in includes | +| | :white_check_mark: Throwing arbitrary data at parser engines | | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | -* not committed to branch yet + +`*` not committed to branch yet ## Motivation I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives. The old and dysfunctional Arduino CDT extension for eclipse somehow stalled (even if it was promising), Sloeber could be an option but the maintainer is disillusioned and the project is more or less dead. Platform IO IDE's license is very [restrictive](https://community.platformio.org/t/what-part-of-platformio-is-open-source-licenced/1447/2). @@ -63,6 +69,7 @@ I will list every supporter here, thanks! 1h coding -> 20$ -> 4 :beers: 2020-02-04 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-05 Elektronik Workshop: 40 :beers: (10h coding) +2020-02-06 Elektronik Workshop: 36 :beers: (9h coding) @@ -83,7 +90,7 @@ I will list every supporter here, thanks! ## Implementation -### `c_cpp_properties.json` Generator +### Build Output Parser #### Intrinsic Include Paths Some include paths are built into gcc and don't have to be specified on the command line. This requires that we have to get them from the compiler. @@ -96,9 +103,9 @@ won't do since not all include directories are named `include`. Fortunately gcc # generally for C++ gcc -xc++ -E -v - # for esp32 -~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc -xc++ -E -v - < /dev/null 2>&1 | tee xtensa-esp32-elf-gcc_built_in_specs.txt +~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc -xc++ -E -v - < /dev/null > xtensa-esp32-elf-gcc_built_in_specs.txt 2>&1 # avr -~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-gcc -xc++ -E -v - < /dev/null 2>&1 | tee avr-gcc_built_in_specs.txt +~/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-gcc -xc++ -E -v - < /dev/null > avr-gcc_built_in_specs.txt 2>&1 ``` The result can be inspected here: * [xtensa-esp32-elf-gcc_built_in_specs.txt](doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt) @@ -127,6 +134,9 @@ End of search list. ``` As one can see with the ESP32-gcc not all include directories are named `include`. Parsing of this output is pretty trivial though. +### `c_cpp_properties.json` Generator + + ### Settings Global user settings, on linux under `~/.config/Code/User/settings.json`, for instance: ```json @@ -148,3 +158,5 @@ Project settings in `.vscode/arduino.json` "port": "/dev/ttyUSB0" } ``` + +### Global Tasks in vscode-arduino diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 8053a2d5..7f0c45fb 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -22,6 +22,7 @@ import { arduinoChannel } from "../common/outputChannel"; import { ArduinoWorkspace } from "../common/workspace"; import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; +import { CCppProperties, CompilerCmdParser, CompilerCmdParserEngineGcc } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; /** @@ -268,7 +269,27 @@ export class ArduinoApp { arduinoChannel.show(); // we need to return the result of verify try { - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args); + const gccParserEngine = new CompilerCmdParserEngineGcc(dc.sketch); + const compilerParser = new CompilerCmdParser([gccParserEngine]); + + await util.spawn(this._settings.commandPath, + arduinoChannel.channel, + args, + undefined, + compilerParser.callback); + + // Write compiler command parser result to IntelliSense + // configuration file in case parsing was successful. + if (compilerParser.result) { + const cppPropsPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + const cppProps = new CCppProperties(cppPropsPath); + cppProps.merge(compilerParser.result); + cppProps.write(); + arduinoChannel.info("IntelliSense configuration generated successfully."); + } else { + arduinoChannel.warning("Failed to generate IntelliSense configuration."); + } + arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); return true; } catch (reason) { @@ -280,7 +301,6 @@ export class ArduinoApp { arduinoChannel.error(msg); return false; } - } public tryToUpdateIncludePaths() { diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts new file mode 100644 index 00000000..2a202b9c --- /dev/null +++ b/src/arduino/intellisense.ts @@ -0,0 +1,354 @@ + // tslint:disable: max-line-length +/* Automatic IntelliSense setup through compiler command parsing. + * + * + * Some Arduino compiler commands from different board packages: + * AVR + * /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-g++ -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10811 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/cores/arduino -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/variants/eightanaloginputs -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/libraries/Wire/src /tmp/arduino_build_776874/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_776874/sketch/lcdpong-butenc.ino.cpp.o + * ESP32 + * /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-g++ -DESP_PLATFORM "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" -DHAVE_CONFIG_H -DGCC_NOT_5_2_0=0 -DWITH_POSIX -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/config -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_trace -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_update -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/asio -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bootloader_support -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/coap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/console -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/driver -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-tls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_adc_cal -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_event -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_client -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_server -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_https_ota -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_ringbuf -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ethernet -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/expat -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fatfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freemodbus -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freertos -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/heap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/idf_test -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/jsmn -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/json -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/libsodium -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/log -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/lwip -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mbedtls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mdns -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/micro-ecc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mqtt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/newlib -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nghttp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nvs_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/openssl -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protobuf-c -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protocomm -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/pthread -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/sdmmc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/smartconfig_ack -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/soc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spi_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spiffs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcp_transport -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcpip_adapter -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ulp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/vfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wear_levelling -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wifi_provisioning -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wpa_supplicant -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/xtensa-debug-module -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32-camera -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fb_gfx -std=gnu++11 -Os -g3 -Wpointer-arith -fexceptions -fstack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror=all -Wextra -Wno-error=maybe-uninitialized -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-missing-field-initializers -Wno-sign-compare -fno-rtti -MMD -c -DF_CPU=240000000L -DARDUINO=10811 -DARDUINO_ESP32_DEV -DARDUINO_ARCH_ESP32 "-DARDUINO_BOARD=\"ESP32_DEV\"" "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" -DESP32 -DCORE_DEBUG_LEVEL=0 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/variants/doitESP32devkitV1 -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Wire/src /tmp/arduino_build_744383/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_744383/sketch/lcdpong-butenc.ino.cpp.o + * ESP8266 + * /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-g++ -DESP_PLATFORM "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" -DHAVE_CONFIG_H -DGCC_NOT_5_2_0=0 -DWITH_POSIX -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/config -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_trace -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_update -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/asio -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bootloader_support -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/coap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/console -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/driver -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-tls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_adc_cal -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_event -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_client -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_server -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_https_ota -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_ringbuf -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ethernet -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/expat -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fatfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freemodbus -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freertos -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/heap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/idf_test -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/jsmn -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/json -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/libsodium -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/log -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/lwip -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mbedtls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mdns -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/micro-ecc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mqtt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/newlib -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nghttp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nvs_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/openssl -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protobuf-c -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protocomm -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/pthread -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/sdmmc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/smartconfig_ack -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/soc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spi_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spiffs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcp_transport -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcpip_adapter -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ulp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/vfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wear_levelling -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wifi_provisioning -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wpa_supplicant -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/xtensa-debug-module -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32-camera -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fb_gfx -std=gnu++11 -Os -g3 -Wpointer-arith -fexceptions -fstack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror=all -Wextra -Wno-error=maybe-uninitialized -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-missing-field-initializers -Wno-sign-compare -fno-rtti -MMD -c -DF_CPU=240000000L -DARDUINO=10811 -DARDUINO_ESP32_DEV -DARDUINO_ARCH_ESP32 "-DARDUINO_BOARD=\"ESP32_DEV\"" "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" -DESP32 -DCORE_DEBUG_LEVEL=0 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/variants/doitESP32devkitV1 -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Wire/src /tmp/arduino_build_470717/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_470717/sketch/lcdpong-butenc.ino.cpp.o + */ +// tslint:enable: max-line-length + +import { spawnSync } from "child_process"; +import * as fs from "fs"; +import * as path from "path"; + +/** + * Data structure carrying the output from a parsed compiler command. + * All compiler specific option prefixes are removed for includes and + * defines. + */ +export class CompilerCmdParserResult { + public includes: Array = []; + public defines: Array = []; + public options: Array = []; + public compiler: string = ""; + /** Dropped arguments like -c -Ox */ + public trash: Array = []; +}; + +export type CompilerCmdParserSearchType = string | RegExp; + +/** + * Base class for any compiler command parser engine. + * If someone needs to write an engine: this is the base class. + * For further inspiration take a look at the implementation of + * CompilerCmdParserEngineGcc. + */ +export abstract class CompilerCmdParserEngine { + /** + * This array should contain the patterns which should match on + * a valid compiler command line to identify the compiler command. + * To be set by the derived class. + */ + protected _match: CompilerCmdParserSearchType[]; + /** + * This array should contain the patterns which should _NOT_ + * match on a valid compiler command line to identify the + * compiler command. + * To be set by the derived class. + */ + protected _nomatch: CompilerCmdParserSearchType[]; + /** + * This function checks if the command line matches the + * requirements given through _match and _nomatch and invokes + * the parse function in case of a match. + * @returns If match was found and parsing was successful + * it returns the result else undefined. + */ + public match(line: string): CompilerCmdParserResult { + // check for regexes that must match + for (const re of this._match) { + if (line.search(re) === -1) { + return undefined; + } + } + // check for regexes that mustn't match + for (const re of this._nomatch) { + if (line.search(re) !== -1) { + return undefined; + } + } + return this.parse(line); + } + + /** + * The parsing function of a matched compiler command line. + * If all conditions hold true (all _match are found and all _nomatch + * are not found), this parsing function is invoked. + * + * Here the derived class has to implement its parsing magic + * to extract the desired includes, defines, compiler flags + * and the compiler command. + * + * @param line A string containing a compiler command line candidate. + * @returns A valid parsing result in case parsing was successful + * and undefined in case it failed fatally. + */ + protected abstract parse(line: string): CompilerCmdParserResult; +} + +/** + * Compiler command parsing engine for gcc compilers. + */ +export class CompilerCmdParserEngineGcc + extends CompilerCmdParserEngine { + constructor(sketch: string) { + super(); + + // TODO: windows and osx variants + + this._nomatch = + [ + // make sure Arduino's not testing libraries + /-o\s\/dev\/null/, + ]; + this._match = + [ + // make sure we're running g++ + /-g\+\+\s+/, + // make sure we're compiling + /\s+-c\s+/, + // check if we're compiling the main sketch + path.basename(sketch) + ".cpp.o", + ]; + } + protected parse(line: string): CompilerCmdParserResult { + const result = new CompilerCmdParserResult(); + const args = line.split(/\s+/); + + for (let arg of args) { + + // drop empty arguments + if (!arg.length) { + continue; + } + + // unpack quoted elements like + // + // "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" + // "-DARDUINO_BOARD=\"ESP32_DEV\"" + // "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" + const packed = arg.match(/^"(.+)"$/); + if (packed) { + arg = packed[1]; + } + + // extract defines + const define = arg.match(/^-D(.+)/); + if (define) { + result.defines.push(define[1]); + continue; + } + + // extract includes + const include = arg.match(/^-I(.+)/); + if (include) { + result.includes.push(include[1]); + continue; + } + + // extract the compiler executable + const c = arg.match(/g\+\+$/); + if (c) { + result.compiler = arg; + continue; + } + + // filter out option trash + const t = arg.match(/^-o|^-O|^-g|^-c|cpp(?:\.o){0,1}$/); + if (t) { + result.trash.push(arg); + continue; + } + + // collect options + const o = arg.match(/^-/); + if (o) { + result.options.push(arg); + continue; + } + + // collect the rest + result.trash.push(arg); + } + + // Query compiler for intrinsic/built-in include paths + if (result.compiler.length > 0) { + + // TODO: Windows + + // Spawn synchronous child process and run bash command + // Source: https://stackoverflow.com/a/6666338 + const compilerinfocmd = `${result.compiler} -xc++ -E -v - < /dev/null 2>&1`; + const child = spawnSync("bash", ["-c", compilerinfocmd], { encoding : "utf8" }); + + if (child.error || child.status !== 0) { + // TODO: report the execution failure + } else { + // Now we look for + // + // #include "..." search starts here: + // #include <...> search starts here: + // ...(include directories list)... + // End of search list. + // + // and extract the include directory list. Could be that some gcc + // even lists something under + // + // #include "..." search starts here: + // + // but I havn't seen it so far. + const includeregex = /^#include\s+<\.\.\.>\ssearch\sstarts\shere\:$(.+)^End\sof\ssearch\slist\.$/ms; + const match = child.stdout.match(includeregex); + if (match) { + // Split list by newlines. Should be platform independent + let lines = match[1].split(/\s*(?:\r|\n)\s*/); + // Filter out empty elements (in most cases only the last element) + lines = lines.filter((val: string) => val !== ""); + // Add built-in includes to command line includes + result.includes = [...result.includes, ...lines]; + } else { + // TODO: issue info that include section has not been found + } + } + } + return result; + } +} + +/** + * A compiler command parser. + * Takes compiler commands line by line and tries to find the compile command + * for the main .ino sketch. From that it tries to extract all includes, + * defines, options and the compiler command itself. + * + * TODO: Make it more generic to support other compilers than gcc + */ +export class CompilerCmdParser { + private _result: CompilerCmdParserResult; + private _engines: CompilerCmdParserEngine[]; + + /** + * Create a compiler command parser. + * Sets up parsing operation. + * @param engines Parsing engines for different compilers + */ + constructor(engines: CompilerCmdParserEngine[]) { + this._engines = engines; + } + /** + * Returns the parsing result. + * Returns undefined when the parser fails or when the + * parser didn't run. + */ + get result(): CompilerCmdParserResult { + return this._result; + } + /** + * Takes a command line and tries to parse it. + * + * @param line Compiler command line candidate. + * @returns The parsing result if the command line was parsed + * successfully. It returns undefined if no match was found or + * parsing failed. + */ + public parse(line: string): boolean { + for (const engine of this._engines) { + this._result = engine.match(line); + if (this._result) { + return true; + } + } + return false; + } + /** + * Returns a callback which can be passed on to other functions + * to call. For instance from stdout callbacks of child processes. + */ + public get callback(): (line: string) => void { + return (line: string) => { + if (!this._result) { + this.parse(line); + } + } + } +} + +/** + * Class representing the contents of the IntelliSense + * c_cpp_properties.json configuration file. + * + * @see https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference + */ +export class CCppPropertiesConfiguration { + public name: string = "Arduino"; + public compilerPath: string = ""; + public compilerArgs: string [] = []; + public intelliSenseMode: string = "gcc-x64"; // since we're using arduino's compiler + public includePath: string[] = []; + public forcedInclude: string[] = []; + public cStandard: string = "c11"; + public cppStandard: string = "c++11"; // as of 1.8.11 arduino is on C++11 + public defines: string[] = []; + + constructor(result: CompilerCmdParserResult) { + this.compilerPath = result.compiler; + this.compilerArgs = result.options; + this.includePath = result.includes; + this.defines = result.defines; + } +} + +export class CCppPropertiesContent { + public configurations: Array + + constructor(result: CompilerCmdParserResult) { + this.configurations = [new CCppPropertiesConfiguration(result)]; + } +}; + +export class CCppProperties { + public proppath: string; + public propFileContent: CCppPropertiesContent; + + constructor(proppath: string) { + this.proppath = proppath; + } + + public read() { + if (!fs.existsSync(this.proppath)) { + return; + } + const propFileContentPlain = fs.readFileSync(this.proppath, "utf8"); + + // NOTE: in JSON backslashes are escaped to \\\\ + + this.propFileContent = JSON.parse(propFileContentPlain) as CCppPropertiesContent; + } + public merge(result: CompilerCmdParserResult) { + const pc = new CCppPropertiesContent(result); + + // TODO: + // * merge with existing configuration if desired + // * check if contents changed after merging + // + + this.propFileContent = pc; + } + + public write() { + // NOTE: in JSON backslashes are escaped to \\\\ + // TODO: + // * check if path exists, create if necessary + // * write file only if modified + + if (this.propFileContent) { + const content = JSON.stringify(this.propFileContent, null, 4); + fs.writeFileSync(this.proppath, content); + } + } +} diff --git a/src/common/util.ts b/src/common/util.ts index 696c99ea..4344f5ad 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -200,14 +200,23 @@ export function isArduinoFile(filePath): boolean { return fileExistsSync(filePath) && (path.extname(filePath) === ".ino" || path.extname(filePath) === ".pde"); } +// TODO: remove output channel and just provide callback hooks for stdout and +// stderr /** * Send a command to arduino * @param {string} command - base command path (either Arduino IDE or CLI) * @param {vscode.OutputChannel} outputChannel - output display channel * @param {string[]} [args=[]] - arguments to pass to the command * @param {any} [options={}] - options and flags for the arguments + * @param {(string) => {}} - callback for stdout text */ -export function spawn(command: string, outputChannel: vscode.OutputChannel, args: string[] = [], options: any = {}): Thenable { +export function spawn( + command: string, + outputChannel: vscode.OutputChannel, + args: string[] = [], + options: any = {}, + stdoutCallback?: (s: string) => void, +): Thenable { return new Promise((resolve, reject) => { const stdout = ""; const stderr = ""; @@ -228,7 +237,12 @@ export function spawn(command: string, outputChannel: vscode.OutputChannel, args if (outputChannel) { child.stdout.on("data", (data: Buffer) => { - outputChannel.append(decodeData(data, codepage)); + const decoded = decodeData(data, codepage); + if (stdoutCallback) { + stdoutCallback(decoded); + } else { + outputChannel.append(decoded); + } }); child.stderr.on("data", (data: Buffer) => { outputChannel.append(decodeData(data, codepage)); From 79bd4762d825682665bc3735f085cba01934fb30 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Thu, 6 Feb 2020 23:29:32 +0100 Subject: [PATCH 012/142] Improved line splitting of built-in include parser after commenting on a pull request :) --- src/arduino/intellisense.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts index 2a202b9c..706104e7 100644 --- a/src/arduino/intellisense.ts +++ b/src/arduino/intellisense.ts @@ -206,7 +206,7 @@ export class CompilerCmdParserEngineGcc const match = child.stdout.match(includeregex); if (match) { // Split list by newlines. Should be platform independent - let lines = match[1].split(/\s*(?:\r|\n)\s*/); + let lines = match[1].split(/\s*(?:\r|\r\n|\n)\s*/); // Filter out empty elements (in most cases only the last element) lines = lines.filter((val: string) => val !== ""); // Add built-in includes to command line includes From baaf069352eff3f8502e15ffdc0f7de906a3eb8d Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Thu, 6 Feb 2020 23:30:36 +0100 Subject: [PATCH 013/142] Pre-build command updates * Moved pre-build command into separate member function to reduce code replication, better maintainablility and readability * Added pre-build command to "upload using programmer" since it was (probably unintentional) missing there --- src/arduino/arduino.ts | 55 +++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 7f0c45fb..a558a9cf 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -139,16 +139,8 @@ export class ArduinoApp { UsbDetector.getInstance().pauseListening(); await vscode.workspace.saveAll(false); - if (dc.prebuild) { - arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); - const prebuildargs = dc.prebuild.split(" "); - const prebuildCommand = prebuildargs.shift(); - try { - await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, { shell: true, cwd: ArduinoWorkspace.rootPath }); - } catch (ex) { - arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); - return; - } + if (!await this.runPreBuildCommand(dc)) { + return; } if (!compile && !this.useArduinoCli()) { @@ -174,6 +166,10 @@ export class ArduinoApp { args.push("--port", dc.port); } args.push(appPath); + if (!await this.runPreBuildCommand(dc)) { + return; + } + if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); } @@ -228,16 +224,8 @@ export class ArduinoApp { arduinoChannel.start(`Verify sketch - ${dc.sketch}`); - if (dc.prebuild) { - arduinoChannel.info(`Run prebuild command: ${dc.prebuild}`); - const prebuildargs = dc.prebuild.split(" "); - const prebuildCommand = prebuildargs.shift(); - try { - await util.spawn(prebuildCommand, arduinoChannel.channel, prebuildargs, { shell: true, cwd: ArduinoWorkspace.rootPath }); - } catch (ex) { - arduinoChannel.error(`Run prebuild failed: \n${ex.error}`); - return; - } + if (!await this.runPreBuildCommand(dc)) { + return false; } const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); @@ -779,6 +767,33 @@ export class ArduinoApp { this._programmerManager = value; } + /** + * Runs the pre build command. + * Usually before one of + * * verify + * * upload + * * upload using programmer + * @param dc Device context prepared during one of the above actions + * @returns True if successful, false on error. + */ + protected async runPreBuildCommand(dc: DeviceContext): Promise { + if (dc.prebuild) { + arduinoChannel.info(`Running pre-build command: ${dc.prebuild}`); + const prebuildargs = dc.prebuild.split(" "); + const prebuildCommand = prebuildargs.shift(); + try { + await util.spawn(prebuildCommand, + arduinoChannel.channel, + prebuildargs, + { shell: true, cwd: ArduinoWorkspace.rootPath }); + } catch (ex) { + arduinoChannel.error(`Running pre-build command failed: ${os.EOL}${ex.error}`); + return false; + } + } + return true; + } + /** * Checks if the arduino cli is being used * @returns {bool} - true if arduino cli is being use From bc2b29228ad7dd21682da335436095ca9d90d296 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Thu, 6 Feb 2020 23:34:40 +0100 Subject: [PATCH 014/142] Documentation updates * Notes regarding settings * Notes where to run the auto-generation --- BRANCHNOTES.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 7c0c283f..e9a84633 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -48,6 +48,7 @@ src/arduino/arduino.ts | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | +| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies | `*` not committed to branch yet @@ -150,7 +151,7 @@ Global user settings, on linux under `~/.config/Code/User/settings.json`, for in } ``` Project settings in `.vscode/arduino.json` -``` +```json { "board": "arduino:avr:nano", "configuration": "cpu=atmega328old", @@ -158,5 +159,10 @@ Project settings in `.vscode/arduino.json` "port": "/dev/ttyUSB0" } ``` - +The global settings are [here](src/arduino/vscodeSettings.ts) +```ts +if (VscodeSettings.getInstance().logLevel === "verbose") { + args.push("--verbose"); +} +``` ### Global Tasks in vscode-arduino From 3da2f7600c6bcb9b4b66dcbfb6b82a23e9c9dd7f Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 7 Feb 2020 02:55:26 +0100 Subject: [PATCH 015/142] IntelliSense on/off configuration and compiler parser injection preparation * Added a global configuration switch which allows the IntelliSense auto-configuration to be turned off * Prepared the compiler parser code to be injected into "upload" and "upload using programmer" without overhead * Updated branch documentation --- BRANCHNOTES.md | 35 +++++++++++---------- package.json | 5 +++ src/arduino/arduino.ts | 58 ++++++++++++++++++++++++++--------- src/arduino/intellisense.ts | 11 ++++++- src/arduino/vscodeSettings.ts | 6 ++++ 5 files changed, 83 insertions(+), 32 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index e9a84633..5f99989f 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -40,7 +40,9 @@ src/arduino/arduino.ts | | :heavy_check_mark: Basic file output | | | :white_check_mark: Merging of parsing result and existing file content | | | :white_check_mark: Handling inexistent files and folders | -| **Configuration flags** | :white_check_mark: | +| **Configuration flags** | :heavy_check_mark: Disable flag for IntelliSense auto-config | +| | :white_check_mark: Perhaps a general IntelliSense flag `{off/manual, auto, oldstyle}` whereas the old can be removed at some point | +| | :white_check_mark: Fine grained IntelliSense control: Global en-/disable and project override. This is probably more useful since the most boards will hopefully work and for the very special cases the user can disable the feature for this single project but still can enjoy it within his regular projects. | | **Unit tests** | :white_check_mark: Basic parser (known boards, match/no match)| | | :white_check_mark: Querying of compiler built-in includes | | | :white_check_mark: Throwing arbitrary data at parser engines | @@ -48,7 +50,7 @@ src/arduino/arduino.ts | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | -| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies | +| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? | `*` not committed to branch yet @@ -139,30 +141,31 @@ As one can see with the ESP32-gcc not all include directories are named `include ### Settings -Global user settings, on linux under `~/.config/Code/User/settings.json`, for instance: +#### Global Settings +Under linux at `~/.config/Code/User/settings.json`, for instance: ```json { "arduino.additionalUrls": "", "arduino.logLevel": "verbose", - "C_Cpp.default.cppStandard": "c++11", - "C_Cpp.default.cStandard": "c11", "arduino.disableTestingOpen": true, "workbench.editor.enablePreview": false } ``` -Project settings in `.vscode/arduino.json` +Code: [src/arduino/arduinoSettings.ts](src/arduino/arduinoSettings.ts) +Code: [src/arduino/vscodeSettings.ts](src/arduino/vscodeSettings.ts) +Validator: [package.json](package.json) + +#### Project Settings +Path in project `.vscode/arduino.json` ```json { - "board": "arduino:avr:nano", - "configuration": "cpu=atmega328old", - "sketch": "examples/lcdpong-butenc/lcdpong-butenc.ino", - "port": "/dev/ttyUSB0" -} -``` -The global settings are [here](src/arduino/vscodeSettings.ts) -```ts -if (VscodeSettings.getInstance().logLevel === "verbose") { - args.push("--verbose"); + "board": "arduino:avr:nano", + "configuration": "cpu=atmega328old", + "sketch": "examples/lcdpong-butenc/lcdpong-butenc.ino", + "port": "/dev/ttyUSB0" } ``` +Code: [src/deviceContext.ts](src/deviceContext.ts) +Validator: [misc/arduinoValidator.json](misc/arduinoValidator.json) + ### Global Tasks in vscode-arduino diff --git a/package.json b/package.json index dd27fa3f..24b961ec 100644 --- a/package.json +++ b/package.json @@ -504,6 +504,11 @@ "arduino.defaultBaudRate": { "type": "number", "default": 115200 + }, + "arduino.disableIntelliSenseAutoGen": { + "type": "boolean", + "default": false, + "description": "When disabled vscode-arduino will not auto-generate an IntelliSense configuration (i.e. c_cpp_properties.json) by analyzing the compiler output." } } }, diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index a558a9cf..6c70285f 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -255,29 +255,19 @@ export class ArduinoApp { } arduinoChannel.show(); - // we need to return the result of verify + try { - const gccParserEngine = new CompilerCmdParserEngineGcc(dc.sketch); - const compilerParser = new CompilerCmdParser([gccParserEngine]); + const compilerParserContext = this.makeCompilerParserContext(dc); await util.spawn(this._settings.commandPath, arduinoChannel.channel, args, undefined, - compilerParser.callback); + compilerParserContext.callback); - // Write compiler command parser result to IntelliSense - // configuration file in case parsing was successful. - if (compilerParser.result) { - const cppPropsPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); - const cppProps = new CCppProperties(cppPropsPath); - cppProps.merge(compilerParser.result); - cppProps.write(); - arduinoChannel.info("IntelliSense configuration generated successfully."); - } else { - arduinoChannel.warning("Failed to generate IntelliSense configuration."); + if (compilerParserContext.conclude) { + compilerParserContext.conclude(); } - arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); return true; } catch (reason) { @@ -803,6 +793,44 @@ export class ArduinoApp { // return VscodeSettings.getInstance().useArduinoCli; } + /** + * Creates a context which is used for compiler command parsing + * during building (verify, upload, ...). + * + * This context makes sure that it can be used in those sections + * without having to check whether this feature is en- or disabled + * and keeps the calling context more readable. + * + * @param dc The device context of the caller. + */ + private makeCompilerParserContext(dc: DeviceContext) + : { callback: (s: string) => void; conclude: () => void; } { + if (!VscodeSettings.getInstance().disableIntelliSenseAutoGen) { + + // setup the parser with its engines + const gccParserEngine = new CompilerCmdParserEngineGcc(dc.sketch); + const compilerParser = new CompilerCmdParser([gccParserEngine]); + + // set up the function to be called after parsing + const _conclude = () => { + const cppPropsPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + if (compilerParser.processResult(cppPropsPath)) { + arduinoChannel.info("IntelliSense configuration generated successfully."); + } else { + arduinoChannel.warning("Failed to generate IntelliSense configuration."); + } + }; + return { + callback: compilerParser.callback, + conclude: _conclude, + }; + } + return { + callback: undefined, + conclude: undefined, + } + }; + private getProgrammerString(): string { const selectProgrammer = this.programmerManager.currentProgrammer; if (!selectProgrammer) { diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts index 706104e7..8b322056 100644 --- a/src/arduino/intellisense.ts +++ b/src/arduino/intellisense.ts @@ -26,7 +26,7 @@ export class CompilerCmdParserResult { public defines: Array = []; public options: Array = []; public compiler: string = ""; - /** Dropped arguments like -c -Ox */ + /** Dropped arguments like -c -Ox -o, the input and output file. */ public trash: Array = []; }; @@ -276,6 +276,15 @@ export class CompilerCmdParser { } } } + public processResult(configPath: string): boolean { + if (this._result) { + const cppProps = new CCppProperties(configPath); + cppProps.merge(this._result); + cppProps.write(); + return true; + } + return false; + } } /** diff --git a/src/arduino/vscodeSettings.ts b/src/arduino/vscodeSettings.ts index 14726cd8..74f7bedb 100644 --- a/src/arduino/vscodeSettings.ts +++ b/src/arduino/vscodeSettings.ts @@ -16,6 +16,7 @@ const configKeys = { SKIP_HEADER_PROVIDER: "arduino.skipHeaderProvider", DEFAULT_BAUD_RATE: "arduino.defaultBaudRate", USE_ARDUINO_CLI: "arduino.useArduinoCli", + DISABLE_INTELLISENSE_AUTO_GEN: "arduino.disableIntelliSenseAutoGen", }; export interface IVscodeSettings { @@ -30,6 +31,7 @@ export interface IVscodeSettings { skipHeaderProvider: boolean; defaultBaudRate: number; useArduinoCli: boolean; + disableIntelliSenseAutoGen: boolean; updateAdditionalUrls(urls: string | string[]): void; } @@ -93,6 +95,10 @@ export class VscodeSettings implements IVscodeSettings { return this.getConfigValue(configKeys.SKIP_HEADER_PROVIDER); } + public get disableIntelliSenseAutoGen(): boolean { + return this.getConfigValue(configKeys.DISABLE_INTELLISENSE_AUTO_GEN); + } + public async updateAdditionalUrls(value) { await this.setConfigValue(configKeys.ADDITIONAL_URLS, value, true); } From ce1b656c50a24362b69c3493817a746dfd97b280 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 7 Feb 2020 03:11:14 +0100 Subject: [PATCH 016/142] Added two more TODOs to branch documentation --- BRANCHNOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 5f99989f..4c399fe9 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -51,6 +51,8 @@ src/arduino/arduino.ts | | :white_check_mark: Configuration merging | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | | | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? | +| | :white_check_mark: Document configuration settings in [README.md](README.md) | +| | :white_check_mark: Document features in [README.md](README.md) | `*` not committed to branch yet From 59c78cb31382416a24a0b1b950f03d3e1ad5e318 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 7 Feb 2020 03:15:53 +0100 Subject: [PATCH 017/142] Added even more TODOs to branch documentation --- BRANCHNOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 4c399fe9..c1291a75 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -49,10 +49,12 @@ src/arduino/arduino.ts | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | +| | :white_check_mark: Write configuration on change only | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | | | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? | | | :white_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) | +| | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet From 4a91647ceeb469151f3960607fe616ca12f7629e Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 7 Feb 2020 03:19:09 +0100 Subject: [PATCH 018/142] Moved TODO in branch documentation --- BRANCHNOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index c1291a75..17b5712f 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -40,6 +40,7 @@ src/arduino/arduino.ts | | :heavy_check_mark: Basic file output | | | :white_check_mark: Merging of parsing result and existing file content | | | :white_check_mark: Handling inexistent files and folders | +| | :white_check_mark: Write configuration on change only | | **Configuration flags** | :heavy_check_mark: Disable flag for IntelliSense auto-config | | | :white_check_mark: Perhaps a general IntelliSense flag `{off/manual, auto, oldstyle}` whereas the old can be removed at some point | | | :white_check_mark: Fine grained IntelliSense control: Global en-/disable and project override. This is probably more useful since the most boards will hopefully work and for the very special cases the user can disable the feature for this single project but still can enjoy it within his regular projects. | @@ -49,7 +50,6 @@ src/arduino/arduino.ts | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | -| | :white_check_mark: Write configuration on change only | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | | | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? | | | :white_check_mark: Document configuration settings in [README.md](README.md) | From d3372955031ce673c09b2d232bcb9cc51232d8c8 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 03:10:06 +0100 Subject: [PATCH 019/142] Documenting ... --- BRANCHNOTES.md | 58 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 17b5712f..d7a81581 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -14,14 +14,6 @@ The generator takes the parser's output and transforms it into a valid `c_cpp_pr Provide a configuration flag which allows the user to turn this feature off - this is useful for the cases in which this magic fails or the user has a very specific setup. Although this branch tries to eliminate most of the latter cases. ### Global Tasks in vscode-arduino -Places where `c_cpp_properties.json` gets altered (list in progress) -``` -src/extension.ts - 260, 53: arduinoContextModule.default.arduinoApp.tryToUpdateIncludePaths(); -src/arduino/arduino.ts - 328, 12: public tryToUpdateIncludePaths() { - -``` ### Status **2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) @@ -41,19 +33,21 @@ src/arduino/arduino.ts | | :white_check_mark: Merging of parsing result and existing file content | | | :white_check_mark: Handling inexistent files and folders | | | :white_check_mark: Write configuration on change only | -| **Configuration flags** | :heavy_check_mark: Disable flag for IntelliSense auto-config | -| | :white_check_mark: Perhaps a general IntelliSense flag `{off/manual, auto, oldstyle}` whereas the old can be removed at some point | -| | :white_check_mark: Fine grained IntelliSense control: Global en-/disable and project override. This is probably more useful since the most boards will hopefully work and for the very special cases the user can disable the feature for this single project but still can enjoy it within his regular projects. | +| | :white_check_mark: Option to backup old configurations? | +| **Configuration flags** | :heavy_check_mark: Provide global disable flag for IntelliSense auto-config | +| | :white_check_mark: Provide project specific override for the global flag - most users will likely use the default setup and disable auto-generation for very specific projects | | **Unit tests** | :white_check_mark: Basic parser (known boards, match/no match)| | | :white_check_mark: Querying of compiler built-in includes | | | :white_check_mark: Throwing arbitrary data at parser engines | | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | -| **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` | -| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? | +| **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | +| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | | | :white_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) | +| | :white_check_mark: How to handle compilation failure? Only set if more comprehensive | +| | :white_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and tests can be run without the heavy vscode-arduino rucksack | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet @@ -85,27 +79,35 @@ I will list every supporter here, thanks! * [`c_cpp_properties.json` reference](https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference) * [Interactive regex debugger](https://regex101.com/) * [Git branch management](https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/) +* [Collapsible Markdown](https://gist.githubusercontent.com/joyrexus/16041f2426450e73f5df9391f7f7ae5f/raw/f774f242feff6bae4a5be7d6c71aa5df2e3fcb0e/README.md) +## Issues Concerning this Project + * https://github.com/Microsoft/vscode-cpptools/issues/1750 + * Problems with IntelliSense itself https://github.com/microsoft/vscode-cpptools/issues/1034 + * Logging for IntelliSense https://code.visualstudio.com/docs/cpp/enable-logging-cpp ## Future Work * Proper interactive serial terminal (this is the second major show stopper in my opinion) * Lots of redundant code * e.g. "upload is a superset of "verify" * general lack of modularity - the above is the result * It seems that this extension is pretty chaotic. Most probably some refactoring is necessary. - +* Possibility to jump to compilation errors from compiler output and highlight compiler errors ---- ## Implementation +**Note** Check this vscode feature: +``` +Configuration provider +The ID of a VS Code extension that can provide IntelliSense configuration information for source files. For example, use the VS Code extension ID ms-vscode.cmake-tools to provide configuration information from the CMake Tools extension. +``` ### Build Output Parser #### Intrinsic Include Paths -Some include paths are built into gcc and don't have to be specified on the command line. This requires that we have to get them from the compiler. - -Just searching the compiler installation directory with something like +Some include paths are built into gcc and don't have to be specified on the command line. Just searching the compiler installation directory with something like ```bash find ~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/ -name "include*" ``` -won't do since not all include directories are named `include`. Fortunately gcc can be queried about its configuration ([source](https://stackoverflow.com/a/6666338)): +won't do since not all include directories are named `include`. Fortunately gcc can be queried about its configuration ([source](https://stackoverflow.com/a/6666338)) -- built-in include paths are part of it: ```bash # generally for C++ gcc -xc++ -E -v - @@ -117,6 +119,7 @@ gcc -xc++ -E -v - The result can be inspected here: * [xtensa-esp32-elf-gcc_built_in_specs.txt](doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt) * [avr-gcc_built_in_specs.txt](doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt) + To show the most interesting section in the output of the above commands, for ESP32: ``` #include "..." search starts here: @@ -172,4 +175,21 @@ Path in project `.vscode/arduino.json` Code: [src/deviceContext.ts](src/deviceContext.ts) Validator: [misc/arduinoValidator.json](misc/arduinoValidator.json) -### Global Tasks in vscode-arduino +### General Tasks +#### Removing existing Attempts which mess with c_cpp_properties.json or Intellisense + +Remove these as they are helpless attempts to get IntelliSense working: +```ts +//src/arduino/arduino.ts + tryToUpdateIncludePaths() + addLibPath(libraryPath: string) + getDefaultForcedIncludeFiles() + // parts in + openExample() + + //probably not needed anymore: + getDefaultPackageLibPaths() + +``` +Remove this as this messes in an unpredictable and helpless way with Intellisense +[src/langService/completionProvider.ts](src/langService/completionProvider.ts) From 0865c97b571fa63c05d4b950453bb1c64b14728d Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 12:28:25 +0100 Subject: [PATCH 020/142] Moved compiler parser to cocopa package for better testability and maintainability --- BRANCHNOTES.md | 6 +- package-lock.json | 45 ++++- package.json | 5 +- src/arduino/arduino.ts | 8 +- src/arduino/intellisense.ts | 363 ------------------------------------ webpack.config.js | 10 + 6 files changed, 63 insertions(+), 374 deletions(-) delete mode 100644 src/arduino/intellisense.ts diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index d7a81581..0255d506 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -36,8 +36,8 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: Option to backup old configurations? | | **Configuration flags** | :heavy_check_mark: Provide global disable flag for IntelliSense auto-config | | | :white_check_mark: Provide project specific override for the global flag - most users will likely use the default setup and disable auto-generation for very specific projects | -| **Unit tests** | :white_check_mark: Basic parser (known boards, match/no match)| -| | :white_check_mark: Querying of compiler built-in includes | +| **Unit tests** | :heavy_check_mark: Basic parser (known boards, match/no match)| +| | :heavy_check_mark: Querying of compiler built-in includes (Note: to be changed to generic compiler such that Arduino is not necessary for unit testing) | | | :white_check_mark: Throwing arbitrary data at parser engines | | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | @@ -47,7 +47,7 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) | | | :white_check_mark: How to handle compilation failure? Only set if more comprehensive | -| | :white_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and tests can be run without the heavy vscode-arduino rucksack | +| | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet diff --git a/package-lock.json b/package-lock.json index a7a49f3d..69660e47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1410,6 +1410,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -1556,6 +1561,16 @@ "semver": "^5.4.1" } }, + "cocopa": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.3.tgz", + "integrity": "sha512-zFHtJcwkobUCNkeBwxyftvsQ4Ev1jJS1NX78hfBF1/T8V5f820WlNRGZx2mlBWoNV4uAFxARVgjRZfoEHT1D6A==", + "requires": { + "child_process": "^1.0.2", + "fs": "0.0.1-security", + "path": "^0.12.7" + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3243,6 +3258,11 @@ "readable-stream": "^2.0.0" } }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -6791,6 +6811,25 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + }, + "dependencies": { + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + } + } + } + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -8538,9 +8577,9 @@ } }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "unc-path-regex": { diff --git a/package.json b/package.json index 24b961ec..490cab30 100644 --- a/package.json +++ b/package.json @@ -588,15 +588,18 @@ "plugin-error": "^1.0.1", "tslint": "^5.20.1", "typemoq": "^1.6.0", - "typescript": "^2.2.1", + "typescript": "^3.7.5", "vscode-test": "^1.4.0", "webpack": "^4.44.1" }, "dependencies": { "body-parser": "^1.16.1", + "child_process": "^1.0.2", + "cocopa": "0.0.3", "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", + "fs": "0.0.1-security", "glob": "^7.1.1", "iconv-lite": "^0.4.18", "impor": "^0.1.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 6c70285f..6cac39ea 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import * as ccp from "cocopa"; import * as fs from "fs"; import * as glob from "glob"; import * as os from "os"; @@ -22,7 +23,6 @@ import { arduinoChannel } from "../common/outputChannel"; import { ArduinoWorkspace } from "../common/workspace"; import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; -import { CCppProperties, CompilerCmdParser, CompilerCmdParserEngineGcc } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; /** @@ -808,8 +808,8 @@ export class ArduinoApp { if (!VscodeSettings.getInstance().disableIntelliSenseAutoGen) { // setup the parser with its engines - const gccParserEngine = new CompilerCmdParserEngineGcc(dc.sketch); - const compilerParser = new CompilerCmdParser([gccParserEngine]); + const gccParserEngine = new ccp.ParserGcc(dc.sketch); + const compilerParser = new ccp.Runner([gccParserEngine]); // set up the function to be called after parsing const _conclude = () => { @@ -821,7 +821,7 @@ export class ArduinoApp { } }; return { - callback: compilerParser.callback, + callback: compilerParser.callback(), conclude: _conclude, }; } diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts deleted file mode 100644 index 8b322056..00000000 --- a/src/arduino/intellisense.ts +++ /dev/null @@ -1,363 +0,0 @@ - // tslint:disable: max-line-length -/* Automatic IntelliSense setup through compiler command parsing. - * - * - * Some Arduino compiler commands from different board packages: - * AVR - * /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-g++ -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10811 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/cores/arduino -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/variants/eightanaloginputs -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/arduino/hardware/avr/1.8.2/libraries/Wire/src /tmp/arduino_build_776874/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_776874/sketch/lcdpong-butenc.ino.cpp.o - * ESP32 - * /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-g++ -DESP_PLATFORM "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" -DHAVE_CONFIG_H -DGCC_NOT_5_2_0=0 -DWITH_POSIX -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/config -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_trace -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_update -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/asio -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bootloader_support -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/coap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/console -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/driver -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-tls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_adc_cal -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_event -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_client -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_server -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_https_ota -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_ringbuf -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ethernet -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/expat -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fatfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freemodbus -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freertos -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/heap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/idf_test -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/jsmn -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/json -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/libsodium -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/log -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/lwip -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mbedtls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mdns -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/micro-ecc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mqtt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/newlib -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nghttp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nvs_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/openssl -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protobuf-c -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protocomm -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/pthread -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/sdmmc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/smartconfig_ack -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/soc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spi_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spiffs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcp_transport -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcpip_adapter -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ulp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/vfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wear_levelling -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wifi_provisioning -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wpa_supplicant -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/xtensa-debug-module -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32-camera -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fb_gfx -std=gnu++11 -Os -g3 -Wpointer-arith -fexceptions -fstack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror=all -Wextra -Wno-error=maybe-uninitialized -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-missing-field-initializers -Wno-sign-compare -fno-rtti -MMD -c -DF_CPU=240000000L -DARDUINO=10811 -DARDUINO_ESP32_DEV -DARDUINO_ARCH_ESP32 "-DARDUINO_BOARD=\"ESP32_DEV\"" "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" -DESP32 -DCORE_DEBUG_LEVEL=0 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/variants/doitESP32devkitV1 -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Wire/src /tmp/arduino_build_744383/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_744383/sketch/lcdpong-butenc.ino.cpp.o - * ESP8266 - * /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-g++ -DESP_PLATFORM "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" -DHAVE_CONFIG_H -DGCC_NOT_5_2_0=0 -DWITH_POSIX -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/config -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_trace -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/app_update -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/asio -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bootloader_support -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/bt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/coap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/console -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/driver -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-tls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_adc_cal -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_event -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_client -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_http_server -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_https_ota -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp_ringbuf -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ethernet -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/expat -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fatfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freemodbus -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/freertos -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/heap -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/idf_test -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/jsmn -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/json -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/libsodium -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/log -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/lwip -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mbedtls -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mdns -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/micro-ecc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/mqtt -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/newlib -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nghttp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/nvs_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/openssl -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protobuf-c -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/protocomm -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/pthread -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/sdmmc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/smartconfig_ack -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/soc -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spi_flash -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/spiffs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcp_transport -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/tcpip_adapter -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/ulp -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/vfs -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wear_levelling -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wifi_provisioning -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/wpa_supplicant -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/xtensa-debug-module -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp32-camera -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/esp-face -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/include/fb_gfx -std=gnu++11 -Os -g3 -Wpointer-arith -fexceptions -fstack-protector -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror=all -Wextra -Wno-error=maybe-uninitialized -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-missing-field-initializers -Wno-sign-compare -fno-rtti -MMD -c -DF_CPU=240000000L -DARDUINO=10811 -DARDUINO_ESP32_DEV -DARDUINO_ARCH_ESP32 "-DARDUINO_BOARD=\"ESP32_DEV\"" "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" -DESP32 -DCORE_DEBUG_LEVEL=0 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32 -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/variants/doitESP32devkitV1 -I/home/uli/Projects/arduino/libraries/NewLiquidCrystal_lib -I/home/uli/Projects/arduino/libraries/LcdPong/src -I/home/uli/Projects/arduino/libraries/Encoder -I/home/uli/Projects/arduino/libraries/Button -I/home/uli/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/Wire/src /tmp/arduino_build_470717/sketch/lcdpong-butenc.ino.cpp -o /tmp/arduino_build_470717/sketch/lcdpong-butenc.ino.cpp.o - */ -// tslint:enable: max-line-length - -import { spawnSync } from "child_process"; -import * as fs from "fs"; -import * as path from "path"; - -/** - * Data structure carrying the output from a parsed compiler command. - * All compiler specific option prefixes are removed for includes and - * defines. - */ -export class CompilerCmdParserResult { - public includes: Array = []; - public defines: Array = []; - public options: Array = []; - public compiler: string = ""; - /** Dropped arguments like -c -Ox -o, the input and output file. */ - public trash: Array = []; -}; - -export type CompilerCmdParserSearchType = string | RegExp; - -/** - * Base class for any compiler command parser engine. - * If someone needs to write an engine: this is the base class. - * For further inspiration take a look at the implementation of - * CompilerCmdParserEngineGcc. - */ -export abstract class CompilerCmdParserEngine { - /** - * This array should contain the patterns which should match on - * a valid compiler command line to identify the compiler command. - * To be set by the derived class. - */ - protected _match: CompilerCmdParserSearchType[]; - /** - * This array should contain the patterns which should _NOT_ - * match on a valid compiler command line to identify the - * compiler command. - * To be set by the derived class. - */ - protected _nomatch: CompilerCmdParserSearchType[]; - /** - * This function checks if the command line matches the - * requirements given through _match and _nomatch and invokes - * the parse function in case of a match. - * @returns If match was found and parsing was successful - * it returns the result else undefined. - */ - public match(line: string): CompilerCmdParserResult { - // check for regexes that must match - for (const re of this._match) { - if (line.search(re) === -1) { - return undefined; - } - } - // check for regexes that mustn't match - for (const re of this._nomatch) { - if (line.search(re) !== -1) { - return undefined; - } - } - return this.parse(line); - } - - /** - * The parsing function of a matched compiler command line. - * If all conditions hold true (all _match are found and all _nomatch - * are not found), this parsing function is invoked. - * - * Here the derived class has to implement its parsing magic - * to extract the desired includes, defines, compiler flags - * and the compiler command. - * - * @param line A string containing a compiler command line candidate. - * @returns A valid parsing result in case parsing was successful - * and undefined in case it failed fatally. - */ - protected abstract parse(line: string): CompilerCmdParserResult; -} - -/** - * Compiler command parsing engine for gcc compilers. - */ -export class CompilerCmdParserEngineGcc - extends CompilerCmdParserEngine { - constructor(sketch: string) { - super(); - - // TODO: windows and osx variants - - this._nomatch = - [ - // make sure Arduino's not testing libraries - /-o\s\/dev\/null/, - ]; - this._match = - [ - // make sure we're running g++ - /-g\+\+\s+/, - // make sure we're compiling - /\s+-c\s+/, - // check if we're compiling the main sketch - path.basename(sketch) + ".cpp.o", - ]; - } - protected parse(line: string): CompilerCmdParserResult { - const result = new CompilerCmdParserResult(); - const args = line.split(/\s+/); - - for (let arg of args) { - - // drop empty arguments - if (!arg.length) { - continue; - } - - // unpack quoted elements like - // - // "-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"" - // "-DARDUINO_BOARD=\"ESP32_DEV\"" - // "-DARDUINO_VARIANT=\"doitESP32devkitV1\"" - const packed = arg.match(/^"(.+)"$/); - if (packed) { - arg = packed[1]; - } - - // extract defines - const define = arg.match(/^-D(.+)/); - if (define) { - result.defines.push(define[1]); - continue; - } - - // extract includes - const include = arg.match(/^-I(.+)/); - if (include) { - result.includes.push(include[1]); - continue; - } - - // extract the compiler executable - const c = arg.match(/g\+\+$/); - if (c) { - result.compiler = arg; - continue; - } - - // filter out option trash - const t = arg.match(/^-o|^-O|^-g|^-c|cpp(?:\.o){0,1}$/); - if (t) { - result.trash.push(arg); - continue; - } - - // collect options - const o = arg.match(/^-/); - if (o) { - result.options.push(arg); - continue; - } - - // collect the rest - result.trash.push(arg); - } - - // Query compiler for intrinsic/built-in include paths - if (result.compiler.length > 0) { - - // TODO: Windows - - // Spawn synchronous child process and run bash command - // Source: https://stackoverflow.com/a/6666338 - const compilerinfocmd = `${result.compiler} -xc++ -E -v - < /dev/null 2>&1`; - const child = spawnSync("bash", ["-c", compilerinfocmd], { encoding : "utf8" }); - - if (child.error || child.status !== 0) { - // TODO: report the execution failure - } else { - // Now we look for - // - // #include "..." search starts here: - // #include <...> search starts here: - // ...(include directories list)... - // End of search list. - // - // and extract the include directory list. Could be that some gcc - // even lists something under - // - // #include "..." search starts here: - // - // but I havn't seen it so far. - const includeregex = /^#include\s+<\.\.\.>\ssearch\sstarts\shere\:$(.+)^End\sof\ssearch\slist\.$/ms; - const match = child.stdout.match(includeregex); - if (match) { - // Split list by newlines. Should be platform independent - let lines = match[1].split(/\s*(?:\r|\r\n|\n)\s*/); - // Filter out empty elements (in most cases only the last element) - lines = lines.filter((val: string) => val !== ""); - // Add built-in includes to command line includes - result.includes = [...result.includes, ...lines]; - } else { - // TODO: issue info that include section has not been found - } - } - } - return result; - } -} - -/** - * A compiler command parser. - * Takes compiler commands line by line and tries to find the compile command - * for the main .ino sketch. From that it tries to extract all includes, - * defines, options and the compiler command itself. - * - * TODO: Make it more generic to support other compilers than gcc - */ -export class CompilerCmdParser { - private _result: CompilerCmdParserResult; - private _engines: CompilerCmdParserEngine[]; - - /** - * Create a compiler command parser. - * Sets up parsing operation. - * @param engines Parsing engines for different compilers - */ - constructor(engines: CompilerCmdParserEngine[]) { - this._engines = engines; - } - /** - * Returns the parsing result. - * Returns undefined when the parser fails or when the - * parser didn't run. - */ - get result(): CompilerCmdParserResult { - return this._result; - } - /** - * Takes a command line and tries to parse it. - * - * @param line Compiler command line candidate. - * @returns The parsing result if the command line was parsed - * successfully. It returns undefined if no match was found or - * parsing failed. - */ - public parse(line: string): boolean { - for (const engine of this._engines) { - this._result = engine.match(line); - if (this._result) { - return true; - } - } - return false; - } - /** - * Returns a callback which can be passed on to other functions - * to call. For instance from stdout callbacks of child processes. - */ - public get callback(): (line: string) => void { - return (line: string) => { - if (!this._result) { - this.parse(line); - } - } - } - public processResult(configPath: string): boolean { - if (this._result) { - const cppProps = new CCppProperties(configPath); - cppProps.merge(this._result); - cppProps.write(); - return true; - } - return false; - } -} - -/** - * Class representing the contents of the IntelliSense - * c_cpp_properties.json configuration file. - * - * @see https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference - */ -export class CCppPropertiesConfiguration { - public name: string = "Arduino"; - public compilerPath: string = ""; - public compilerArgs: string [] = []; - public intelliSenseMode: string = "gcc-x64"; // since we're using arduino's compiler - public includePath: string[] = []; - public forcedInclude: string[] = []; - public cStandard: string = "c11"; - public cppStandard: string = "c++11"; // as of 1.8.11 arduino is on C++11 - public defines: string[] = []; - - constructor(result: CompilerCmdParserResult) { - this.compilerPath = result.compiler; - this.compilerArgs = result.options; - this.includePath = result.includes; - this.defines = result.defines; - } -} - -export class CCppPropertiesContent { - public configurations: Array - - constructor(result: CompilerCmdParserResult) { - this.configurations = [new CCppPropertiesConfiguration(result)]; - } -}; - -export class CCppProperties { - public proppath: string; - public propFileContent: CCppPropertiesContent; - - constructor(proppath: string) { - this.proppath = proppath; - } - - public read() { - if (!fs.existsSync(this.proppath)) { - return; - } - const propFileContentPlain = fs.readFileSync(this.proppath, "utf8"); - - // NOTE: in JSON backslashes are escaped to \\\\ - - this.propFileContent = JSON.parse(propFileContentPlain) as CCppPropertiesContent; - } - public merge(result: CompilerCmdParserResult) { - const pc = new CCppPropertiesContent(result); - - // TODO: - // * merge with existing configuration if desired - // * check if contents changed after merging - // - - this.propFileContent = pc; - } - - public write() { - // NOTE: in JSON backslashes are escaped to \\\\ - // TODO: - // * check if path exists, create if necessary - // * write file only if modified - - if (this.propFileContent) { - const content = JSON.stringify(this.propFileContent, null, 4); - fs.writeFileSync(this.proppath, content); - } - } -} diff --git a/webpack.config.js b/webpack.config.js index b18b814a..66cab632 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,6 +21,16 @@ function getEntry() { const list = getDependenciesFromNpm(mod); const moduleList = list.filter((value, index, self) => { + /* Earlier versions of cocopa contains incorrect nodejs dependencies. Remove this workaround after updating to version v0.0.7. + * Resulted in webpack errors like: + * Unhandled rejection Error in plugin "webpack" + * Message: + * ["Entry module not found: Error: Can't resolve './node_modules/child_process' in ... + * ... "Entry module not found: Error: Can't resolve './node_modules/fs' in ... + */ + if (value === "fs" || value === "child_process") { + return false; + } return self.indexOf(value) === index && unbundledModule.indexOf(value) === -1 && !/^@types\//.test(value); }); From f28dd7e8d1a4fe16fda2ffd37465a420f5e090ce Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 13:08:59 +0100 Subject: [PATCH 021/142] Updated earned :beers: --- BRANCHNOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 0255d506..7903847f 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -71,6 +71,7 @@ I will list every supporter here, thanks! 2020-02-04 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-05 Elektronik Workshop: 40 :beers: (10h coding) 2020-02-06 Elektronik Workshop: 36 :beers: (9h coding) +2020-02-07 Elektronik Workshop: 48 :beers: (12h coding) From 819d2f1b470c329d05e7427760778605738f3bce Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 16:12:58 +0100 Subject: [PATCH 022/142] Added IntelliSense setup auto-generation project (sketch-context) configuration flag which can override the global flag --- BRANCHNOTES.md | 17 ++++++++++++----- README.md | 12 +++++++++++- misc/arduinoValidator.json | 12 +++++++++++- src/arduino/arduino.ts | 7 ++++++- src/deviceContext.ts | 20 ++++++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 7903847f..789d191e 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -18,6 +18,8 @@ Provide a configuration flag which allows the user to turn this feature off - th ### Status **2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) **2020-02-06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. +**2020-02-07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. +**2020-02-08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md) | | Tasks | |-----:|:--------| @@ -35,7 +37,7 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: Write configuration on change only | | | :white_check_mark: Option to backup old configurations? | | **Configuration flags** | :heavy_check_mark: Provide global disable flag for IntelliSense auto-config | -| | :white_check_mark: Provide project specific override for the global flag - most users will likely use the default setup and disable auto-generation for very specific projects | +| | :heavy_check_mark: Provide project specific override for the global flag - most users will likely use the default setup and disable auto-generation for very specific projects | | **Unit tests** | :heavy_check_mark: Basic parser (known boards, match/no match)| | | :heavy_check_mark: Querying of compiler built-in includes (Note: to be changed to generic compiler such that Arduino is not necessary for unit testing) | | | :white_check_mark: Throwing arbitrary data at parser engines | @@ -43,9 +45,9 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | -| | :white_check_mark: Auto-run verify after setting a board to generate a valid `c_cpp_properties.json`, identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | -| | :white_check_mark: Document configuration settings in [README.md](README.md) | -| | :white_check_mark: Document features in [README.md](README.md) | +| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | +| | :heavy_check_mark: Document configuration settings in [README.md](README.md) | +| | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :white_check_mark: How to handle compilation failure? Only set if more comprehensive | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :white_check_mark: Finally: go through my code and look for TODOs | @@ -67,7 +69,7 @@ I will list every supporter here, thanks! ### Supporters 5$ -> 1 :beer: -1h coding -> 20$ -> 4 :beers: +1h coding -> 20$ -> 4 :beers: (very moderate wage though) 2020-02-04 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-05 Elektronik Workshop: 40 :beers: (10h coding) 2020-02-06 Elektronik Workshop: 36 :beers: (9h coding) @@ -194,3 +196,8 @@ Remove these as they are helpless attempts to get IntelliSense working: ``` Remove this as this messes in an unpredictable and helpless way with Intellisense [src/langService/completionProvider.ts](src/langService/completionProvider.ts) + +Remove this folder as this is not necessary when Intellisense works properly: +``` +syntaxes/ +``` diff --git a/README.md b/README.md index 30f60388..068eba6e 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ This extension provides several commands in the Command Palette (F1 o | `arduino.disableTestingOpen` | Enable/disable automatic sending of a test message to the serial port for checking the open status. The default value is `false` (a test message will be sent). | | `arduino.skipHeaderProvider` | Enable/disable the extension providing completion items for headers. This functionality is included in newer versions of the C++ extension. The default value is `false`.| | `arduino.defaultBaudRate` | Default baud rate for the serial port monitor. The default value is 115200. Supported values are 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400 and 250000 | +| `arduino.disableIntelliSenseAutoGen` | When `true` vscode-arduino will not auto-generate an IntelliSense configuration (i.e. `.vscode/c_cpp_properties.json`) by analyzing Arduino's compiler output. | The following Visual Studio Code settings are available for the Arduino extension. These can be set in global user preferences Ctrl + , or workspace settings (`.vscode/settings.json`). The latter overrides the former. @@ -98,7 +99,8 @@ The following settings are as per sketch settings of the Arduino extension. You "board": "adafruit:samd:adafruit_feather_m0", "output": "../build", "debugger": "jlink", - "prebuild": "bash prebuild.sh" + "prebuild": "bash prebuild.sh", + "disableIntelliSenseAutoGen": "global" } ``` - `sketch` - The main sketch file name of Arduino. @@ -107,6 +109,14 @@ The following settings are as per sketch settings of the Arduino extension. You - `output` - Arduino build output path. If not set, Arduino will create a new temporary output folder each time, which means it cannot reuse the intermediate result of the previous build leading to long verify/upload time, so it is recommended to set the field. Arduino requires that the output path should not be the workspace itself or in a subfolder of the workspace, otherwise, it may not work correctly. By default, this option is not set. It's worth noting that the contents of this file could be deleted during the build process, so pick (or create) a directory that will not store files you want to keep. - `debugger` - The short name of the debugger that will be used when the board itself does not have a debugger and there is more than one debugger available. You can find the list of debuggers [here](https://github.com/Microsoft/vscode-arduino/blob/master/misc/debuggerUsbMapping.json). By default, this option is not set. - `prebuild` - External command before building the sketch file. You should only set one `prebuild` command. `command1 && command2` does not work. If you need to run multiple commands before the build, then create a script. +- `disableIntelliSenseAutoGen` - Override the global auto-generation of the IntelliSense configuration (i.e. `.vscode/c_cpp_properties.json`). Three options are available: + - `"global"`: Use the global settings (default) + - `"disable"`: Disable the auto-generation even if globally enabled + - `"enable"`: Enable the auto-generation even if globally disabled + +## IntelliSense +vscode-arduino auto-configures IntelliSense by default. vscode-arduino analyzes Arduino's compiler output during verify and generates the corresponding configuration file at `.vscode/c_cpp_properties.json` and tries as hard as possible to keep things up to date, e.g. running verify when switching the board or the sketch. +It doesn't makes sense though to run verify repeatedly. Therefore if the workspace reports problems (for instance after adding new includes to a new library) run *verify* such that IntelliSense knows of the new include directories (since the Arduino-backend performs the library resolution externally). ## Debugging Arduino Code preview Before you start to debug your Arduino code, please read [this document](https://code.visualstudio.com/docs/editor/debugging) to learn about the basic mechanisms of debugging in Visual Studio Code. Also see [debugging for C++ in VSCode](https://code.visualstudio.com/docs/languages/cpp#_debugging) for further reference. diff --git a/misc/arduinoValidator.json b/misc/arduinoValidator.json index 73baff4f..48362cb1 100644 --- a/misc/arduinoValidator.json +++ b/misc/arduinoValidator.json @@ -32,6 +32,16 @@ "description": "Arduino Debugger Settings", "type": "string", "minLength": 1 + }, + "disableIntelliSenseAutoGen": { + "description": "Disable/enable the automatic generation of the IntelliSense configuration file (c_cpp_properties.json) for this project (overrides the global setting). When set to \"global\" the global extension settings will be used.", + "type": "string", + "default": "global", + "enum": [ + "global", + "disable", + "enable" + ] } } -} \ No newline at end of file +} diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 6cac39ea..d8ca187f 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -805,7 +805,12 @@ export class ArduinoApp { */ private makeCompilerParserContext(dc: DeviceContext) : { callback: (s: string) => void; conclude: () => void; } { - if (!VscodeSettings.getInstance().disableIntelliSenseAutoGen) { + + const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; + const project = dc.disableIntelliSenseAutoGen; + + if (project !== "disable" && !globalDisable || + project === "enable") { // setup the parser with its engines const gccParserEngine = new ccp.ParserGcc(dc.sketch); diff --git a/src/deviceContext.ts b/src/deviceContext.ts index 5e24aea1..3c0daf6e 100644 --- a/src/deviceContext.ts +++ b/src/deviceContext.ts @@ -57,6 +57,11 @@ export interface IDeviceContext { */ configuration: string; + /** + * IntelliSense configuration auto-generation project override. + */ + disableIntelliSenseAutoGen: string; + onDidChange: vscode.Event; initialize(): void; @@ -82,6 +87,8 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { private _debugger: string; + private _disableIntelliSenseAutoGen: string; + private _configuration: string; private _extensionPath: string; @@ -151,6 +158,7 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = deviceConfigJson.configuration; this._output = deviceConfigJson.output; this._debugger = deviceConfigJson["debugger"]; + this._disableIntelliSenseAutoGen = deviceConfigJson.disableIntelliSenseAutoGen; this._prebuild = deviceConfigJson.prebuild; this._programmer = deviceConfigJson.programmer; this._onDidChange.fire(); @@ -164,6 +172,7 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = null; this._output = null; this._debugger = null; + this._disableIntelliSenseAutoGen = null; this._prebuild = null; this._programmer = null; this._onDidChange.fire(); @@ -182,6 +191,7 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this._configuration = null; this._output = null; this._debugger = null; + this._disableIntelliSenseAutoGen = null; this._prebuild = null; this._programmer = null; this._onDidChange.fire(); @@ -217,6 +227,7 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { deviceConfigJson.board = this.board; deviceConfigJson.output = this.output; deviceConfigJson["debugger"] = this.debugger_; + deviceConfigJson.disableIntelliSenseAutoGen = this.disableIntelliSenseAutoGen; deviceConfigJson.configuration = this.configuration; deviceConfigJson.programmer = this.programmer; @@ -282,6 +293,15 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { this.saveContext(); } + public get disableIntelliSenseAutoGen() { + return this._disableIntelliSenseAutoGen; + } + + public set disableIntelliSenseAutoGen(value: string) { + this._disableIntelliSenseAutoGen = value; + this.saveContext(); + } + public get configuration() { return this._configuration; } From bb2ce83e97d82d69c532cc7abbaa8a9a92aaa04d Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 8 Feb 2020 16:29:08 +0100 Subject: [PATCH 023/142] Try to generate IntelliSense configuration even when the compilation fails --- BRANCHNOTES.md | 9 ++++++--- src/arduino/arduino.ts | 21 ++++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 789d191e..ebf2865b 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -48,16 +48,19 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) (partially done) | -| | :white_check_mark: How to handle compilation failure? Only set if more comprehensive | +| | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet ## Motivation -I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives. The old and dysfunctional Arduino CDT extension for eclipse somehow stalled (even if it was promising), Sloeber could be an option but the maintainer is disillusioned and the project is more or less dead. Platform IO IDE's license is very [restrictive](https://community.platformio.org/t/what-part-of-platformio-is-open-source-licenced/1447/2). +I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives: +* The old and dysfunctional Arduino CDT extension for eclipse somehow stalled (even if it was promising) +* Sloeber could be an option but the maintainer is disillusioned and the project is more or less dead. Furthermore Eclipse is pretty heavy and less accessible to beginners +* Platform IO IDE's license is very [restrictive](https://community.platformio.org/t/what-part-of-platformio-is-open-source-licenced/1447/2). -Then remains vscode-arduino. It seems that it isn't completely dead - but almost. Most of the core functionality seems to work (I used it a few days now). But the biggest show stopper is the bad IntelliSense support. +Then remains vscode-arduino. It seems that it isn't completely dead - but almost. Most of the core functionality seems to work (I used it a few days now). But the biggest show stopper is the bad IntelliSense support -- which I'll address here now. ## Beer Money :beers: You can chip in some beer money to keep me motivated - this is really appreciated. diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index d8ca187f..b2794207 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -256,20 +256,17 @@ export class ArduinoApp { arduinoChannel.show(); - try { - const compilerParserContext = this.makeCompilerParserContext(dc); + let verifyResult: boolean; + const compilerParserContext = this.makeCompilerParserContext(dc); + try { await util.spawn(this._settings.commandPath, arduinoChannel.channel, args, undefined, compilerParserContext.callback); - - if (compilerParserContext.conclude) { - compilerParserContext.conclude(); - } - arduinoChannel.end(`Finished verify sketch - ${dc.sketch}${os.EOL}`); - return true; + arduinoChannel.end(`Finished verifying sketch - ${dc.sketch}${os.EOL}`); + verifyResult = true; } catch (reason) { const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : @@ -277,8 +274,14 @@ export class ArduinoApp { reason.message : JSON.stringify(reason); arduinoChannel.error(msg); - return false; + verifyResult = false; + } + + if (compilerParserContext.conclude) { + compilerParserContext.conclude(); } + + return verifyResult; } public tryToUpdateIncludePaths() { From dd3254dd5b1025a8840c993841720e6b0bb23f68 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 9 Feb 2020 04:24:52 +0100 Subject: [PATCH 024/142] Incorporated the latest progress from cocopa development --- BRANCHNOTES.md | 2 +- package-lock.json | 6 +++--- package.json | 2 +- src/arduino/arduino.ts | 20 +++++++++++++++++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index ebf2865b..92cc2926 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -19,7 +19,7 @@ Provide a configuration flag which allows the user to turn this feature off - th **2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) **2020-02-06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. **2020-02-07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. -**2020-02-08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md) +**2020-02-08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md). vscode-arduino now tries to generate an IntelliSense configuration even if compilation (verify) should fail. vscode-arduino now tries to generate a IntelliSense configuration even if Arduino's verify failed (if the main sketch compilation was invoked before anything failed) | | Tasks | |-----:|:--------| diff --git a/package-lock.json b/package-lock.json index 69660e47..973ebc60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1562,9 +1562,9 @@ } }, "cocopa": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.3.tgz", - "integrity": "sha512-zFHtJcwkobUCNkeBwxyftvsQ4Ev1jJS1NX78hfBF1/T8V5f820WlNRGZx2mlBWoNV4uAFxARVgjRZfoEHT1D6A==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.4.tgz", + "integrity": "sha512-qqxiNxHMaCoz/sfh9fo7kEEH8j9BlQVu2FbPifRbYnZjYAQNBMhEqdxW6wZSKEQQWVp//ksJtTo3wlXol7EqZg==", "requires": { "child_process": "^1.0.2", "fs": "0.0.1-security", diff --git a/package.json b/package.json index 490cab30..93ca5f82 100644 --- a/package.json +++ b/package.json @@ -595,7 +595,7 @@ "dependencies": { "body-parser": "^1.16.1", "child_process": "^1.0.2", - "cocopa": "0.0.3", + "cocopa": "0.0.4", "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index b2794207..d9e0603b 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -815,9 +815,8 @@ export class ArduinoApp { if (project !== "disable" && !globalDisable || project === "enable") { - // setup the parser with its engines - const gccParserEngine = new ccp.ParserGcc(dc.sketch); - const compilerParser = new ccp.Runner([gccParserEngine]); + const parserEngines = this.makeCompilerParserEngines(dc); + const compilerParser = new ccp.Runner(parserEngines); // set up the function to be called after parsing const _conclude = () => { @@ -839,6 +838,21 @@ export class ArduinoApp { } }; + private makeCompilerParserEngines(dc: DeviceContext) { + const matchPattern = [ + // trigger parser when compiling the main sketch + ` ${path.basename(dc.sketch)}.cpp.o`, + ]; + const dontMatchPattern = [ + // make sure Arduino's not testing libraries + /-o\s\/dev\/null/, + ]; + + // setup the parser with its engines + const gccParserEngine = new ccp.ParserGcc(matchPattern, dontMatchPattern); + return [gccParserEngine]; + } + private getProgrammerString(): string { const selectProgrammer = this.programmerManager.currentProgrammer; if (!selectProgrammer) { From 39c3f798008108282c10c3a82abc7e1569ca3488 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 9 Feb 2020 11:51:43 +0100 Subject: [PATCH 025/142] Fix issue 771 * Fixes the line splitting regex as outlined in https://github.com/microsoft/vscode-arduino/pull/771 * Removed a redundand condition --- src/arduino/board.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arduino/board.ts b/src/arduino/board.ts index 39908022..88c1b6c8 100644 --- a/src/arduino/board.ts +++ b/src/arduino/board.ts @@ -8,7 +8,7 @@ export function parseBoardDescriptor(boardDescriptor: string, plat: IPlatform): const boardLineRegex = /([^\.]+)\.(\S+)=(.+)/; const result = new Map(); - const lines = boardDescriptor.split(/[\r|\r\n|\n]/); + const lines = boardDescriptor.split(/(?:\r|\r\n|\n)/); const menuMap = new Map(); lines.forEach((line) => { @@ -18,7 +18,7 @@ export function parseBoardDescriptor(boardDescriptor: string, plat: IPlatform): } const match = boardLineRegex.exec(line); - if (match && match.length > 3) { + if (match) { if (line.startsWith("menu.")) { menuMap.set(match[2], match[3]); return; From d87402d021b997a97b6062a43546baf8a58a64fa Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 9 Feb 2020 13:23:48 +0100 Subject: [PATCH 026/142] Fix regression and improve regex matching * Fixed regression introduced with adaptions to latest version of cocopa * Made compile command regex match more stringent --- src/arduino/arduino.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index d9e0603b..99e1bf5f 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -838,14 +838,24 @@ export class ArduinoApp { } }; + /** + * + * @param dc + */ private makeCompilerParserEngines(dc: DeviceContext) { + + let sketch = path.basename(dc.sketch); + const dotcpp = sketch.endsWith(".ino") ? ".cpp" : ""; + sketch = `-o\\s+\\S*${ccp.regExEscape(sketch)}${dotcpp}\\.o`; + const matchPattern = [ // trigger parser when compiling the main sketch - ` ${path.basename(dc.sketch)}.cpp.o`, + RegExp(sketch), ]; + const dontMatchPattern = [ // make sure Arduino's not testing libraries - /-o\s\/dev\/null/, + /-o\s+\/dev\/null/, ]; // setup the parser with its engines From 3cba4121b1cf38f7148fd6ae8f36d8b36a73f0ad Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 9 Feb 2020 16:40:00 +0100 Subject: [PATCH 027/142] Notes about serial monitor * Added more serial monitor ideas from [John Lonergan](https://github.com/microsoft/vscode-arduino/issues/463#issuecomment-583853833) * Added some ideas how to implement a better serial monitor --- BRANCHNOTES.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 92cc2926..ab0584aa 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -44,15 +44,17 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: JSON input | | | :white_check_mark: JSON output | | | :white_check_mark: Configuration merging | +| | :white_check_mark: Test with cpp sketches | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | -| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | +| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch* c) *workbench initialized and no `c_cpp_properties.json` has been found*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :white_check_mark: Finally: go through my code and look for TODOs | -`*` not committed to branch yet +`*` not committed to branch yet +`>` most of the actual parsing and configuration generation is part of [cocopa](https://github.com/elektronikworkshop/cocopa/) ([here](https://www.npmjs.com/package/cocopa)'s the npm package) ## Motivation I write a lot of code for Arduino, especially libraries. The Arduino IDE is not suited for more complex projects and I tried several alternatives: @@ -93,6 +95,25 @@ I will list every supporter here, thanks! * Logging for IntelliSense https://code.visualstudio.com/docs/cpp/enable-logging-cpp ## Future Work * Proper interactive serial terminal (this is the second major show stopper in my opinion) + * Command history option + * From https://github.com/microsoft/vscode-arduino/issues/463#issuecomment-583846263 and following: + * allow input on the serial monitor in a convenient way - ie just type and hit return, just like the Arduino IDE + * have the serial monitor window NOT keep turning off autoscroll (there is a separate ticket for this) + * have the option of the serial monitor and/or compile window auto clear each time the sketch is compiled + * Plus there is the annoying default where the compile runs in verbose mode and we have to manually edit config files to turn off the trace output + * Plus plus... Is there a way to automatically select the right serial port? + * Oh and one more. I want the serial output and perhaps compile windows to be undocked or at least I want them to sit to the right of my code window but they seem rigidly stuck at the bottom of the screen. + * And I would probably prioritize ease of use over better editing/intelligence. + * Being able to set baud rate within monitor + * Possible implementation hooks + * run node program in native terminal and connect it to extension + * https://github.com/serialport/node-serialport + * [General](https://serialport.io/docs/guide-about) + * [CLI](https://serialport.io/docs/guide-cli) + * [API](https://serialport.io/docs/guide-usage) + * write a [debugger extension](https://code.visualstudio.com/api/extension-guides/debugger-extension) with a [mock](https://github.com/Microsoft/vscode-mock-debug) which communicates with the serial + +Plus plus... Is there a way to automatically select the right serial port? * Lots of redundant code * e.g. "upload is a superset of "verify" * general lack of modularity - the above is the result From 67281ae618dfc7036749c8a0b48135d0f29daa68 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sun, 9 Feb 2020 18:25:01 +0100 Subject: [PATCH 028/142] Moved arduino specifics from cocopa to vscode-arduino --- package-lock.json | 6 +++--- package.json | 2 +- src/arduino/arduino.ts | 33 ++++++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 973ebc60..708aba1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1562,9 +1562,9 @@ } }, "cocopa": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.4.tgz", - "integrity": "sha512-qqxiNxHMaCoz/sfh9fo7kEEH8j9BlQVu2FbPifRbYnZjYAQNBMhEqdxW6wZSKEQQWVp//ksJtTo3wlXol7EqZg==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.5.tgz", + "integrity": "sha512-vRmACP2rPKk9irEYXD2x50G0G1tm1OIf6M9Cw+rBR1QwR9oNImxsDwiHek94MJ59w2KN13BF5WJk/P1yFpSFOw==", "requires": { "child_process": "^1.0.2", "fs": "0.0.1-security", diff --git a/package.json b/package.json index 93ca5f82..e6094647 100644 --- a/package.json +++ b/package.json @@ -595,7 +595,7 @@ "dependencies": { "body-parser": "^1.16.1", "child_process": "^1.0.2", - "cocopa": "0.0.4", + "cocopa": "0.0.5", "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 99e1bf5f..a839c725 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -815,20 +815,35 @@ export class ArduinoApp { if (project !== "disable" && !globalDisable || project === "enable") { - const parserEngines = this.makeCompilerParserEngines(dc); - const compilerParser = new ccp.Runner(parserEngines); + const engines = this.makeCompilerParserEngines(dc); + const runner = new ccp.Runner(engines); // set up the function to be called after parsing const _conclude = () => { - const cppPropsPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); - if (compilerParser.processResult(cppPropsPath)) { - arduinoChannel.info("IntelliSense configuration generated successfully."); - } else { + if (!runner.result) { arduinoChannel.warning("Failed to generate IntelliSense configuration."); + return; + } + const pPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + // TODO: check what kind of result we've got: gcc or other architecture: + // and instantiate content accordingly (to be implemented within cocopa) + const content = new ccp.CCppPropertiesContentResult(runner.result, + "Arduino", + ccp.CCppPropertiesISMode.Gcc_X64, + ccp.CCppPropertiesCStandard.C11, + // as of 1.8.11 arduino is on C++11 + ccp.CCppPropertiesCppStandard.Cpp11); + const prop = new ccp.CCppProperties(); + prop.read(pPath); + prop.merge(content); + if (prop.write(pPath)) { + arduinoChannel.info("IntelliSense configuration updated."); + } else { + arduinoChannel.info("IntelliSense configuration already up to date."); } }; return { - callback: compilerParser.callback(), + callback: runner.callback(), conclude: _conclude, }; } @@ -849,6 +864,10 @@ export class ArduinoApp { sketch = `-o\\s+\\S*${ccp.regExEscape(sketch)}${dotcpp}\\.o`; const matchPattern = [ + // make sure we're running g++ + /(?:^|-)g\+\+\s+/, + // make sure we're compiling + /\s+-c\s+/, // trigger parser when compiling the main sketch RegExp(sketch), ]; From 40bb52df5ab3a5fc89cd3933204308817f13e76a Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Mon, 10 Feb 2020 02:26:03 +0100 Subject: [PATCH 029/142] Merge c_cpp_properties results with existing configuration * More unit testing within cocopa. * Implemented c_cpp_properties merging -> compiler analysis results are merged into existing configuration and will preserve configurations of different name than the vscode-studio default configuration name (currently "Arduino"). This opens up the possibility for users to write their own configurations without having to disable the autogeneration. * Implemented "write on change" - `c_cpp_properties.json` will only be written if a new configuration has been detected. --- BRANCHNOTES.md | 18 +++++++++--------- package-lock.json | 6 +++--- package.json | 2 +- src/arduino/arduino.ts | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index ab0584aa..a3bc85ce 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -20,6 +20,8 @@ Provide a configuration flag which allows the user to turn this feature off - th **2020-02-06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. **2020-02-07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. **2020-02-08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md). vscode-arduino now tries to generate an IntelliSense configuration even if compilation (verify) should fail. vscode-arduino now tries to generate a IntelliSense configuration even if Arduino's verify failed (if the main sketch compilation was invoked before anything failed) +**2020-02-09** Moved vscode-arduino specific from cocopa over (to keep cocopa as generic as possible). More unit testing within cocopa. Some research regarding future serial monitor implementation. Implemented c_cpp_properties merging -> compiler analysis results are merged into existing configuration and will preserve configurations of different name than the vscode-studio default configuration name (currently "Arduino"). This opens up the possibility for users to write their own configurations without having to disable the autogeneration. Implemented "write on change" - `c_cpp_properties.json` will only be written if a new configuration has been detected. Now loads of tests have to be written for cocopa. + | | Tasks | |-----:|:--------| @@ -32,18 +34,14 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :heavy_check_mark: Basic setting of parsing result | | | :heavy_check_mark: Basic file input | | | :heavy_check_mark: Basic file output | -| | :white_check_mark: Merging of parsing result and existing file content | -| | :white_check_mark: Handling inexistent files and folders | -| | :white_check_mark: Write configuration on change only | +| | :heavy_check_mark: Merging of parsing result and existing file content | +| | :heavy_check_mark: Handling inexistent files and folders | +| | :heavy_check_mark: Write configuration on change only | | | :white_check_mark: Option to backup old configurations? | | **Configuration flags** | :heavy_check_mark: Provide global disable flag for IntelliSense auto-config | | | :heavy_check_mark: Provide project specific override for the global flag - most users will likely use the default setup and disable auto-generation for very specific projects | | **Unit tests** | :heavy_check_mark: Basic parser (known boards, match/no match)| -| | :heavy_check_mark: Querying of compiler built-in includes (Note: to be changed to generic compiler such that Arduino is not necessary for unit testing) | -| | :white_check_mark: Throwing arbitrary data at parser engines | -| | :white_check_mark: JSON input | -| | :white_check_mark: JSON output | -| | :white_check_mark: Configuration merging | +| | :white_check_mark: All unit tests in cocopa | | | :white_check_mark: Test with cpp sketches | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | | | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch* c) *workbench initialized and no `c_cpp_properties.json` has been found*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | @@ -53,7 +51,7 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :white_check_mark: Finally: go through my code and look for TODOs | -`*` not committed to branch yet +`*` not committed to branch yet `>` most of the actual parsing and configuration generation is part of [cocopa](https://github.com/elektronikworkshop/cocopa/) ([here](https://www.npmjs.com/package/cocopa)'s the npm package) ## Motivation @@ -79,6 +77,8 @@ I will list every supporter here, thanks! 2020-02-05 Elektronik Workshop: 40 :beers: (10h coding) 2020-02-06 Elektronik Workshop: 36 :beers: (9h coding) 2020-02-07 Elektronik Workshop: 48 :beers: (12h coding) +2020-02-08 Elektronik Workshop: 52 :beers: (13h coding) +2020-02-09 Elektronik Workshop: 40 :beers: (10h coding) diff --git a/package-lock.json b/package-lock.json index 708aba1a..3fbb2e87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1562,9 +1562,9 @@ } }, "cocopa": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.5.tgz", - "integrity": "sha512-vRmACP2rPKk9irEYXD2x50G0G1tm1OIf6M9Cw+rBR1QwR9oNImxsDwiHek94MJ59w2KN13BF5WJk/P1yFpSFOw==", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.6.tgz", + "integrity": "sha512-iRMDGthPTi0yhQRq2n+ZqL0zuuY08JVa5UFSkQbTb1BLBDhH4fmG/uEo6xLT91+clTZCkTocAS05xDquJo8kwQ==", "requires": { "child_process": "^1.0.2", "fs": "0.0.1-security", diff --git a/package.json b/package.json index e6094647..4aedf4f9 100644 --- a/package.json +++ b/package.json @@ -595,7 +595,7 @@ "dependencies": { "body-parser": "^1.16.1", "child_process": "^1.0.2", - "cocopa": "0.0.5", + "cocopa": "0.0.6", "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index a839c725..c8af33ce 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -835,7 +835,7 @@ export class ArduinoApp { ccp.CCppPropertiesCppStandard.Cpp11); const prop = new ccp.CCppProperties(); prop.read(pPath); - prop.merge(content); + prop.merge(content, ccp.CCppPropertiesMergeMode.ReplaceSameNames); if (prop.write(pPath)) { arduinoChannel.info("IntelliSense configuration updated."); } else { From 4f6565c60725b44bdd9cfbffb1d2403bc69f7546 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Mon, 10 Feb 2020 02:33:24 +0100 Subject: [PATCH 030/142] Minor documentation rub --- BRANCHNOTES.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index a3bc85ce..788e4e17 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -97,11 +97,11 @@ I will list every supporter here, thanks! * Proper interactive serial terminal (this is the second major show stopper in my opinion) * Command history option * From https://github.com/microsoft/vscode-arduino/issues/463#issuecomment-583846263 and following: - * allow input on the serial monitor in a convenient way - ie just type and hit return, just like the Arduino IDE - * have the serial monitor window NOT keep turning off autoscroll (there is a separate ticket for this) - * have the option of the serial monitor and/or compile window auto clear each time the sketch is compiled - * Plus there is the annoying default where the compile runs in verbose mode and we have to manually edit config files to turn off the trace output - * Plus plus... Is there a way to automatically select the right serial port? + * Allow input on the serial monitor in a convenient way - ie just type and hit return, just like the Arduino IDE + * Have the serial monitor window NOT keep turning off autoscroll (there is a separate ticket for this) + * Have the option of the serial monitor and/or compile window auto clear each time the sketch is compiled + * There is the annoying default where the compile runs in verbose mode and we have to manually edit config files to turn off the trace output + * Is there a way to automatically select the right serial port? * Oh and one more. I want the serial output and perhaps compile windows to be undocked or at least I want them to sit to the right of my code window but they seem rigidly stuck at the bottom of the screen. * And I would probably prioritize ease of use over better editing/intelligence. * Being able to set baud rate within monitor @@ -112,8 +112,6 @@ I will list every supporter here, thanks! * [CLI](https://serialport.io/docs/guide-cli) * [API](https://serialport.io/docs/guide-usage) * write a [debugger extension](https://code.visualstudio.com/api/extension-guides/debugger-extension) with a [mock](https://github.com/Microsoft/vscode-mock-debug) which communicates with the serial - -Plus plus... Is there a way to automatically select the right serial port? * Lots of redundant code * e.g. "upload is a superset of "verify" * general lack of modularity - the above is the result From 6a42d3f3d94dd44cda9f647bf17af4a552e909e6 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Mon, 10 Feb 2020 05:17:19 +0100 Subject: [PATCH 031/142] Moved intellisense function to a separate file --- BRANCHNOTES.md | 3 + package-lock.json | 118 +++++++++++++++++++++--------------- package.json | 7 +-- src/arduino/arduino.ts | 90 +-------------------------- src/arduino/intellisense.ts | 98 ++++++++++++++++++++++++++++++ webpack.config.js | 10 --- 6 files changed, 175 insertions(+), 151 deletions(-) create mode 100644 src/arduino/intellisense.ts diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 788e4e17..80877b96 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -49,6 +49,7 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | +| | :white_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command. This has to be fixed. | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet @@ -79,6 +80,7 @@ I will list every supporter here, thanks! 2020-02-07 Elektronik Workshop: 48 :beers: (12h coding) 2020-02-08 Elektronik Workshop: 52 :beers: (13h coding) 2020-02-09 Elektronik Workshop: 40 :beers: (10h coding) +2020-02-10 Elektronik Workshop: 32 :beers: (8h coding) @@ -88,6 +90,7 @@ I will list every supporter here, thanks! * [Interactive regex debugger](https://regex101.com/) * [Git branch management](https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/) * [Collapsible Markdown](https://gist.githubusercontent.com/joyrexus/16041f2426450e73f5df9391f7f7ae5f/raw/f774f242feff6bae4a5be7d6c71aa5df2e3fcb0e/README.md) +* [Arduino CLI manpage](https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc) ## Issues Concerning this Project * https://github.com/Microsoft/vscode-cpptools/issues/1750 diff --git a/package-lock.json b/package-lock.json index 3fbb2e87..9c9bab37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -480,15 +480,15 @@ } }, "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "json-schema-traverse": "^0.3.0" } }, "ajv-errors": { @@ -1410,11 +1410,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" - }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -1561,15 +1556,16 @@ "semver": "^5.4.1" } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "cocopa": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.6.tgz", - "integrity": "sha512-iRMDGthPTi0yhQRq2n+ZqL0zuuY08JVa5UFSkQbTb1BLBDhH4fmG/uEo6xLT91+clTZCkTocAS05xDquJo8kwQ==", - "requires": { - "child_process": "^1.0.2", - "fs": "0.0.1-security", - "path": "^0.12.7" - } + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/cocopa/-/cocopa-0.0.7.tgz", + "integrity": "sha512-L/BaneLuO56MCjAkbzUFRRZhKf0P37LY+3JClzV/poVQTEDHxS8KWa8Rw5IpCFOdZiGitQvy6r1L/5I/TJt9nA==" }, "code-point-at": { "version": "1.1.0", @@ -2439,6 +2435,18 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -2454,6 +2462,12 @@ "ms": "^2.1.1" } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -2469,6 +2483,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2954,9 +2974,9 @@ } }, "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, "fast-json-stable-stringify": { @@ -3258,11 +3278,6 @@ "readable-stream": "^2.0.0" } }, - "fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" - }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -5487,9 +5502,9 @@ "dev": true }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -6811,25 +6826,6 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "requires": { - "process": "^0.11.1", - "util": "^0.10.3" - }, - "dependencies": { - "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "requires": { - "inherits": "2.0.3" - } - } - } - }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -8169,6 +8165,18 @@ "string-width": "^3.0.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -8181,12 +8189,24 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", diff --git a/package.json b/package.json index 4aedf4f9..292c447c 100644 --- a/package.json +++ b/package.json @@ -564,10 +564,11 @@ "devDependencies": { "@types/compare-versions": "^3.0.0", "@types/mocha": "^5.2.7", - "@types/node": "^6.0.40", + "@types/node": "^6.14.9", "@types/vscode": "^1.43.0", "@types/winreg": "^1.2.30", "acorn": "^7.4.0", + "ajv": "^5.0.0", "del": "^2.2.2", "eslint": "^6.8.0", "eslint-config-standard": "^10.2.1", @@ -594,12 +595,10 @@ }, "dependencies": { "body-parser": "^1.16.1", - "child_process": "^1.0.2", - "cocopa": "0.0.6", + "cocopa": "^0.0.7", "compare-versions": "^3.4.0", "eventemitter2": "^4.1.0", "express": "^4.14.1", - "fs": "0.0.1-security", "glob": "^7.1.1", "iconv-lite": "^0.4.18", "impor": "^0.1.1", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index c8af33ce..b6ca1059 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import * as ccp from "cocopa"; import * as fs from "fs"; import * as glob from "glob"; import * as os from "os"; @@ -23,6 +22,7 @@ import { arduinoChannel } from "../common/outputChannel"; import { ArduinoWorkspace } from "../common/workspace"; import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; +import { makeCompilerParserContext } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; /** @@ -257,7 +257,7 @@ export class ArduinoApp { arduinoChannel.show(); let verifyResult: boolean; - const compilerParserContext = this.makeCompilerParserContext(dc); + const compilerParserContext = makeCompilerParserContext(dc); try { await util.spawn(this._settings.commandPath, @@ -796,92 +796,6 @@ export class ArduinoApp { // return VscodeSettings.getInstance().useArduinoCli; } - /** - * Creates a context which is used for compiler command parsing - * during building (verify, upload, ...). - * - * This context makes sure that it can be used in those sections - * without having to check whether this feature is en- or disabled - * and keeps the calling context more readable. - * - * @param dc The device context of the caller. - */ - private makeCompilerParserContext(dc: DeviceContext) - : { callback: (s: string) => void; conclude: () => void; } { - - const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; - const project = dc.disableIntelliSenseAutoGen; - - if (project !== "disable" && !globalDisable || - project === "enable") { - - const engines = this.makeCompilerParserEngines(dc); - const runner = new ccp.Runner(engines); - - // set up the function to be called after parsing - const _conclude = () => { - if (!runner.result) { - arduinoChannel.warning("Failed to generate IntelliSense configuration."); - return; - } - const pPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); - // TODO: check what kind of result we've got: gcc or other architecture: - // and instantiate content accordingly (to be implemented within cocopa) - const content = new ccp.CCppPropertiesContentResult(runner.result, - "Arduino", - ccp.CCppPropertiesISMode.Gcc_X64, - ccp.CCppPropertiesCStandard.C11, - // as of 1.8.11 arduino is on C++11 - ccp.CCppPropertiesCppStandard.Cpp11); - const prop = new ccp.CCppProperties(); - prop.read(pPath); - prop.merge(content, ccp.CCppPropertiesMergeMode.ReplaceSameNames); - if (prop.write(pPath)) { - arduinoChannel.info("IntelliSense configuration updated."); - } else { - arduinoChannel.info("IntelliSense configuration already up to date."); - } - }; - return { - callback: runner.callback(), - conclude: _conclude, - }; - } - return { - callback: undefined, - conclude: undefined, - } - }; - - /** - * - * @param dc - */ - private makeCompilerParserEngines(dc: DeviceContext) { - - let sketch = path.basename(dc.sketch); - const dotcpp = sketch.endsWith(".ino") ? ".cpp" : ""; - sketch = `-o\\s+\\S*${ccp.regExEscape(sketch)}${dotcpp}\\.o`; - - const matchPattern = [ - // make sure we're running g++ - /(?:^|-)g\+\+\s+/, - // make sure we're compiling - /\s+-c\s+/, - // trigger parser when compiling the main sketch - RegExp(sketch), - ]; - - const dontMatchPattern = [ - // make sure Arduino's not testing libraries - /-o\s+\/dev\/null/, - ]; - - // setup the parser with its engines - const gccParserEngine = new ccp.ParserGcc(matchPattern, dontMatchPattern); - return [gccParserEngine]; - } - private getProgrammerString(): string { const selectProgrammer = this.programmerManager.currentProgrammer; if (!selectProgrammer) { diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts new file mode 100644 index 00000000..7663241d --- /dev/null +++ b/src/arduino/intellisense.ts @@ -0,0 +1,98 @@ +import * as ccp from "cocopa"; +import * as path from "path"; +import * as constants from "../common/constants"; +import { arduinoChannel } from "../common/outputChannel"; +import { ArduinoWorkspace } from "../common/workspace"; +import { DeviceContext } from "../deviceContext"; +import { VscodeSettings } from "./vscodeSettings"; + +/** + * Creates a context which is used for compiler command parsing + * during building (verify, upload, ...). + * + * This context makes sure that it can be used in those sections + * without having to check whether this feature is en- or disabled + * and keeps the calling context more readable. + * + * @param dc The device context of the caller. + */ +export function makeCompilerParserContext(dc: DeviceContext) + : { callback: (s: string) => void; conclude: () => void; } { + + const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; + const project = dc.disableIntelliSenseAutoGen; + + if (project !== "disable" && !globalDisable || + project === "enable") { + + const engines = makeCompilerParserEngines(dc); + const runner = new ccp.Runner(engines); + + // set up the function to be called after parsing + const _conclude = () => { + if (!runner.result) { + arduinoChannel.warning("Failed to generate IntelliSense configuration."); + return; + } + const pPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + // TODO: check what kind of result we've got: gcc or other architecture: + // and instantiate content accordingly (to be implemented within cocopa) + const content = new ccp.CCppPropertiesContentResult(runner.result, + "Arduino", + ccp.CCppPropertiesISMode.Gcc_X64, + ccp.CCppPropertiesCStandard.C11, + // as of 1.8.11 arduino is on C++11 + ccp.CCppPropertiesCppStandard.Cpp11); + try { + const prop = new ccp.CCppProperties(); + prop.read(pPath); + prop.merge(content, ccp.CCppPropertiesMergeMode.ReplaceSameNames); + if (prop.write(pPath)) { + arduinoChannel.info("IntelliSense configuration updated."); + } else { + arduinoChannel.info("IntelliSense configuration already up to date."); + } + } catch (e) { + // tslint:disable-next-line: no-console + console.log(e); + } + }; + return { + callback: runner.callback(), + conclude: _conclude, + } + } + return { + callback: undefined, + conclude: undefined, + } +}; + +/** + * + * @param dc + */ +function makeCompilerParserEngines(dc: DeviceContext) { + + let sketch = path.basename(dc.sketch); + const dotcpp = sketch.endsWith(".ino") ? ".cpp" : ""; + sketch = `-o\\s+\\S*${ccp.regExEscape(sketch)}${dotcpp}\\.o`; + + const matchPattern = [ + // make sure we're running g++ + /(?:^|-)g\+\+\s+/, + // make sure we're compiling + /\s+-c\s+/, + // trigger parser when compiling the main sketch + RegExp(sketch), + ]; + + const dontMatchPattern = [ + // make sure Arduino's not testing libraries + /-o\s+\/dev\/null/, + ]; + + // setup the parser with its engines + const gccParserEngine = new ccp.ParserGcc(matchPattern, dontMatchPattern); + return [gccParserEngine]; +} diff --git a/webpack.config.js b/webpack.config.js index 66cab632..b18b814a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -21,16 +21,6 @@ function getEntry() { const list = getDependenciesFromNpm(mod); const moduleList = list.filter((value, index, self) => { - /* Earlier versions of cocopa contains incorrect nodejs dependencies. Remove this workaround after updating to version v0.0.7. - * Resulted in webpack errors like: - * Unhandled rejection Error in plugin "webpack" - * Message: - * ["Entry module not found: Error: Can't resolve './node_modules/child_process' in ... - * ... "Entry module not found: Error: Can't resolve './node_modules/fs' in ... - */ - if (value === "fs" || value === "child_process") { - return false; - } return self.indexOf(value) === index && unbundledModule.indexOf(value) === -1 && !/^@types\//.test(value); }); From e80a338642eb54bf2eb08f64167ec2a7ffcf584e Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 12 Feb 2020 17:00:26 +0100 Subject: [PATCH 032/142] Added instructions how to run development code --- BRANCHNOTES.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 80877b96..137e1659 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -122,6 +122,63 @@ I will list every supporter here, thanks! * Possibility to jump to compilation errors from compiler output and highlight compiler errors ---- +## How to beta test cutting edge code from the repo + +*I wrote the follwing for @iFreilicht, if anyone has some additional findings please let me know to keep this documentation up to date* + +that's the reason I embarked on this project. Great if you'd like to help out - this comes in really handy. Getting the stuff rock solid is one of my central objectives and testing is one cornerstones here. + +Currently my work is at the following state: +* Parsing works +* Enable/disable via global or project flags +* You can have personal configurations which aren't overwritten + +But I'm not fully there yet. Things which are yet to be done are: + +* Move the parser/configuration-generator out of the "verify" code, since it requires the build to be run with `--verbose` flag enabled which most probably few like (except for me :) I always switch on all warnings and set it to verbose). + This is something I can implement pretty fast +* I have not removed the original "IntelliSense configurator" which ruins the `c_cpp_properties.json` every now and then but that's one of my next tasks as well. And you can simply delete the file as you can re-generate the perfect version with my analyzer/generator by simply verifying your sketch +* My development system runs on Ubuntu-GNU/linux and I haven't done any work for Windows yet but that's one of the next steps. The chances that it works on OSX "out of the box" are pretty good + +To run the development version clone my [repository](https://github.com/elektronikworkshop/vscode-arduino) and checkout the `intellisense-autoconfig` branch + +The following steps requires you to have `git`, `vscode`, `npm` and `nodejs` at recent versions. On my Ubuntu system I don't use the versions supplied by my package manager (`apt`) as they are usually pretty outdated - I usually install `nodejs` and `npm` from their respective/official websites. If you're on Windows you'll have to be a bit more patient since I haven't set up a virtual machine yet for testing and Windows is definitely not my domain. But I'll document the setup process as soon as I'll get to it. + +```bash +git clone https://github.com/elektronikworkshop/vscode-arduino +cd vscode-arduino +# switch to the feature branch (not necessary probably because this branch is set to default) +git checkout intellisense-autoconfig +# check if you're on the right branch +git status +# install module dependencies +npm install +# install gulp builder globally to make it available to the path (requires relaunching your shell) +npm install -g gulp +# to make sure that gulp is actually working type +gulp --tasks +# if not -> configure your $PATH and open a new terminal to make sure it's added, then +# open vscode +code . +``` +Making sure that gulp is on your `$PATH` is essential. As long this isn't the case the following steps must not be carried out. + +Then hit F5 to debug or select it from the *Debug* menu. vscode will then complain that there's *No task defined* and you let it generate the configuration for you by clicking the button *Configure Task*. After configuring the tasks debug (`F5`) or build (`Ctrl + Shift + B`) should work. + +As soon as you've got it up and running (`F5` spawns a new window), just navigate to your Arduino project. Configure in the vscode-arduino global settings the build output to `verbose` and run verify (`Ctrl + Alt + R`) as you know it. This will then generate a fresh `c_cpp_properties.json`. As long as I haven't removed the generator from the current maintainers you'll have to regenerate it as soon as you see those double asterisk-paths like `whatever/path/**` - if I get to it today, I'll give it a try and will remove/disable it for testing. You can then pull my changes in by running +```bash +git pull +``` +from within your terminal inside vscode (and your `vscode-arduino` folder). + +Different IntelliSense configurations can be selected in vscode in the lower right. The configuration my branch generates is called *Arduino* as depicted here: + +![74001156-cfce8280-496a-11ea-9b9d-7d30c83765c1](https://user-images.githubusercontent.com/21954933/74351237-2696ea80-4db7-11ea-9f7a-1bfc652ad5f5.png) + +Sometimes IntelliSense has problems within the extension host (which you're running when launching it with `F5`) and fails to resolve some header paths. This is a problem of vscode and not the extension. Then just restart. The extension host is a bit buggy if you ask me, so I hope I can provide some development snapshots in the future which will render all the steps above superfluous and let you run the latest development version with your regular vscode. + +---- + ## Implementation **Note** Check this vscode feature: ``` From 4341261e91981a2687beb70b8583277a8b5d5e7e Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 12 Feb 2020 17:53:28 +0100 Subject: [PATCH 033/142] More on beta testing, added link to chat room --- BRANCHNOTES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 137e1659..69a5ce03 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -124,8 +124,17 @@ I will list every supporter here, thanks! ## How to beta test cutting edge code from the repo +For development and testing of this fork/branch I've set up a dedicated chat room: + +[![Gitter](https://badges.gitter.im/vscode-arduino-ew-fork/community.svg)](https://gitter.im/vscode-arduino-ew-fork/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +**Note:** the topic is strictly development and testing of this feature enhancement branch. It's not a place where you can ask for help on your Arduino problems. There are dedicated forums for that and I'll delete any question of this type. + + *I wrote the follwing for @iFreilicht, if anyone has some additional findings please let me know to keep this documentation up to date* + > I'd love to try this out and give feedback. It would be a huge quality of life improvement if this was to work. Could you point me to a resource that helps me install your version of the extension? + that's the reason I embarked on this project. Great if you'd like to help out - this comes in really handy. Getting the stuff rock solid is one of my central objectives and testing is one cornerstones here. Currently my work is at the following state: From 56c963420f33222f2cba49d33ab15ef601cb3571 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 12 Feb 2020 18:12:43 +0100 Subject: [PATCH 034/142] Note how to run vscode without having gulp on your user path --- BRANCHNOTES.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 69a5ce03..3eb4bae2 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -163,6 +163,7 @@ git status # install module dependencies npm install # install gulp builder globally to make it available to the path (requires relaunching your shell) +# there's another option below, see ./launchcode.sh npm install -g gulp # to make sure that gulp is actually working type gulp --tasks @@ -172,7 +173,18 @@ code . ``` Making sure that gulp is on your `$PATH` is essential. As long this isn't the case the following steps must not be carried out. -Then hit F5 to debug or select it from the *Debug* menu. vscode will then complain that there's *No task defined* and you let it generate the configuration for you by clicking the button *Configure Task*. After configuring the tasks debug (`F5`) or build (`Ctrl + Shift + B`) should work. +Another option to launch code with gulp on your path is (within bash or similar) +```bash +# create launch script +echo "PATH=./node_modules/.bin:$PATH code ." > launchcode +# make it executable +chmod +x launchcode +# now you can launch vscode like this +./launchcode +``` +This way you don't have to install gulp globally anymore (no `npm install -g gulp`). The path to the vscode dependency module binary is set as temporary environment variable when launching vscode. + +When everything's fine and vscode running, hit F5 to debug or select it from the *Debug* menu. vscode will then complain that there's *No task defined* and you let it generate the configuration for you by clicking the button *Configure Task*. After configuring the tasks debug (`F5`) or build (`Ctrl + Shift + B`) should work. As soon as you've got it up and running (`F5` spawns a new window), just navigate to your Arduino project. Configure in the vscode-arduino global settings the build output to `verbose` and run verify (`Ctrl + Alt + R`) as you know it. This will then generate a fresh `c_cpp_properties.json`. As long as I haven't removed the generator from the current maintainers you'll have to regenerate it as soon as you see those double asterisk-paths like `whatever/path/**` - if I get to it today, I'll give it a try and will remove/disable it for testing. You can then pull my changes in by running ```bash From 5945d8dd0c1aa4552769b84b018adab84c285404 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 15 Feb 2020 12:47:31 +0100 Subject: [PATCH 035/142] Updated beer money log --- BRANCHNOTES.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 3eb4bae2..6de2f022 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -81,6 +81,9 @@ I will list every supporter here, thanks! 2020-02-08 Elektronik Workshop: 52 :beers: (13h coding) 2020-02-09 Elektronik Workshop: 40 :beers: (10h coding) 2020-02-10 Elektronik Workshop: 32 :beers: (8h coding) +2020-02-11 Elektronik Workshop: 16 :beers: (4h coding) +2020-02-12 Elektronik Workshop: 32 :beers: (8h coding) +2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) @@ -91,6 +94,8 @@ I will list every supporter here, thanks! * [Git branch management](https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/) * [Collapsible Markdown](https://gist.githubusercontent.com/joyrexus/16041f2426450e73f5df9391f7f7ae5f/raw/f774f242feff6bae4a5be7d6c71aa5df2e3fcb0e/README.md) * [Arduino CLI manpage](https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc) +* [Install extensions from file](https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/) +* [Publish extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) ## Issues Concerning this Project * https://github.com/Microsoft/vscode-cpptools/issues/1750 @@ -124,9 +129,9 @@ I will list every supporter here, thanks! ## How to beta test cutting edge code from the repo -For development and testing of this fork/branch I've set up a dedicated chat room: +For development and testing of this fork/branch I've set up a dedicated chat room: -[![Gitter](https://badges.gitter.im/vscode-arduino-ew-fork/community.svg)](https://gitter.im/vscode-arduino-ew-fork/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Gitter](https://badges.gitter.im/vscode-arduino-ew-fork/community.svg)](https://gitter.im/vscode-arduino-ew-fork/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) **Note:** the topic is strictly development and testing of this feature enhancement branch. It's not a place where you can ask for help on your Arduino problems. There are dedicated forums for that and I'll delete any question of this type. @@ -146,7 +151,7 @@ But I'm not fully there yet. Things which are yet to be done are: * Move the parser/configuration-generator out of the "verify" code, since it requires the build to be run with `--verbose` flag enabled which most probably few like (except for me :) I always switch on all warnings and set it to verbose). This is something I can implement pretty fast -* I have not removed the original "IntelliSense configurator" which ruins the `c_cpp_properties.json` every now and then but that's one of my next tasks as well. And you can simply delete the file as you can re-generate the perfect version with my analyzer/generator by simply verifying your sketch +* I have not removed the original "IntelliSense configurator" which ruins the `c_cpp_properties.json` every now and then but that's one of my next tasks as well. And you can simply delete the file as you can re-generate the perfect version with my analyzer/generator by simply verifying your sketch * My development system runs on Ubuntu-GNU/linux and I haven't done any work for Windows yet but that's one of the next steps. The chances that it works on OSX "out of the box" are pretty good To run the development version clone my [repository](https://github.com/elektronikworkshop/vscode-arduino) and checkout the `intellisense-autoconfig` branch From a5d4695d5201dfea5e7e69c67ba1318cbc07332a Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 15 Feb 2020 20:49:13 +0100 Subject: [PATCH 036/142] Updated branch notes -- documented changes not committed though. --- BRANCHNOTES.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 6de2f022..a5713e73 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -14,15 +14,28 @@ The generator takes the parser's output and transforms it into a valid `c_cpp_pr Provide a configuration flag which allows the user to turn this feature off - this is useful for the cases in which this magic fails or the user has a very specific setup. Although this branch tries to eliminate most of the latter cases. ### Global Tasks in vscode-arduino +See table below. + +### Branch Log +**2020 02 05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) +**2020 02 06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. +**2020 02 07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. +**2020 02 08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md). vscode-arduino now tries to generate an IntelliSense configuration even if compilation (verify) should fail. vscode-arduino now tries to generate a IntelliSense configuration even if Arduino's verify failed (if the main sketch compilation was invoked before anything failed) +**2020 02 09** Moved vscode-arduino specific from cocopa over (to keep cocopa as generic as possible). More unit testing within cocopa. Some research regarding future serial monitor implementation. Implemented c_cpp_properties merging -> compiler analysis results are merged into existing configuration and will preserve configurations of different name than the vscode-studio default configuration name (currently "Arduino"). This opens up the possibility for users to write their own configurations without having to disable the autogeneration. Implemented "write on change" - `c_cpp_properties.json` will only be written if a new configuration has been detected. Now loads of tests have to be written for cocopa. +**2020 02 10-12** Worked primarily on cocopa and test cases, fixed some npm and build errors on vscode-arduino within my setup. +**2020 02 15** Merged `upload` `uploadUsingProgrammer` and `verify` into a single function since they shared mostly the same code +* Better readability +* Better maintainability +* Less code redundancy -> less code -> less bugs +* Keeps the calls to the Arduino build CLI at a single location + +During merging I found some bugs within those functions - mainly due to the above problem. The most notable were: +* The serial monitor state wasn't restored when something went wrong +* In one of the `upload` functions the original authors forgot to invoke the "pre build command" +* Error message formatting was fixed within `verify` only +* No consistent return values within `verify` (when it bailed out early it returned `void`) ### Status -**2020-02-05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) -**2020-02-06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. -**2020-02-07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. -**2020-02-08** Integrated `cocopa` into vscode-arduino. Added project configuration flag which can override the global flag in both ways (forced off, forced on). Made code tslint compliant. Began some documentation in [README.md](README.md). vscode-arduino now tries to generate an IntelliSense configuration even if compilation (verify) should fail. vscode-arduino now tries to generate a IntelliSense configuration even if Arduino's verify failed (if the main sketch compilation was invoked before anything failed) -**2020-02-09** Moved vscode-arduino specific from cocopa over (to keep cocopa as generic as possible). More unit testing within cocopa. Some research regarding future serial monitor implementation. Implemented c_cpp_properties merging -> compiler analysis results are merged into existing configuration and will preserve configurations of different name than the vscode-studio default configuration name (currently "Arduino"). This opens up the possibility for users to write their own configurations without having to disable the autogeneration. Implemented "write on change" - `c_cpp_properties.json` will only be written if a new configuration has been detected. Now loads of tests have to be written for cocopa. - - | | Tasks | |-----:|:--------| | **Build output parser** | :heavy_check_mark: Basic parser working | @@ -44,12 +57,13 @@ Provide a configuration flag which allows the user to turn this feature off - th | | :white_check_mark: All unit tests in cocopa | | | :white_check_mark: Test with cpp sketches | | **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | -| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch* c) *workbench initialized and no `c_cpp_properties.json` has been found*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *verify*? -> Good moment would be after the workbench initialization -> message in arduino channel | +| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch* c) *workbench initialized and no `c_cpp_properties.json` has been found*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *Arduino: Rebuild IntelliSense Configuration*? -> Good moment would be after the workbench initialization -> message in arduino channel | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | -| | :white_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command. This has to be fixed. | +| | :heavy_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command - this has to be fixed (done, see next item) | +| | :heavy_check_mark: Implement a *Rebuild IntelliSense Configuration* command which runs verify verbosely internally and therefore allows us to find and parse the compiler command | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet @@ -84,6 +98,7 @@ I will list every supporter here, thanks! 2020-02-11 Elektronik Workshop: 16 :beers: (4h coding) 2020-02-12 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) +2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) From e4c13d022f966f34490688dc6e73bcf8d8c87056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Tue, 8 Dec 2020 22:24:21 +0100 Subject: [PATCH 037/142] Rename Logger to logger --- src/arduino/arduino.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index b6ca1059..5beac3aa 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -9,7 +9,7 @@ import * as vscode from "vscode"; import * as constants from "../common/constants"; import * as util from "../common/util"; -import * as Logger from "../logger/logger"; +import * as logger from "../logger/logger"; import { DeviceContext } from "../deviceContext"; import { IArduinoSettings } from "./arduinoSettings"; @@ -177,7 +177,7 @@ export class ArduinoApp { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); + logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); return; } @@ -237,7 +237,7 @@ export class ArduinoApp { const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { - Logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); + logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); return false; } @@ -382,7 +382,7 @@ export class ArduinoApp { deviceContext = util.tryParseJSON(fs.readFileSync(configFilePath, "utf8")); } if (!deviceContext) { - Logger.notifyAndThrowUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); + logger.notifyAndThrowUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); } deviceContext.configurations = deviceContext.configurations || []; @@ -799,7 +799,7 @@ export class ArduinoApp { private getProgrammerString(): string { const selectProgrammer = this.programmerManager.currentProgrammer; if (!selectProgrammer) { - Logger.notifyUserError("getProgrammerString", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); + logger.notifyUserError("getProgrammerString", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); return; } return selectProgrammer; @@ -808,7 +808,7 @@ export class ArduinoApp { private getBoardBuildString(): string { const selectedBoard = this.boardManager.currentBoard; if (!selectedBoard) { - Logger.notifyUserError("getBoardBuildString", new Error(constants.messages.NO_BOARD_SELECTED)); + logger.notifyUserError("getBoardBuildString", new Error(constants.messages.NO_BOARD_SELECTED)); return; } return selectedBoard.getBuildConfig(); From dd308accb7bd38228b6165c91f4b6ccd06121be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Tue, 8 Dec 2020 22:40:05 +0100 Subject: [PATCH 038/142] Rename verifyResult to success --- src/arduino/arduino.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 5beac3aa..4c008baf 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -256,7 +256,7 @@ export class ArduinoApp { arduinoChannel.show(); - let verifyResult: boolean; + let success = false; const compilerParserContext = makeCompilerParserContext(dc); try { @@ -266,7 +266,7 @@ export class ArduinoApp { undefined, compilerParserContext.callback); arduinoChannel.end(`Finished verifying sketch - ${dc.sketch}${os.EOL}`); - verifyResult = true; + success = true; } catch (reason) { const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : @@ -274,14 +274,13 @@ export class ArduinoApp { reason.message : JSON.stringify(reason); arduinoChannel.error(msg); - verifyResult = false; } if (compilerParserContext.conclude) { compilerParserContext.conclude(); } - return verifyResult; + return success; } public tryToUpdateIncludePaths() { From 3879639377d7948b385a1ffb7038021769b58e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 20:21:12 +0100 Subject: [PATCH 039/142] Move args variable till top of upload, uploadUsingProgrammer and verify --- src/arduino/arduino.ts | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 4c008baf..35a099c2 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -101,10 +101,14 @@ export class ArduinoApp { */ public async upload(compile: boolean = true, useProgrammer: boolean = false) { const dc = DeviceContext.getInstance(); + const args: string[] = []; const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return; } + if (!this.useArduinoCli()) { + args.push("--board", boardDescriptor); + } const selectProgrammer = useProgrammer ? this.getProgrammerString() : null; if (useProgrammer && !selectProgrammer) { @@ -149,10 +153,17 @@ export class ArduinoApp { } const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) - const args = (!compile && this.useArduinoCli()) ? ["upload", "-b", boardDescriptor] : - this.useArduinoCli() ? ["compile", "--upload", "-b", boardDescriptor] : - ["--upload", "--board", boardDescriptor]; + if (!this.useArduinoCli()) { + args.push("--upload"); + } else { + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + if (compile) { + args.push("compile", "--upload"); + } else { + args.push("upload"); + } + args.push("-b", boardDescriptor); + } if (useProgrammer) { if (this.useArduinoCli()) { @@ -206,10 +217,14 @@ export class ArduinoApp { public async verify(output: string = "") { const dc = DeviceContext.getInstance(); + const args: string[] = []; const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return false; } + if (!this.useArduinoCli()) { + args.push("--board", boardDescriptor); + } if (!ArduinoWorkspace.rootPath) { vscode.window.showWarningMessage("Cannot find the sketch file."); @@ -229,7 +244,14 @@ export class ArduinoApp { } const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - const args = this.useArduinoCli() ? ["compile", "-b", boardDescriptor, appPath] : ["--verify", "--board", boardDescriptor, appPath]; + + if (!this.useArduinoCli()) { + args.push("--verify"); + } else { + args.push("compile", "-b", boardDescriptor); + } + + args.push(appPath); if (VscodeSettings.getInstance().logLevel === "verbose") { args.push("--verbose"); } From a6968e07dd1ce1c782c192f540851c5d2ed8b3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 20:45:53 +0100 Subject: [PATCH 040/142] Introduce verbose variable --- src/arduino/arduino.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 35a099c2..e638e7f6 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -181,7 +181,8 @@ export class ArduinoApp { return; } - if (VscodeSettings.getInstance().logLevel === "verbose") { + const verbose = VscodeSettings.getInstance().logLevel === "verbose"; + if (verbose) { args.push("--verbose"); } if (dc.output && compile) { @@ -252,7 +253,8 @@ export class ArduinoApp { } args.push(appPath); - if (VscodeSettings.getInstance().logLevel === "verbose") { + const verbose = VscodeSettings.getInstance().logLevel === "verbose"; + if (verbose) { args.push("--verbose"); } if (output || dc.output) { From b28f574dde43c89160d49377ba902d83bb96a170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 20:59:19 +0100 Subject: [PATCH 041/142] Unify util.spawn invocation --- src/arduino/arduino.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index e638e7f6..9725e10d 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -205,7 +205,11 @@ export class ArduinoApp { const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; arduinoChannel.warning(msg); } - await util.spawn(this._settings.commandPath, arduinoChannel.channel, args).then(async () => { + await util.spawn( + this._settings.commandPath, + arduinoChannel.channel, + args, + ).then(async () => { UsbDetector.getInstance().resumeListening(); if (needRestore) { await serialMonitor.openSerialMonitor(); @@ -283,22 +287,23 @@ export class ArduinoApp { let success = false; const compilerParserContext = makeCompilerParserContext(dc); - try { - await util.spawn(this._settings.commandPath, - arduinoChannel.channel, - args, - undefined, - compilerParserContext.callback); + await util.spawn( + this._settings.commandPath, + arduinoChannel.channel, + args, + undefined, + compilerParserContext.callback, + ).then(() => { arduinoChannel.end(`Finished verifying sketch - ${dc.sketch}${os.EOL}`); success = true; - } catch (reason) { + }, (reason) => { const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : reason.message ? reason.message : JSON.stringify(reason); arduinoChannel.error(msg); - } + }); if (compilerParserContext.conclude) { compilerParserContext.conclude(); From 0678c9af5bed34465c389b9491541245aa955869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 21:09:50 +0100 Subject: [PATCH 042/142] Unify exit error message --- src/arduino/arduino.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 9725e10d..300afbe2 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -216,7 +216,12 @@ export class ArduinoApp { } arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); }, (reason) => { - arduinoChannel.error(`Exit with code=${reason.code}${os.EOL}`); + const msg = reason.code ? + `Exit with code=${reason.code}${os.EOL}` : + reason.message ? + reason.message : + JSON.stringify(reason); + arduinoChannel.error(msg); }); } From 990c1a16a4da489cd1b7c6a00ae337d09d4e8b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 21:36:40 +0100 Subject: [PATCH 043/142] Add cleanup expression --- src/arduino/arduino.ts | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 300afbe2..c14092bb 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -137,10 +137,6 @@ export class ArduinoApp { arduinoChannel.show(); arduinoChannel.start(`Upload sketch - ${dc.sketch}`); - const serialMonitor = SerialMonitor.getInstance(); - - const needRestore = await serialMonitor.closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); await vscode.workspace.saveAll(false); if (!await this.runPreBuildCommand(dc)) { @@ -205,17 +201,28 @@ export class ArduinoApp { const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; arduinoChannel.warning(msg); } + + // stop serial monitor when everything is prepared and good + // what makes restoring of its previous state easier + const restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + + const cleanup = async () => { + UsbDetector.getInstance().resumeListening(); + if (restoreSerialMonitor) { + await SerialMonitor.getInstance().openSerialMonitor(); + } + } + await util.spawn( this._settings.commandPath, arduinoChannel.channel, args, ).then(async () => { - UsbDetector.getInstance().resumeListening(); - if (needRestore) { - await serialMonitor.openSerialMonitor(); - } + await cleanup(); arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); - }, (reason) => { + }, async (reason) => { + await cleanup(); const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : reason.message ? @@ -292,16 +299,22 @@ export class ArduinoApp { let success = false; const compilerParserContext = makeCompilerParserContext(dc); + const cleanup = async () => { + await Promise.resolve(); + } + await util.spawn( this._settings.commandPath, arduinoChannel.channel, args, undefined, compilerParserContext.callback, - ).then(() => { + ).then(async () => { + await cleanup(); arduinoChannel.end(`Finished verifying sketch - ${dc.sketch}${os.EOL}`); success = true; - }, (reason) => { + }, async (reason) => { + await cleanup(); const msg = reason.code ? `Exit with code=${reason.code}${os.EOL}` : reason.message ? From 82270144542ed9620de7bb524f35f7b995300a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 21:52:59 +0100 Subject: [PATCH 044/142] Add selectSerial expression --- src/arduino/arduino.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index c14092bb..498e5382 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -124,13 +124,17 @@ export class ArduinoApp { await this.getMainSketch(dc); } - if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { + const selectSerial = async () => { const choice = await vscode.window.showInformationMessage( "Serial port is not specified. Do you want to select a serial port for uploading?", "Yes", "No"); if (choice === "Yes") { vscode.commands.executeCommand("arduino.selectSerialPort"); } + } + + if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { + await selectSerial(); return; } From 88bed5e34ef42caa251507649ea5f57618567b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 21:57:40 +0100 Subject: [PATCH 045/142] Inline and move appPath later --- src/arduino/arduino.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 498e5382..7df8f722 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -152,7 +152,6 @@ export class ArduinoApp { return; } - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); if (!this.useArduinoCli()) { args.push("--upload"); } else { @@ -176,7 +175,7 @@ export class ArduinoApp { if (dc.port) { args.push("--port", dc.port); } - args.push(appPath); + if (!await this.runPreBuildCommand(dc)) { return; } @@ -211,6 +210,9 @@ export class ArduinoApp { const restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); UsbDetector.getInstance().pauseListening(); + // Push sketch as last argument + args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); + const cleanup = async () => { UsbDetector.getInstance().resumeListening(); if (restoreSerialMonitor) { @@ -264,15 +266,12 @@ export class ArduinoApp { return false; } - const appPath = path.join(ArduinoWorkspace.rootPath, dc.sketch); - if (!this.useArduinoCli()) { args.push("--verify"); } else { args.push("compile", "-b", boardDescriptor); } - args.push(appPath); const verbose = VscodeSettings.getInstance().logLevel === "verbose"; if (verbose) { args.push("--verbose"); @@ -303,6 +302,9 @@ export class ArduinoApp { let success = false; const compilerParserContext = makeCompilerParserContext(dc); + // Push sketch as last argument + args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); + const cleanup = async () => { await Promise.resolve(); } From e03dc382a5a74c36236275d4d54ced439acfaada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 22:26:39 +0100 Subject: [PATCH 046/142] Move arg buildup earlier in functions --- src/arduino/arduino.ts | 43 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 7df8f722..75e4581a 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -138,15 +138,6 @@ export class ArduinoApp { return; } - arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); - - await vscode.workspace.saveAll(false); - - if (!await this.runPreBuildCommand(dc)) { - return; - } - if (!compile && !this.useArduinoCli()) { arduinoChannel.error("This command is only availble when using the Arduino CLI"); return; @@ -176,14 +167,20 @@ export class ArduinoApp { args.push("--port", dc.port); } - if (!await this.runPreBuildCommand(dc)) { - return; - } - const verbose = VscodeSettings.getInstance().logLevel === "verbose"; if (verbose) { args.push("--verbose"); } + + await vscode.workspace.saveAll(false); + + arduinoChannel.show(); + arduinoChannel.start(`Upload sketch - ${dc.sketch}`); + + if (!await this.runPreBuildCommand(dc)) { + return; + } + if (dc.output && compile) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); const dirPath = path.dirname(outputPath); @@ -258,14 +255,6 @@ export class ArduinoApp { await this.getMainSketch(dc); } - await vscode.workspace.saveAll(false); - - arduinoChannel.start(`Verify sketch - ${dc.sketch}`); - - if (!await this.runPreBuildCommand(dc)) { - return false; - } - if (!this.useArduinoCli()) { args.push("--verify"); } else { @@ -276,6 +265,16 @@ export class ArduinoApp { if (verbose) { args.push("--verbose"); } + + await vscode.workspace.saveAll(false); + + arduinoChannel.show(); + arduinoChannel.start(`Verify sketch - ${dc.sketch}`); + + if (!await this.runPreBuildCommand(dc)) { + return false; + } + if (output || dc.output) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); const dirPath = path.dirname(outputPath); @@ -297,8 +296,6 @@ export class ArduinoApp { arduinoChannel.warning(msg); } - arduinoChannel.show(); - let success = false; const compilerParserContext = makeCompilerParserContext(dc); From cd9365b34ac84049a9082742eff2ccd2d11d044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 23:00:46 +0100 Subject: [PATCH 047/142] Move selectProgrammer down --- src/arduino/arduino.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 75e4581a..c17e67f5 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -110,11 +110,6 @@ export class ArduinoApp { args.push("--board", boardDescriptor); } - const selectProgrammer = useProgrammer ? this.getProgrammerString() : null; - if (useProgrammer && !selectProgrammer) { - return; - } - if (!ArduinoWorkspace.rootPath) { vscode.window.showWarningMessage("Cannot find the sketch file."); return; @@ -133,6 +128,11 @@ export class ArduinoApp { } } + const selectProgrammer = useProgrammer ? this.getProgrammerString() : null; + if (useProgrammer && !selectProgrammer) { + return; + } + if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { await selectSerial(); return; From 86719a36a65a9c93116a5fe325de68180bb604b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Tue, 5 Jan 2021 23:03:58 +0100 Subject: [PATCH 048/142] Spelling correction --- src/arduino/arduino.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index c17e67f5..09978b60 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -139,7 +139,7 @@ export class ArduinoApp { } if (!compile && !this.useArduinoCli()) { - arduinoChannel.error("This command is only availble when using the Arduino CLI"); + arduinoChannel.error("This command is only available when using the Arduino CLI"); return; } From 76c74cf99520c0236d988e0be9c5ca695bde2afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 23:26:46 +0100 Subject: [PATCH 049/142] Add buildMode argument to upload and uploadUsingProgrammer --- src/arduino/arduino.ts | 82 ++++++++++++++++++++++++------------------ src/extension.ts | 9 ++--- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 09978b60..c81bc6a7 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -25,6 +25,11 @@ import { UsbDetector } from "../serialmonitor/usbDetector"; import { makeCompilerParserContext } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; +export enum BuildMode { + Upload = "Uploading", + UploadProgrammer = "Uploading (programmer)", +}; + /** * Represent an Arduino application based on the official Arduino IDE. */ @@ -99,9 +104,10 @@ export class ArduinoApp { * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload * @param {bool} [useProgrammer=false] - Indicate whether a specific programmer should be used */ - public async upload(compile: boolean = true, useProgrammer: boolean = false) { + public async upload(buildMode: BuildMode, compile: boolean = true, useProgrammer: boolean = false) { const dc = DeviceContext.getInstance(); const args: string[] = []; + let restoreSerialMonitor: boolean = false; const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return; @@ -133,38 +139,40 @@ export class ArduinoApp { return; } - if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { - await selectSerial(); - return; - } + if (buildMode === BuildMode.Upload) { + if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { + await selectSerial(); + return; + } - if (!compile && !this.useArduinoCli()) { - arduinoChannel.error("This command is only available when using the Arduino CLI"); - return; - } + if (!compile && !this.useArduinoCli()) { + arduinoChannel.error("This command is only available when using the Arduino CLI"); + return; + } - if (!this.useArduinoCli()) { - args.push("--upload"); - } else { - // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) - if (compile) { - args.push("compile", "--upload"); + if (!this.useArduinoCli()) { + args.push("--upload"); } else { - args.push("upload"); + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + if (compile) { + args.push("compile", "--upload"); + } else { + args.push("upload"); + } + args.push("-b", boardDescriptor); } - args.push("-b", boardDescriptor); - } - if (useProgrammer) { - if (this.useArduinoCli()) { - args.push("--programmer", selectProgrammer) - } else { - args.push("--useprogrammer", "--pref", "programmer=arduino:" + selectProgrammer) + if (useProgrammer) { + if (this.useArduinoCli()) { + args.push("--programmer", selectProgrammer) + } else { + args.push("--useprogrammer", "--pref", "programmer=arduino:" + selectProgrammer) + } } - } - if (dc.port) { - args.push("--port", dc.port); + if (dc.port) { + args.push("--port", dc.port); + } } const verbose = VscodeSettings.getInstance().logLevel === "verbose"; @@ -175,7 +183,7 @@ export class ArduinoApp { await vscode.workspace.saveAll(false); arduinoChannel.show(); - arduinoChannel.start(`Upload sketch - ${dc.sketch}`); + arduinoChannel.start(`${buildMode} sketch '${dc.sketch}'`); if (!await this.runPreBuildCommand(dc)) { return; @@ -204,16 +212,20 @@ export class ArduinoApp { // stop serial monitor when everything is prepared and good // what makes restoring of its previous state easier - const restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); + if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { + restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + } // Push sketch as last argument args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); const cleanup = async () => { - UsbDetector.getInstance().resumeListening(); - if (restoreSerialMonitor) { - await SerialMonitor.getInstance().openSerialMonitor(); + if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { + UsbDetector.getInstance().resumeListening(); + if (restoreSerialMonitor) { + await SerialMonitor.getInstance().openSerialMonitor(); + } } } @@ -223,15 +235,15 @@ export class ArduinoApp { args, ).then(async () => { await cleanup(); - arduinoChannel.end(`Uploaded the sketch: ${dc.sketch}${os.EOL}`); + arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); }, async (reason) => { await cleanup(); const msg = reason.code ? - `Exit with code=${reason.code}${os.EOL}` : + `Exit with code=${reason.code}` : reason.message ? reason.message : JSON.stringify(reason); - arduinoChannel.error(msg); + arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); }); } diff --git a/src/extension.ts b/src/extension.ts index b6cf7a78..722a5f2e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -27,6 +27,7 @@ const completionProviderModule = impor("./langService/completionProvider") as ty import * as Logger from "./logger/logger"; const nsatModule = impor("./nsat") as typeof import ("./nsat"); +import { BuildMode } from "./arduino/arduino"; import { SerialMonitor } from "./serialmonitor/serialMonitor"; const usbDetectorModule = impor("./serialmonitor/usbDetector") as typeof import ("./serialmonitor/usbDetector"); @@ -144,7 +145,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Uploading...", }, async () => { - await arduinoContextModule.default.arduinoApp.upload(); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.Upload); }); } catch (ex) { } @@ -162,7 +163,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Using CLI to upload...", }, async () => { - await arduinoContextModule.default.arduinoApp.upload(false); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.Upload, false); }); } catch (ex) { } @@ -197,7 +198,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "upload"; try { - await arduinoContextModule.default.arduinoApp.upload(true, true); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, true, true); } catch (ex) { } delete status.compile; @@ -210,7 +211,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "cliUpload"; try { - await arduinoContextModule.default.arduinoApp.upload(false, true); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, false, true); } catch (ex) { } delete status.compile; From a14f777f7d8cdf6b394e678b33ec7d4f3a7ca394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 23:36:46 +0100 Subject: [PATCH 050/142] Make upload and uploadUsingProgrammer identical --- src/arduino/arduino.ts | 50 +++++++++++++++++++++++++++++++----------- src/extension.ts | 4 ++-- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index c81bc6a7..2c4cc8a6 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -101,10 +101,13 @@ export class ArduinoApp { /** * Upload code to selected board + * @param {BuildMode} buildMode Build mode. + * * BuildMode.Upload: Compile and upload + * * BuildMode.UploadProgrammer: Compile and upload using the user + * selectable programmer * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload - * @param {bool} [useProgrammer=false] - Indicate whether a specific programmer should be used */ - public async upload(buildMode: BuildMode, compile: boolean = true, useProgrammer: boolean = false) { + public async upload(buildMode: BuildMode, compile: boolean = true) { const dc = DeviceContext.getInstance(); const args: string[] = []; let restoreSerialMonitor: boolean = false; @@ -134,11 +137,6 @@ export class ArduinoApp { } } - const selectProgrammer = useProgrammer ? this.getProgrammerString() : null; - if (useProgrammer && !selectProgrammer) { - return; - } - if (buildMode === BuildMode.Upload) { if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { await selectSerial(); @@ -162,17 +160,43 @@ export class ArduinoApp { args.push("-b", boardDescriptor); } - if (useProgrammer) { - if (this.useArduinoCli()) { - args.push("--programmer", selectProgrammer) + if (dc.port) { + args.push("--port", dc.port); + } + } else if (buildMode === BuildMode.UploadProgrammer) { + const programmer = this.getProgrammerString(); + if (!programmer) { + return; + } + if (!dc.port) { + await selectSerial(); + return; + } + + if (!compile && !this.useArduinoCli()) { + arduinoChannel.error("This command is only available when using the Arduino CLI"); + return; + } + + if (!this.useArduinoCli()) { + args.push("--upload"); + } else { + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + if (compile) { + args.push("compile", "--upload"); } else { - args.push("--useprogrammer", "--pref", "programmer=arduino:" + selectProgrammer) + args.push("upload"); } + args.push("-b", boardDescriptor); } - if (dc.port) { - args.push("--port", dc.port); + if (this.useArduinoCli()) { + args.push("--programmer", programmer) + } else { + args.push("--useprogrammer", "--pref", "programmer=arduino:" + programmer) } + + args.push("--port", dc.port); } const verbose = VscodeSettings.getInstance().logLevel === "verbose"; diff --git a/src/extension.ts b/src/extension.ts index 722a5f2e..c09f099d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -198,7 +198,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "upload"; try { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, true, true); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, true); } catch (ex) { } delete status.compile; @@ -211,7 +211,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "cliUpload"; try { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, false, true); + await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, false); } catch (ex) { } delete status.compile; From e4a70b02b1c600b47acb9df468dfdcda6704ec4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 23:39:12 +0100 Subject: [PATCH 051/142] Merge upload and uploadUsingProgrammer into one build function --- src/arduino/arduino.ts | 2 +- src/extension.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 2c4cc8a6..463f2201 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -107,7 +107,7 @@ export class ArduinoApp { * selectable programmer * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload */ - public async upload(buildMode: BuildMode, compile: boolean = true) { + public async build(buildMode: BuildMode, compile: boolean = true) { const dc = DeviceContext.getInstance(); const args: string[] = []; let restoreSerialMonitor: boolean = false; diff --git a/src/extension.ts b/src/extension.ts index c09f099d..d59c9a1b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -145,7 +145,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Uploading...", }, async () => { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.Upload); + await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload); }); } catch (ex) { } @@ -163,7 +163,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Using CLI to upload...", }, async () => { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.Upload, false); + await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, false); }); } catch (ex) { } @@ -198,7 +198,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "upload"; try { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, true); + await arduinoContextModule.default.arduinoApp.build(BuildMode.UploadProgrammer, true); } catch (ex) { } delete status.compile; @@ -211,7 +211,7 @@ export async function activate(context: vscode.ExtensionContext) { if (!status.compile) { status.compile = "cliUpload"; try { - await arduinoContextModule.default.arduinoApp.upload(BuildMode.UploadProgrammer, false); + await arduinoContextModule.default.arduinoApp.build(BuildMode.UploadProgrammer, false); } catch (ex) { } delete status.compile; From 4aa7ddcac2ceae01059b6f368d0d2e5572d746d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Thu, 10 Dec 2020 23:52:29 +0100 Subject: [PATCH 052/142] Add buildMode argument to verify --- src/arduino/arduino.ts | 21 ++++++++++++--------- src/debug/configurationProvider.ts | 4 ++-- src/extension.ts | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 463f2201..5d72c187 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -26,6 +26,7 @@ import { makeCompilerParserContext } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; export enum BuildMode { + Verify = "Verifying", Upload = "Uploading", UploadProgrammer = "Uploading (programmer)", }; @@ -271,7 +272,7 @@ export class ArduinoApp { }); } - public async verify(output: string = "") { + public async verify(buildMode: BuildMode, output: string = "") { const dc = DeviceContext.getInstance(); const args: string[] = []; const boardDescriptor = this.getBoardBuildString(); @@ -291,10 +292,12 @@ export class ArduinoApp { await this.getMainSketch(dc); } - if (!this.useArduinoCli()) { - args.push("--verify"); - } else { - args.push("compile", "-b", boardDescriptor); + if (buildMode === BuildMode.Verify) { + if (!this.useArduinoCli()) { + args.push("--verify"); + } else { + args.push("compile", "-b", boardDescriptor); + } } const verbose = VscodeSettings.getInstance().logLevel === "verbose"; @@ -305,7 +308,7 @@ export class ArduinoApp { await vscode.workspace.saveAll(false); arduinoChannel.show(); - arduinoChannel.start(`Verify sketch - ${dc.sketch}`); + arduinoChannel.start(`${buildMode} sketch '${dc.sketch}'`); if (!await this.runPreBuildCommand(dc)) { return false; @@ -350,16 +353,16 @@ export class ArduinoApp { compilerParserContext.callback, ).then(async () => { await cleanup(); - arduinoChannel.end(`Finished verifying sketch - ${dc.sketch}${os.EOL}`); + arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); success = true; }, async (reason) => { await cleanup(); const msg = reason.code ? - `Exit with code=${reason.code}${os.EOL}` : + `Exit with code=${reason.code}` : reason.message ? reason.message : JSON.stringify(reason); - arduinoChannel.error(msg); + arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); }); if (compilerParserContext.conclude) { diff --git a/src/debug/configurationProvider.ts b/src/debug/configurationProvider.ts index c08546f2..315378db 100644 --- a/src/debug/configurationProvider.ts +++ b/src/debug/configurationProvider.ts @@ -4,7 +4,7 @@ import * as path from "path"; import * as vscode from "vscode"; -import { ArduinoApp } from "../arduino/arduino"; +import { ArduinoApp, BuildMode } from "../arduino/arduino"; import ArduinoActivator from "../arduinoActivator"; import ArduinoContext from "../arduinoContext"; @@ -136,7 +136,7 @@ export class ArduinoDebugConfigurationProvider implements vscode.DebugConfigurat config.program = path.join(ArduinoWorkspace.rootPath, outputFolder, `${path.basename(dc.sketch)}.elf`); // always compile elf to make sure debug the right elf - if (!await ArduinoContext.arduinoApp.verify(outputFolder)) { + if (!await ArduinoContext.arduinoApp.verify(BuildMode.Verify, outputFolder)) { vscode.window.showErrorMessage("Failure to verify the program, please check output for details."); return false; } diff --git a/src/extension.ts b/src/extension.ts index d59c9a1b..c7b904f5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -124,7 +124,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Verifying...", }, async () => { - await arduinoContextModule.default.arduinoApp.verify(); + await arduinoContextModule.default.arduinoApp.verify(BuildMode.Verify); }); } catch (ex) { } From 335d80aafcd42cea7e063f19c4e6b87e4e1c9733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 00:10:17 +0100 Subject: [PATCH 053/142] Add stdoutCallback expression --- src/arduino/arduino.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 5d72c187..9ef01568 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -254,10 +254,18 @@ export class ArduinoApp { } } + // TODO: Get rid of spawn's channel parameter and just support + // stdout and stderr callbacks + const stdoutCallback = (line: string) => { + arduinoChannel.channel.append(line); + } + await util.spawn( this._settings.commandPath, arduinoChannel.channel, args, + undefined, + stdoutCallback, ).then(async () => { await cleanup(); arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); @@ -345,12 +353,18 @@ export class ArduinoApp { await Promise.resolve(); } + // TODO: Get rid of spawn's channel parameter and just support + // stdout and stderr callbacks + const stdoutCallback = (line: string) => { + arduinoChannel.channel.append(line); + } + await util.spawn( this._settings.commandPath, arduinoChannel.channel, args, undefined, - compilerParserContext.callback, + stdoutCallback, ).then(async () => { await cleanup(); arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); From 0b2b77ac34cc7dc78dc56b03a70d02581b3dbdb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 00:27:21 +0100 Subject: [PATCH 054/142] Rename output to buildDir --- src/arduino/arduino.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 9ef01568..484e6c2b 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -280,7 +280,7 @@ export class ArduinoApp { }); } - public async verify(buildMode: BuildMode, output: string = "") { + public async verify(buildMode: BuildMode, buildDir: string = "") { const dc = DeviceContext.getInstance(); const args: string[] = []; const boardDescriptor = this.getBoardBuildString(); @@ -322,8 +322,8 @@ export class ArduinoApp { return false; } - if (output || dc.output) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, output || dc.output); + if (buildDir || dc.output) { + const outputPath = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); From fef2db0447df7a6a20c4cc68fceecc33688862a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 18:07:15 +0100 Subject: [PATCH 055/142] Use cocopa and add Analyze build mode --- src/arduino/arduino.ts | 36 +++++++++--- src/arduino/intellisense.ts | 106 +++++++++++++++++++++--------------- 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 484e6c2b..8027b750 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -15,6 +15,7 @@ import { DeviceContext } from "../deviceContext"; import { IArduinoSettings } from "./arduinoSettings"; import { BoardManager } from "./boardManager"; import { ExampleManager } from "./exampleManager"; +import { ICoCoPaContext, isCompilerParserEnabled, makeCompilerParserContext } from "./intellisense"; import { LibraryManager } from "./libraryManager"; import { VscodeSettings } from "./vscodeSettings"; @@ -22,11 +23,11 @@ import { arduinoChannel } from "../common/outputChannel"; import { ArduinoWorkspace } from "../common/workspace"; import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; -import { makeCompilerParserContext } from "./intellisense"; import { ProgrammerManager } from "./programmerManager"; export enum BuildMode { Verify = "Verifying", + Analyze = "Analyzing", Upload = "Uploading", UploadProgrammer = "Uploading (programmer)", }; @@ -283,6 +284,8 @@ export class ArduinoApp { public async verify(buildMode: BuildMode, buildDir: string = "") { const dc = DeviceContext.getInstance(); const args: string[] = []; + let cocopa: ICoCoPaContext; + const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return false; @@ -300,7 +303,17 @@ export class ArduinoApp { await this.getMainSketch(dc); } - if (buildMode === BuildMode.Verify) { + if (buildMode === BuildMode.Analyze) { + if (!isCompilerParserEnabled()) { + return false; + } + cocopa = makeCompilerParserContext(dc); + if (!this.useArduinoCli()) { + args.push("--verify", "--verbose"); + } else { + args.push("compile", "--verbose", "-b", boardDescriptor); + } + } else { if (!this.useArduinoCli()) { args.push("--verify"); } else { @@ -309,7 +322,7 @@ export class ArduinoApp { } const verbose = VscodeSettings.getInstance().logLevel === "verbose"; - if (verbose) { + if (buildMode !== BuildMode.Analyze && verbose) { args.push("--verbose"); } @@ -344,19 +357,28 @@ export class ArduinoApp { } let success = false; - const compilerParserContext = makeCompilerParserContext(dc); // Push sketch as last argument args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); const cleanup = async () => { + if (cocopa) { + cocopa.conclude(); + } await Promise.resolve(); } // TODO: Get rid of spawn's channel parameter and just support // stdout and stderr callbacks const stdoutCallback = (line: string) => { - arduinoChannel.channel.append(line); + if (cocopa) { + cocopa.callback(line); + if (verbose) { + arduinoChannel.channel.append(line); + } + } else { + arduinoChannel.channel.append(line); + } } await util.spawn( @@ -379,10 +401,6 @@ export class ArduinoApp { arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); }); - if (compilerParserContext.conclude) { - compilerParserContext.conclude(); - } - return success; } diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts index 7663241d..ad98a0ef 100644 --- a/src/arduino/intellisense.ts +++ b/src/arduino/intellisense.ts @@ -1,11 +1,33 @@ import * as ccp from "cocopa"; import * as path from "path"; + import * as constants from "../common/constants"; import { arduinoChannel } from "../common/outputChannel"; import { ArduinoWorkspace } from "../common/workspace"; import { DeviceContext } from "../deviceContext"; + import { VscodeSettings } from "./vscodeSettings"; +export interface ICoCoPaContext { + callback: (s: string) => void; + conclude: () => void; +}; + +/** + * Returns true if the combination of global enable/disable and project + * specific override enable the auto-generation of the IntelliSense + * configuration. + */ +export function isCompilerParserEnabled(dc?: DeviceContext) { + if (!dc) { + dc = DeviceContext.getInstance(); + } + const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; + const projectSetting = dc.disableIntelliSenseAutoGen; + return projectSetting !== "disable" && !globalDisable || + projectSetting === "enable"; +} + /** * Creates a context which is used for compiler command parsing * during building (verify, upload, ...). @@ -16,61 +38,55 @@ import { VscodeSettings } from "./vscodeSettings"; * * @param dc The device context of the caller. */ -export function makeCompilerParserContext(dc: DeviceContext) - : { callback: (s: string) => void; conclude: () => void; } { - - const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; - const project = dc.disableIntelliSenseAutoGen; - - if (project !== "disable" && !globalDisable || - project === "enable") { +export function makeCompilerParserContext(dc: DeviceContext): ICoCoPaContext { - const engines = makeCompilerParserEngines(dc); - const runner = new ccp.Runner(engines); + const engines = makeCompilerParserEngines(dc); + const runner = new ccp.Runner(engines); - // set up the function to be called after parsing - const _conclude = () => { - if (!runner.result) { - arduinoChannel.warning("Failed to generate IntelliSense configuration."); - return; - } - const pPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); - // TODO: check what kind of result we've got: gcc or other architecture: - // and instantiate content accordingly (to be implemented within cocopa) - const content = new ccp.CCppPropertiesContentResult(runner.result, - "Arduino", - ccp.CCppPropertiesISMode.Gcc_X64, - ccp.CCppPropertiesCStandard.C11, - // as of 1.8.11 arduino is on C++11 - ccp.CCppPropertiesCppStandard.Cpp11); - try { - const prop = new ccp.CCppProperties(); - prop.read(pPath); - prop.merge(content, ccp.CCppPropertiesMergeMode.ReplaceSameNames); - if (prop.write(pPath)) { - arduinoChannel.info("IntelliSense configuration updated."); - } else { - arduinoChannel.info("IntelliSense configuration already up to date."); - } - } catch (e) { - // tslint:disable-next-line: no-console - console.log(e); + // Set up the callback to be called after parsing + const _conclude = () => { + if (!runner.result) { + arduinoChannel.warning("Failed to generate IntelliSense configuration."); + return; + } + const pPath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); + // TODO: check what kind of result we've got: gcc or other architecture: + // and instantiate content accordingly (to be implemented within cocopa) + const content = new ccp.CCppPropertiesContentResult(runner.result, + "Arduino", + ccp.CCppPropertiesISMode.Gcc_X64, + ccp.CCppPropertiesCStandard.C11, + // as of 1.8.11 arduino is on C++11 + ccp.CCppPropertiesCppStandard.Cpp11); + try { + const prop = new ccp.CCppProperties(); + prop.read(pPath); + prop.merge(content, ccp.CCppPropertiesMergeMode.ReplaceSameNames); + if (prop.write(pPath)) { + arduinoChannel.info("IntelliSense configuration updated."); + } else { + arduinoChannel.info("IntelliSense configuration already up to date."); } - }; - return { - callback: runner.callback(), - conclude: _conclude, + } catch (e) { + const estr = JSON.stringify(e); + arduinoChannel.error(`Failed to read or write IntelliSense configuration: ${estr}`); } - } + }; return { - callback: undefined, - conclude: undefined, + callback: runner.callback(), + conclude: _conclude, } }; /** + * Assembles compiler parser engines which then will be used to find the main + * sketch's compile command and parse the infomation from it required for + * assembling an IntelliSense configuration from it. + * + * It could return multiple engines for different compilers or - if necessary - + * return specialized engines based on the current board architecture. * - * @param dc + * @param dc Current device context used to generate the engines. */ function makeCompilerParserEngines(dc: DeviceContext) { From 2039739b749095e5324ce3e63e8d0f2a44e9320e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 22:53:33 +0100 Subject: [PATCH 056/142] Make build return boolean --- src/arduino/arduino.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 8027b750..d7507227 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -115,7 +115,7 @@ export class ArduinoApp { let restoreSerialMonitor: boolean = false; const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { - return; + return false; } if (!this.useArduinoCli()) { args.push("--board", boardDescriptor); @@ -123,7 +123,7 @@ export class ArduinoApp { if (!ArduinoWorkspace.rootPath) { vscode.window.showWarningMessage("Cannot find the sketch file."); - return; + return false; } if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { @@ -142,12 +142,12 @@ export class ArduinoApp { if (buildMode === BuildMode.Upload) { if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { await selectSerial(); - return; + return false; } if (!compile && !this.useArduinoCli()) { arduinoChannel.error("This command is only available when using the Arduino CLI"); - return; + return false; } if (!this.useArduinoCli()) { @@ -168,16 +168,16 @@ export class ArduinoApp { } else if (buildMode === BuildMode.UploadProgrammer) { const programmer = this.getProgrammerString(); if (!programmer) { - return; + return false; } if (!dc.port) { await selectSerial(); - return; + return false; } if (!compile && !this.useArduinoCli()) { arduinoChannel.error("This command is only available when using the Arduino CLI"); - return; + return false; } if (!this.useArduinoCli()) { @@ -212,7 +212,7 @@ export class ArduinoApp { arduinoChannel.start(`${buildMode} sketch '${dc.sketch}'`); if (!await this.runPreBuildCommand(dc)) { - return; + return false; } if (dc.output && compile) { @@ -220,7 +220,7 @@ export class ArduinoApp { const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return; + return false; } if (this.useArduinoCli()) { @@ -243,6 +243,8 @@ export class ArduinoApp { UsbDetector.getInstance().pauseListening(); } + let success = false; + // Push sketch as last argument args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); @@ -270,6 +272,7 @@ export class ArduinoApp { ).then(async () => { await cleanup(); arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); + success = true; }, async (reason) => { await cleanup(); const msg = reason.code ? @@ -279,6 +282,7 @@ export class ArduinoApp { JSON.stringify(reason); arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); }); + return success; } public async verify(buildMode: BuildMode, buildDir: string = "") { From 2a2db50081b24750b7e84f8bed80b28429c08092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 23:12:03 +0100 Subject: [PATCH 057/142] Make build and verify identical --- src/arduino/arduino.ts | 138 +++++++++++++++++++++++++++++++++++++---- src/extension.ts | 2 +- 2 files changed, 126 insertions(+), 14 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index d7507227..612a53ba 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -109,10 +109,12 @@ export class ArduinoApp { * selectable programmer * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload */ - public async build(buildMode: BuildMode, compile: boolean = true) { + public async build(buildMode: BuildMode, compile: boolean, buildDir?: string): Promise { const dc = DeviceContext.getInstance(); const args: string[] = []; let restoreSerialMonitor: boolean = false; + let cocopa: ICoCoPaContext; + const boardDescriptor = this.getBoardBuildString(); if (!boardDescriptor) { return false; @@ -199,10 +201,26 @@ export class ArduinoApp { } args.push("--port", dc.port); + } else if (buildMode === BuildMode.Analyze) { + if (!isCompilerParserEnabled()) { + return false; + } + cocopa = makeCompilerParserContext(dc); + if (!this.useArduinoCli()) { + args.push("--verify", "--verbose"); + } else { + args.push("compile", "--verbose", "-b", boardDescriptor); + } + } else { + if (!this.useArduinoCli()) { + args.push("--verify"); + } else { + args.push("compile", "-b", boardDescriptor); + } } const verbose = VscodeSettings.getInstance().logLevel === "verbose"; - if (verbose) { + if (buildMode !== BuildMode.Analyze && verbose) { args.push("--verbose"); } @@ -215,8 +233,8 @@ export class ArduinoApp { return false; } - if (dc.output && compile) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, dc.output); + if ((buildDir || dc.output) && compile) { + const outputPath = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); @@ -230,9 +248,9 @@ export class ArduinoApp { args.push("--pref", `build.path=${outputPath}`); } - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + arduinoChannel.info(`Please see the build logs in output path: ${outputPath}`); } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Upload could be slow. See README."; + const msg = "Output path is not specified. Unable to reuse previously compiled files. Build will be slower. See README."; arduinoChannel.warning(msg); } @@ -249,6 +267,9 @@ export class ArduinoApp { args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); const cleanup = async () => { + if (cocopa) { + cocopa.conclude(); + } if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { UsbDetector.getInstance().resumeListening(); if (restoreSerialMonitor) { @@ -260,7 +281,14 @@ export class ArduinoApp { // TODO: Get rid of spawn's channel parameter and just support // stdout and stderr callbacks const stdoutCallback = (line: string) => { - arduinoChannel.channel.append(line); + if (cocopa) { + cocopa.callback(line); + if (verbose) { + arduinoChannel.channel.append(line); + } + } else { + arduinoChannel.channel.append(line); + } } await util.spawn( @@ -282,12 +310,15 @@ export class ArduinoApp { JSON.stringify(reason); arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); }); + return success; } - public async verify(buildMode: BuildMode, buildDir: string = "") { + public async verify(buildMode: BuildMode, buildDir?: string): Promise { + const compile = true; const dc = DeviceContext.getInstance(); const args: string[] = []; + let restoreSerialMonitor: boolean = false; let cocopa: ICoCoPaContext; const boardDescriptor = this.getBoardBuildString(); @@ -307,7 +338,76 @@ export class ArduinoApp { await this.getMainSketch(dc); } - if (buildMode === BuildMode.Analyze) { + const selectSerial = async () => { + const choice = await vscode.window.showInformationMessage( + "Serial port is not specified. Do you want to select a serial port for uploading?", + "Yes", "No"); + if (choice === "Yes") { + vscode.commands.executeCommand("arduino.selectSerialPort"); + } + } + + if (buildMode === BuildMode.Upload) { + if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { + await selectSerial(); + return false; + } + + if (!compile && !this.useArduinoCli()) { + arduinoChannel.error("This command is only available when using the Arduino CLI"); + return; + } + + if (!this.useArduinoCli()) { + args.push("--upload"); + } else { + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + if (compile) { + args.push("compile", "--upload"); + } else { + args.push("upload"); + } + args.push("-b", boardDescriptor); + } + + if (dc.port) { + args.push("--port", dc.port); + } + } else if (buildMode === BuildMode.UploadProgrammer) { + const programmer = this.getProgrammerString(); + if (!programmer) { + return false; + } + if (!dc.port) { + await selectSerial(); + return false; + } + + if (!compile && !this.useArduinoCli()) { + arduinoChannel.error("This command is only available when using the Arduino CLI"); + return; + } + + if (!this.useArduinoCli()) { + args.push("--upload"); + } else { + // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) + if (compile) { + args.push("compile", "--upload"); + } else { + args.push("upload"); + } + args.push("-b", boardDescriptor); + } + + if (this.useArduinoCli()) { + args.push("--programmer", programmer) + } else { + args.push("--useprogrammer", "--pref", "programmer=arduino:" + programmer) + } + + args.push("--port", dc.port); + } else if (buildMode === BuildMode.Analyze) { if (!isCompilerParserEnabled()) { return false; } @@ -339,7 +439,7 @@ export class ArduinoApp { return false; } - if (buildDir || dc.output) { + if ((buildDir || dc.output) && compile) { const outputPath = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output); const dirPath = path.dirname(outputPath); if (!util.directoryExistsSync(dirPath)) { @@ -354,12 +454,19 @@ export class ArduinoApp { args.push("--pref", `build.path=${outputPath}`); } - arduinoChannel.info(`Please see the build logs in Output path: ${outputPath}`); + arduinoChannel.info(`Please see the build logs in output path: ${outputPath}`); } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Verify could be slow. See README."; + const msg = "Output path is not specified. Unable to reuse previously compiled files. Build will be slower. See README."; arduinoChannel.warning(msg); } + // stop serial monitor when everything is prepared and good + // what makes restoring of its previous state easier + if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { + restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); + UsbDetector.getInstance().pauseListening(); + } + let success = false; // Push sketch as last argument @@ -369,7 +476,12 @@ export class ArduinoApp { if (cocopa) { cocopa.conclude(); } - await Promise.resolve(); + if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { + UsbDetector.getInstance().resumeListening(); + if (restoreSerialMonitor) { + await SerialMonitor.getInstance().openSerialMonitor(); + } + } } // TODO: Get rid of spawn's channel parameter and just support diff --git a/src/extension.ts b/src/extension.ts index c7b904f5..eb7bd209 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -145,7 +145,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Uploading...", }, async () => { - await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload); + await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, true); }); } catch (ex) { } From 216e1b8ec2a7a748b388bb8ffe2fdd226539204f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 23:14:26 +0100 Subject: [PATCH 058/142] Merge build and verify --- src/arduino/arduino.ts | 221 ++--------------------------- src/debug/configurationProvider.ts | 2 +- src/extension.ts | 2 +- 3 files changed, 11 insertions(+), 214 deletions(-) diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 612a53ba..5263b533 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -102,12 +102,15 @@ export class ArduinoApp { } /** - * Upload code to selected board - * @param {BuildMode} buildMode Build mode. - * * BuildMode.Upload: Compile and upload - * * BuildMode.UploadProgrammer: Compile and upload using the user - * selectable programmer - * @param {bool} [compile=true] - Indicates whether to compile the code when using the CLI to upload + * Runs the arduino builder to build/compile and - if necessary - upload + * the current sketch. + * @param buildMode Build mode. BuildMode.Upload: Compile and upload, + * BuildMode.UploadProgrammer: Compile and upload using the user selectable + * programmer, BuildMode.Analyze: Compile, analyze the output and generate + * IntelliSense configuration from it, BuildMode.Verify: Just compile. + * @param {bool} compile - Indicates whether to compile the code when using the CLI to upload + * @param buildDir Override the build directory set by the project settings + * with the given directory. */ public async build(buildMode: BuildMode, compile: boolean, buildDir?: string): Promise { const dc = DeviceContext.getInstance(); @@ -314,212 +317,6 @@ export class ArduinoApp { return success; } - public async verify(buildMode: BuildMode, buildDir?: string): Promise { - const compile = true; - const dc = DeviceContext.getInstance(); - const args: string[] = []; - let restoreSerialMonitor: boolean = false; - let cocopa: ICoCoPaContext; - - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { - return false; - } - if (!this.useArduinoCli()) { - args.push("--board", boardDescriptor); - } - - if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); - return false; - } - - if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); - } - - const selectSerial = async () => { - const choice = await vscode.window.showInformationMessage( - "Serial port is not specified. Do you want to select a serial port for uploading?", - "Yes", "No"); - if (choice === "Yes") { - vscode.commands.executeCommand("arduino.selectSerialPort"); - } - } - - if (buildMode === BuildMode.Upload) { - if ((!dc.configuration || !/upload_method=[^=,]*st[^,]*link/i.test(dc.configuration)) && !dc.port) { - await selectSerial(); - return false; - } - - if (!compile && !this.useArduinoCli()) { - arduinoChannel.error("This command is only available when using the Arduino CLI"); - return; - } - - if (!this.useArduinoCli()) { - args.push("--upload"); - } else { - // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) - if (compile) { - args.push("compile", "--upload"); - } else { - args.push("upload"); - } - args.push("-b", boardDescriptor); - } - - if (dc.port) { - args.push("--port", dc.port); - } - } else if (buildMode === BuildMode.UploadProgrammer) { - const programmer = this.getProgrammerString(); - if (!programmer) { - return false; - } - if (!dc.port) { - await selectSerial(); - return false; - } - - if (!compile && !this.useArduinoCli()) { - arduinoChannel.error("This command is only available when using the Arduino CLI"); - return; - } - - if (!this.useArduinoCli()) { - args.push("--upload"); - } else { - // TODO: add the --clean argument to the cli args when v 0.14 is released (this will clean up the build folder after uploading) - if (compile) { - args.push("compile", "--upload"); - } else { - args.push("upload"); - } - args.push("-b", boardDescriptor); - } - - if (this.useArduinoCli()) { - args.push("--programmer", programmer) - } else { - args.push("--useprogrammer", "--pref", "programmer=arduino:" + programmer) - } - - args.push("--port", dc.port); - } else if (buildMode === BuildMode.Analyze) { - if (!isCompilerParserEnabled()) { - return false; - } - cocopa = makeCompilerParserContext(dc); - if (!this.useArduinoCli()) { - args.push("--verify", "--verbose"); - } else { - args.push("compile", "--verbose", "-b", boardDescriptor); - } - } else { - if (!this.useArduinoCli()) { - args.push("--verify"); - } else { - args.push("compile", "-b", boardDescriptor); - } - } - - const verbose = VscodeSettings.getInstance().logLevel === "verbose"; - if (buildMode !== BuildMode.Analyze && verbose) { - args.push("--verbose"); - } - - await vscode.workspace.saveAll(false); - - arduinoChannel.show(); - arduinoChannel.start(`${buildMode} sketch '${dc.sketch}'`); - - if (!await this.runPreBuildCommand(dc)) { - return false; - } - - if ((buildDir || dc.output) && compile) { - const outputPath = path.resolve(ArduinoWorkspace.rootPath, buildDir || dc.output); - const dirPath = path.dirname(outputPath); - if (!util.directoryExistsSync(dirPath)) { - logger.notifyUserError("InvalidOutPutPath", new Error(constants.messages.INVALID_OUTPUT_PATH + outputPath)); - return false; - } - - if (this.useArduinoCli()) { - args.push("--build-path", outputPath); - - } else { - args.push("--pref", `build.path=${outputPath}`); - } - - arduinoChannel.info(`Please see the build logs in output path: ${outputPath}`); - } else { - const msg = "Output path is not specified. Unable to reuse previously compiled files. Build will be slower. See README."; - arduinoChannel.warning(msg); - } - - // stop serial monitor when everything is prepared and good - // what makes restoring of its previous state easier - if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { - restoreSerialMonitor = await SerialMonitor.getInstance().closeSerialMonitor(dc.port); - UsbDetector.getInstance().pauseListening(); - } - - let success = false; - - // Push sketch as last argument - args.push(path.join(ArduinoWorkspace.rootPath, dc.sketch)); - - const cleanup = async () => { - if (cocopa) { - cocopa.conclude(); - } - if (buildMode === BuildMode.Upload || buildMode === BuildMode.UploadProgrammer) { - UsbDetector.getInstance().resumeListening(); - if (restoreSerialMonitor) { - await SerialMonitor.getInstance().openSerialMonitor(); - } - } - } - - // TODO: Get rid of spawn's channel parameter and just support - // stdout and stderr callbacks - const stdoutCallback = (line: string) => { - if (cocopa) { - cocopa.callback(line); - if (verbose) { - arduinoChannel.channel.append(line); - } - } else { - arduinoChannel.channel.append(line); - } - } - - await util.spawn( - this._settings.commandPath, - arduinoChannel.channel, - args, - undefined, - stdoutCallback, - ).then(async () => { - await cleanup(); - arduinoChannel.end(`${buildMode} sketch '${dc.sketch}'${os.EOL}`); - success = true; - }, async (reason) => { - await cleanup(); - const msg = reason.code ? - `Exit with code=${reason.code}` : - reason.message ? - reason.message : - JSON.stringify(reason); - arduinoChannel.error(`${buildMode} sketch '${dc.sketch}': ${msg}${os.EOL}`); - }); - - return success; - } - public tryToUpdateIncludePaths() { const configFilePath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); if (!fs.existsSync(configFilePath)) { diff --git a/src/debug/configurationProvider.ts b/src/debug/configurationProvider.ts index 315378db..51c5368c 100644 --- a/src/debug/configurationProvider.ts +++ b/src/debug/configurationProvider.ts @@ -136,7 +136,7 @@ export class ArduinoDebugConfigurationProvider implements vscode.DebugConfigurat config.program = path.join(ArduinoWorkspace.rootPath, outputFolder, `${path.basename(dc.sketch)}.elf`); // always compile elf to make sure debug the right elf - if (!await ArduinoContext.arduinoApp.verify(BuildMode.Verify, outputFolder)) { + if (!await ArduinoContext.arduinoApp.build(BuildMode.Verify, true, outputFolder)) { vscode.window.showErrorMessage("Failure to verify the program, please check output for details."); return false; } diff --git a/src/extension.ts b/src/extension.ts index eb7bd209..5e3b1545 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -124,7 +124,7 @@ export async function activate(context: vscode.ExtensionContext) { location: vscode.ProgressLocation.Window, title: "Arduino: Verifying...", }, async () => { - await arduinoContextModule.default.arduinoApp.verify(BuildMode.Verify); + await arduinoContextModule.default.arduinoApp.build(BuildMode.Verify, true); }); } catch (ex) { } From f914a4b3270e7d3c3e6f256e3b5cca471bc36ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 23:18:01 +0100 Subject: [PATCH 059/142] Add arduino.rebuildIntelliSenseConfig --- README.md | 9 +++++++++ package.json | 10 ++++++++++ src/debug/configurationProvider.ts | 2 +- src/extension.ts | 22 ++++++++++++++++++++-- test/extension.test.ts | 1 + 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 068eba6e..a1e084a3 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,12 @@ This extension provides several commands in the Command Palette (F1 o - **Arduino: Upload**: Build sketch and upload to Arduino board. - **Arduino: Upload Using Programmer**: Upload using an external programmer. - **Arduino: Verify**: Build sketch. +- **Arduino: Rebuild IntelliSense Configuration**: Forced/manual rebuild of the IntelliSense configuration. The extension analyzes Arduino's build output and sets the Intellisense include paths, defines, compiler arguments accordingly. ## Keybindings - **Arduino: Upload** Alt + Cmd + U *or* Alt + Ctrl + U - **Arduino: Verify** Alt + Cmd + R *or* Alt + Ctrl + R +- **Arduino: Rebuild IntelliSense Configuration** Alt + Cmd + I *or* Alt + Ctrl + I ## Options | Option | Description | @@ -115,9 +117,16 @@ The following settings are as per sketch settings of the Arduino extension. You - `"enable"`: Enable the auto-generation even if globally disabled ## IntelliSense +*TODO: Rewrite this section* vscode-arduino auto-configures IntelliSense by default. vscode-arduino analyzes Arduino's compiler output during verify and generates the corresponding configuration file at `.vscode/c_cpp_properties.json` and tries as hard as possible to keep things up to date, e.g. running verify when switching the board or the sketch. It doesn't makes sense though to run verify repeatedly. Therefore if the workspace reports problems (for instance after adding new includes to a new library) run *verify* such that IntelliSense knows of the new include directories (since the Arduino-backend performs the library resolution externally). +TODO: Note about configuration selection in lower right. + +Manual rebuild: **Ardino: Rebuild IntelliSense Configuration**, +Keybindings: **Arduino: Rebuild IntelliSense Configuration** Alt + Cmd + I *or* Alt + Ctrl + I + + ## Debugging Arduino Code preview Before you start to debug your Arduino code, please read [this document](https://code.visualstudio.com/docs/editor/debugging) to learn about the basic mechanisms of debugging in Visual Studio Code. Also see [debugging for C++ in VSCode](https://code.visualstudio.com/docs/languages/cpp#_debugging) for further reference. diff --git a/package.json b/package.json index 292c447c..b846d0fa 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "onCommand:arduino.verify", "onCommand:arduino.upload", "onCommand:arduino.uploadUsingProgrammer", + "onCommand:arduino.rebuildIntelliSenseConfig", "onCommand:arduino.selectProgrammer", "onCommand:arduino.selectSerialPort", "onCommand:arduino.changeBaudRate", @@ -105,6 +106,10 @@ "command": "arduino.cliUploadUsingProgrammer", "title": "Arduino CLI: Upload Using Programmer" }, + { + "command": "arduino.rebuildIntelliSenseConfig", + "title": "Arduino: Rebuild IntelliSense Configuration" + }, { "command": "arduino.selectProgrammer", "title": "Arduino: Select Programmer" @@ -444,6 +449,11 @@ "command": "arduino.upload", "key": "ctrl+alt+u", "mac": "cmd+alt+u" + }, + { + "command": "arduino.rebuildIntelliSenseConfig", + "key": "ctrl+alt+i", + "mac": "cmd+alt+i" } ], "configuration": { diff --git a/src/debug/configurationProvider.ts b/src/debug/configurationProvider.ts index 51c5368c..19588160 100644 --- a/src/debug/configurationProvider.ts +++ b/src/debug/configurationProvider.ts @@ -137,7 +137,7 @@ export class ArduinoDebugConfigurationProvider implements vscode.DebugConfigurat // always compile elf to make sure debug the right elf if (!await ArduinoContext.arduinoApp.build(BuildMode.Verify, true, outputFolder)) { - vscode.window.showErrorMessage("Failure to verify the program, please check output for details."); + vscode.window.showErrorMessage("Failed to verify the program, please check the output for details."); return false; } diff --git a/src/extension.ts b/src/extension.ts index 5e3b1545..5f461a4c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -44,8 +44,8 @@ export async function activate(context: vscode.ExtensionContext) { const workingFile = path.normalize(openEditor.document.fileName); const workspaceFolder = (vscode.workspace && ArduinoWorkspace.rootPath) || ""; if (!workspaceFolder || workingFile.indexOf(path.normalize(workspaceFolder)) < 0) { - vscode.window.showWarningMessage(`The working file "${workingFile}" is not under the workspace folder, ` + - "the arduino extension might not work appropriately."); + vscode.window.showWarningMessage(`The open file "${workingFile}" is not inside the workspace folder, ` + + "the arduino extension might not work properly."); } } const vscodeSettings = VscodeSettings.getInstance(); @@ -197,6 +197,9 @@ export async function activate(context: vscode.ExtensionContext) { registerArduinoCommand("arduino.uploadUsingProgrammer", async () => { if (!status.compile) { status.compile = "upload"; + // TODO: no progress indicator + // and: exceptions should be handled within build + // function try { await arduinoContextModule.default.arduinoApp.build(BuildMode.UploadProgrammer, true); } catch (ex) { @@ -220,6 +223,21 @@ export async function activate(context: vscode.ExtensionContext) { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); + registerArduinoCommand("arduino.rebuildIntelliSenseConfig", async () => { + if (!status.compile) { + status.compile = "intellisenserebuild"; + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Rebuilding IS Configuration...", + }, async () => { + await arduinoContextModule.default.arduinoApp.build(BuildMode.Analyze, true); + }); + delete status.compile; + } + }, () => { + return { board: arduinoContextModule.default.boardManager.currentBoard.name }; + }); + registerArduinoCommand("arduino.selectProgrammer", async () => { if (!status.compile) { status.compile = "upload"; diff --git a/test/extension.test.ts b/test/extension.test.ts index 9c56fc41..566fb6b1 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -37,6 +37,7 @@ suite("Arduino: Extension Tests", () => { "arduino.verify", "arduino.upload", "arduino.uploadUsingProgrammer", + "arduino.rebuildIntelliSenseConfig", "arduino.selectProgrammer", "arduino.showBoardManager", "arduino.showLibraryManager", From 3418458f72f0df6b79c42bb806491f8497f02a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20L=C3=B8vdal?= Date: Fri, 11 Dec 2020 23:25:51 +0100 Subject: [PATCH 060/142] Fix whitespace --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1e084a3..a7dda5ae 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ This extension provides several commands in the Command Palette (F1 o | `arduino.commandPath` | Path to an executable (or script) relative to `arduino.path`. The default value is `arduino_debug.exe` for windows,`Contents/MacOS/Arduino` for Mac and `arduino` for Linux, You also can use a custom launch script to run Arduino by modifying this setting. (Requires a restart after change) Example: `run-arduino.bat` for Windows, `Contents/MacOS/run-arduino.sh` for Mac and `bin/run-arduino.sh` for Linux. | | `arduino.additionalUrls` | Additional Boards Manager URLs for 3rd party packages. You can have multiple URLs in one string with a comma(`,`) as separator, or have a string array. The default value is empty. | | `arduino.logLevel` | CLI output log level. Could be info or verbose. The default value is `"info"`. | -| `arduino.allowPDEFiletype` | Allow the VSCode Arduino extension to open .pde files from pre-1.0.0 versions of Ardiuno. Note that this will break Processing code. Default value is `false`. | +| `arduino.allowPDEFiletype` | Allow the VSCode Arduino extension to open .pde files from pre-1.0.0 versions of Ardiuno. Note that this will break Processing code. Default value is `false`. | | `arduino.enableUSBDetection` | Enable/disable USB detection from the VSCode Arduino extension. The default value is `true`. When your device is plugged in to your computer, it will pop up a message "`Detected board ****, Would you like to switch to this board type`". After clicking the `Yes` button, it will automatically detect which serial port (COM) is connected a USB device. If your device does not support this feature, please provide us with the PID/VID of your device; the code format is defined in `misc/usbmapping.json`.To learn more about how to list the vid/pid, use the following tools: https://github.com/EmergingTechnologyAdvisors/node-serialport `npm install -g serialport` `serialport-list -f jsonline`| | `arduino.disableTestingOpen` | Enable/disable automatic sending of a test message to the serial port for checking the open status. The default value is `false` (a test message will be sent). | | `arduino.skipHeaderProvider` | Enable/disable the extension providing completion items for headers. This functionality is included in newer versions of the C++ extension. The default value is `false`.| @@ -78,7 +78,7 @@ The following Visual Studio Code settings are available for the Arduino extensio "arduino.path": "C:/Program Files (x86)/Arduino", "arduino.commandPath": "arduino_debug.exe", "arduino.logLevel": "info", - "arduino.allowPDEFiletype": false, + "arduino.allowPDEFiletype": false, "arduino.enableUSBDetection": true, "arduino.disableTestingOpen": false, "arduino.skipHeaderProvider": false, From 41bcd445013571bdf824a7d6b1d8d0cf22ec61dd Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Mon, 17 Feb 2020 16:38:12 +0100 Subject: [PATCH 061/142] Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE` --- BRANCHNOTES.md | 20 ++++++++++++++++++++ package.json | 1 - src/arduino/arduino.ts | 21 ++++++++++++++++++--- src/arduino/arduinoContentProvider.ts | 3 ++- src/arduino/boardManager.ts | 3 ++- src/common/util.ts | 1 + src/extension.ts | 6 ++++-- src/langService/completionProvider.ts | 23 ++++++++++++++++++++++- test/boardmanager.test.ts | 3 ++- test/commands.test.ts | 3 +++ test/extension.test.ts | 4 +++- 11 files changed, 77 insertions(+), 11 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index a5713e73..62ffff05 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -35,6 +35,8 @@ During merging I found some bugs within those functions - mainly due to the abov * Error message formatting was fixed within `verify` only * No consistent return values within `verify` (when it bailed out early it returned `void`) +**2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal. + ### Status | | Tasks | |-----:|:--------| @@ -99,6 +101,7 @@ I will list every supporter here, thanks! 2020-02-12 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) 2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) +2020-02-16 Elektronik Workshop: x :beers: (xh coding) @@ -116,6 +119,7 @@ I will list every supporter here, thanks! * https://github.com/Microsoft/vscode-cpptools/issues/1750 * Problems with IntelliSense itself https://github.com/microsoft/vscode-cpptools/issues/1034 * Logging for IntelliSense https://code.visualstudio.com/docs/cpp/enable-logging-cpp + ## Future Work * Proper interactive serial terminal (this is the second major show stopper in my opinion) * Command history option @@ -140,6 +144,22 @@ I will list every supporter here, thanks! * general lack of modularity - the above is the result * It seems that this extension is pretty chaotic. Most probably some refactoring is necessary. * Possibility to jump to compilation errors from compiler output and highlight compiler errors + +## Non-categorized Notes +### Integrate upstream changes into fork +```bash +git remote add upstream https://github.com/microsoft/vscode-arduino.git +git remote -v +git fetch upstream +# make sure your working directory is clean, then +git checkout master +git merge upstream/master +git push origin master +# to pull the changes into you feature branch: +git checkout intellisense-autoconfig +git merge master +git push origin intellisense-autoconfig +``` ---- ## How to beta test cutting edge code from the repo diff --git a/package.json b/package.json index b846d0fa..0d39907c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "onCommand:arduino.selectProgrammer", "onCommand:arduino.selectSerialPort", "onCommand:arduino.changeBaudRate", - "onCommand:arduino.addLibPath", "onCommand:arduino.openSerialMonitor", "onCommand:arduino.sendMessageToSerialPort", "onCommand:arduino.closeSerialMonitor", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index 5263b533..e6a749b3 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -317,6 +317,8 @@ export class ArduinoApp { return success; } + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* public tryToUpdateIncludePaths() { const configFilePath = path.join(ArduinoWorkspace.rootPath, constants.CPP_CONFIG_FILE); if (!fs.existsSync(configFilePath)) { @@ -358,6 +360,7 @@ export class ArduinoApp { configuration.defines.push(define); } } + */ // remove all unexisting paths // concern mistake removal, comment temporary // for (let pathIndex = 0; pathIndex < configuration.includePath.length; pathIndex++) { @@ -385,12 +388,15 @@ export class ArduinoApp { // pathIndex--; // } // } - + /* if (cppConfigFileUpdated) { fs.writeFileSync(configFilePath, JSON.stringify(cppConfig, null, 4)); } } + */ + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* // Add selected library path to the intellisense search path. public addLibPath(libraryPath: string) { let libPaths; @@ -466,7 +472,7 @@ export class ArduinoApp { fs.writeFileSync(configFilePath, JSON.stringify(deviceContext, null, 4)); } - + */ // Include the *.h header files from selected library to the arduino sketch. public async includeLibrary(libraryPath: string) { if (!ArduinoWorkspace.rootPath) { @@ -610,6 +616,8 @@ export class ArduinoApp { arduinoChannel.end(`Removed library - ${libName}${os.EOL}`); } + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* public getDefaultPackageLibPaths(): string[] { const result = []; const boardDescriptor = this._boardManager.currentBoard; @@ -644,8 +652,10 @@ export class ArduinoApp { result.push(path.normalize(path.join(toolPath, "**"))); } return result; - } + }*/ + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* public getDefaultForcedIncludeFiles(): string[] { const result = []; const boardDescriptor = this._boardManager.currentBoard; @@ -665,6 +675,7 @@ export class ArduinoApp { result.push("USBCON"); return result; } + */ public openExample(example) { function tmpName(name) { @@ -705,6 +716,7 @@ export class ArduinoApp { const dc = DeviceContext.getInstance(); const arduinoJson = { sketch: sketchFile, + // TODO: COM1 is Windows specific - what about OSX and Linux users? port: dc.port || "COM1", board: dc.board, configuration: dc.configuration, @@ -713,6 +725,8 @@ export class ArduinoApp { util.mkdirRecursivelySync(path.dirname(arduinoConfigFilePath)); fs.writeFileSync(arduinoConfigFilePath, JSON.stringify(arduinoJson, null, 4)); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* // Generate cpptools intellisense config const cppConfigFilePath = path.join(destExample, constants.CPP_CONFIG_FILE); @@ -749,6 +763,7 @@ export class ArduinoApp { }; util.mkdirRecursivelySync(path.dirname(cppConfigFilePath)); fs.writeFileSync(cppConfigFilePath, JSON.stringify(cppConfig, null, 4)); + */ } // Step 3: Open the arduino project at a new vscode window. diff --git a/src/arduino/arduinoContentProvider.ts b/src/arduino/arduinoContentProvider.ts index af0a0b1b..4602404a 100644 --- a/src/arduino/arduinoContentProvider.ts +++ b/src/arduino/arduinoContentProvider.ts @@ -206,7 +206,8 @@ export class ArduinoContentProvider implements vscode.TextDocumentContentProvide return res.status(400).send("BAD Request! Missing { libraryPath } parameters!"); } else { try { - await ArduinoContext.arduinoApp.addLibPath(req.body.libraryPath); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // await ArduinoContext.arduinoApp.addLibPath(req.body.libraryPath); await ArduinoContext.arduinoApp.includeLibrary(req.body.libraryPath); return res.json({ status: "OK", diff --git a/src/arduino/boardManager.ts b/src/arduino/boardManager.ts index d8267afa..dd35a80c 100644 --- a/src/arduino/boardManager.ts +++ b/src/arduino/boardManager.ts @@ -123,7 +123,8 @@ export class BoardManager { this._currentBoard = targetBoard; dc.configuration = this._currentBoard.customConfig; this._boardConfigStatusBar.text = targetBoard.name; - this._arduinoApp.addLibPath(null); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // this._arduinoApp.addLibPath(null); this._onBoardTypeChanged.fire(); } diff --git a/src/common/util.ts b/src/common/util.ts index 4344f5ad..d1b51389 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -13,6 +13,7 @@ import { arduinoChannel } from "./outputChannel"; const encodingMapping: object = JSON.parse(fs.readFileSync(path.join(__dirname, "../../../misc", "codepageMapping.json"), "utf8")); +// IS-REMOVE: to be removed completely when IntelliSense implementation is merged /** * This function will return the VSCode C/C++ extesnion compatible platform literals. * @function getCppConfigPlatform diff --git a/src/extension.ts b/src/extension.ts index 5f461a4c..36d5a71e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -254,7 +254,8 @@ export async function activate(context: vscode.ExtensionContext) { }; }); - registerArduinoCommand("arduino.addLibPath", (path) => arduinoContextModule.default.arduinoApp.addLibPath(path)); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // registerArduinoCommand("arduino.addLibPath", (path) => arduinoContextModule.default.arduinoApp.addLibPath(path)); registerArduinoCommand("arduino.openExample", (path) => arduinoContextModule.default.arduinoApp.openExample(path)); registerArduinoCommand("arduino.loadPackages", async () => await arduinoContextModule.default.boardManager.loadPackages(true)); registerArduinoCommand("arduino.installBoard", async (packageName, arch, version: string = "") => { @@ -308,7 +309,8 @@ export async function activate(context: vscode.ExtensionContext) { SerialMonitor.getInstance().initialize(); } arduinoContextModule.default.boardManager.updateStatusBar(true); - arduinoContextModule.default.arduinoApp.tryToUpdateIncludePaths(); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // arduinoContextModule.default.arduinoApp.tryToUpdateIncludePaths(); vscode.commands.executeCommand("setContext", "vscode-arduino:showExampleExplorer", true); })(); } diff --git a/src/langService/completionProvider.ts b/src/langService/completionProvider.ts index 12782819..0a21bef4 100644 --- a/src/langService/completionProvider.ts +++ b/src/langService/completionProvider.ts @@ -12,6 +12,25 @@ import ArduinoActivator from "../arduinoActivator"; import ArduinoContext from "../arduinoContext"; import { ArduinoWorkspace } from "../common/workspace"; +/** + * Provides completions for library header includes. + * + * NOTE: With the new IntelliSense auto-configuration this doesn't make + * much sense in its current state, since it tries to fetch includes + * from the wrongly guessed include paths. And it tries to fetch includes + * from the c_cpp_properties which is now automatically generated from the + * files already included -> therefore the user already included the header + * and doesn't need a completion. Furthermore IntelliSense knows the location + * as well and can complete it too. + * + * To make this useful it has to parse the actual library folders and then + * it makes only sense if it reads the library information and checks if + * the individual libraries are actually compatible with the current board + * before offering a completion. + * + * EW + * 2020-02-17 + */ export class CompletionProvider implements vscode.CompletionItemProvider { private _headerFiles = new Set(); @@ -65,10 +84,12 @@ export class CompletionProvider implements vscode.CompletionItemProvider { } this._libPaths.clear(); this._headerFiles.clear(); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + /* ArduinoContext.arduinoApp.getDefaultPackageLibPaths().forEach((defaultPath) => { this._libPaths.add(defaultPath); }); - + */ if (fs.existsSync(this._cppConfigFile)) { const deviceConfig = util.tryParseJSON(fs.readFileSync(this._cppConfigFile, "utf8")); if (deviceConfig) { diff --git a/test/boardmanager.test.ts b/test/boardmanager.test.ts index b02526df..561feee2 100644 --- a/test/boardmanager.test.ts +++ b/test/boardmanager.test.ts @@ -28,7 +28,8 @@ suite("Arduino: Board Manager.", () => { const arduinoApp = TypeMoq.Mock.ofType(ArduinoApp); arduinoApp.setup((x) => x.setPref(TypeMoq.It.isAny(), TypeMoq.It.isAny())); arduinoApp.setup((x) => x.initialize(TypeMoq.It.isAny())); - arduinoApp.setup((x) => x.addLibPath(TypeMoq.It.isAny())); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // arduinoApp.setup((x) => x.addLibPath(TypeMoq.It.isAny())); try { boardManager = new BoardManager(arduinoSettings.object, arduinoApp.object); diff --git a/test/commands.test.ts b/test/commands.test.ts index c188dbb5..124bd403 100644 --- a/test/commands.test.ts +++ b/test/commands.test.ts @@ -38,8 +38,10 @@ suite("Arduino: Commands Tests", () => { } }); + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged // Arduino: Library Manager: Add library to include path. // tslint:disable-next-line: only-arrow-functions + /* test("should be able to run command: arduino.addLibPath", function(done) { this.timeout(60 * 1000); try { @@ -52,6 +54,7 @@ suite("Arduino: Commands Tests", () => { done(new Error(error)); } }); + */ // Arduino: Boards Manager : Manage packages for boards // tslint:disable-next-line: only-arrow-functions diff --git a/test/extension.test.ts b/test/extension.test.ts index 566fb6b1..6cde8586 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -45,7 +45,9 @@ suite("Arduino: Extension Tests", () => { "arduino.showExamples", "arduino.changeBoardType", "arduino.initialize", - "arduino.addLibPath", + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged + // note: it has been removed from package.json commands already + // "arduino.addLibPath", "arduino.selectSerialPort", "arduino.openSerialMonitor", "arduino.changeBaudRate", From 6c1fe68b34cd45e1055d063f09f8ce5e5bbfab1a Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Tue, 18 Feb 2020 01:54:11 +0100 Subject: [PATCH 062/142] Updated project log --- BRANCHNOTES.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 62ffff05..591a7354 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -35,7 +35,7 @@ During merging I found some bugs within those functions - mainly due to the abov * Error message formatting was fixed within `verify` only * No consistent return values within `verify` (when it bailed out early it returned `void`) -**2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal. +**2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. ### Status | | Tasks | @@ -66,6 +66,7 @@ During merging I found some bugs within those functions - mainly due to the abov | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :heavy_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command - this has to be fixed (done, see next item) | | | :heavy_check_mark: Implement a *Rebuild IntelliSense Configuration* command which runs verify verbosely internally and therefore allows us to find and parse the compiler command | +| | :white_check_mark: Implement proper event generation for `DeviceContext`. a) Events should be issued only when something actually changes, b) Events should be issued for each setting separately | | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet @@ -101,7 +102,7 @@ I will list every supporter here, thanks! 2020-02-12 Elektronik Workshop: 32 :beers: (8h coding) 2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) 2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) -2020-02-16 Elektronik Workshop: x :beers: (xh coding) +2020-02-17 Elektronik Workshop: 52 :beers: (13h coding) @@ -114,6 +115,7 @@ I will list every supporter here, thanks! * [Arduino CLI manpage](https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc) * [Install extensions from file](https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/) * [Publish extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) +* [Arduino Dev Tools](https://playground.arduino.cc/Main/DevelopmentTools/) ## Issues Concerning this Project * https://github.com/Microsoft/vscode-cpptools/issues/1750 @@ -144,6 +146,8 @@ I will list every supporter here, thanks! * general lack of modularity - the above is the result * It seems that this extension is pretty chaotic. Most probably some refactoring is necessary. * Possibility to jump to compilation errors from compiler output and highlight compiler errors +* Further IntelliSense enhancements/features: + * When having adding a library folder to the workspace IntelliSense should use the same configuration for it to enable library navigation and code completion. ## Non-categorized Notes ### Integrate upstream changes into fork From d712a94b21f85284a27a0a1637a45f7d74b77dd1 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Tue, 18 Feb 2020 13:26:09 +0100 Subject: [PATCH 063/142] Don't use hard coded paths when possible. --- src/common/workspace.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/workspace.ts b/src/common/workspace.ts index 55b38e98..215fa92a 100644 --- a/src/common/workspace.ts +++ b/src/common/workspace.ts @@ -4,6 +4,7 @@ import * as fs from "fs"; import * as path from "path"; import * as vscode from "vscode"; +import { ARDUINO_CONFIG_FILE } from "./constants"; export class ArduinoWorkspace { static get rootPath(): string|undefined { @@ -14,7 +15,7 @@ export class ArduinoWorkspace { for (const workspaceFolder of workspaceFolders) { const workspaceFolderPath = workspaceFolder.uri.fsPath; - const arduinoConfigPath = path.join(workspaceFolderPath, ".vscode", "arduino.json"); + const arduinoConfigPath = path.join(workspaceFolderPath, ARDUINO_CONFIG_FILE); if (fs.existsSync(arduinoConfigPath)) { return workspaceFolderPath; } From d6c7b5ac74131b7d5614745bfd7536c9031a249c Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Tue, 18 Feb 2020 18:21:21 +0100 Subject: [PATCH 064/142] New classes which will improve the device settings management and event handling. To be integrated with one of the next commits. --- src/deviceSettings.ts | 234 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 src/deviceSettings.ts diff --git a/src/deviceSettings.ts b/src/deviceSettings.ts new file mode 100644 index 00000000..1ac678db --- /dev/null +++ b/src/deviceSettings.ts @@ -0,0 +1,234 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as vscode from "vscode"; + +import * as constants from "./common/constants"; +import * as util from "./common/util"; +import * as logger from "./logger/logger"; + +/** + * Generic class which provides monitoring of a specific settings value. + * If the value is modified a flag is set and an event is emitted. + * + * Usually you want to specialize the setter for any given value type + * to prevent invalid or badly formatted values to enter your settings. + */ +class Setting { + /** The setting's default value. */ + public readonly default: T; + /** The actual value of the setting. */ + private _value: T | undefined; + /** Indicates if the value was changed since the last call to this.commit(). */ + private _modified: boolean; + /** Event emitter which fires when the value is changed. */ + private _emitter: vscode.EventEmitter = new vscode.EventEmitter(); + + constructor(defaultValue?: T) { + this.default = defaultValue; + this._value = this.default; + } + /** + * Value-setter - sets the value. + * If modified, the modified flag is set and the modified event is + * fired. + */ + public set value(value: T | undefined) { + if (value !== this._value) { + this._value = value; + this._modified = true; + this._emitter.fire(this._value); + } + } + /** Value-getter - returns the internal value. */ + public get value() { + return this._value; + } + /** + * Returns true, if the internal value has been modified. + * To clear the modified flag call commit(). + */ + public get modified() { + return this._modified; + } + /** Returns the modified-event emitter. */ + public get emitter() { + return this._emitter; + } + /** + * Returns the internal value to its default value. + * If the default value is different from the previous value, + * it triggers the modified event and the modified flag is set. + */ + public reset() { + this.value = this.default; + } + /** Reset the modified flag (if you know what you're doing) */ + public commit() { + this._modified = false; + } +} + +/** + * String specialization of the Setting class. + */ +class StrSetting extends Setting { + /** + * When we override setter (below) we have to override getter as well + * (see JS language specs). + */ + public get value() { + return super.value; + } + /** + * Set string value. Anything else than a string will set the value to + * its default value (undefined). White spaces at the front and back are + * trimmed before setting the value. + * If the setting's value is changed during this operation, the base + * class' event emitter will fire and the modified flag will be set. + */ + public set value(value: string) { + if (typeof value !== "string") { + value = this.default; + } else { + value = value.trim(); + } + super.value = value; + } +} + +/** + * This class encapsulates all device/project specific settings and + * provides common operations on them. + */ +export class DeviceSettings { + public port = new StrSetting(); + public board = new StrSetting(); + public sketch = new StrSetting(); + public output = new StrSetting(); + public debugger = new StrSetting(); + public disableIntelliSenseAutoGen = new StrSetting(); + public configuration = new StrSetting(); + public prebuild = new StrSetting(); + public programmer = new StrSetting(); + + /** + * @returns true if any of the settings values has its modified flag + * set. + */ + public get modified() { + return this.port.modified || + this.board.modified || + this.sketch.modified || + this.output.modified || + this.debugger.modified || + this.disableIntelliSenseAutoGen.modified || + this.configuration.modified || + this.prebuild.modified || + this.programmer.modified; + } + /** + * Clear modified flags of all settings values. + */ + public commit() { + this.port.commit(); + this.board.commit(); + this.sketch.commit(); + this.output.commit(); + this.debugger.commit(); + this.disableIntelliSenseAutoGen.commit(); + this.configuration.commit(); + this.prebuild.commit(); + this.programmer.commit(); + } + /** + * Resets all settings values to their default values. + * @param commit If true clear the modified flags after all values are + * reset. + */ + public reset(commit: boolean = true) { + this.port.reset(); + this.board.reset(); + this.sketch.reset(); + this.output.reset(); + this.debugger.reset(); + this.disableIntelliSenseAutoGen.reset(); + this.configuration.reset(); + this.prebuild.reset(); + this.programmer.reset(); + if (commit) { + this.commit(); + } + } + /** + * Load settings values from the given file. + * If a value is changed through this operation, its event emitter will + * fire. + * @param file Path to the file the settings should be loaded from. + * @param commit If true reset the modified flags after all values are read. + * @returns true if the settings are loaded successfully. + */ + public load(file: string, commit: boolean = true) { + const settings = util.tryParseJSON(fs.readFileSync(file, "utf8")); + if (settings) { + this.port.value = settings.port; + this.board.value = settings.board; + this.sketch.value = settings.sketch; + this.configuration.value = settings.configuration; + this.output.value = settings.output; + this.debugger.value = settings.debugger; + this.disableIntelliSenseAutoGen.value = settings.disableIntelliSenseAutoGen; + this.prebuild.value = settings.prebuild; + this.programmer.value = settings.programmer; + if (commit) { + this.commit(); + } + return true; + } else { + logger.notifyUserError("arduinoFileError", + new Error(constants.messages.ARDUINO_FILE_ERROR)); + return false; + } + } + /** + * Writes the settings to the given file if there are modified + * values. The modification flags are reset (commit()) on successful write. + * On write failure the modification flags are left unmodified. + * @param file Path to file the JSON representation of the settings should + * written to. If either the folder or the file does not exist they are + * created. + * @returns true on succes, false on write failure. + */ + public save(file: string) { + + if (!this.modified) { + return true; + } + + let settings: any = {}; + if (util.fileExistsSync(file)) { + settings = util.tryParseJSON(fs.readFileSync(file, "utf8")); + } + if (!settings) { + logger.notifyUserError( + "arduinoFileError", + new Error(constants.messages.ARDUINO_FILE_ERROR)); + return false; + } + + settings.sketch = this.sketch.value; + settings.port = this.port.value; + settings.board = this.board.value; + settings.output = this.output.value; + settings.debugger = this.debugger.value; + settings.disableIntelliSenseAutoGen = this.disableIntelliSenseAutoGen.value; + settings.configuration = this.configuration.value; + settings.programmer = this.programmer.value; + + util.mkdirRecursivelySync(path.dirname(file)); + fs.writeFileSync(file, JSON.stringify(settings, undefined, 4)); + + this.commit(); + + return true; + } +} From cabeed61fb077a9154dd926dd0ca608f806a213f Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Tue, 18 Feb 2020 20:12:07 +0100 Subject: [PATCH 065/142] Updated log and issues * Added/documented all known related issues * Updated log * Updated and extended status * General brushing of structure and text --- BRANCHNOTES.md | 118 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 591a7354..8777a386 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -1,22 +1,54 @@ # IntelliSense Autoconfiguration Branch ## Problem -This branch more or less addresses [these](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) issues (about seven). - -It implements a parser which parses the output from Arduino's build process to generate a very precise `c_cpp_properties.json` which in turn hopefully renders any user interaction with this file obsolete +This branch more or less addresses the following issues: +| # | Issue | Title | Comment | +|--:|:------|:------------|:--------| +| 1| [#438](https://github.com/microsoft/vscode-arduino/issues/438) | **The Extension should automagically fill the c_cpp_properties.json, so intellisense works out of the box** | This is the issue to which I usually report news concerning the progress of this project | +| 2| [#876](https://github.com/microsoft/vscode-arduino/issues/876) | **Missing #define ARDUINO 10808 logic?** | Marked as bug but it's just the same problem again | +| 3| [#829](https://github.com/microsoft/vscode-arduino/issues/829) | **`Arduino.h` and ESP8266 includes have squiggles** | Stale issue | +| 4| [#969](https://github.com/microsoft/vscode-arduino/issues/969) | **INO Files defines undefined but can jump to definition** | | +| 5| [#959](https://github.com/microsoft/vscode-arduino/issues/959) | **Update board type command does not update intellisense config automatically** | | +| 6| [#892](https://github.com/microsoft/vscode-arduino/issues/892) | **Default IntelliSense config** | | +| 7| [#850](https://github.com/microsoft/vscode-arduino/issues/850) | **How to prevent modifications of c_cpp_properties.json by the extension?** | Asks if the current implementation can be turned off, because it overwrites a user's config with non working IS config -- this is sad. | +| 8| [#833](https://github.com/microsoft/vscode-arduino/issues/833) | **Allow C_Cpp.intelliSenseEngine to be set to "Default" instead of "Tag Parser" for better code completion/suggestions** | | +| 9| [#808](https://github.com/microsoft/vscode-arduino/issues/808) | **Identifier "Serial" is undefined** | | +| 10| [#474](https://github.com/microsoft/vscode-arduino/issues/474) | **Enrich device develop experience** | | + + + + + + +-- the list is probably incomplete - I didn't search exhaustively and didn't consider closed issues as long as I didn't stumble upon one. New duplicates are popping up at a rate of about one per week. + +Further related issues +* [vscode-arduino issue search for IntelliSense](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) +* [Wrongly attributed to vscode instead of vscode-arduino](https://github.com/Microsoft/vscode-cpptools/issues/1750) +* [Problems with IntelliSense itself](https://github.com/microsoft/vscode-cpptools/issues/1034) + +## Solution +Implement and add a a parser which parses the output from Arduino's build process and generate a very precise `c_cpp_properties.json` which in turn hopefully renders any user interaction with this file obsolete. + +This mechanism should try it's best to detect situations in which the setup changes (board changed, sketch changed and so forth) and re-generate this configuration. For all other situations I'll provide a command to manually re-generate it. ## Branch Goals ### Build Output Parser -The parser which parses the relevant includes, defines, compiler paths and flags from Arduino's build output +The parser which identifies the main compilation command from Arduino's build output. It then parses the relevant includes, defines, compiler paths and flags from it. + ### `c_cpp_properties.json` Generator -The generator takes the parser's output and transforms it into a valid `c_cpp_properties.json` file. +The generator takes the parser's output and transforms it into a valid `c_cpp_properties.json` file. It merges the generated configuration with the existing (e.g. user-) configurations and writes it back to the configuration file. ### Configuration Flags -Provide a configuration flag which allows the user to turn this feature off - this is useful for the cases in which this magic fails or the user has a very specific setup. Although this branch tries to eliminate most of the latter cases. +Provide a global configuration flag which allows the user to turn this feature off. A project- (sketch-) specific override will be provided which allows the user to turn it off or on - regardless of the global setting. +This is useful for the rare cases for which this magic should fail or the user has a very exotic setup. This branch tries to eliminate most of the latter cases though. Especially it will always write to the `Arduino` configuration. If the user sets up a custom configuration she/he must simply name it differently, e.g. `John's Custom Config`, and the generator won't touch it. ### Global Tasks in vscode-arduino -See table below. +* Integrate it into vscode-arduino's build mechanics +* Install event trigger generation/handling to run the analysis as soon as something changes +* Remove the current implementation +For more details see table below. -### Branch Log +## Branch Log **2020 02 05** Currently I'm able to generate error free IntelliSense setups for AVR and ESP32 using the preliminary implementation. For ESP32 I just had to add the intrinsic compiler paths manually. A solution has to be found for these ... which there is, see [here](https://stackoverflow.com/a/6666338) **2020 02 06** Got it fully working (with built-in include directories) for AVR, ESP32, ESP8266. Rewrote the backend to facilitate writing of further parser engines in the future. **2020 02 07** Wrote compiler command parser npm package [cocopa](https://www.npmjs.com/package/cocopa) and began writing a test framework for it. Added a global configuration switch which allows the IntelliSense configuration generation to be turned off. @@ -36,8 +68,9 @@ During merging I found some bugs within those functions - mainly due to the abov * No consistent return values within `verify` (when it bailed out early it returned `void`) **2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. +**2020 02 18** Finished basic event triggering. Rewrote `DeviceContext` for proper settings modification detection (trigger events only on actual change) and generation of setting specific events (e.g. board changed) instead of one global event (aka. "something in the settings changed"). -### Status +## Status | | Tasks | |-----:|:--------| | **Build output parser** | :heavy_check_mark: Basic parser working | @@ -58,15 +91,23 @@ During merging I found some bugs within those functions - mainly due to the abov | **Unit tests** | :heavy_check_mark: Basic parser (known boards, match/no match)| | | :white_check_mark: All unit tests in cocopa | | | :white_check_mark: Test with cpp sketches | -| **General** | :white_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense. (Partially done - documented in the [General Tasks](#General-Tasks) section | -| | :white_check_mark: Auto-run verify after a) *setting a board* b) *changing the sketch* c) *workbench initialized and no `c_cpp_properties.json` has been found*. We have to generate a valid `c_cpp_properties.json` to keep IntelliSense working in such situations. Identify other occasions where this applies (usually when adding new libraries), hint the user to run *Arduino: Rebuild IntelliSense Configuration*? -> Good moment would be after the workbench initialization -> message in arduino channel | +| **General** | :heavy_check_mark: Review and remove previous attempts messing with `c_cpp_properties.json` or IntelliSense (documented in the [General Tasks](#General-Tasks) section) `*` | +| | :white_check_mark: *Auto-run verify when* | +| |     :heavy_check_mark: a) setting a board `*` | +| |     :heavy_check_mark: b) changing the board's configuration `*` | +| |     :heavy_check_mark: c) changing the sketch `*` | +| |     :white_check_mark: d) workbench initialized and no `c_cpp_properties.json` found | +| |     :white_check_mark: e) Identify other occasions where this applies (usually when adding new libraries) | +| | :white_check_mark: Hint the user to run *Arduino: Rebuild IntelliSense Configuration*? -> Good moment would be after the workbench initialization -> message in arduino channel | +| | :white_check_mark: Better build management such that regular builds and analyze builds do not interfere | +| | :white_check_mark: Analyze task queue which fits in the latter | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | -| | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack. Done, see [cocopa](https://www.npmjs.com/package/cocopa) | +| | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack -- done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :heavy_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command - this has to be fixed (done, see next item) | | | :heavy_check_mark: Implement a *Rebuild IntelliSense Configuration* command which runs verify verbosely internally and therefore allows us to find and parse the compiler command | -| | :white_check_mark: Implement proper event generation for `DeviceContext`. a) Events should be issued only when something actually changes, b) Events should be issued for each setting separately | +| | :heavy_check_mark: Implement proper event generation for `DeviceContext`. a) Events should be issued only when something actually changes, b) Events should be issued for each setting separately `*`| | | :white_check_mark: Finally: go through my code and look for TODOs | `*` not committed to branch yet @@ -80,8 +121,8 @@ I write a lot of code for Arduino, especially libraries. The Arduino IDE is not Then remains vscode-arduino. It seems that it isn't completely dead - but almost. Most of the core functionality seems to work (I used it a few days now). But the biggest show stopper is the bad IntelliSense support -- which I'll address here now. -## Beer Money :beers: -You can chip in some beer money to keep me motivated - this is really appreciated. +## Beer Money :beers: -- Support +You can chip in some beer money to keep me motivated - this is *really* appreciated. [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PVLCSRZHBJ28G&source=url) @@ -103,24 +144,20 @@ I will list every supporter here, thanks! 2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) 2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) 2020-02-17 Elektronik Workshop: 52 :beers: (13h coding) +2020-02-18 Elektronik Workshop: xx :beers: (xxh coding) ## Useful Links -* [IntelliSense issues on vscode-arduino](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) * [`c_cpp_properties.json` reference](https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference) * [Interactive regex debugger](https://regex101.com/) * [Git branch management](https://blog.scottlowe.org/2015/01/27/using-fork-branch-git-workflow/) * [Collapsible Markdown](https://gist.githubusercontent.com/joyrexus/16041f2426450e73f5df9391f7f7ae5f/raw/f774f242feff6bae4a5be7d6c71aa5df2e3fcb0e/README.md) * [Arduino CLI manpage](https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc) * [Install extensions from file](https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/) -* [Publish extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) -* [Arduino Dev Tools](https://playground.arduino.cc/Main/DevelopmentTools/) - -## Issues Concerning this Project - * https://github.com/Microsoft/vscode-cpptools/issues/1750 - * Problems with IntelliSense itself https://github.com/microsoft/vscode-cpptools/issues/1034 - * Logging for IntelliSense https://code.visualstudio.com/docs/cpp/enable-logging-cpp +* [Publish vscode extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) +* [Debug logging for IntelliSense](https://code.visualstudio.com/docs/cpp/enable-logging-cpp) +* [Arduino Dev Tools](https://playground.arduino.cc/Main/DevelopmentTools/) (obsolete/outdated) ## Future Work * Proper interactive serial terminal (this is the second major show stopper in my opinion) @@ -244,15 +281,11 @@ Sometimes IntelliSense has problems within the extension host (which you're runn ---- -## Implementation -**Note** Check this vscode feature: -``` -Configuration provider -The ID of a VS Code extension that can provide IntelliSense configuration information for source files. For example, use the VS Code extension ID ms-vscode.cmake-tools to provide configuration information from the CMake Tools extension. -``` +# Implementation +Here are some implementation notes. Probably only of interest to me. -### Build Output Parser -#### Intrinsic Include Paths +## Build Output Parser +### Intrinsic Include Paths Some include paths are built into gcc and don't have to be specified on the command line. Just searching the compiler installation directory with something like ```bash find ~/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/ -name "include*" @@ -294,11 +327,11 @@ End of search list. ``` As one can see with the ESP32-gcc not all include directories are named `include`. Parsing of this output is pretty trivial though. -### `c_cpp_properties.json` Generator +## `c_cpp_properties.json` Generator -### Settings -#### Global Settings +## Settings +### Global Settings Under linux at `~/.config/Code/User/settings.json`, for instance: ```json { @@ -312,7 +345,7 @@ Code: [src/arduino/arduinoSettings.ts](src/arduino/arduinoSettings.ts) Code: [src/arduino/vscodeSettings.ts](src/arduino/vscodeSettings.ts) Validator: [package.json](package.json) -#### Project Settings +### Project Settings Path in project `.vscode/arduino.json` ```json { @@ -325,8 +358,8 @@ Path in project `.vscode/arduino.json` Code: [src/deviceContext.ts](src/deviceContext.ts) Validator: [misc/arduinoValidator.json](misc/arduinoValidator.json) -### General Tasks -#### Removing existing Attempts which mess with c_cpp_properties.json or Intellisense +## General Tasks +### Removing existing Attempts which mess with c_cpp_properties.json or Intellisense Remove these as they are helpless attempts to get IntelliSense working: ```ts @@ -344,7 +377,14 @@ Remove these as they are helpless attempts to get IntelliSense working: Remove this as this messes in an unpredictable and helpless way with Intellisense [src/langService/completionProvider.ts](src/langService/completionProvider.ts) -Remove this folder as this is not necessary when Intellisense works properly: +Review this folder as some of this is probably obsolete when Intellisense works properly: ``` -syntaxes/ +syntaxes/arduino.configuration.json +syntaxes/arduino.tmLanguage +# Within package.json + { + "language": "cpp", + "path": "./syntaxes/arduino.tmLanguage", + "scopeName": "source.cpp.arduino" + }, ``` From 59bad214e52ae13f1564db9e34ee58c1af231927 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 19 Feb 2020 16:18:15 +0100 Subject: [PATCH 066/142] Updated project log and status --- BRANCHNOTES.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 8777a386..f2b2849a 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -69,6 +69,7 @@ During merging I found some bugs within those functions - mainly due to the abov **2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. **2020 02 18** Finished basic event triggering. Rewrote `DeviceContext` for proper settings modification detection (trigger events only on actual change) and generation of setting specific events (e.g. board changed) instead of one global event (aka. "something in the settings changed"). +**2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. ## Status | | Tasks | @@ -95,12 +96,12 @@ During merging I found some bugs within those functions - mainly due to the abov | | :white_check_mark: *Auto-run verify when* | | |     :heavy_check_mark: a) setting a board `*` | | |     :heavy_check_mark: b) changing the board's configuration `*` | -| |     :heavy_check_mark: c) changing the sketch `*` | +| |     :heavy_check_mark: c) selecting another sketch `*` | | |     :white_check_mark: d) workbench initialized and no `c_cpp_properties.json` found | | |     :white_check_mark: e) Identify other occasions where this applies (usually when adding new libraries) | | | :white_check_mark: Hint the user to run *Arduino: Rebuild IntelliSense Configuration*? -> Good moment would be after the workbench initialization -> message in arduino channel | -| | :white_check_mark: Better build management such that regular builds and analyze builds do not interfere | -| | :white_check_mark: Analyze task queue which fits in the latter | +| | :heavy_check_mark: Better build management such that regular builds and analyze builds do not interfere (done, 2020-02-19) `*` | +| | :heavy_check_mark: Analyze task queue which fits in the latter (done, 2020-02-19) `*` | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | | | :white_check_mark: Document features in [README.md](README.md) (partially done) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | @@ -144,7 +145,8 @@ I will list every supporter here, thanks! 2020-02-15 T.D.: 4 :beers: (20$ - Thanks a lot!) 2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) 2020-02-17 Elektronik Workshop: 52 :beers: (13h coding) -2020-02-18 Elektronik Workshop: xx :beers: (xxh coding) +2020-02-18 Elektronik Workshop: 36 :beers: (9h coding) +2020-02-19 Elektronik Workshop: x :beers: (xh coding) @@ -185,6 +187,7 @@ I will list every supporter here, thanks! * Possibility to jump to compilation errors from compiler output and highlight compiler errors * Further IntelliSense enhancements/features: * When having adding a library folder to the workspace IntelliSense should use the same configuration for it to enable library navigation and code completion. + * Optimization: Abort analysis build as soon as compiler statement has been found ## Non-categorized Notes ### Integrate upstream changes into fork From 548c714b1369630238af3306e40f012e43dc45a5 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 19 Feb 2020 19:36:36 +0100 Subject: [PATCH 067/142] Updated and completed documentation of the current state within README.md * Updated project log and status --- BRANCHNOTES.md | 4 ++-- README.md | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index f2b2849a..bcd5a047 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -69,7 +69,7 @@ During merging I found some bugs within those functions - mainly due to the abov **2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. **2020 02 18** Finished basic event triggering. Rewrote `DeviceContext` for proper settings modification detection (trigger events only on actual change) and generation of setting specific events (e.g. board changed) instead of one global event (aka. "something in the settings changed"). -**2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. +**2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. Updated and completed the documentation for the IntelliSense usage within [README](README.md). Alpha test builds of the extension containing the latest implemented features and fixes are now available from the following [Dropbox folder](https://www.dropbox.com/sh/whmcdt26chyjgby/AAB1Ld2fzZ9Z_NfM3CRay17wa). Please note, that Windows is currently not supported yet. ## Status | | Tasks | @@ -103,7 +103,7 @@ During merging I found some bugs within those functions - mainly due to the abov | | :heavy_check_mark: Better build management such that regular builds and analyze builds do not interfere (done, 2020-02-19) `*` | | | :heavy_check_mark: Analyze task queue which fits in the latter (done, 2020-02-19) `*` | | | :heavy_check_mark: Document configuration settings in [README.md](README.md) | -| | :white_check_mark: Document features in [README.md](README.md) (partially done) | +| | :heavy_check_mark: Document features in [README.md](README.md) | | | :heavy_check_mark: Try to auto-generate even if verify (i.e. compilation) fails | | | :heavy_check_mark: Extract compiler command parser from vscode-arduino and [publish](https://itnext.io/step-by-step-building-and-publishing-an-npm-typescript-package-44fe7164964c) it as a separate package which will allow reusage and easy testing without heavy vscode-arduino rucksack -- done, see [cocopa](https://www.npmjs.com/package/cocopa) | | | :heavy_check_mark: Parser only works when arduino is set to `verbose`, since this is the only way we get the compiler invocation command - this has to be fixed (done, see next item) | diff --git a/README.md b/README.md index a7dda5ae..1df1360b 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ The following settings are as per sketch settings of the Arduino extension. You "output": "../build", "debugger": "jlink", "prebuild": "bash prebuild.sh", - "disableIntelliSenseAutoGen": "global" + "intelliSenseGen": "global" } ``` - `sketch` - The main sketch file name of Arduino. @@ -111,21 +111,27 @@ The following settings are as per sketch settings of the Arduino extension. You - `output` - Arduino build output path. If not set, Arduino will create a new temporary output folder each time, which means it cannot reuse the intermediate result of the previous build leading to long verify/upload time, so it is recommended to set the field. Arduino requires that the output path should not be the workspace itself or in a subfolder of the workspace, otherwise, it may not work correctly. By default, this option is not set. It's worth noting that the contents of this file could be deleted during the build process, so pick (or create) a directory that will not store files you want to keep. - `debugger` - The short name of the debugger that will be used when the board itself does not have a debugger and there is more than one debugger available. You can find the list of debuggers [here](https://github.com/Microsoft/vscode-arduino/blob/master/misc/debuggerUsbMapping.json). By default, this option is not set. - `prebuild` - External command before building the sketch file. You should only set one `prebuild` command. `command1 && command2` does not work. If you need to run multiple commands before the build, then create a script. -- `disableIntelliSenseAutoGen` - Override the global auto-generation of the IntelliSense configuration (i.e. `.vscode/c_cpp_properties.json`). Three options are available: +- `intelliSenseGen` - Override the global setting for auto-generation of the IntelliSense configuration (i.e. `.vscode/c_cpp_properties.json`). Three options are available: - `"global"`: Use the global settings (default) - `"disable"`: Disable the auto-generation even if globally enabled - `"enable"`: Enable the auto-generation even if globally disabled ## IntelliSense -*TODO: Rewrite this section* -vscode-arduino auto-configures IntelliSense by default. vscode-arduino analyzes Arduino's compiler output during verify and generates the corresponding configuration file at `.vscode/c_cpp_properties.json` and tries as hard as possible to keep things up to date, e.g. running verify when switching the board or the sketch. -It doesn't makes sense though to run verify repeatedly. Therefore if the workspace reports problems (for instance after adding new includes to a new library) run *verify* such that IntelliSense knows of the new include directories (since the Arduino-backend performs the library resolution externally). +vscode-arduino auto-configures IntelliSense by default. vscode-arduino analyzes Arduino's compiler output by running a separate build and generates the corresponding configuration file at `.vscode/c_cpp_properties.json`. vscode-arduino tries as hard as possible to keep things up to date, e.g. it runs the analysis when switching the board or the sketch. -TODO: Note about configuration selection in lower right. +It doesn't makes sense though to run the analysis repeatedly. Therefore if the workspace reports problems ("squiggles") - for instance after adding new includes from a new library - run the analysis manually: -Manual rebuild: **Ardino: Rebuild IntelliSense Configuration**, -Keybindings: **Arduino: Rebuild IntelliSense Configuration** Alt + Cmd + I *or* Alt + Ctrl + I +Manual rebuild: **Arduino: Rebuild IntelliSense Configuration**, +Keybindings: Alt + Cmd + I *or* Alt + Ctrl + I +When the analysis is invoked manually it ignores any global and project specific disable. + +### IntelliSense Configurations +vscode-arduino's analysis stores the result as a dedicated IntelliSense-configuration named `Arduino`. You have to select it from the far right of the status bar when you're in one of your source files as shown here: + +![74001156-cfce8280-496a-11ea-9b9d-7d30c83765c1](https://user-images.githubusercontent.com/21954933/74351237-2696ea80-4db7-11ea-9f7a-1bfc652ad5f5.png) + +This system allows you to setup and use own IntelliSense configurations in parallel to the automatically generated configurations provided through vscode-arduino. Just add your configuration to `c_cpp_properties.json` and name it differently from the default configuration (`Arduino`), e.g. `My awesome configuration` and select it from the status bar or via the command palette command **C/C++: Select a Configuration...** ## Debugging Arduino Code preview Before you start to debug your Arduino code, please read [this document](https://code.visualstudio.com/docs/editor/debugging) to learn about the basic mechanisms of debugging in Visual Studio Code. Also see [debugging for C++ in VSCode](https://code.visualstudio.com/docs/languages/cpp#_debugging) for further reference. From c15dc08fa617b34e5b00643ce14d0ea8e827fc9d Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 19 Feb 2020 22:45:54 +0100 Subject: [PATCH 068/142] IntelliSense auto-analysis integration * Reworked DeviceContext for fine grained event generation on settings change * Added dedicated settings classes which allows for the latter * Changed board manager and serial monitor to used the new fine grained events instead of the "something changed event" - which makes the code marginally more efficient * Implemented an analysis manager which takes care of analysis requests generated by settings change events and makes sure that analysis builds don't interfere with regular builds * Adapted the build infrastructure within ArduinoApp such that it allows for the above * Removed the global build guard and moved it to ArduinoApp (there is a corner case though for programmer selection within extension.ts which I marked with a TODO * Fixed some linting issues but did not lint most of the stuff committed here - this will be part of a later commit * I left notes here and there where I saw things which should/could be improved/changed * Removed the try/catch guards from the build invocations within extension.ts since exceptions are handled within the build function itself * Changed the project setting parameter for the IntelliSense setup to reflect its workings better --- misc/arduinoValidator.json | 2 +- src/arduino/arduino.ts | 167 +++++++++++++------ src/arduino/boardManager.ts | 5 +- src/arduino/intellisense.ts | 195 ++++++++++++++++++++++- src/arduino/programmerManager.ts | 2 +- src/arduinoContext.ts | 6 + src/common/util.ts | 2 +- src/deviceContext.ts | 221 +++++++++++--------------- src/deviceSettings.ts | 15 +- src/extension.ts | 95 +++++------ src/langService/completionProvider.ts | 3 +- src/serialmonitor/serialMonitor.ts | 24 ++- src/serialmonitor/usbDetector.ts | 3 + 13 files changed, 480 insertions(+), 260 deletions(-) diff --git a/misc/arduinoValidator.json b/misc/arduinoValidator.json index 48362cb1..57ca7be8 100644 --- a/misc/arduinoValidator.json +++ b/misc/arduinoValidator.json @@ -33,7 +33,7 @@ "type": "string", "minLength": 1 }, - "disableIntelliSenseAutoGen": { + "intelliSenseGen": { "description": "Disable/enable the automatic generation of the IntelliSense configuration file (c_cpp_properties.json) for this project (overrides the global setting). When set to \"global\" the global extension settings will be used.", "type": "string", "default": "global", diff --git a/src/arduino/arduino.ts b/src/arduino/arduino.ts index e6a749b3..c3159e7d 100644 --- a/src/arduino/arduino.ts +++ b/src/arduino/arduino.ts @@ -15,7 +15,10 @@ import { DeviceContext } from "../deviceContext"; import { IArduinoSettings } from "./arduinoSettings"; import { BoardManager } from "./boardManager"; import { ExampleManager } from "./exampleManager"; -import { ICoCoPaContext, isCompilerParserEnabled, makeCompilerParserContext } from "./intellisense"; +import { AnalysisManager, + ICoCoPaContext, + isCompilerParserEnabled, + makeCompilerParserContext } from "./intellisense"; import { LibraryManager } from "./libraryManager"; import { VscodeSettings } from "./vscodeSettings"; @@ -25,6 +28,11 @@ import { SerialMonitor } from "../serialmonitor/serialMonitor"; import { UsbDetector } from "../serialmonitor/usbDetector"; import { ProgrammerManager } from "./programmerManager"; +/** + * Supported build modes. For further explanation see the documentation + * of ArduinoApp.build(). + * The strings are used for status reporting within the above function. + */ export enum BuildMode { Verify = "Verifying", Analyze = "Analyzing", @@ -45,10 +53,30 @@ export class ArduinoApp { private _programmerManager: ProgrammerManager; + /** + * IntelliSense analysis manager. + * Makes sure that analysis builds and regular builds go along + * and that multiple subsequent analysis requests - as triggered + * by board/board-configuration changes - are bundled to a single + * analysis build run. + */ + private _analysisManager: AnalysisManager; + + /** + * Indicates if a build is currently in progress. + * If so any call to this.build() will return false immediately. + */ + private _building: boolean = false; + /** * @param {IArduinoSettings} _settings ArduinoSetting object. */ constructor(private _settings: IArduinoSettings) { + const analysisDelayMs = 1000 * 3; + this._analysisManager = new AnalysisManager( + () => this._building, + async () => { await this.build(BuildMode.Analyze, true); }, + analysisDelayMs); } /** @@ -71,6 +99,17 @@ export class ArduinoApp { } catch (ex) { } } + + // set up event handling for IntelliSense analysis + const requestAnalysis = async () => { + if (isCompilerParserEnabled()) { + await this._analysisManager.requestAnalysis(); + } + }; + const dc = DeviceContext.getInstance(); + dc.onChangeBoard(requestAnalysis); + dc.onChangeConfiguration(requestAnalysis); + dc.onChangeSketch(requestAnalysis); } /** @@ -101,38 +140,96 @@ export class ArduinoApp { } } + /** + * Returns true if a build is currently in progress. + */ + public get building() { + return this._building; + } + /** * Runs the arduino builder to build/compile and - if necessary - upload * the current sketch. - * @param buildMode Build mode. BuildMode.Upload: Compile and upload, - * BuildMode.UploadProgrammer: Compile and upload using the user selectable - * programmer, BuildMode.Analyze: Compile, analyze the output and generate - * IntelliSense configuration from it, BuildMode.Verify: Just compile. + * @param buildMode Build mode. + * * BuildMode.Upload: Compile and upload + * * BuildMode.UploadProgrammer: Compile and upload using the user + * selectable programmer + * * BuildMode.Analyze: Compile, analyze the output and generate + * IntelliSense configuration from it. + * * BuildMode.Verify: Just compile. + * All build modes except for BuildMode.Analyze run interactively, i.e. if + * something is missing, it tries to query the user for the missing piece + * of information (sketch, board, etc.). Analyze runs non interactively and + * just returns false. * @param {bool} compile - Indicates whether to compile the code when using the CLI to upload * @param buildDir Override the build directory set by the project settings * with the given directory. + * @returns true on success, false if + * * another build is currently in progress + * * board- or programmer-manager aren't initialized yet + * * or something went wrong during the build */ - public async build(buildMode: BuildMode, compile: boolean, buildDir?: string): Promise { + public async build(buildMode: BuildMode, compile: boolean, buildDir?: string) { + + if (!this._boardManager || !this._programmerManager || this._building) { + return false; + } + + this._building = true; + + return await this._build(buildMode, compile, buildDir) + .then((ret) => { + this._building = false; + return ret; + }) + .catch((reason) => { + this._building = false; + // TODO EW, 2020-02-19: Report unhandled error (Logger?) + return false; + }); + } + +// Not moving _build around in the file (yet?) because it would create too much merge/rebase problems. +/* tslint:disable:member-ordering */ + + /** + * Private implementation. Not to be called directly. The wrapper build() + * manages the build state. + * @param buildMode See build() + * @param buildDir See build() + */ + public async _build(buildMode: BuildMode, compile: boolean, buildDir?: string): Promise { const dc = DeviceContext.getInstance(); const args: string[] = []; let restoreSerialMonitor: boolean = false; let cocopa: ICoCoPaContext; - const boardDescriptor = this.getBoardBuildString(); - if (!boardDescriptor) { + if (!this.boardManager.currentBoard) { + if (buildMode !== BuildMode.Analyze) { + logger.notifyUserError("boardManager.currentBoard", new Error(constants.messages.NO_BOARD_SELECTED)); + } return false; } + const boardDescriptor = this.boardManager.currentBoard.getBuildConfig(); + if (!this.useArduinoCli()) { args.push("--board", boardDescriptor); } if (!ArduinoWorkspace.rootPath) { - vscode.window.showWarningMessage("Cannot find the sketch file."); + vscode.window.showWarningMessage("Workspace doesn't seem to have a folder added to it yet."); return false; } if (!dc.sketch || !util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, dc.sketch))) { - await this.getMainSketch(dc); + if (buildMode === BuildMode.Analyze) { + // Analyze runs non interactively + return false; + } + if (!await dc.resolveMainSketch()) { + vscode.window.showErrorMessage("No sketch file was found. Please specify the sketch in the arduino.json file"); + return false; + } } const selectSerial = async () => { @@ -171,8 +268,9 @@ export class ArduinoApp { args.push("--port", dc.port); } } else if (buildMode === BuildMode.UploadProgrammer) { - const programmer = this.getProgrammerString(); + const programmer = this.programmerManager.currentProgrammer; if (!programmer) { + logger.notifyUserError("programmerManager.currentProgrammer", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); return false; } if (!dc.port) { @@ -205,9 +303,6 @@ export class ArduinoApp { args.push("--port", dc.port); } else if (buildMode === BuildMode.Analyze) { - if (!isCompilerParserEnabled()) { - return false; - } cocopa = makeCompilerParserContext(dc); if (!this.useArduinoCli()) { args.push("--verify", "--verbose"); @@ -229,6 +324,8 @@ export class ArduinoApp { await vscode.workspace.saveAll(false); + // we prepare the channel here since all following code will + // or at leas can possibly output to it arduinoChannel.show(); arduinoChannel.start(`${buildMode} sketch '${dc.sketch}'`); @@ -716,7 +813,7 @@ export class ArduinoApp { const dc = DeviceContext.getInstance(); const arduinoJson = { sketch: sketchFile, - // TODO: COM1 is Windows specific - what about OSX and Linux users? + // TODO EW, 2020-02-18: COM1 is Windows specific - what about OSX and Linux users? port: dc.port || "COM1", board: dc.board, configuration: dc.configuration, @@ -818,14 +915,15 @@ export class ArduinoApp { * @returns True if successful, false on error. */ protected async runPreBuildCommand(dc: DeviceContext): Promise { - if (dc.prebuild) { - arduinoChannel.info(`Running pre-build command: ${dc.prebuild}`); - const prebuildargs = dc.prebuild.split(" "); - const prebuildCommand = prebuildargs.shift(); + const prebuildcmdline = dc.prebuild; + if (prebuildcmdline) { + arduinoChannel.info(`Running pre-build command: ${prebuildcmdline}`); + const args = prebuildcmdline.split(/\s+/); + const cmd = args.shift(); try { - await util.spawn(prebuildCommand, + await util.spawn(cmd, arduinoChannel.channel, - prebuildargs, + args, { shell: true, cwd: ArduinoWorkspace.rootPath }); } catch (ex) { arduinoChannel.error(`Running pre-build command failed: ${os.EOL}${ex.error}`); @@ -843,30 +941,5 @@ export class ArduinoApp { return this._settings.useArduinoCli; // return VscodeSettings.getInstance().useArduinoCli; } - - private getProgrammerString(): string { - const selectProgrammer = this.programmerManager.currentProgrammer; - if (!selectProgrammer) { - logger.notifyUserError("getProgrammerString", new Error(constants.messages.NO_PROGRAMMMER_SELECTED)); - return; - } - return selectProgrammer; - } - - private getBoardBuildString(): string { - const selectedBoard = this.boardManager.currentBoard; - if (!selectedBoard) { - logger.notifyUserError("getBoardBuildString", new Error(constants.messages.NO_BOARD_SELECTED)); - return; - } - return selectedBoard.getBuildConfig(); - } - - private async getMainSketch(dc: DeviceContext) { - await dc.resolveMainSketch(); - if (!dc.sketch) { - vscode.window.showErrorMessage("No sketch file was found. Please specify the sketch in the arduino.json file"); - throw new Error("No sketch file was found."); - } - } +/* tslint:enable:member-ordering */ } diff --git a/src/arduino/boardManager.ts b/src/arduino/boardManager.ts index dd35a80c..79af7780 100644 --- a/src/arduino/boardManager.ts +++ b/src/arduino/boardManager.ts @@ -73,7 +73,10 @@ export class BoardManager { this._boardConfigStatusBar.show(); const dc = DeviceContext.getInstance(); - dc.onDidChange(() => { + dc.onChangeBoard(() => { + this.updateStatusBar(); + }); + dc.onChangeConfiguration(() => { this.updateStatusBar(); }); } diff --git a/src/arduino/intellisense.ts b/src/arduino/intellisense.ts index ad98a0ef..57f5588c 100644 --- a/src/arduino/intellisense.ts +++ b/src/arduino/intellisense.ts @@ -1,3 +1,6 @@ +// Copyright (c) Elektronik Workshop. All rights reserved. +// Licensed under the MIT license. + import * as ccp from "cocopa"; import * as path from "path"; @@ -23,7 +26,7 @@ export function isCompilerParserEnabled(dc?: DeviceContext) { dc = DeviceContext.getInstance(); } const globalDisable = VscodeSettings.getInstance().disableIntelliSenseAutoGen; - const projectSetting = dc.disableIntelliSenseAutoGen; + const projectSetting = dc.intelliSenseGen; return projectSetting !== "disable" && !globalDisable || projectSetting === "enable"; } @@ -94,6 +97,8 @@ function makeCompilerParserEngines(dc: DeviceContext) { const dotcpp = sketch.endsWith(".ino") ? ".cpp" : ""; sketch = `-o\\s+\\S*${ccp.regExEscape(sketch)}${dotcpp}\\.o`; + // TODO: handle other architectures here + const matchPattern = [ // make sure we're running g++ /(?:^|-)g\+\+\s+/, @@ -112,3 +117,191 @@ function makeCompilerParserEngines(dc: DeviceContext) { const gccParserEngine = new ccp.ParserGcc(matchPattern, dontMatchPattern); return [gccParserEngine]; } + +/** + * Possible states of AnalysisManager's state machine. + */ +enum AnalysisState { + /** + * No analysis request pending. + */ + Idle = "idle", + /** + * Analysis request pending. Waiting for the time out to expire or for + * another build to complete. + */ + Waiting = "waiting", + /** + * Analysis in progress. + */ + Analyzing = "analyzing", + /** + * Analysis in progress with yet another analysis request pending. + * As soon as the current analysis completes the manager will directly + * enter the Waiting state. + */ + AnalyzingWaiting = "analyzing and waiting", +} + +/** + * Events (edges) which cause state changes within AnalysisManager. + */ +enum AnalysisEvent { + /** + * The only external event. Requests an analysis to be run. + */ + AnalysisRequest, + /** + * The internal wait timeout expired. + */ + WaitTimeout, + /** + * The current analysis build finished. + */ + AnalysisBuildDone, +} + +/** + * This class manages analysis builds for the automatic IntelliSense + * configuration synthesis. Its primary purposes are: + * + * * delaying analysis requests caused by DeviceContext setting change + * events such that multiple subsequent requests don't cause + * multiple analysis builds + * * make sure that an analysis request is postponed when another build + * is currently in progress + * + * TODO: initialization sequence: make sure events generated + * during initialization are not lost + * + * TODO: check time of c_cpp_properties.json and compare it with + * * arduino.json + * * main sketch file + * This way we can perhaps optimize this further. But be aware + * that settings events fire before their corresponding values + * are actually written to arduino.json -> time of arduino.json + * is outdated if no countermeasure is taken. + */ +export class AnalysisManager { + + /** The manager's state. */ + private _state: AnalysisState = AnalysisState.Idle; + /** A callback used by the manager to query if the build backend is busy. */ + private _isBuilding: () => boolean; + /** A callback used by the manager to initiate an analysis build. */ + private _doBuild: () => Promise; + /** Timeout for the timeouts/delays in milliseconds. */ + private _waitPeriodMs: number; + /** The internal timer used to implement the above timeouts and delays. */ + private _timer: NodeJS.Timer; + + /** + * Constructor. + * @param isBuilding Provide a callback which returns true if another build + * is currently in progress. + * @param doBuild Provide a callback which runs the analysis build. + * @param waitPeriodMs The delay the manger should wait for potential new + * analysis request. This delay is used as polling interval as well when + * checking for ongoing builds. + */ + constructor(isBuilding: () => boolean, + doBuild: () => Promise, + waitPeriodMs: number = 1000) { + this._isBuilding = isBuilding; + this._doBuild = doBuild; + this._waitPeriodMs = waitPeriodMs; + } + + /** + * File an analysis request. + * The analysis will be delayed until no further requests are filed + * within a wait period or until any build in progress has terminated. + */ + public async requestAnalysis() { + await this.update(AnalysisEvent.AnalysisRequest); + } + + /** + * Update the manager's state machine. + * @param event The event which will cause the state transition. + * + * Implementation note: asynchronous edge actions must be called after + * setting the new state since they don't return immediately. + */ + private async update(event: AnalysisEvent) { + + switch (this._state) { + + case AnalysisState.Idle: + if (event === AnalysisEvent.AnalysisRequest) { + this._state = AnalysisState.Waiting; + this.startWaitTimeout(); + } + break; + + case AnalysisState.Waiting: + if (event === AnalysisEvent.AnalysisRequest) { + // every new request restarts timer + this.startWaitTimeout(); + } else if (event === AnalysisEvent.WaitTimeout) { + if (this._isBuilding()) { + // another build in progress, continue waiting + this.startWaitTimeout(); + } else { + // no other build in progress -> launch analysis + this._state = AnalysisState.Analyzing; + await this.startAnalysis(); + } + } + break; + + case AnalysisState.Analyzing: + if (event === AnalysisEvent.AnalysisBuildDone) { + this._state = AnalysisState.Idle; + } else if (event === AnalysisEvent.AnalysisRequest) { + this._state = AnalysisState.AnalyzingWaiting; + } + break; + + case AnalysisState.AnalyzingWaiting: + if (event === AnalysisEvent.AnalysisBuildDone) { + // emulate the transition from idle to waiting + // (we don't care if this adds an additional + // timeout - event driven analysis is not time- + // critical) + this._state = AnalysisState.Idle; + await this.update(AnalysisEvent.AnalysisRequest); + } + break; + } + } + + /** + * Starts the wait timeout timer. + * If it's already running, the current timer is stopped and restarted. + * The timeout callback will then update the state machine. + */ + private startWaitTimeout() { + if (this._timer) { + clearTimeout(this._timer); + } + this._timer = setTimeout(() => { + this.update(AnalysisEvent.WaitTimeout); + this._timer = undefined; + }, this._waitPeriodMs); + } + + /** + * Starts the analysis build. + * When done, the callback will update the state machine. + */ + private async startAnalysis() { + await this._doBuild() + .then(() => { + this.update(AnalysisEvent.AnalysisBuildDone); + }) + .catch((reason) => { + this.update(AnalysisEvent.AnalysisBuildDone); + }); + } +} diff --git a/src/arduino/programmerManager.ts b/src/arduino/programmerManager.ts index 08d3a425..7a7e2991 100644 --- a/src/arduino/programmerManager.ts +++ b/src/arduino/programmerManager.ts @@ -35,7 +35,7 @@ export class ProgrammerManager { this._programmerStatusBar.tooltip = "Select Programmer"; this.setProgrammerValue(DeviceContext.getInstance().programmer); this._programmerStatusBar.show(); - DeviceContext.getInstance().onDidChange(() => { + DeviceContext.getInstance().onChangeProgrammer(() => { this.setProgrammerValue(DeviceContext.getInstance().programmer); }); } diff --git a/src/arduinoContext.ts b/src/arduinoContext.ts index 58a3f8a4..321d2857 100644 --- a/src/arduinoContext.ts +++ b/src/arduinoContext.ts @@ -19,6 +19,8 @@ class ArduinoContext { this._arduinoApp = value; } + // TODO EW: This is redundant: the board manager is already part of + // the arduino app public get boardManager() { return this._boardManager; } @@ -38,6 +40,10 @@ class ArduinoContext { return this._debuggerManager; } + // TODO EW: You don't have to initialize members to null + // if they don't get a default value or aren't initialized + // within a constructor they are "undefined" by default. + // This makes comparing against null (above) superfluous. private _arduinoApp: ArduinoApp = null; private _debuggerManager: DebuggerManager = null; private _boardManager: BoardManager = null; diff --git a/src/common/util.ts b/src/common/util.ts index d1b51389..b37bea47 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -277,7 +277,7 @@ export function tryParseJSON(jsonString: string) { } } catch (ex) { } - return false; + return undefined; } export function isJunk(filename: string): boolean { diff --git a/src/deviceContext.ts b/src/deviceContext.ts index 3c0daf6e..dd687c45 100644 --- a/src/deviceContext.ts +++ b/src/deviceContext.ts @@ -6,10 +6,10 @@ import * as path from "path"; import * as vscode from "vscode"; import * as constants from "./common/constants"; import * as util from "./common/util"; -import * as Logger from "./logger/logger"; import { ARDUINO_CONFIG_FILE } from "./common/constants"; import { ArduinoWorkspace } from "./common/workspace"; +import { DeviceSettings } from "./deviceSettings" /** * Interface that represents the arduino context information. @@ -60,9 +60,7 @@ export interface IDeviceContext { /** * IntelliSense configuration auto-generation project override. */ - disableIntelliSenseAutoGen: string; - - onDidChange: vscode.Event; + intelliSenseGen: string; initialize(): void; } @@ -75,22 +73,17 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { private static _deviceContext: DeviceContext = new DeviceContext(); - private _onDidChange = new vscode.EventEmitter(); - - private _port: string; - - private _board: string; - - private _sketch: string; - - private _output: string; - - private _debugger: string; - - private _disableIntelliSenseAutoGen: string; - - private _configuration: string; - + private _settings = new DeviceSettings(); + /** + * TODO EW, 2020-02-17: + * The absolute file path of the directory containing the vscode-arduino + * extension. Not sure why this is stored here (it's a bit misplaced) and + * not in a dedicated extension object containing the extension context + * passed during activation. Another way would be a function in util.ts + * using a mechanism like + * + * path.normalize(path.join(path.dirname(__filename), "..")) + */ private _extensionPath: string; private _watcher: vscode.FileSystemWatcher; @@ -99,10 +92,6 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { private _sketchStatusBar: vscode.StatusBarItem; - private _prebuild: string; - - private _programmer: string; - /** * @constructor */ @@ -143,39 +132,25 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { * TODO: Current we use the Arduino default settings. For future release, this dependency might be removed * and the setting only depends on device.json. * @method + * + * TODO EW, 2020-02-18: + * A problem I discovered: here you try to find the config file location + * and when you're writing below, you use a hard-coded location. When + * resorting to "find", you have to store the file's location at least and + * reuse it when saving. + * But I think the intention is: load a config file from anywhere and save + * it under .vscode/arduino.json. But then the initial load has to use find + * and afterwards it must not use find anymore. */ public loadContext(): Thenable { return vscode.workspace.findFiles(ARDUINO_CONFIG_FILE, null, 1) .then((files) => { - let deviceConfigJson: any = {}; if (files && files.length > 0) { - const configFile = files[0]; - deviceConfigJson = util.tryParseJSON(fs.readFileSync(configFile.fsPath, "utf8")); - if (deviceConfigJson) { - this._port = deviceConfigJson.port; - this._board = deviceConfigJson.board; - this._sketch = deviceConfigJson.sketch; - this._configuration = deviceConfigJson.configuration; - this._output = deviceConfigJson.output; - this._debugger = deviceConfigJson["debugger"]; - this._disableIntelliSenseAutoGen = deviceConfigJson.disableIntelliSenseAutoGen; - this._prebuild = deviceConfigJson.prebuild; - this._programmer = deviceConfigJson.programmer; - this._onDidChange.fire(); - } else { - Logger.notifyUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); - } + this._settings.load(files[0].fsPath); + // on invalid configuration we continue with current settings } else { - this._port = null; - this._board = null; - this._sketch = null; - this._configuration = null; - this._output = null; - this._debugger = null; - this._disableIntelliSenseAutoGen = null; - this._prebuild = null; - this._programmer = null; - this._onDidChange.fire(); + // No configuration file found, starting over with defaults + this._settings.reset(); } return this; }, (reason) => { @@ -185,148 +160,112 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { // Logger.notifyUserError("arduinoFileUnhandleError", new Error(reason.toString())); // Workaround for change in API, populate required props for arduino.json - this._port = null; - this._board = null; - this._sketch = null; - this._configuration = null; - this._output = null; - this._debugger = null; - this._disableIntelliSenseAutoGen = null; - this._prebuild = null; - this._programmer = null; - this._onDidChange.fire(); - + this._settings.reset(); return this; }); } public showStatusBar() { - if (!this._sketch) { + if (!this._settings.sketch.value) { return false; } - - this._sketchStatusBar.text = this._sketch; + this._sketchStatusBar.text = this._settings.sketch.value; this._sketchStatusBar.show(); } - public saveContext() { - if (!ArduinoWorkspace.rootPath) { - return; - } - const deviceConfigFile = path.join(ArduinoWorkspace.rootPath, ARDUINO_CONFIG_FILE); - let deviceConfigJson: any = {}; - if (util.fileExistsSync(deviceConfigFile)) { - deviceConfigJson = util.tryParseJSON(fs.readFileSync(deviceConfigFile, "utf8")); - } - if (!deviceConfigJson) { - Logger.notifyUserError("arduinoFileError", new Error(constants.messages.ARDUINO_FILE_ERROR)); - return; - } - deviceConfigJson.sketch = this.sketch; - deviceConfigJson.port = this.port; - deviceConfigJson.board = this.board; - deviceConfigJson.output = this.output; - deviceConfigJson["debugger"] = this.debugger_; - deviceConfigJson.disableIntelliSenseAutoGen = this.disableIntelliSenseAutoGen; - deviceConfigJson.configuration = this.configuration; - deviceConfigJson.programmer = this.programmer; - - util.mkdirRecursivelySync(path.dirname(deviceConfigFile)); - fs.writeFileSync(deviceConfigFile, JSON.stringify(deviceConfigJson, (key, value) => { - if (value === null) { - return undefined; - } - return value; - }, 4)); - } - - public get onDidChange(): vscode.Event { - return this._onDidChange.event; - } + public get onChangePort() { return this._settings.port.emitter.event } + public get onChangeBoard() { return this._settings.board.emitter.event } + public get onChangeSketch() { return this._settings.sketch.emitter.event } + public get onChangeOutput() { return this._settings.output.emitter.event } + public get onChangeDebugger() { return this._settings.debugger.emitter.event } + public get onChangeISAutoGen() { return this._settings.intelliSenseGen.emitter.event } + public get onChangeConfiguration() { return this._settings.configuration.emitter.event } + public get onChangePrebuild() { return this._settings.prebuild.emitter.event } + public get onChangeProgrammer() { return this._settings.programmer.emitter.event } public get port() { - return this._port; + return this._settings.port.value; } public set port(value: string) { - this._port = value; + this._settings.port.value = value; this.saveContext(); } public get board() { - return this._board; + return this._settings.board.value; } public set board(value: string) { - this._board = value; + this._settings.board.value = value; this.saveContext(); } public get sketch() { - return this._sketch; + return this._settings.sketch.value; } public set sketch(value: string) { - this._sketch = value; + this._settings.sketch.value = value; this.saveContext(); } public get prebuild() { - return this._prebuild ? this._prebuild.trim() : ""; + return this._settings.prebuild.value; } public get output() { - return this._output; + return this._settings.output.value; } public set output(value: string) { - this._output = value; + this._settings.output.value = value; this.saveContext(); } public get debugger_() { - return this._debugger; + return this._settings.debugger.value; } public set debugger_(value: string) { - this._debugger = value; + this._settings.debugger.value = value; this.saveContext(); } - public get disableIntelliSenseAutoGen() { - return this._disableIntelliSenseAutoGen; + public get intelliSenseGen() { + return this._settings.intelliSenseGen.value; } - public set disableIntelliSenseAutoGen(value: string) { - this._disableIntelliSenseAutoGen = value; + public set intelliSenseGen(value: string) { + this._settings.intelliSenseGen.value = value; this.saveContext(); } public get configuration() { - return this._configuration; + return this._settings.configuration.value; } public set configuration(value: string) { - this._configuration = value; + this._settings.configuration.value = value; this.saveContext(); } public get programmer() { - return this._programmer; + return this._settings.programmer.value; } public set programmer(value: string) { - this._programmer = value; + this._settings.programmer.value = value; this.saveContext(); } public async initialize() { if (ArduinoWorkspace.rootPath && util.fileExistsSync(path.join(ArduinoWorkspace.rootPath, ARDUINO_CONFIG_FILE))) { - vscode.window.showInformationMessage("Arduino.json is already generated."); + vscode.window.showInformationMessage("Arduino.json already generated."); return; } else { if (!ArduinoWorkspace.rootPath) { - vscode.window.showInformationMessage("Please open an folder first."); + vscode.window.showInformationMessage("Please open a folder first."); return; } await this.resolveMainSketch(); @@ -334,20 +273,41 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { await vscode.commands.executeCommand("arduino.changeBoardType"); vscode.window.showInformationMessage("The workspace is initialized with the Arduino extension support."); } else { - vscode.window.showInformationMessage("No *.ino sketch file was found or selected, so skip initialize command."); + vscode.window.showInformationMessage("No sketch (*.ino or *.cpp) was found or selected - initialization skipped."); } } } + /** + * Note: We're using the class' setter for the sketch (i.e. this.sketch = ...) + * to make sure that any changes are synched to the configuration file. + */ public async resolveMainSketch() { - return await vscode.workspace.findFiles("**/*.ino", null) + // TODO (EW, 2020-02-18): Here you look for *.ino files but below you allow + // *.cpp/*.c files to be set as sketch + await vscode.workspace.findFiles("**/*.ino", null) .then(async (fileUris) => { if (fileUris.length === 0) { let newSketchFileName = await vscode.window.showInputBox({ - value: "app.ino", - prompt: "No .ino file was found on workspace, initialize sketch first", - placeHolder: "Input the sketch file name", + value: "my-sketch.ino", + prompt: "No sketch (*.ino) found in workspace, please provide a name", + placeHolder: "Sketch file name (*.ino or *.cpp)", validateInput: (value) => { + /* TODO (EW, 2020-02-18): + * is 'c' actually allowed? Also found on within other files. + * And the regular expression doesn't need the internal groups. + * The outer group can be an anonymous group. + * And \w doesn't match dashes - so any sketch containing dashes + * will not be found. + * The correct expression therefore would be something like this: + * + * /^[\w\-]+\.(?:ino|cpp)$/ + * + * I'd recommend to define such regular expressions (including) + * line splitting etc.) at the global constants file. + * This is true for any hard coded paths (like the snippets below) + * as well. + */ if (value && /^\w+\.((ino)|(cpp)|c)$/.test(value.trim())) { return null; } else { @@ -364,7 +324,7 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { const textDocument = await vscode.workspace.openTextDocument(path.join(ArduinoWorkspace.rootPath, newSketchFileName)); vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One, true); } else { - this._sketch = undefined; + this.sketch = undefined; } } else if (fileUris.length === 1) { this.sketch = path.relative(ArduinoWorkspace.rootPath, fileUris[0].fsPath); @@ -380,5 +340,14 @@ export class DeviceContext implements IDeviceContext, vscode.Disposable { } } }); + return this.sketch; + } + + private saveContext() { + if (!ArduinoWorkspace.rootPath) { + return; + } + const deviceConfigFile = path.join(ArduinoWorkspace.rootPath, ARDUINO_CONFIG_FILE); + this._settings.save(deviceConfigFile); } } diff --git a/src/deviceSettings.ts b/src/deviceSettings.ts index 1ac678db..d930cdc2 100644 --- a/src/deviceSettings.ts +++ b/src/deviceSettings.ts @@ -1,3 +1,6 @@ +// Copyright (c) Elektronik Workshop. All rights reserved. +// Licensed under the MIT license. + import * as fs from "fs"; import * as path from "path"; import * as vscode from "vscode"; @@ -106,7 +109,7 @@ export class DeviceSettings { public sketch = new StrSetting(); public output = new StrSetting(); public debugger = new StrSetting(); - public disableIntelliSenseAutoGen = new StrSetting(); + public intelliSenseGen = new StrSetting(); public configuration = new StrSetting(); public prebuild = new StrSetting(); public programmer = new StrSetting(); @@ -121,7 +124,7 @@ export class DeviceSettings { this.sketch.modified || this.output.modified || this.debugger.modified || - this.disableIntelliSenseAutoGen.modified || + this.intelliSenseGen.modified || this.configuration.modified || this.prebuild.modified || this.programmer.modified; @@ -135,7 +138,7 @@ export class DeviceSettings { this.sketch.commit(); this.output.commit(); this.debugger.commit(); - this.disableIntelliSenseAutoGen.commit(); + this.intelliSenseGen.commit(); this.configuration.commit(); this.prebuild.commit(); this.programmer.commit(); @@ -151,7 +154,7 @@ export class DeviceSettings { this.sketch.reset(); this.output.reset(); this.debugger.reset(); - this.disableIntelliSenseAutoGen.reset(); + this.intelliSenseGen.reset(); this.configuration.reset(); this.prebuild.reset(); this.programmer.reset(); @@ -176,7 +179,7 @@ export class DeviceSettings { this.configuration.value = settings.configuration; this.output.value = settings.output; this.debugger.value = settings.debugger; - this.disableIntelliSenseAutoGen.value = settings.disableIntelliSenseAutoGen; + this.intelliSenseGen.value = settings.intelliSenseGen; this.prebuild.value = settings.prebuild; this.programmer.value = settings.programmer; if (commit) { @@ -220,7 +223,7 @@ export class DeviceSettings { settings.board = this.board.value; settings.output = this.output.value; settings.debugger = this.debugger.value; - settings.disableIntelliSenseAutoGen = this.disableIntelliSenseAutoGen.value; + settings.intelliSenseGen = this.intelliSenseGen.value; settings.configuration = this.configuration.value; settings.programmer = this.programmer.value; diff --git a/src/extension.ts b/src/extension.ts index 36d5a71e..9e7e3fe0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,8 +31,6 @@ import { BuildMode } from "./arduino/arduino"; import { SerialMonitor } from "./serialmonitor/serialMonitor"; const usbDetectorModule = impor("./serialmonitor/usbDetector") as typeof import ("./serialmonitor/usbDetector"); -const status: any = {}; - export async function activate(context: vscode.ExtensionContext) { Logger.configure(context); const activeGuid = uuidModule().replace(/-/g, ""); @@ -117,18 +115,13 @@ export async function activate(context: vscode.ExtensionContext) { registerArduinoCommand("arduino.initialize", async () => await deviceContext.initialize()); registerArduinoCommand("arduino.verify", async () => { - if (!status.compile) { - status.compile = "verify"; - try { - await vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: "Arduino: Verifying...", - }, async () => { - await arduinoContextModule.default.arduinoApp.build(BuildMode.Verify, true); - }); - } catch (ex) { - } - delete status.compile; + if (!arduinoContextModule.default.arduinoApp.building) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Verifying...", + }, async () => { + await arduinoContextModule.default.arduinoApp.build(BuildMode.Verify, true); + }); } }, () => { return { @@ -138,36 +131,26 @@ export async function activate(context: vscode.ExtensionContext) { }); registerArduinoCommand("arduino.upload", async () => { - if (!status.compile) { - status.compile = "upload"; - try { - await vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: "Arduino: Uploading...", - }, async () => { - await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, true); - }); - } catch (ex) { - } - delete status.compile; + if (!arduinoContextModule.default.arduinoApp.building) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Uploading...", + }, async () => { + await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, true); + }); } }, () => { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); registerArduinoCommand("arduino.cliUpload", async () => { - if (!status.compile) { - status.compile = "cliUpload"; - try { - await vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: "Arduino: Using CLI to upload...", - }, async () => { - await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, false); - }); - } catch (ex) { - } - delete status.compile; + if (!arduinoContextModule.default.arduinoApp.building) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Using CLI to upload...", + }, async () => { + await arduinoContextModule.default.arduinoApp.build(BuildMode.Upload, false); + }); } }, () => { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; @@ -195,57 +178,51 @@ export async function activate(context: vscode.ExtensionContext) { }); registerArduinoCommand("arduino.uploadUsingProgrammer", async () => { - if (!status.compile) { - status.compile = "upload"; - // TODO: no progress indicator - // and: exceptions should be handled within build - // function - try { + if (!arduinoContextModule.default.arduinoApp.building) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Uploading (programmer)...", + }, async () => { await arduinoContextModule.default.arduinoApp.build(BuildMode.UploadProgrammer, true); - } catch (ex) { - } - delete status.compile; + }); } }, () => { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); registerArduinoCommand("arduino.cliUploadUsingProgrammer", async () => { - if (!status.compile) { - status.compile = "cliUpload"; - try { + if (!arduinoContextModule.default.arduinoApp.building) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: "Arduino: Using CLI to upload (programmer)...", + }, async () => { await arduinoContextModule.default.arduinoApp.build(BuildMode.UploadProgrammer, false); - } catch (ex) { - } - delete status.compile; + }); } }, () => { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); registerArduinoCommand("arduino.rebuildIntelliSenseConfig", async () => { - if (!status.compile) { - status.compile = "intellisenserebuild"; + if (!arduinoContextModule.default.arduinoApp.building) { await vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: "Arduino: Rebuilding IS Configuration...", }, async () => { await arduinoContextModule.default.arduinoApp.build(BuildMode.Analyze, true); }); - delete status.compile; } }, () => { return { board: arduinoContextModule.default.boardManager.currentBoard.name }; }); registerArduinoCommand("arduino.selectProgrammer", async () => { - if (!status.compile) { - status.compile = "upload"; + // TODO EW: this guard does not prevent building when setting the programmer + if (!arduinoContextModule.default.arduinoApp.building) { try { await arduinoContextModule.default.arduinoApp.programmerManager.selectProgrammer(); } catch (ex) { } - delete status.compile; } }, () => { return { diff --git a/src/langService/completionProvider.ts b/src/langService/completionProvider.ts index 0a21bef4..1b5d9d31 100644 --- a/src/langService/completionProvider.ts +++ b/src/langService/completionProvider.ts @@ -28,8 +28,7 @@ import { ArduinoWorkspace } from "../common/workspace"; * the individual libraries are actually compatible with the current board * before offering a completion. * - * EW - * 2020-02-17 + * EW, 2020-02-17 */ export class CompletionProvider implements vscode.CompletionItemProvider { diff --git a/src/serialmonitor/serialMonitor.ts b/src/serialmonitor/serialMonitor.ts index 812801d5..28b58fa7 100644 --- a/src/serialmonitor/serialMonitor.ts +++ b/src/serialmonitor/serialMonitor.ts @@ -48,18 +48,6 @@ export class SerialMonitor implements vscode.Disposable { private _outputChannel: vscode.OutputChannel; - private constructor() { - const dc = DeviceContext.getInstance(); - dc.onDidChange(() => { - if (dc.port) { - if (!this.initialized) { - this.initialize(); - } - this.updatePortListStatus(null); - } - }); - } - public initialize() { let defaultBaudRate; if (ArduinoContext.arduinoApp && ArduinoContext.arduinoApp.settings && ArduinoContext.arduinoApp.settings.defaultBaudRate) { @@ -85,6 +73,11 @@ export class SerialMonitor implements vscode.Disposable { this._baudRateStatusBar.tooltip = "Baud Rate"; this._baudRateStatusBar.text = defaultBaudRate.toString(); this.updatePortListStatus(null); + + const dc = DeviceContext.getInstance(); + dc.onChangePort(() => { + this.updatePortListStatus(null); + }); } public get initialized(): boolean { return !!this._outputChannel; @@ -185,15 +178,15 @@ export class SerialMonitor implements vscode.Disposable { const rates = SerialMonitor.listBaudRates(); const chosen = await vscode.window.showQuickPick(rates.map((rate) => rate.toString())); if (!chosen) { - Logger.warn("No rate is selected, keep baud rate no changed."); + Logger.warn("No baud rate selected, keeping previous baud rate."); return; } if (!parseInt(chosen, 10)) { - Logger.warn("Invalid baud rate, keep baud rate no changed.", { value: chosen }); + Logger.warn("Invalid baud rate, keeping previous baud rate.", { value: chosen }); return; } if (!this._serialPortCtrl) { - Logger.warn("Serial Monitor have not been started."); + Logger.warn("Serial Monitor has not been started."); return; } const selectedRate: number = parseInt(chosen, 10); @@ -217,6 +210,7 @@ export class SerialMonitor implements vscode.Disposable { } } + // TODO EW: use default value for port function parameter and change all updatePortListStatus(null) calls accordingly private updatePortListStatus(port: string) { const dc = DeviceContext.getInstance(); if (port) { diff --git a/src/serialmonitor/usbDetector.ts b/src/serialmonitor/usbDetector.ts index 51dfe872..a5b71a96 100644 --- a/src/serialmonitor/usbDetector.ts +++ b/src/serialmonitor/usbDetector.ts @@ -76,6 +76,9 @@ export class UsbDetector { if (!SerialMonitor.getInstance().initialized) { SerialMonitor.getInstance().initialize(); } + + // TODO EW: this is board manager code which should be moved into board manager + let bd = ArduinoContext.boardManager.installedBoards.get(boardKey); const openEditor = vscode.window.activeTextEditor; if (ArduinoWorkspace.rootPath && ( From 5fd1a2706f1f1d6ba37fc9bbbc668f91aef2ddd7 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 19 Feb 2020 23:01:36 +0100 Subject: [PATCH 069/142] Updated log and beers. --- BRANCHNOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index bcd5a047..d2b4f807 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -69,7 +69,7 @@ During merging I found some bugs within those functions - mainly due to the abov **2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. **2020 02 18** Finished basic event triggering. Rewrote `DeviceContext` for proper settings modification detection (trigger events only on actual change) and generation of setting specific events (e.g. board changed) instead of one global event (aka. "something in the settings changed"). -**2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. Updated and completed the documentation for the IntelliSense usage within [README](README.md). Alpha test builds of the extension containing the latest implemented features and fixes are now available from the following [Dropbox folder](https://www.dropbox.com/sh/whmcdt26chyjgby/AAB1Ld2fzZ9Z_NfM3CRay17wa). Please note, that Windows is currently not supported yet. +**2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. Updated and completed the documentation for the IntelliSense usage within [README](README.md). Alpha test builds of the extension containing the latest implemented features and fixes are now available from the following [Dropbox folder](https://www.dropbox.com/sh/whmcdt26chyjgby/AAB1Ld2fzZ9Z_NfM3CRay17wa). Please note, that Windows is currently not supported yet. Reviewed, documented/commented all changes and committed the automatic analysis integration changes. ## Status | | Tasks | @@ -146,7 +146,7 @@ I will list every supporter here, thanks! 2020-02-15 Elektronik Workshop: 28 :beers: (7h coding) 2020-02-17 Elektronik Workshop: 52 :beers: (13h coding) 2020-02-18 Elektronik Workshop: 36 :beers: (9h coding) -2020-02-19 Elektronik Workshop: x :beers: (xh coding) +2020-02-19 Elektronik Workshop: 48 :beers: (12h coding) From 01e92e9d6158fad87e1d8d3c8e081e494188c5b3 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Wed, 19 Feb 2020 23:59:01 +0100 Subject: [PATCH 070/142] Added "some" additional issues -- currently I've found 28! --- BRANCHNOTES.md | 51 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index d2b4f807..9e3c3173 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -3,26 +3,45 @@ This branch more or less addresses the following issues: | # | Issue | Title | Comment | |--:|:------|:------------|:--------| -| 1| [#438](https://github.com/microsoft/vscode-arduino/issues/438) | **The Extension should automagically fill the c_cpp_properties.json, so intellisense works out of the box** | This is the issue to which I usually report news concerning the progress of this project | -| 2| [#876](https://github.com/microsoft/vscode-arduino/issues/876) | **Missing #define ARDUINO 10808 logic?** | Marked as bug but it's just the same problem again | -| 3| [#829](https://github.com/microsoft/vscode-arduino/issues/829) | **`Arduino.h` and ESP8266 includes have squiggles** | Stale issue | -| 4| [#969](https://github.com/microsoft/vscode-arduino/issues/969) | **INO Files defines undefined but can jump to definition** | | -| 5| [#959](https://github.com/microsoft/vscode-arduino/issues/959) | **Update board type command does not update intellisense config automatically** | | -| 6| [#892](https://github.com/microsoft/vscode-arduino/issues/892) | **Default IntelliSense config** | | -| 7| [#850](https://github.com/microsoft/vscode-arduino/issues/850) | **How to prevent modifications of c_cpp_properties.json by the extension?** | Asks if the current implementation can be turned off, because it overwrites a user's config with non working IS config -- this is sad. | -| 8| [#833](https://github.com/microsoft/vscode-arduino/issues/833) | **Allow C_Cpp.intelliSenseEngine to be set to "Default" instead of "Tag Parser" for better code completion/suggestions** | | -| 9| [#808](https://github.com/microsoft/vscode-arduino/issues/808) | **Identifier "Serial" is undefined** | | -| 10| [#474](https://github.com/microsoft/vscode-arduino/issues/474) | **Enrich device develop experience** | | - - - - - +| 1| [#438](https://github.com/microsoft/vscode-arduino/issues/438) | **The Extension should automagically fill the `c_cpp_properties.json`, so intellisense works out of the box** | This is the issue to which I usually report news concerning the progress of this project | +| 2| [#969](https://github.com/microsoft/vscode-arduino/issues/969) | **INO Files defines undefined but can jump to definition** | | +| 3| [#959](https://github.com/microsoft/vscode-arduino/issues/959) | **Update board type command does not update intellisense config automatically** | | +| 4| [#892](https://github.com/microsoft/vscode-arduino/issues/892) | **Default IntelliSense config** | | +| 5| [#876](https://github.com/microsoft/vscode-arduino/issues/876) | **Missing #define ARDUINO 10808 logic?** | Marked as bug but it's just the same problem again | +| 6| [#850](https://github.com/microsoft/vscode-arduino/issues/850) | **How to prevent modifications of `c_cpp_properties.json` by the extension?** | Asks if the current implementation can be turned off, because it overwrites a user's config with non working IS config -- this is sad. | +| 7| [#833](https://github.com/microsoft/vscode-arduino/issues/833) | **Allow C_Cpp.intelliSenseEngine to be set to "Default" instead of "Tag Parser" for better code completion/suggestions** | | +| 8| [#831](https://github.com/microsoft/vscode-arduino/issues/831) | **IntelliSenseEngine doesn't work as default** | | +| 9| [#829](https://github.com/microsoft/vscode-arduino/issues/829) | **`Arduino.h` and ESP8266 includes have squiggles** | Stale issue | +| 10| [#823](https://github.com/microsoft/vscode-arduino/issues/823) | **Intellisense is not highlighting code** | Stale issue | +| 11| [#818](https://github.com/microsoft/vscode-arduino/issues/818) | **Warning with default includePath after initialize, cannot open source file `avr/pgmspace.h`** | Stale issue | +| 12| [#808](https://github.com/microsoft/vscode-arduino/issues/808) | **Identifier `Serial` is undefined** | | +| 13| [#776](https://github.com/microsoft/vscode-arduino/issues/776) | **Can not open source file `omp.h` (dependency of `ESP8266WiFi.h`** | | +| 14| [#772](https://github.com/microsoft/vscode-arduino/issues/772) | **How to fix red squiggles under constants like D2 (upload works fine)** | | +| 15| [#761](https://github.com/microsoft/vscode-arduino/issues/761) | **When creating the `c_cpp_properties.json` it should include the libraries folder as well as all of the other folders.** | | +| 16| [#749](https://github.com/microsoft/vscode-arduino/issues/749) | **Non-fatal error on Adafruit Feather M0: cannot open source file `sam.h`** | Stale issue | +| 17| [#727](https://github.com/microsoft/vscode-arduino/issues/727) | **Intellisense for Arduino Tabs** | Stale issue | +| 18| [#684](https://github.com/microsoft/vscode-arduino/issues/684) | **Default C/C++ configuration uses MSVC for IntelliSense rather than GCC** | Stale issue | +| 19| [#678](https://github.com/microsoft/vscode-arduino/issues/678) | **Dependency error `avr32/io.h`** | Stale issue | +| 20| [#645](https://github.com/microsoft/vscode-arduino/issues/645) | **Should IntelliSense suggest only built in function?** | Stale issue | +| 21| [#613](https://github.com/microsoft/vscode-arduino/issues/613) | **Read content of `keywords.txt` for text highlights** | This will become obsolete as well since `keywords.txt` is a dirty workaround for the arduino IDE which doesn't have IntelliSense at all. | +| 22| [#563](https://github.com/microsoft/vscode-arduino/issues/563) | **Support of intellisence for library Wire(sam)** | | +| 23| [#525](https://github.com/microsoft/vscode-arduino/issues/525) | **`#include` errors detected - Tag Parser.** | Closed but still not fixed properly | +| 24| [#474](https://github.com/microsoft/vscode-arduino/issues/474) | **Enrich device develop experience** | | +| 25| [#127](https://github.com/microsoft/vscode-arduino/issues/127) | **Syntax highlighting for some of the Classes/Instances is missing** | Closed but still not fixed properly | +| 26| [#126](https://github.com/microsoft/vscode-arduino/issues/126) | **Syntax highlighting for some of the Macros is missing** | Closed but still not fixed properly | +| 27| [#125](https://github.com/microsoft/vscode-arduino/issues/125) | **Missing syntax highlighting for partial arduino constants** | Closed but still not fixed properly | +| 28| [#115](https://github.com/microsoft/vscode-arduino/issues/115) | **Dot prompting the methods doesn't work on Mac** | Closed but still not fixed properly | + + + + + -- the list is probably incomplete - I didn't search exhaustively and didn't consider closed issues as long as I didn't stumble upon one. New duplicates are popping up at a rate of about one per week. -Further related issues +Some issue searches and other related issues * [vscode-arduino issue search for IntelliSense](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=intellisense+is%3Aopen) +* [vscode-arduino issue search for intellisense label](https://github.com/microsoft/vscode-arduino/issues?utf8=%E2%9C%93&q=label%3Aintellisense) * [Wrongly attributed to vscode instead of vscode-arduino](https://github.com/Microsoft/vscode-cpptools/issues/1750) * [Problems with IntelliSense itself](https://github.com/microsoft/vscode-cpptools/issues/1034) From 98dbc0d35246cff5201b04f5ae2abb4e733f628f Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 21 Feb 2020 00:40:16 +0100 Subject: [PATCH 071/142] Updated log, status and beers --- BRANCHNOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BRANCHNOTES.md b/BRANCHNOTES.md index 9e3c3173..c8600a7b 100644 --- a/BRANCHNOTES.md +++ b/BRANCHNOTES.md @@ -89,6 +89,7 @@ During merging I found some bugs within those functions - mainly due to the abov **2020 02 17** Disabled and marked all previous implementations of IntelliSense support for later removal using `IS-REMOVE`. Pulled changes from upstream and merged them into the intellisense feature branch. Began to work on event handling/generation: vscode-arduino should detect when sketch/board/configuration and so on has changed, then re-analyze the current setup and set the IntelliSense configuration accordingly. This works more or less but there's a lot to fix in the current implementation which kept me busy till late today (I need some sleep now). Cleanup and commits follow tomorrow. Approaching alpha version for curious testers. OSX and Linux comes first, Windows will follow later. **2020 02 18** Finished basic event triggering. Rewrote `DeviceContext` for proper settings modification detection (trigger events only on actual change) and generation of setting specific events (e.g. board changed) instead of one global event (aka. "something in the settings changed"). **2020 02 19** Implemented proper build scheduling for analysis build by writing an `AnalysisManager` class. This class collects multiple changes (e.g. board and configuration, which often are changed shortly after another) before running an analysis. In case another build or analysis is in progress it postpones newly filed analysis requests until the other build has completed. Updated and completed the documentation for the IntelliSense usage within [README](README.md). Alpha test builds of the extension containing the latest implemented features and fixes are now available from the following [Dropbox folder](https://www.dropbox.com/sh/whmcdt26chyjgby/AAB1Ld2fzZ9Z_NfM3CRay17wa). Please note, that Windows is currently not supported yet. Reviewed, documented/commented all changes and committed the automatic analysis integration changes. +**2020 02 20** Windows support - what a PITA. This OS is so foobar'ed... The only positive outcome from this experience: I found some substantial bugs in the parser which - of course (Murphy) - didn't affect me up to now. The parser should be much more resistant against strange paths and escapes now: Added proper command line lexer to cocopa and worked around several ridiculous Windows shortcomings (Microsoft owes me at least 50 crates of beer). The whole mess is not cleaned up and committed yet so please don't build from the repository and use the alpha release packages as outlined above. ## Status | | Tasks | @@ -97,7 +98,7 @@ During merging I found some bugs within those functions - mainly due to the abov | | :heavy_check_mark: Support for different boards (done for AVR, ESP32, ESP8266) -- The code has been designed such that it is easy to write/add new parser engines (for non gcc compilers for instance) | | | :heavy_check_mark: Getting intrinsic gcc include paths | | | :heavy_check_mark: Handling quoted arguments | -| | :white_check_mark: X-platform support | +| | :heavy_check_mark: X-platform support | | **`c_cpp_properties.json` generator** | :heavy_check_mark: Basic objects | | | :heavy_check_mark: Basic setting of parsing result | | | :heavy_check_mark: Basic file input | @@ -166,6 +167,7 @@ I will list every supporter here, thanks! 2020-02-17 Elektronik Workshop: 52 :beers: (13h coding) 2020-02-18 Elektronik Workshop: 36 :beers: (9h coding) 2020-02-19 Elektronik Workshop: 48 :beers: (12h coding) +2020-02-20 Elektronik Workshop: 56 :beers: (14h coding) From 640f269725d46d172c6bf93b39ac3066a2ad7e60 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Fri, 21 Feb 2020 21:45:52 +0100 Subject: [PATCH 072/142] Removed obsolete doc folder as all this is now part of cocopa --- .../compilerinfo/avr-gcc_built_in_specs.txt | 33 ------------------- .../xtensa-esp32-elf-gcc_built_in_specs.txt | 32 ------------------ 2 files changed, 65 deletions(-) delete mode 100644 doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt delete mode 100644 doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt diff --git a/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt b/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt deleted file mode 100644 index 950f0c06..00000000 --- a/doc/intellisense/compilerinfo/avr-gcc_built_in_specs.txt +++ /dev/null @@ -1,33 +0,0 @@ -Using built-in specs. -Reading specs from /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/device-specs/specs-avr2 -COLLECT_GCC=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/avr-gcc -Target: avr -Configured with: ../gcc/configure --enable-fixed-point --enable-languages=c,c++ --prefix=/home/jenkins/workspace/avr-gcc-staging/label/debian7-x86_64/objdir --disable-nls --disable-libssp --disable-libada --disable-shared --with-avrlibc=yes --with-dwarf2 --disable-doc --target=avr -Thread model: single -gcc version 7.3.0 (GCC) -COLLECT_GCC_OPTIONS='-E' '-v' '-specs=device-specs/specs-avr2' - /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/avr/7.3.0/cc1plus -E -quiet -v -iprefix /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/ - -mn-flash=6 -mskip-bug -mn-flash=6 -mskip-bug -fno-rtti -fno-enforce-eh-specs -fno-exceptions -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/avr" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/backward" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/sys-include" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/avr" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include/c++/7.3.0/backward" -ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include" -ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/include-fixed" -ignoring nonexistent directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/sys-include" -ignoring duplicate directory "/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/../../lib/gcc/avr/7.3.0/../../../../avr/include" -#include "..." search starts here: -#include <...> search starts here: - /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include - /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/include-fixed - /home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/include -End of search list. -# 1 "" -# 1 "" -# 1 "" -# 1 "" -COMPILER_PATH=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/avr/7.3.0/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../libexec/gcc/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ -LIBRARY_PATH=/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/:/home/uli/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino5/bin/../lib/gcc/avr/7.3.0/../../../../avr/lib/ -COLLECT_GCC_OPTIONS='-E' '-v' '-specs=device-specs/specs-avr2' diff --git a/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt b/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt deleted file mode 100644 index 7e39fe2e..00000000 --- a/doc/intellisense/compilerinfo/xtensa-esp32-elf-gcc_built_in_specs.txt +++ /dev/null @@ -1,32 +0,0 @@ -Using built-in specs. -COLLECT_GCC=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc -Target: xtensa-esp32-elf -Configured with: /builds/idf/crosstool-NG/.build/src/gcc-5.2.0/configure --build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu --target=xtensa-esp32-elf --prefix=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf --with-local-prefix=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot --with-sysroot=/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot --with-newlib --enable-threads=no --disable-shared --with-pkgversion='crosstool-NG crosstool-ng-1.22.0-80-g6c4433a' --disable-__cxa_atexit --enable-cxx-flags='-fno-rtti -ffunction-sections' --with-gmp=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-mpfr=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-mpc=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-isl=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-cloog=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --with-libelf=/builds/idf/crosstool-NG/.build/xtensa-esp32-elf/buildtools --enable-lto --enable-target-optspace --without-long-double-128 --disable-libgomp --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libquadmath-support --disable-nls --enable-languages=c,c++ --disable-libstdcxx-verbose --enable-threads=posix --enable-gcov-custom-rtio -Thread model: posix -gcc version 5.2.0 (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) -COLLECT_GCC_OPTIONS='-E' '-v' - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/xtensa-esp32-elf/5.2.0/cc1plus -E -quiet -v -iprefix /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/ -isysroot /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot - -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0" -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf" -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/backward" -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/include" -ignoring nonexistent directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/builds/idf/crosstool-NG/builds/xtensa-esp32-elf/xtensa-esp32-elf/sysroot/include" -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed" -ignoring duplicate directory "/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/../../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include" -#include "..." search starts here: -#include <...> search starts here: - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0 - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/xtensa-esp32-elf - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include/c++/5.2.0/backward - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/include-fixed - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/include - /home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/usr/include -End of search list. -# 1 "" -# 1 "" -# 1 "" -# 1 "" -COMPILER_PATH=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/xtensa-esp32-elf/5.2.0/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../libexec/gcc/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ -LIBRARY_PATH=/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/lib/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/lib/:/home/uli/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/1.22.0-80-g6c4433a-5.2.0/bin/../xtensa-esp32-elf/sysroot/usr/lib/ -COLLECT_GCC_OPTIONS='-E' '-v' From 2d52e526b85b70c38bc60b3aea4c1fe460e9a036 Mon Sep 17 00:00:00 2001 From: Uli Franke Date: Sat, 22 Feb 2020 14:25:22 +0100 Subject: [PATCH 073/142] Fixed handling of invalid manual configurations. Improved code locality for board configuration manipulation and fixed a race condition with fine grained event handling. Details: Added missing checks when board configurations are loaded from the configuration file: * Up to now vscode-arduino blindly loaded any board configuration from `arduino.json` even if this would result in invalid board configurations which in turn lead to compilation (verify, upload) failure. * Up to now this state couldn't be recovered by simply removing the offending configuration from the configuration file. Even worse: it stored the wrong configuration in between board changes. To reproduce the bug in 0.2.29 1. Select Arduino Nano with the *Arduino Board Configuration* 2. Set configuration in `arduino.json` to `cpu=cray2` and save 3. Verify -> fails 4. Switch board to Arduino Uno 5. Switch back to Arduino Nano: The wrong configuration is back and now the user can't even select another (correct) configuration from the *Arduino Board Configuration* window 6. Delete the wrong configuration and save -> verify still fails `vscode-arduino` does not fall back to a default configuration. The user has now two options: find the correct configuration by himself and set it within arduino.json. Very experienced users could probably accomplish that. Everone else can just restart vscode. I corrected that by enhancing IBoard.loadConfig and IBoard.updateConfig member functions to * check for proper formatting of the config string loaded from `arduino.json` * check if the configuration IDs and the option IDs are valid If any of the above fails, the functions bail out and return the error. The board manager then loads a default configuration and issues a warning, that the configuration is invalid. This way the user gets the chance to fix her/his configuration but gets informed at the same time, that a different configuration than the intended is loaded (prevents surprises). This situation is only relevant, when users start fiddling with the configuration values in `arduino.json`. As long as they just set the board and the configurations from within the *Arduino Board Configuration Window* nothing bad can happen. But now custom configurations are handled in a cleaner way. The DeviceContext's board configuration was set in board.ts and boardManager.ts in different places - even when it was loaded after a DeviceContext's configuration update event which is prone to infinite loops. This has been resolved and it's not re-written/re-set during loading a configuration on change. This is valid for board manager's updateStatusBar function which fiddled with the board and the configuration. Now updateStatusBar really just updates the status bar. And it isn't necessary to call it from outside the board manager anymore due to proper event handling which identifies the situations during which the status bar has to be updated. Therefore this member is now private. In board manager itself operations that affect device context and current board now happen only within doChangeBoardType and the event handlers of DeviceContext callbacks onDeviceContextConfigurationChange and onDeviceContextBoardChange. This prevents the accidental creation of infinite event loops, makes the code more understandable, maintainable and therefore resilient against future bugs. --- src/arduino/arduinoContentProvider.ts | 3 + src/arduino/board.ts | 92 ++++++++++++++----- src/arduino/boardManager.ts | 125 ++++++++++++++++++++------ src/arduino/package.ts | 55 +++++++++++- src/extension.ts | 2 - 5 files changed, 223 insertions(+), 54 deletions(-) diff --git a/src/arduino/arduinoContentProvider.ts b/src/arduino/arduinoContentProvider.ts index 4602404a..2ea28c91 100644 --- a/src/arduino/arduinoContentProvider.ts +++ b/src/arduino/arduinoContentProvider.ts @@ -8,6 +8,7 @@ import ArduinoActivator from "../arduinoActivator"; import ArduinoContext from "../arduinoContext"; import * as Constants from "../common/constants"; import * as JSONHelper from "../common/cycle"; +import { DeviceContext } from "../deviceContext"; import * as Logger from "../logger/logger"; import LocalWebServer from "./localWebServer"; @@ -262,6 +263,8 @@ export class ArduinoContentProvider implements vscode.TextDocumentContentProvide } else { try { ArduinoContext.boardManager.currentBoard.updateConfig(req.body.configId, req.body.optionId); + const dc = DeviceContext.getInstance(); + dc.configuration = ArduinoContext.boardManager.currentBoard.customConfig; return res.json({ status: "OK", }); diff --git a/src/arduino/board.ts b/src/arduino/board.ts index 88c1b6c8..77c15df0 100644 --- a/src/arduino/board.ts +++ b/src/arduino/board.ts @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { DeviceContext } from "../deviceContext"; -import { IBoard, IBoardConfigItem, IPlatform } from "./package"; +import { BoardConfigResult, IBoard, IBoardConfigItem, IPlatform } from "./package"; export function parseBoardDescriptor(boardDescriptor: string, plat: IPlatform): Map { const boardLineRegex = /([^\.]+)\.(\S+)=(.+)/; @@ -52,15 +51,15 @@ export class Board implements IBoard { this._configItems = []; } - public get board(): string { + public get board() { return this._board; } - public get platform(): IPlatform { + public get platform() { return this._platform; } - public addParameter(key: string, value: string): void { + public addParameter(key: string, value: string) { const match = key.match(MENU_REGEX); if (match) { const existingItem = this._configItems.find((item) => item.id === match[1]); @@ -82,8 +81,7 @@ export class Board implements IBoard { } } } - - public getBuildConfig(): string { + public getBuildConfig() { return `${this.getPackageName()}:${this.platform.architecture}:${this.board}${this.customConfig ? ":" + this.customConfig : ""}`; } @@ -100,36 +98,86 @@ export class Board implements IBoard { } } - public get configItems(): IBoardConfigItem[] { + public get configItems() { return this._configItems; } - public loadConfig(configString: string): void { + public loadConfig(configString: string) { + // An empty or undefined config string resets the configuration + if (!configString) { + this.resetConfig(); + return BoardConfigResult.Success; + } const configSections = configString.split(","); const keyValueRegex = /(\S+)=(\S+)/; - configSections.forEach((configSection) => { - const match = configSection.match(keyValueRegex); - if (match && match.length >= 2) { - this.updateConfig(match[1], match[2]); + let result = BoardConfigResult.Success; + for (const section of configSections) { + const match = section.match(keyValueRegex); + if (!match) { + return BoardConfigResult.InvalidFormat; } - }); + const r = this.updateConfig(match[1], match[2]); + switch (r) { + case BoardConfigResult.SuccessNoChange: + result = r; + break; + case BoardConfigResult.Success: + break; + default: + return r; + } + }; + return result; } - public updateConfig(configId: string, optionId: string): boolean { + /** + * For documentation see the documentation on IBoard.updateConfig(). + */ + public updateConfig(configId: string, optionId: string) { const targetConfig = this._configItems.find((config) => config.id === configId); if (!targetConfig) { - return false; + return BoardConfigResult.InvalidConfigID; + } + // Iterate through all options and ... + for (const o of targetConfig.options) { + // Make sure that we only set valid options, e.g. when loading + // from config files. + if (o.id === optionId) { + if (targetConfig.selectedOption !== optionId) { + targetConfig.selectedOption = optionId; + return BoardConfigResult.Success; + } + return BoardConfigResult.SuccessNoChange; + } else { + return BoardConfigResult.InvalidOptionID; + } } - if (targetConfig.selectedOption !== optionId) { - targetConfig.selectedOption = optionId; - const dc = DeviceContext.getInstance(); - dc.configuration = this.customConfig; - return true; + return BoardConfigResult.InvalidOptionID; + } + + public resetConfig() { + for (const c of this._configItems) { + c.selectedOption = c.options[0].id; } - return false; } public getPackageName() { return this.platform.packageName ? this.platform.packageName : this.platform.package.name; } } + +/** + * Test if two boards are of the same type, i.e. have the same key. + * @param {IBoard | undefined} a A board. + * @param {IBoard | undefined} b And another board. + * @returns {boolean} true if two boards are of the same type, else false. + */ +export function boardEqual(a: IBoard | undefined, + b: IBoard | undefined) { + if (a && b) { + return a.key === b.key; + } else if (a || b) { + return false; + } + return true; +} diff --git a/src/arduino/boardManager.ts b/src/arduino/boardManager.ts index 79af7780..34a1ef30 100644 --- a/src/arduino/boardManager.ts +++ b/src/arduino/boardManager.ts @@ -12,8 +12,8 @@ import { arduinoChannel } from "../common/outputChannel"; import { DeviceContext } from "../deviceContext"; import { ArduinoApp } from "./arduino"; import { IArduinoSettings } from "./arduinoSettings"; -import { parseBoardDescriptor } from "./board"; -import { IBoard, IPackage, IPlatform } from "./package"; +import { boardEqual, parseBoardDescriptor } from "./board"; +import { BoardConfigResult, IBoard, IPackage, IPlatform } from "./package"; import { VscodeSettings } from "./vscodeSettings"; export class BoardManager { @@ -67,18 +67,17 @@ export class BoardManager { // Load default platforms from arduino installation directory and user manually installed platforms. this.loadInstalledPlatforms(); - // Load all supported boards type. + // Load all supported board types this.loadInstalledBoards(); - this.updateStatusBar(); - this._boardConfigStatusBar.show(); const dc = DeviceContext.getInstance(); - dc.onChangeBoard(() => { - this.updateStatusBar(); - }); - dc.onChangeConfiguration(() => { - this.updateStatusBar(); - }); + dc.onChangeBoard(() => this.onDeviceContextBoardChange()); + dc.onChangeConfiguration(() => this.onDeviceContextConfigurationChange()); + + // load initial board from DeviceContext by emulating + // a board change event. + this.onDeviceContextBoardChange(); + this.updateStatusBar(true); } public async changeBoardType() { @@ -122,14 +121,24 @@ export class BoardManager { public doChangeBoardType(targetBoard: IBoard) { const dc = DeviceContext.getInstance(); + + if (dc.board === targetBoard.key) { + return; + } + + // Resetting the board first that we don't overwrite the configuration + // of the previous board. + this._currentBoard = null; + // This will cause a configuration changed event which will have no + // effect because no current board is set. + dc.configuration = targetBoard.customConfig; + // This will generate a device context board event which will set the + // correct board and configuration. We know that it will trigger - we + // made sure above that the boards actually differ dc.board = targetBoard.key; - this._currentBoard = targetBoard; - dc.configuration = this._currentBoard.customConfig; - this._boardConfigStatusBar.text = targetBoard.name; + // IS-REMOVE: to be removed completely when IntelliSense implementation is merged // this._arduinoApp.addLibPath(null); - - this._onBoardTypeChanged.fire(); } public get packages(): IPackage[] { @@ -251,19 +260,12 @@ export class BoardManager { } } - public updateStatusBar(show: boolean = true): void { + private updateStatusBar(show: boolean = true): void { if (show) { this._boardConfigStatusBar.show(); - const dc = DeviceContext.getInstance(); - const selectedBoard = this._boards.get(dc.board); - if (selectedBoard) { - this._currentBoard = selectedBoard; - this._boardConfigStatusBar.text = selectedBoard.name; - if (dc.configuration) { - this._currentBoard.loadConfig(dc.configuration); - } + if (this._currentBoard) { + this._boardConfigStatusBar.text = this._currentBoard.name; } else { - this._currentBoard = null; this._boardConfigStatusBar.text = ""; - private _programmerStatusBar: vscode.StatusBarItem; + private _programmerValue: string; + private _programmerDisplayName: string; - // Static list of 'available' programmers. This should be repopulated by the currently selected board type. - private _availableProgrammers = { - avrisp: "AVR ISP", - avrispmkii: "AVRISP mkII", - usbtinyisp: "USBtinyISP", - arduinoisp: "ArduinoISP", - usbasp: "USBasp", - parallel: "Parallel Programmer", - arduinoasisp: "Arduino as ISP", - usbGemma: "Arduino Gemma", - buspirate: "BusPirate as ISP", - stk500: "Atmel STK500 development board", - jtag3isp: "Atmel JTAGICE3 (ISP mode)", - jtag3: "Atmel JTAGICE3 (JTAG mode)", - atmel_ice: "Atmel-ICE (AVR)", - }; + private _programmerStatusBar: vscode.StatusBarItem; constructor(private _settings: IArduinoSettings, private _arduinoApp: ArduinoApp) { this._programmerStatusBar = vscode.window.createStatusBarItem( @@ -41,7 +28,11 @@ export class ProgrammerManager { } public get currentProgrammer(): string { - return this._programmervalue; + return this._programmerValue; + } + + public get currentDisplayName(): string { + return this._programmerDisplayName; } /** @@ -50,10 +41,10 @@ export class ProgrammerManager { * List format: programmer_name:friendly_name */ public async selectProgrammer() { - const selectionItems = Object.keys(this._availableProgrammers).map( + const selectionItems = this.getAvailableProgrammers(this._arduinoApp.boardManager.currentBoard).map( (programmer) => ({ - label: this.getFriendlyName(programmer), - description: programmer, + label: programmer.displayName, + description: programmer.name, programmer })); const chosen = await vscode.window.showQuickPick(selectionItems, { placeHolder: "Select programmer", @@ -62,20 +53,37 @@ export class ProgrammerManager { return; } - this.setProgrammerValue(chosen.programmer); - const dc = DeviceContext.getInstance(); - dc.programmer = chosen.programmer; + this.setProgrammerValue(chosen.programmer.name); + DeviceContext.getInstance().programmer = this._programmerValue; + } + + private setProgrammerValue(programmerName: string | null) { + const programmer = this._arduinoApp.boardManager.installedProgrammers.get(programmerName); + this._programmerValue = this._settings.useArduinoCli ? programmerName : programmer ? programmer.key : programmerName; + this._programmerDisplayName = this._programmerValue + ? this.getDisplayName(programmerName) + : ProgrammerManager.notFoundDisplayValue; + this._programmerStatusBar.text = this._programmerDisplayName; } - private setProgrammerValue(programmer: string | null) { - this._programmervalue = programmer; - this._programmerStatusBar.text = this._programmervalue - ? this.getFriendlyName(this._programmervalue) - : "