From 7ef1b58d43ed3a65763249a3c2fbc3995b417bb2 Mon Sep 17 00:00:00 2001
From: Akos Kitta <a.kitta@arduino.cc>
Date: Mon, 9 Jan 2023 15:01:28 +0100
Subject: [PATCH] feat: let the editor handle any debug errors

arduino/arduino-ide#808

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
---
 src/extension.ts | 66 +++++++++++++++++++++++++-----------------------
 1 file changed, 34 insertions(+), 32 deletions(-)

diff --git a/src/extension.ts b/src/extension.ts
index 880ec0c..df4eb58 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,6 +1,6 @@
 import * as path from 'path';
 import { promises as fs } from 'fs';
-import { spawnSync } from 'child_process';
+import { spawn } from 'child_process';
 import deepEqual from 'deep-equal';
 import WebRequest from 'web-request';
 import deepmerge from 'deepmerge';
@@ -108,37 +108,45 @@ export function activate(context: ExtensionContext) {
     );
 }
 
+async function exec(command: string, args: string[]): Promise<{ stdout: string, stderr: string }> {
+    return new Promise<{ stdout: string, stderr: string }>((resolve, reject) => {
+        let out = '';
+        let err = '';
+        const cp = spawn(command, args);
+        cp.stdout.on('data', data => out += data.toString());
+        cp.stderr.on('data', data => err += data.toString());
+        cp.on('error', reject);
+        cp.on('close', (code, signal) => {
+            const stdout = out.trim();
+            const stderr = err.trim();
+            if (code) {
+                reject(new Error(stderr ?? `Exit code: ${code}`));
+            }
+            if (signal) {
+                reject(new Error(stderr ?? `Exit signal: ${signal}`));
+            }
+            if (err.trim()) {
+                reject(new Error(stderr));
+            }
+            resolve({ stdout, stderr });
+        });
+    });
+}
+
 async function startDebug(_: ExtensionContext, config: DebugConfig): Promise<boolean> {
     let info: DebugInfo | undefined = undefined;
-    let rawStdout: string | undefined = undefined;
-    let rawStdErr: string | undefined = undefined;
     try {
         const args = ['debug', '-I', '-b', config.board.fqbn, config.sketchPath, '--format', 'json'];
-        const { stdout, stderr } = spawnSync(config?.cliPath || '.', args, { encoding: 'utf8' });
-        rawStdout = stdout.trim();
-        rawStdErr = stderr.trim();
-    } catch (err) {
-        showError(err);
-        return false;
-    }
-    if (!rawStdout) {
-        if (rawStdErr) {
-            if (rawStdErr.toLowerCase().indexOf('compiled sketch not found in') !== -1) {
-                vscode.window.showErrorMessage(`Sketch '${path.basename(config.sketchPath)}' was not compiled. Please compile the sketch and start debugging again.`);
-            } else {
-                vscode.window.showErrorMessage(rawStdErr);
-            }
+        const { stdout, stderr } = await exec(config?.cliPath || '.', args);
+        if (!stdout && stderr) {
+            throw new Error(stderr);
+        }
+        info = JSON.parse(stdout);
+        if (!info) {
+            return false;
         }
-        return false;
-    }
-    try {
-        info = JSON.parse(rawStdout);
     } catch (err) {
-        console.error(`Could not parse JSON: <${rawStdout}>`);
-        showError(err);
-    }
-    if (!info) {
-        return false;
+        throw err;
     }
     const defaultDebugConfig = {
         cwd: '${workspaceRoot}',
@@ -270,12 +278,6 @@ async function buildLanguageClient(config: LanguageServerConfig): Promise<Langua
     );
 }
 
-function showError(err: unknown): void {
-    console.error(err);
-    const message = err instanceof Error ? err.message : typeof err === 'string' ? err : String(err);
-    vscode.window.showErrorMessage(message);
-}
-
 /**
  * Instead of writing the `launch.json` to the workspace, the file is written to the temporary binary output location.
  */