From 7663699970869e29771f2f836509b9c86f7d6d4e Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 17 Jul 2025 15:27:38 +0800 Subject: [PATCH 01/15] add nlp idf command run language tool --- .../en/additionalfeatures/language-tools.rst | 145 ++++++++++++++++++ .../additionalfeatures/language-tools.rst | 145 ++++++++++++++++++ package.json | 84 +++++++++- src/extension.ts | 5 + src/langTools/index.ts | 85 ++++++++++ yarn.lock | 8 +- 6 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 docs_espressif/en/additionalfeatures/language-tools.rst create mode 100644 docs_espressif/zh_CN/additionalfeatures/language-tools.rst create mode 100644 src/langTools/index.ts diff --git a/docs_espressif/en/additionalfeatures/language-tools.rst b/docs_espressif/en/additionalfeatures/language-tools.rst new file mode 100644 index 000000000..0d612a899 --- /dev/null +++ b/docs_espressif/en/additionalfeatures/language-tools.rst @@ -0,0 +1,145 @@ +Language Tools +============== + +This module provides a language model tool for the ESP-IDF extension, allowing users to execute common ESP-IDF operations through VS Code's chat interface using the Language Model Tools API. + +Overview +-------- + +The ESP-IDF Language Tool registers a single tool called ``espIdfCommands`` that can be invoked from VS Code's chat interface. This tool accepts a ``command`` parameter and executes the corresponding ESP-IDF operation, making it easier to perform common ESP-IDF development tasks through natural language interaction. + +Implementation +-------------- + +The tool is implemented using the VS Code Language Model Tools API (``vscode.lm.registerTool``) and is properly registered in ``package.json`` under the ``languageModelTools`` contribution point. + +Tool Registration +~~~~~~~~~~~~~~~~~ + +The tool is registered with: + +* **ID**: ``espIdfCommands`` +* **Display Name**: "ESP-IDF Commands" +* **Description**: "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, and more. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks." + +Tags and Natural Language Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The tool includes extensive tags that support natural language interaction. When users use the following phrases, the language model will prioritize this tool: + +**Command Tags**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace + +**Natural Language Patterns**: +- "build the project" +- "flash the device" +- "monitor the output" +- "clean the project" +- "configure the project" +- "analyze size" +- "erase flash" +- "select port" +- "set target" +- "run doctor" +- "create new project" +- "edit partition table" +- "manage components" +- "start app trace" +- "start heap trace" + +Input Schema +~~~~~~~~~~~ + +The tool accepts a JSON object with the following schema: + +.. code-block:: json + + { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "The ESP-IDF command to execute", + "enum": [ + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "menuconfig", + "size", + "eraseFlash", + "selectPort", + "setTarget", + "doctor", + "newProject", + "partitionTable", + "componentManager", + "apptrace", + "heaptrace" + ] + } + }, + "required": ["command"] + } + +Available Commands +----------------- + +The tool supports the following ESP-IDF commands: + +Build and Flash Commands +~~~~~~~~~~~~~~~~~~~~~~~ + +* **``build``** - Build the ESP-IDF project (``espIdf.buildDevice``) +* **``flash``** - Flash the built application to the device (``espIdf.flashDevice``) +* **``monitor``** - Monitor the device output (``espIdf.monitorDevice``) +* **``buildFlashMonitor``** - Build, flash, and monitor the project in one command (``espIdf.buildFlashMonitor``) + +Project Management Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* **``fullClean``** - Perform a full clean of the project (``espIdf.fullClean``) +* **``menuconfig``** - Open the ESP-IDF menuconfig interface (``espIdf.menuconfig.start``) +* **``size``** - Analyze the application size (``espIdf.size``) +* **``eraseFlash``** - Erase the device flash memory (``espIdf.eraseFlash``) + +Configuration Commands +~~~~~~~~~~~~~~~~~~~~~ + +* **``selectPort``** - Select the serial port for communication (``espIdf.selectPort``) +* **``setTarget``** - Set the ESP32 target device (``espIdf.setTarget``) +* **``doctor``** - Run the ESP-IDF doctor command to diagnose issues (``espIdf.doctorCommand``) + +Development Commands +~~~~~~~~~~~~~~~~~~~ + +* **``newProject``** - Create a new ESP-IDF project (``espIdf.newProject.start``) +* **``partitionTable``** - Open the partition table editor (``esp.webview.open.partition-table``) +* **``componentManager``** - Open the ESP component manager (``esp.component-manager.ui.show``) +* **``apptrace``** - Start application tracing (``espIdf.apptrace``) +* **``heaptrace``** - Start heap tracing (``espIdf.heaptrace``) + +Usage +----- + +Users can invoke the tool through VS Code's chat interface by referencing it with the ``#espIdfCommands`` syntax and providing the desired command: + +.. code-block:: text + + #espIdfCommands {"command": "build"} + +The tool will execute the specified ESP-IDF command and return a confirmation message. + +Integration +---------- + +The language tool is automatically activated when the extension starts and is properly disposed when the extension deactivates. It uses the ``onLanguageModelTool:espIdfCommands`` activation event to ensure it's available when needed. + +Error Handling +------------- + +The tool includes proper error handling: + +* Validates that the provided command exists in the supported command list +* Returns descriptive error messages for unknown commands +* Provides confirmation messages for successful command execution diff --git a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst new file mode 100644 index 000000000..b8a4b9c2a --- /dev/null +++ b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst @@ -0,0 +1,145 @@ +语言工具 +======== + +本模块为 ESP-IDF 扩展提供了语言模型工具,允许用户通过 VS Code 的聊天界面使用语言模型工具 API 执行常见的 ESP-IDF 操作。 + +概述 +---- + +ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以通过 VS Code 的聊天界面调用。该工具接受一个 ``command`` 参数并执行相应的 ESP-IDF 操作,使通过自然语言交互执行常见 ESP-IDF 开发任务变得更加容易。 + +实现 +---- + +该工具使用 VS Code 语言模型工具 API (``vscode.lm.registerTool``) 实现,并在 ``package.json`` 中的 ``languageModelTools`` 贡献点下正确注册。 + +工具注册 +~~~~~~~~ + +该工具注册时包含以下信息: + +* **ID**: ``espIdfCommands`` +* **显示名称**: "ESP-IDF 命令" +* **描述**: "执行 ESP-IDF 扩展命令,用于构建、烧录、监控和管理 ESP32 项目。始终使用此工具进行 ESP-IDF 开发任务,而不是 shell 命令或终端任务。当用户询问'构建项目'、'烧录设备'、'监控输出'、'清理项目'、'配置项目'、'分析大小'、'创建新项目'或任何 ESP-IDF 相关任务时,请使用此工具。支持:构建、烧录、监控、menuconfig、大小分析、项目创建、组件管理等。这是在 VS Code 中与 ESP-IDF 项目交互的唯一方式 - 不要使用 shell 命令进行 ESP-IDF 任务。" + +标签和自然语言支持 +~~~~~~~~~~~~~~~~~~~~ + +该工具包含广泛的标签,支持自然语言交互。当用户使用以下短语时,语言模型将优先选择此工具: + +**命令标签**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace + +**自然语言模式**: +- "构建项目" (build the project) +- "烧录设备" (flash the device) +- "监控输出" (monitor the output) +- "清理项目" (clean the project) +- "配置项目" (configure the project) +- "分析大小" (analyze size) +- "擦除闪存" (erase flash) +- "选择端口" (select port) +- "设置目标" (set target) +- "运行诊断" (run doctor) +- "创建新项目" (create new project) +- "编辑分区表" (edit partition table) +- "管理组件" (manage components) +- "启动应用跟踪" (start app trace) +- "启动堆跟踪" (start heap trace) + +输入模式 +~~~~~~~~ + +该工具接受具有以下模式的 JSON 对象: + +.. code-block:: json + + { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "要执行的 ESP-IDF 命令", + "enum": [ + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "menuconfig", + "size", + "eraseFlash", + "selectPort", + "setTarget", + "doctor", + "newProject", + "partitionTable", + "componentManager", + "apptrace", + "heaptrace" + ] + } + }, + "required": ["command"] + } + +可用命令 +-------- + +该工具支持以下 ESP-IDF 命令: + +构建和烧录命令 +~~~~~~~~~~~~~~ + +* **``build``** - 构建 ESP-IDF 项目 (``espIdf.buildDevice``) +* **``flash``** - 将构建的应用程序烧录到设备 (``espIdf.flashDevice``) +* **``monitor``** - 监控设备输出 (``espIdf.monitorDevice``) +* **``buildFlashMonitor``** - 在一个命令中构建、烧录和监控项目 (``espIdf.buildFlashMonitor``) + +项目管理命令 +~~~~~~~~~~ + +* **``fullClean``** - 执行项目的完全清理 (``espIdf.fullClean``) +* **``menuconfig``** - 打开 ESP-IDF menuconfig 界面 (``espIdf.menuconfig.start``) +* **``size``** - 分析应用程序大小 (``espIdf.size``) +* **``eraseFlash``** - 擦除设备闪存 (``espIdf.eraseFlash``) + +配置命令 +~~~~~~~~ + +* **``selectPort``** - 选择用于通信的串口 (``espIdf.selectPort``) +* **``setTarget``** - 设置 ESP32 目标设备 (``espIdf.setTarget``) +* **``doctor``** - 运行 ESP-IDF doctor 命令诊断问题 (``espIdf.doctorCommand``) + +开发命令 +~~~~~~~~ + +* **``newProject``** - 创建新的 ESP-IDF 项目 (``espIdf.newProject.start``) +* **``partitionTable``** - 打开分区表编辑器 (``esp.webview.open.partition-table``) +* **``componentManager``** - 打开 ESP 组件管理器 (``esp.component-manager.ui.show``) +* **``apptrace``** - 启动应用程序跟踪 (``espIdf.apptrace``) +* **``heaptrace``** - 启动堆跟踪 (``espIdf.heaptrace``) + +使用方法 +-------- + +用户可以通过 VS Code 的聊天界面使用 ``#espIdfCommands`` 语法调用该工具,并提供所需的命令: + +.. code-block:: text + + #espIdfCommands {"command": "build"} + +该工具将执行指定的 ESP-IDF 命令并返回确认消息。 + +集成 +---- + +语言工具在扩展启动时自动激活,在扩展停用时正确释放。它使用 ``onLanguageModelTool:espIdfCommands`` 激活事件确保在需要时可用。 + +错误处理 +-------- + +该工具包含适当的错误处理: + +* 验证提供的命令是否存在于支持的命令列表中 +* 为未知命令返回描述性错误消息 +* 为成功的命令执行提供确认消息 \ No newline at end of file diff --git a/package.json b/package.json index 7e57822eb..e8bf826ce 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.82.0" + "vscode": "^1.102.0" }, "repository": { "type": "git", @@ -109,6 +109,7 @@ "onView:idfPartitionExplorer", "onView:espRainmaker", "onView:idfComponents", + "onLanguageModelTool:espIdfCommands", "workspaceContains:**/CMakeLists.txt" ], "main": "./dist/extension", @@ -2505,6 +2506,85 @@ "type" ] } + ], + "languageModelTools": [ + { + "name": "espIdfCommands", + "displayName": "ESP-IDF Commands", + "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, and more. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", + "canBeReferencedInPrompt": true, + "toolReferenceName": "espIdfCommands", + "tags": [ + "esp-idf", + "idf", + "idf.py", + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "menuconfig", + "size", + "eraseFlash", + "selectPort", + "setTarget", + "doctor", + "newProject", + "partitionTable", + "componentManager", + "apptrace", + "heaptrace", + "project", + "development", + "build the project", + "flash the device", + "monitor the output", + "clean the project", + "configure the project", + "analyze size", + "erase flash", + "select port", + "set target", + "run doctor", + "create new project", + "edit partition table", + "manage components", + "start app trace", + "start heap trace" + ], + "icon": "$(run-view-icon)", + "inputSchema": { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "The ESP-IDF command to execute. Use this for ESP-IDF project operations like building, flashing, monitoring, and project management.", + "enum": [ + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "menuconfig", + "size", + "eraseFlash", + "selectPort", + "setTarget", + "doctor", + "newProject", + "partitionTable", + "componentManager", + "apptrace", + "heaptrace" + ], + "default": "build" + } + }, + "required": [ + "command" + ] + } + } ] }, "scripts": { @@ -2541,7 +2621,7 @@ "@types/sanitize-html": "^2.6.2", "@types/tar-fs": "^2.0.1", "@types/tmp": "0.0.33", - "@types/vscode": "^1.82.0", + "@types/vscode": "^1.102.0", "@types/ws": "^7.2.5", "@types/xml2js": "^0.4.11", "@types/yauzl": "^2.9.1", diff --git a/src/extension.ts b/src/extension.ts index 685196d89..d3ca47105 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -187,6 +187,7 @@ import { import { configureClangSettings } from "./clang"; import { OpenOCDErrorMonitor } from "./espIdf/hints/openocdhint"; import { updateHintsStatusBarItem } from "./statusBar"; +import { activateLanguageTool, deactivateLanguageTool } from "./langTools"; // Global variables shared by commands let workspaceRoot: vscode.Uri; @@ -375,6 +376,9 @@ export async function activate(context: vscode.ExtensionContext) { // Create Kconfig Language Server Client KconfigLangClient.startKconfigLangServer(context); + // Initialize ESP-IDF Language Tool for chat commands + activateLanguageTool(context); + openOCDManager = OpenOCDManager.init(); qemuManager = QemuManager.init(); const commandDictionary = createCommandDictionary(); @@ -4662,6 +4666,7 @@ export function deactivate() { covRenderer.dispose(); } KconfigLangClient.stopKconfigLangServer(); + deactivateLanguageTool(); } class IdfDebugConfigurationProvider diff --git a/src/langTools/index.ts b/src/langTools/index.ts new file mode 100644 index 000000000..a1d39d652 --- /dev/null +++ b/src/langTools/index.ts @@ -0,0 +1,85 @@ +import * as vscode from "vscode"; + +// Map of command names to their corresponding VS Code command IDs +const COMMAND_MAP: Record = { + build: "espIdf.buildDevice", + flash: "espIdf.flashDevice", + monitor: "espIdf.monitorDevice", + buildFlashMonitor: "espIdf.buildFlashMonitor", + fullClean: "espIdf.fullClean", + menuconfig: "espIdf.menuconfig.start", + size: "espIdf.size", + eraseFlash: "espIdf.eraseFlash", + selectPort: "espIdf.selectPort", + setTarget: "espIdf.setTarget", + doctor: "espIdf.doctorCommand", + newProject: "espIdf.newProject.start", + partitionTable: "esp.webview.open.partition-table", + componentManager: "esp.component-manager.ui.show", + apptrace: "espIdf.apptrace", + heaptrace: "espIdf.heaptrace", +}; + +// Commands that require confirmation due to potential side effects +const CONFIRMATION_COMMANDS = new Set([ + "build", + "flash", + "menuconfig", + "buildFlashMonitor", + "eraseFlash", + "fullClean", + "setTarget", +]); + +let disposable: vscode.Disposable | undefined; + +export function activateLanguageTool(context: vscode.ExtensionContext) { + disposable = vscode.lm.registerTool("espIdfCommands", { + async invoke( + options: { input: { command: string } }, + token: vscode.CancellationToken + ) { + const commandName = options.input.command; + const commandId = COMMAND_MAP[commandName]; + + if (commandId) { + await vscode.commands.executeCommand(commandId); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `Command "${commandName}" executed successfully.` + ), + ]); + } else { + throw new Error(`Unknown ESP-IDF command: ${commandName}`); + } + }, + + async prepareInvocation( + options: { input: { command: string } }, + token: vscode.CancellationToken + ) { + const commandName = options.input.command; + + if (CONFIRMATION_COMMANDS.has(commandName)) { + return { + confirmationMessages: { + title: `Confirm ESP-IDF Command`, + message: `Are you sure you want to run the "${commandName}" command? This may affect your ESP-IDF project or device.`, + }, + }; + } + + return { + invocationMessage: `Executing ESP-IDF command: ${commandName}`, + }; + }, + }); + context.subscriptions.push(disposable); +} + +export function deactivateLanguageTool() { + if (disposable) { + disposable.dispose(); + disposable = undefined; + } +} diff --git a/yarn.lock b/yarn.lock index e0eb1496b..9be5ba460 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1268,10 +1268,10 @@ resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" integrity sha512-gVC1InwyVrO326wbBZw+AO3u2vRXz/iRWq9jYhpG4W8LXyIgDv3ZmcLQ5Q4Gs+gFMyqx+viFoFT+l3p61QFCmQ== -"@types/vscode@^1.82.0": - version "1.85.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.85.0.tgz#46beb07f0f626665b52d1e2294382b2bc63b602e" - integrity sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g== +"@types/vscode@^1.102.0": + version "1.102.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.102.0.tgz#186dd6d4755807754a18ca869384c93b821039f2" + integrity sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA== "@types/ws@*": version "8.18.1" From 71d25f39fe8f857dcb1e991343ab8a8085d2f7d3 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 17 Jul 2025 15:41:15 +0800 Subject: [PATCH 02/15] fix docs issues --- docs_espressif/en/additionalfeatures.rst | 1 + .../en/additionalfeatures/language-tools.rst | 18 +++++++++--------- docs_espressif/zh_CN/additionalfeatures.rst | 1 + .../additionalfeatures/language-tools.rst | 12 ++++++------ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs_espressif/en/additionalfeatures.rst b/docs_espressif/en/additionalfeatures.rst index 66eaea896..2543cadc4 100644 --- a/docs_espressif/en/additionalfeatures.rst +++ b/docs_espressif/en/additionalfeatures.rst @@ -21,6 +21,7 @@ Additional IDE Features Heap Tracing Hints Viewer Install ESP-IDF Components + Language Tools NVS Partition Table Editor Partition Table Editor Project Configuration Editor diff --git a/docs_espressif/en/additionalfeatures/language-tools.rst b/docs_espressif/en/additionalfeatures/language-tools.rst index 0d612a899..3a5477f22 100644 --- a/docs_espressif/en/additionalfeatures/language-tools.rst +++ b/docs_espressif/en/additionalfeatures/language-tools.rst @@ -23,7 +23,7 @@ The tool is registered with: * **Description**: "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, and more. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks." Tags and Natural Language Support -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The tool includes extensive tags that support natural language interaction. When users use the following phrases, the language model will prioritize this tool: @@ -47,7 +47,7 @@ The tool includes extensive tags that support natural language interaction. When - "start heap trace" Input Schema -~~~~~~~~~~~ +~~~~~~~~~~~~ The tool accepts a JSON object with the following schema: @@ -83,12 +83,12 @@ The tool accepts a JSON object with the following schema: } Available Commands ------------------ +------------------ The tool supports the following ESP-IDF commands: Build and Flash Commands -~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ * **``build``** - Build the ESP-IDF project (``espIdf.buildDevice``) * **``flash``** - Flash the built application to the device (``espIdf.flashDevice``) @@ -96,7 +96,7 @@ Build and Flash Commands * **``buildFlashMonitor``** - Build, flash, and monitor the project in one command (``espIdf.buildFlashMonitor``) Project Management Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ * **``fullClean``** - Perform a full clean of the project (``espIdf.fullClean``) * **``menuconfig``** - Open the ESP-IDF menuconfig interface (``espIdf.menuconfig.start``) @@ -104,14 +104,14 @@ Project Management Commands * **``eraseFlash``** - Erase the device flash memory (``espIdf.eraseFlash``) Configuration Commands -~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~ * **``selectPort``** - Select the serial port for communication (``espIdf.selectPort``) * **``setTarget``** - Set the ESP32 target device (``espIdf.setTarget``) * **``doctor``** - Run the ESP-IDF doctor command to diagnose issues (``espIdf.doctorCommand``) Development Commands -~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~ * **``newProject``** - Create a new ESP-IDF project (``espIdf.newProject.start``) * **``partitionTable``** - Open the partition table editor (``esp.webview.open.partition-table``) @@ -131,12 +131,12 @@ Users can invoke the tool through VS Code's chat interface by referencing it wit The tool will execute the specified ESP-IDF command and return a confirmation message. Integration ----------- +----------- The language tool is automatically activated when the extension starts and is properly disposed when the extension deactivates. It uses the ``onLanguageModelTool:espIdfCommands`` activation event to ensure it's available when needed. Error Handling -------------- +-------------- The tool includes proper error handling: diff --git a/docs_espressif/zh_CN/additionalfeatures.rst b/docs_espressif/zh_CN/additionalfeatures.rst index c89f1a44d..73138466c 100644 --- a/docs_espressif/zh_CN/additionalfeatures.rst +++ b/docs_espressif/zh_CN/additionalfeatures.rst @@ -21,6 +21,7 @@ 堆跟踪 提示查看器 安装 ESP-IDF 组件 + 语言工具 NVS 分区表编辑器 分区表编辑器 项目配置编辑器 diff --git a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst index b8a4b9c2a..d52ba1332 100644 --- a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst +++ b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst @@ -23,7 +23,7 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 * **描述**: "执行 ESP-IDF 扩展命令,用于构建、烧录、监控和管理 ESP32 项目。始终使用此工具进行 ESP-IDF 开发任务,而不是 shell 命令或终端任务。当用户询问'构建项目'、'烧录设备'、'监控输出'、'清理项目'、'配置项目'、'分析大小'、'创建新项目'或任何 ESP-IDF 相关任务时,请使用此工具。支持:构建、烧录、监控、menuconfig、大小分析、项目创建、组件管理等。这是在 VS Code 中与 ESP-IDF 项目交互的唯一方式 - 不要使用 shell 命令进行 ESP-IDF 任务。" 标签和自然语言支持 -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~ 该工具包含广泛的标签,支持自然语言交互。当用户使用以下短语时,语言模型将优先选择此工具: @@ -47,7 +47,7 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 - "启动堆跟踪" (start heap trace) 输入模式 -~~~~~~~~ +~~~~~~~~~ 该工具接受具有以下模式的 JSON 对象: @@ -88,7 +88,7 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 该工具支持以下 ESP-IDF 命令: 构建和烧录命令 -~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~ * **``build``** - 构建 ESP-IDF 项目 (``espIdf.buildDevice``) * **``flash``** - 将构建的应用程序烧录到设备 (``espIdf.flashDevice``) @@ -96,7 +96,7 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 * **``buildFlashMonitor``** - 在一个命令中构建、烧录和监控项目 (``espIdf.buildFlashMonitor``) 项目管理命令 -~~~~~~~~~~ +~~~~~~~~~~~~~ * **``fullClean``** - 执行项目的完全清理 (``espIdf.fullClean``) * **``menuconfig``** - 打开 ESP-IDF menuconfig 界面 (``espIdf.menuconfig.start``) @@ -104,14 +104,14 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 * **``eraseFlash``** - 擦除设备闪存 (``espIdf.eraseFlash``) 配置命令 -~~~~~~~~ +~~~~~~~~~ * **``selectPort``** - 选择用于通信的串口 (``espIdf.selectPort``) * **``setTarget``** - 设置 ESP32 目标设备 (``espIdf.setTarget``) * **``doctor``** - 运行 ESP-IDF doctor 命令诊断问题 (``espIdf.doctorCommand``) 开发命令 -~~~~~~~~ +~~~~~~~~~ * **``newProject``** - 创建新的 ESP-IDF 项目 (``espIdf.newProject.start``) * **``partitionTable``** - 打开分区表编辑器 (``esp.webview.open.partition-table``) From beb76e8e10909c24bfda2eb76bf441cd41b92043 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 17 Jul 2025 16:59:43 +0800 Subject: [PATCH 03/15] update redhat extension tester to last version --- package.json | 2 +- yarn.lock | 212 ++++++++------------------------------------------- 2 files changed, 31 insertions(+), 183 deletions(-) diff --git a/package.json b/package.json index e8bf826ce..23b61eb94 100644 --- a/package.json +++ b/package.json @@ -2658,7 +2658,7 @@ "ts-loader": "^9.4.4", "typescript": "^5.2.2", "vite": "^5.4.20", - "vscode-extension-tester": "^8.15.0", + "vscode-extension-tester": "^8.17.0", "vue-hot-reload-api": "^2.3.2", "vue-loader": "^17.2.2", "vue-router": "^4.2.4", diff --git a/yarn.lock b/yarn.lock index 9be5ba460..b8fac4acd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -592,15 +592,15 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@redhat-developer/locators@^1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@redhat-developer/locators/-/locators-1.13.0.tgz#3d4c0707f542828d45c1121b617f66d69581a250" - integrity sha512-+v+FtxaPnCDguiwiU+UpWiNFaEyBXpRQMbf35SlJVAT5Yszb6qnDOFf9iTwtZNeeny4Cv+PVlYmoe/u9bRqByw== +"@redhat-developer/locators@^1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@redhat-developer/locators/-/locators-1.15.0.tgz#dda2530a0807ef4dee65ecf352bacca466f39bcf" + integrity sha512-xxkCoCQsqiA7IVR5kdNfU3MnuT9QSYDeu0KXrwW59FMCcZpUftz6uiQkqgbB212ASlS3mxVhyYpqJan4zr+XtA== -"@redhat-developer/page-objects@^1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@redhat-developer/page-objects/-/page-objects-1.13.0.tgz#16347d54fd440104c75ac81613f7c8f3903dc183" - integrity sha512-k53UVbw/5cD1dh1uagwGX48t7vtx9mqysaKEnx9hjuAaxHJ7aKF5KfF25mi31fvn6xvEdEvpBPrV2yoRgyD6Gg== +"@redhat-developer/page-objects@^1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@redhat-developer/page-objects/-/page-objects-1.15.0.tgz#61cf001a6078828912e7dd8678bae56aab593592" + integrity sha512-Mfr7rVcFB+J16VzLbbqLF9Yo1W7G2bgjkVv0vPUrlkz1dgYdAjZpaQpjs6dVlRO5Uopt11loYu4SS+wnxPopMw== dependencies: clipboardy "^4.0.0" clone-deep "^4.0.1" @@ -725,13 +725,6 @@ dependencies: "@secretlint/types" "^10.1.1" -"@secretlint/config-creator@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/config-creator/-/config-creator-9.3.4.tgz#1d9928c127e48235a6cc7c367bc239e98c1c6e42" - integrity sha512-GRMYfHJ+rewwB26CC3USVObqSQ/mDLXzXcUMJw/wJisPr3HDZmdsYlcsNnaAcGN+EZmvqSDkgSibQm1hyZpzbg== - dependencies: - "@secretlint/types" "^9.3.4" - "@secretlint/config-loader@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/config-loader/-/config-loader-10.1.1.tgz#4889f630f2e37c41a871d6da5b0a719a6cef40ee" @@ -744,18 +737,6 @@ debug "^4.4.1" rc-config-loader "^4.1.3" -"@secretlint/config-loader@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/config-loader/-/config-loader-9.3.4.tgz#d6cbb90943451d21ca85174504b3fa0e718685fd" - integrity sha512-sy+yWDWh4cbAbpQYLiO39DjwNGEK1EUhTqNamLLBo163BdJP10FIWhqpe8mtGQBSBXRtxr8Hg/gc3Xe4meIoww== - dependencies: - "@secretlint/profiler" "^9.3.4" - "@secretlint/resolver" "^9.3.4" - "@secretlint/types" "^9.3.4" - ajv "^8.17.1" - debug "^4.4.1" - rc-config-loader "^4.1.3" - "@secretlint/core@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/core/-/core-10.1.1.tgz#d7443d9e0538a135ff62616a5f35526885ee6a2c" @@ -766,16 +747,6 @@ debug "^4.4.1" structured-source "^4.0.0" -"@secretlint/core@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/core/-/core-9.3.4.tgz#3dffa34dff35734ffa5348822cf0bfef00fd88d9" - integrity sha512-ErIVHI6CJd191qdNKuMkH3bZQo9mWJsrSg++bQx64o0WFuG5nPvkYrDK0p/lebf+iQuOnzvl5HrZU6GU9a6o+Q== - dependencies: - "@secretlint/profiler" "^9.3.4" - "@secretlint/types" "^9.3.4" - debug "^4.4.1" - structured-source "^4.0.0" - "@secretlint/formatter@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/formatter/-/formatter-10.1.1.tgz#e297bbb4c2714edfd62be78d814a0ca6ecb08e3a" @@ -793,23 +764,6 @@ table "^6.9.0" terminal-link "^2.1.1" -"@secretlint/formatter@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/formatter/-/formatter-9.3.4.tgz#384525440c4b72b86f97f25ca24506939a03fbfd" - integrity sha512-ARpoBOKz6WP3ocLITCFkR1/Lj636ugpBknylhlpc45r5aLdvmyvWAJqodlw5zmUCfgD6JXeAMf3Hi60aAiuqWQ== - dependencies: - "@secretlint/resolver" "^9.3.4" - "@secretlint/types" "^9.3.4" - "@textlint/linter-formatter" "^14.7.2" - "@textlint/module-interop" "^14.7.2" - "@textlint/types" "^14.7.2" - chalk "^4.1.2" - debug "^4.4.1" - pluralize "^8.0.0" - strip-ansi "^6.0.1" - table "^6.9.0" - terminal-link "^2.1.1" - "@secretlint/node@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/node/-/node-10.1.1.tgz#7a8b14f6ffaf1d13545a6fd83740de5ba61b9d7f" @@ -824,40 +778,16 @@ debug "^4.4.1" p-map "^7.0.3" -"@secretlint/node@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/node/-/node-9.3.4.tgz#b216422e68cdf0879dbdc87b992fa7cf925626ed" - integrity sha512-S0u8i+CnPmyAKtuccgot9L5cmw6DqJc0F+b3hhVIALd8kkeLt3RIXOOej15tU7N0V1ISph90Gz92V72ovsprgQ== - dependencies: - "@secretlint/config-loader" "^9.3.4" - "@secretlint/core" "^9.3.4" - "@secretlint/formatter" "^9.3.4" - "@secretlint/profiler" "^9.3.4" - "@secretlint/source-creator" "^9.3.4" - "@secretlint/types" "^9.3.4" - debug "^4.4.1" - p-map "^4.0.0" - "@secretlint/profiler@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/profiler/-/profiler-10.1.1.tgz#f9be2a10376f4dbe63d5424266612a6bb78515d3" integrity sha512-kReI+Wr7IQz0LbVwYByzlnPbx4BEF2oEWJBc4Oa45g24alCjHu+jD9h9mzkTJqYUgMnVYD3o7HfzeqxFrV+9XA== -"@secretlint/profiler@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/profiler/-/profiler-9.3.4.tgz#295751010ca1c24fcaa0e63accea6231de36923e" - integrity sha512-99WmaHd4dClNIm5BFsG++E6frNIZ3qVwg6s804Ql/M19pDmtZOoVCl4/UuzWpwNniBqLIgn9rHQZ/iGlIW3wyw== - "@secretlint/resolver@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/resolver/-/resolver-10.1.1.tgz#664fe6abf04bf999170149b1616a48e155962309" integrity sha512-GdQzxnBtdBRjBULvZ8ERkaRqDp0njVwXrzBCav1pb0XshVk76C1cjeDqtTqM4RJ1Awo/g5U5MIWYztYv67v5Gg== -"@secretlint/resolver@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/resolver/-/resolver-9.3.4.tgz#aeef07cd349f94d897d297a47861e9bd10752c01" - integrity sha512-L1lIrcjzqcspPzZttmOvMmOFDpJTYFyRBONg94TZBWrpv4x0w5G2SYR+K7EE1SbYQAiPxw1amoXT1YRP8cZF2A== - "@secretlint/secretlint-formatter-sarif@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.1.1.tgz#b303009f61cbf07f637a8a1b98d9c3a5d7294310" @@ -865,13 +795,6 @@ dependencies: node-sarif-builder "^2.0.3" -"@secretlint/secretlint-formatter-sarif@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-9.3.4.tgz#f2c0ccb1d69f84686136ba4a5f71a4c4a192e06f" - integrity sha512-IpAl5gzKwpTRqoivKOTJB89l6b7uvBwjSNKzJb3oIGD9Jg3vXcQunSntvLv5XGynYtdi1NhANfEpbhavlmMSyA== - dependencies: - node-sarif-builder "^2.0.3" - "@secretlint/secretlint-rule-no-dotenv@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.1.1.tgz#566ef941fc6f64bfc113ee7ca0500de5f6af9fd3" @@ -879,23 +802,11 @@ dependencies: "@secretlint/types" "^10.1.1" -"@secretlint/secretlint-rule-no-dotenv@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-9.3.4.tgz#7e9f96aa484d17b8352bb4a4bcfe94839b3a1c3f" - integrity sha512-lMSVwTrJiZ/zL9VIzpT7tMcb0ClI6u4cyJo2YKGSbuJErJG1zB4gQKtjIwCSt7px5JF6U+aFtpb9M8+s40WWCQ== - dependencies: - "@secretlint/types" "^9.3.4" - "@secretlint/secretlint-rule-preset-recommend@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.1.1.tgz#843cb3762c143444887e02e413afc00c5216fe02" integrity sha512-+GeISCXVgpnoeRZE4ZPsuO97+fm6z8Ge23LNq6LvR9ZJAq018maXVftkJhHj4hnvYB5URUAEerBBkPGNk5/Ong== -"@secretlint/secretlint-rule-preset-recommend@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-9.3.4.tgz#f266289036ee41c1e53868fa36016db42161de8a" - integrity sha512-RvzrLNN2A0B2bYQgRSRjh2dkdaIDuhXjj4SO5bElK1iBtJNiD6VBTxSSY1P3hXYaBeva7MEF+q1PZ3cCL8XYOA== - "@secretlint/source-creator@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/source-creator/-/source-creator-10.1.1.tgz#f00009fbddb1a91de021bfcfac34fb54b3aef801" @@ -904,24 +815,11 @@ "@secretlint/types" "^10.1.1" istextorbinary "^9.5.0" -"@secretlint/source-creator@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/source-creator/-/source-creator-9.3.4.tgz#11debc89e071a8fdbd0ae02d8fb64f3c608b3e73" - integrity sha512-I9ZA1gm9HJNaAhZiQdInY9VM04VTAGDV4bappVbEJzMUDnK/LTbYqfQ88RPqgCGCqa6ee8c0/j5Bn7ypweouIw== - dependencies: - "@secretlint/types" "^9.3.4" - istextorbinary "^9.5.0" - "@secretlint/types@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@secretlint/types/-/types-10.1.1.tgz#6f6176ba1444031f5787ef76e2ac7b767733de4b" integrity sha512-/JGAvVkurVHkargk3AC7UxRy+Ymc+52AVBO/fZA5pShuLW2dX4O/rKc4n8cyhQiOb/3ym5ACSlLQuQ8apPfxrQ== -"@secretlint/types@^9.3.4": - version "9.3.4" - resolved "https://registry.yarnpkg.com/@secretlint/types/-/types-9.3.4.tgz#8ca2bc4820c70a36146604b3b5ec77d49a0525fe" - integrity sha512-z9rdKHNeL4xa48+367RQJVw1d7/Js9HIQ+gTs/angzteM9osfgs59ad3iwVRhCGYbeUoUUDe2yxJG2ylYLaH3Q== - "@serialport/binding-mock@10.2.2": version "10.2.2" resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-10.2.2.tgz#d322a8116a97806addda13c62f50e73d16125874" @@ -1040,7 +938,7 @@ resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-14.8.4.tgz#2f2e1988bfce3613c14de114503cefa7c7cb5108" integrity sha512-+fI7miec/r9VeniFV9ppL4jRCmHNsTxieulTUf/4tvGII3db5hGriKHC4p/diq1SkQ9Sgs7kg6UyydxZtpTz1Q== -"@textlint/linter-formatter@^14.7.2", "@textlint/linter-formatter@^14.8.4": +"@textlint/linter-formatter@^14.8.4": version "14.8.4" resolved "https://registry.yarnpkg.com/@textlint/linter-formatter/-/linter-formatter-14.8.4.tgz#4573223860fac9fbfa19a1651ad9176eec9c6a23" integrity sha512-sZ0UfYRDBNHnfMVBqLqqYnqTB7Ec169ljlmo+SEHR1T+dHUPYy1/DZK4p7QREXlBSFL4cnkswETCbc9xRodm4Q== @@ -1060,7 +958,7 @@ table "^6.9.0" text-table "^0.2.0" -"@textlint/module-interop@14.8.4", "@textlint/module-interop@^14.7.2", "@textlint/module-interop@^14.8.4": +"@textlint/module-interop@14.8.4", "@textlint/module-interop@^14.8.4": version "14.8.4" resolved "https://registry.yarnpkg.com/@textlint/module-interop/-/module-interop-14.8.4.tgz#80b3f8c7b22e4dd964f68b11d09f59f44f05259a" integrity sha512-1LdPYLAVpa27NOt6EqvuFO99s4XLB0c19Hw9xKSG6xQ1K82nUEyuWhzTQKb3KJ5Qx7qj14JlXZLfnEuL6A16Bw== @@ -1070,7 +968,7 @@ resolved "https://registry.yarnpkg.com/@textlint/resolver/-/resolver-14.8.4.tgz#e824bbe83f898e90da8ce7fbd534cadeaaa2dde5" integrity sha512-nMDOgDAVwNU9ommh+Db0U+MCMNDPbQ/1HBNjbnHwxZkCpcT6hsAJwBe38CW/DtWVUv8yeR4R40IYNPT84srNwA== -"@textlint/types@14.8.4", "@textlint/types@^14.7.2", "@textlint/types@^14.8.4": +"@textlint/types@14.8.4", "@textlint/types@^14.8.4": version "14.8.4" resolved "https://registry.yarnpkg.com/@textlint/types/-/types-14.8.4.tgz#8d7dfb32cba33a0d5b213ea19aed0b6e9f61b21f" integrity sha512-9nyY8vVXlr8hHKxa6+37omJhXWCwovMQcgMteuldYd4dOxGm14AK2nXdkgtKEUQnzLGaXy46xwLCfhQy7V7/YA== @@ -1477,43 +1375,6 @@ optionalDependencies: keytar "^7.7.0" -"@vscode/vsce@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-3.5.0.tgz#49e80ddfd9d8c2cc08ab7584b69943ef8823dac9" - integrity sha512-2Eb6fBh8OzNhWqviCjeUPA1MW+d2GCb1QlVxrpOR8lrLHGk8x7HD4LbfELnZPyOz2X33Myz9FE9t4LwYbmeMRg== - dependencies: - "@azure/identity" "^4.1.0" - "@secretlint/node" "^9.3.4" - "@secretlint/secretlint-formatter-sarif" "^9.3.4" - "@secretlint/secretlint-rule-no-dotenv" "^9.3.4" - "@secretlint/secretlint-rule-preset-recommend" "^9.3.4" - "@vscode/vsce-sign" "^2.0.0" - azure-devops-node-api "^12.5.0" - chalk "^4.1.2" - cheerio "^1.0.0-rc.9" - cockatiel "^3.1.2" - commander "^12.1.0" - form-data "^4.0.0" - glob "^11.0.0" - hosted-git-info "^4.0.2" - jsonc-parser "^3.2.0" - leven "^3.1.0" - markdown-it "^14.1.0" - mime "^1.3.4" - minimatch "^3.0.3" - parse-semver "^1.1.1" - read "^1.0.7" - secretlint "^9.3.4" - semver "^7.5.2" - tmp "^0.2.3" - typed-rest-client "^1.8.4" - url-join "^4.0.1" - xml2js "^0.5.0" - yauzl "^2.3.1" - yazl "^2.2.2" - optionalDependencies: - keytar "^7.7.0" - "@vue/compiler-core@3.4.15": version "3.4.15" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.15.tgz#be20d1bbe19626052500b48969302cb6f396d36e" @@ -3474,7 +3335,7 @@ glob@^10.4.1: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^11.0.0, glob@^11.0.2: +glob@^11.0.0, glob@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== @@ -5666,28 +5527,15 @@ secretlint@^10.1.1: globby "^14.1.0" read-pkg "^8.1.0" -secretlint@^9.3.4: - version "9.3.4" - resolved "https://registry.yarnpkg.com/secretlint/-/secretlint-9.3.4.tgz#7dc677e7968ce4a2ba5d4c156365e2435fa4d4b5" - integrity sha512-iNOzgMX/+W1SQNW/TW6eikGChyaPiazr2AEXjzjpoB0R6QJEulvlwhn0KLT1/xjPfdYrk3yiXZM40csUqET8uQ== - dependencies: - "@secretlint/config-creator" "^9.3.4" - "@secretlint/formatter" "^9.3.4" - "@secretlint/node" "^9.3.4" - "@secretlint/profiler" "^9.3.4" - debug "^4.4.1" - globby "^14.1.0" - read-pkg "^8.1.0" - -selenium-webdriver@^4.33.0: - version "4.33.0" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.33.0.tgz#6587a9eab09bf30ad27cf27bc8d0b944457252da" - integrity sha512-5vRhk4iI0B9nYbEitfnCjPDXBfG6o9DNhj5DG2355eQo8idETknhj1tigqqlkHsGephSZwLZqEm/d+3e1stGUA== +selenium-webdriver@^4.34.0: + version "4.34.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.34.0.tgz#0081c3e51a2f1e197d56253c647f25085b22bd62" + integrity sha512-zGfQFcsASAv3KrYzYh+iw4fFqB7iZAgHW7BU6rRz7isK1i1X4x3LvjmZad4bUUgHDwTnAhlqTzDh21byB+zHMg== dependencies: "@bazel/runfiles" "^6.3.1" jszip "^3.10.1" tmp "^0.2.3" - ws "^8.18.0" + ws "^8.18.2" semver@^5.1.0, semver@^5.5.0: version "5.7.2" @@ -6480,26 +6328,26 @@ vite@^5.4.20: optionalDependencies: fsevents "~2.3.3" -vscode-extension-tester@^8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/vscode-extension-tester/-/vscode-extension-tester-8.15.0.tgz#3f1d69c2aa9e9b99b6a0c2e7aa3205d57618ef74" - integrity sha512-7eGada38DMsBWEzKicKy/3/sPGVZPQJM0RU4NW6KhTCHHhgMMTjANWZ4l1pT2oF3Pmets3efUI7RhNJTaVIeNA== +vscode-extension-tester@^8.17.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/vscode-extension-tester/-/vscode-extension-tester-8.17.0.tgz#d57aae77fe19326836955553ab556d284ddb62e0" + integrity sha512-B97Aas11bEcM0dcXCn/D+hg58Xwugf0nSu464gvfq5uXYUCUVWNPM7r971DwXxuWd+mcREcuW6/bz07Mod6zBA== dependencies: - "@redhat-developer/locators" "^1.13.0" - "@redhat-developer/page-objects" "^1.13.0" + "@redhat-developer/locators" "^1.15.0" + "@redhat-developer/page-objects" "^1.15.0" "@types/selenium-webdriver" "^4.1.28" - "@vscode/vsce" "^3.5.0" + "@vscode/vsce" "^3.6.0" c8 "^10.1.3" commander "^14.0.0" compare-versions "^6.1.1" find-up "7.0.0" fs-extra "^11.3.0" - glob "^11.0.2" + glob "^11.0.3" got "^14.4.7" hpagent "^1.2.0" js-yaml "^4.1.0" sanitize-filename "^1.6.3" - selenium-webdriver "^4.33.0" + selenium-webdriver "^4.34.0" targz "^1.0.1" unzipper "^0.12.3" @@ -6763,10 +6611,10 @@ ws@^7.5.10: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.18.0: - version "8.18.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" - integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== +ws@^8.18.2: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== xml2js@^0.5.0: version "0.5.0" From 65a7890222f4117c932e1f8a33ba2ee8863fc00f Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Wed, 23 Jul 2025 17:52:32 +0800 Subject: [PATCH 04/15] lower vscode to support extension tester ui tests --- package.json | 4 ++-- yarn.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 23b61eb94..50282bc8b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "theme": "dark" }, "engines": { - "vscode": "^1.102.0" + "vscode": "^1.100.3" }, "repository": { "type": "git", @@ -2621,7 +2621,7 @@ "@types/sanitize-html": "^2.6.2", "@types/tar-fs": "^2.0.1", "@types/tmp": "0.0.33", - "@types/vscode": "^1.102.0", + "@types/vscode": "^1.100.3", "@types/ws": "^7.2.5", "@types/xml2js": "^0.4.11", "@types/yauzl": "^2.9.1", diff --git a/yarn.lock b/yarn.lock index b8fac4acd..c97b9e6bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1166,7 +1166,7 @@ resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" integrity sha512-gVC1InwyVrO326wbBZw+AO3u2vRXz/iRWq9jYhpG4W8LXyIgDv3ZmcLQ5Q4Gs+gFMyqx+viFoFT+l3p61QFCmQ== -"@types/vscode@^1.102.0": +"@types/vscode@^1.100.3": version "1.102.0" resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.102.0.tgz#186dd6d4755807754a18ca869384c93b821039f2" integrity sha512-V9sFXmcXz03FtYTSUsYsu5K0Q9wH9w9V25slddcxrh5JgORD14LpnOA7ov0L9ALi+6HrTjskLJ/tY5zeRF3TFA== From dcd7649755bf1f1e37174728aa3b82b200f5f8fc Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 14 Aug 2025 15:45:22 +0800 Subject: [PATCH 05/15] update set target to pass idf target argument --- package.json | 110 ++++++++++++++++++++++++++--------------- src/extension.ts | 37 +++++++++++--- src/langTools/index.ts | 29 ++++++++--- 3 files changed, 125 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 50282bc8b..eb38041c2 100644 --- a/package.json +++ b/package.json @@ -2511,47 +2511,60 @@ { "name": "espIdfCommands", "displayName": "ESP-IDF Commands", - "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, and more. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", + "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', 'set target to esp32s3', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, target setting, and more. For setTarget command, you can specify a target parameter (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) to set it directly. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", "canBeReferencedInPrompt": true, "toolReferenceName": "espIdfCommands", - "tags": [ - "esp-idf", - "idf", - "idf.py", - "build", - "flash", - "monitor", - "buildFlashMonitor", - "fullClean", - "menuconfig", - "size", - "eraseFlash", - "selectPort", - "setTarget", - "doctor", - "newProject", - "partitionTable", - "componentManager", - "apptrace", - "heaptrace", - "project", - "development", - "build the project", - "flash the device", - "monitor the output", - "clean the project", - "configure the project", - "analyze size", - "erase flash", - "select port", - "set target", - "run doctor", - "create new project", - "edit partition table", - "manage components", - "start app trace", - "start heap trace" - ], + "tags": [ + "esp-idf", + "idf", + "idf.py", + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "menuconfig", + "size", + "eraseFlash", + "selectPort", + "setTarget", + "doctor", + "newProject", + "partitionTable", + "componentManager", + "apptrace", + "heaptrace", + "project", + "development", + "build the project", + "flash the device", + "monitor the output", + "clean the project", + "configure the project", + "analyze size", + "erase flash", + "select port", + "set target", + "set target to esp32", + "set target to esp32s2", + "set target to esp32s3", + "set target to esp32c3", + "set target to esp32c6", + "set target to esp32h2", + "set target to esp32p4", + "set target to esp32c2", + "set target to esp32c5", + "set target to esp32c61", + "set target to esp32h21", + "set target to esp32h4", + "set target to linux", + "run doctor", + "create new project", + "edit partition table", + "manage components", + "start app trace", + "start heap trace" + ], "icon": "$(run-view-icon)", "inputSchema": { "type": "object", @@ -2578,6 +2591,25 @@ "heaptrace" ], "default": "build" + }, + "target": { + "type": "string", + "description": "The ESP-IDF target to set when using the setTarget command. Common targets include: esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux. This parameter is only used when command is 'setTarget'.", + "enum": [ + "esp32", + "esp32s2", + "esp32c3", + "esp32s3", + "esp32c2", + "esp32c6", + "esp32h2", + "esp32p4", + "linux", + "esp32c5", + "esp32c61", + "esp32h21", + "esp32h4" + ] } }, "required": [ diff --git a/src/extension.ts b/src/extension.ts index d3ca47105..b4382225e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -130,6 +130,7 @@ import { WelcomePanel } from "./welcome/panel"; import { getWelcomePageInitialValues } from "./welcome/welcomeInit"; import { getEspMatter } from "./espMatter/espMatterDownload"; import { setIdfTarget } from "./espIdf/setTarget"; +import { setTargetInIDF } from "./espIdf/setTarget/setTargetInIdf"; import { PeripheralTreeView } from "./espIdf/debugAdapter/peripheralTreeView"; import { PeripheralBaseNode } from "./espIdf/debugAdapter/nodes/base"; import { ExtensionConfigStore } from "./common/store"; @@ -2192,16 +2193,40 @@ export async function activate(context: vscode.ExtensionContext) { } }); - registerIDFCommand("espIdf.setTarget", () => { + registerIDFCommand("espIdf.setTarget", (target?: string) => { PreCheck.perform([openFolderCheck], async () => { - const enterDeviceTargetMsg = vscode.l10n.t( - "Enter target name (IDF_TARGET)" - ); const workspaceFolder = vscode.workspace.getWorkspaceFolder( workspaceRoot ); - await setIdfTarget(enterDeviceTargetMsg, workspaceFolder); - await getIdfTargetFromSdkconfig(workspaceRoot, statusBarItems["target"]); + + if (target) { + // If a target is provided, set it directly + const targetsFromIdf = await getTargetsFromEspIdf(workspaceFolder.uri); + const selectedTarget = targetsFromIdf.find((t) => t.target === target); + + if (selectedTarget) { + await setTargetInIDF(workspaceFolder, selectedTarget); + await getIdfTargetFromSdkconfig( + workspaceRoot, + statusBarItems["target"] + ); + } else { + const listOfTargets = targetsFromIdf.map((t) => t.target).join(", "); + vscode.window.showErrorMessage( + `Invalid target: ${target}. Please use one of the supported targets: ${listOfTargets}.` + ); + } + } else { + // If no target is provided, show the selection dialog + const enterDeviceTargetMsg = vscode.l10n.t( + "Enter target name (IDF_TARGET)" + ); + await setIdfTarget(enterDeviceTargetMsg, workspaceFolder); + await getIdfTargetFromSdkconfig( + workspaceRoot, + statusBarItems["target"] + ); + } }); }); diff --git a/src/langTools/index.ts b/src/langTools/index.ts index a1d39d652..6e2ff8d6b 100644 --- a/src/langTools/index.ts +++ b/src/langTools/index.ts @@ -36,17 +36,25 @@ let disposable: vscode.Disposable | undefined; export function activateLanguageTool(context: vscode.ExtensionContext) { disposable = vscode.lm.registerTool("espIdfCommands", { async invoke( - options: { input: { command: string } }, + options: { input: { command: string; target?: string } }, token: vscode.CancellationToken ) { const commandName = options.input.command; + const target = options.input.target; const commandId = COMMAND_MAP[commandName]; if (commandId) { - await vscode.commands.executeCommand(commandId); + if (commandName === "setTarget" && target) { + // For setTarget command with a specific target, we need to call the command with the target + await vscode.commands.executeCommand(commandId, target); + } else { + await vscode.commands.executeCommand(commandId); + } return new vscode.LanguageModelToolResult([ new vscode.LanguageModelTextPart( - `Command "${commandName}" executed successfully.` + target + ? `Command "${commandName}" with target "${target}" executed successfully.` + : `Command "${commandName}" executed successfully.` ), ]); } else { @@ -55,22 +63,31 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { }, async prepareInvocation( - options: { input: { command: string } }, + options: { input: { command: string; target?: string } }, token: vscode.CancellationToken ) { const commandName = options.input.command; + const target = options.input.target; if (CONFIRMATION_COMMANDS.has(commandName)) { + const message = target + ? `Are you sure you want to run the "${commandName}" command with target "${target}"? This may affect your ESP-IDF project or device.` + : `Are you sure you want to run the "${commandName}" command? This may affect your ESP-IDF project or device.`; + return { confirmationMessages: { title: `Confirm ESP-IDF Command`, - message: `Are you sure you want to run the "${commandName}" command? This may affect your ESP-IDF project or device.`, + message, }, }; } + const invocationMessage = target + ? `Executing ESP-IDF command: ${commandName} with target ${target}` + : `Executing ESP-IDF command: ${commandName}`; + return { - invocationMessage: `Executing ESP-IDF command: ${commandName}`, + invocationMessage, }; }, }); From 54e38075f4348ab500590526773eca3212520600 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 14 Aug 2025 16:30:50 +0800 Subject: [PATCH 06/15] focus on terminal or esp idf output for lang tool cmd execution --- package.json | 2 +- src/langTools/index.ts | 172 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index eb38041c2..f531e4fd2 100644 --- a/package.json +++ b/package.json @@ -2511,7 +2511,7 @@ { "name": "espIdfCommands", "displayName": "ESP-IDF Commands", - "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', 'set target to esp32s3', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, target setting, and more. For setTarget command, you can specify a target parameter (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) to set it directly. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", + "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', 'set target to esp32s3', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, target setting, and more. For setTarget command, you can specify a target parameter (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) to set it directly. This tool provides detailed feedback about command execution status, automatically focuses the terminal for task commands, and shows the ESP-IDF output channel for other operations. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", "canBeReferencedInPrompt": true, "toolReferenceName": "espIdfCommands", "tags": [ diff --git a/src/langTools/index.ts b/src/langTools/index.ts index 6e2ff8d6b..b77cd9042 100644 --- a/src/langTools/index.ts +++ b/src/langTools/index.ts @@ -1,4 +1,5 @@ import * as vscode from "vscode"; +import { OutputChannel } from "../logger/outputChannel"; // Map of command names to their corresponding VS Code command IDs const COMMAND_MAP: Record = { @@ -20,7 +21,6 @@ const COMMAND_MAP: Record = { heaptrace: "espIdf.heaptrace", }; -// Commands that require confirmation due to potential side effects const CONFIRMATION_COMMANDS = new Set([ "build", "flash", @@ -31,6 +31,28 @@ const CONFIRMATION_COMMANDS = new Set([ "setTarget", ]); +const TASK_COMMANDS = new Set([ + "build", + "flash", + "monitor", + "buildFlashMonitor", + "fullClean", + "eraseFlash", + "apptrace", + "heaptrace", +]); + +const WEBVIEW_COMMANDS = new Set([ + "menuconfig", + "size", + "newProject", + "partitionTable", + "componentManager", +]); + +// Commands that show dialogs or simple operations +const DIALOG_COMMANDS = new Set(["selectPort", "setTarget", "doctor"]); + let disposable: vscode.Disposable | undefined; export function activateLanguageTool(context: vscode.ExtensionContext) { @@ -44,19 +66,24 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { const commandId = COMMAND_MAP[commandName]; if (commandId) { - if (commandName === "setTarget" && target) { - // For setTarget command with a specific target, we need to call the command with the target - await vscode.commands.executeCommand(commandId, target); - } else { - await vscode.commands.executeCommand(commandId); + try { + if (commandName === "setTarget" && target) { + await vscode.commands.executeCommand(commandId, target); + } else { + await vscode.commands.executeCommand(commandId); + } + await focusOnAppropriateOutput(commandName); + + const feedback = await getCommandFeedback(commandName, target); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart(feedback), + ]); + } catch (error) { + const errorMessage = `Failed to execute command "${commandName}": ${error.message}`; + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart(errorMessage), + ]); } - return new vscode.LanguageModelToolResult([ - new vscode.LanguageModelTextPart( - target - ? `Command "${commandName}" with target "${target}" executed successfully.` - : `Command "${commandName}" executed successfully.` - ), - ]); } else { throw new Error(`Unknown ESP-IDF command: ${commandName}`); } @@ -70,10 +97,10 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { const target = options.input.target; if (CONFIRMATION_COMMANDS.has(commandName)) { - const message = target + const message = target ? `Are you sure you want to run the "${commandName}" command with target "${target}"? This may affect your ESP-IDF project or device.` : `Are you sure you want to run the "${commandName}" command? This may affect your ESP-IDF project or device.`; - + return { confirmationMessages: { title: `Confirm ESP-IDF Command`, @@ -82,10 +109,8 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { }; } - const invocationMessage = target - ? `Executing ESP-IDF command: ${commandName} with target ${target}` - : `Executing ESP-IDF command: ${commandName}`; - + const invocationMessage = getInvocationMessage(commandName, target); + return { invocationMessage, }; @@ -94,6 +119,115 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { context.subscriptions.push(disposable); } +async function focusOnAppropriateOutput(commandName: string): Promise { + if (TASK_COMMANDS.has(commandName)) { + await focusOnTerminal(); + } else { + OutputChannel.show(); + } +} + +async function focusOnTerminal(): Promise { + try { + await vscode.commands.executeCommand("workbench.action.terminal.focus"); + } catch (error) { + OutputChannel.show(); + } +} + +async function getCommandFeedback( + commandName: string, + target?: string +): Promise { + const targetInfo = target ? ` with target "${target}"` : ""; + + if (TASK_COMMANDS.has(commandName)) { + const taskDescription = getTaskDescription(commandName); + return `Command "${commandName}"${targetInfo} has been started successfully. ${taskDescription} The task is now running in the background. The terminal tab has been focused for you to monitor the task progress. You can also check the output panel or status bar for additional information.`; + } + + if (WEBVIEW_COMMANDS.has(commandName)) { + const webviewDescription = getWebviewDescription(commandName); + return `Command "${commandName}"${targetInfo} has been executed successfully. A webview panel has been opened for you to interact with the ${webviewDescription}. The ESP-IDF output channel has been focused to show any relevant information.`; + } + + if (DIALOG_COMMANDS.has(commandName)) { + if (commandName === "setTarget" && target) { + return `Command "${commandName}" with target "${target}" is now running. The ESP-IDF target is being set to ${target}. The ESP-IDF output channel has been focused to show the operation details. You can verify this in the status bar or by checking your project configuration.`; + } + if (commandName === "selectPort") { + return `Command "${commandName}"${targetInfo} is now running. A port selection dialog has been opened. The ESP-IDF output channel has been focused to show any relevant information. Please select the appropriate serial port for your ESP-IDF device.`; + } + if (commandName === "doctor") { + return `Command "${commandName}"${targetInfo} is now running. The ESP-IDF doctor diagnostic tool is now running. The ESP-IDF output channel has been focused to show detailed information about your ESP-IDF setup and any potential issues.`; + } + return `Command "${commandName}"${targetInfo} is now running. A dialog or interface has been opened for you to complete the operation. The ESP-IDF output channel has been focused to show any relevant information.`; + } + + return `Command "${commandName}"${targetInfo} is now running. The ESP-IDF output channel has been focused to show any relevant information.`; +} + +function getInvocationMessage(commandName: string, target?: string): string { + const targetInfo = target ? ` with target ${target}` : ""; + + if (TASK_COMMANDS.has(commandName)) { + const taskDescription = getTaskDescription(commandName); + return `Starting ESP-IDF task: ${commandName}${targetInfo}. ${taskDescription} This may take some time to complete. The terminal will be focused to show task progress.`; + } + + if (WEBVIEW_COMMANDS.has(commandName)) { + return `Opening ${getWebviewDescription( + commandName + )}: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; + } + + if (DIALOG_COMMANDS.has(commandName)) { + return `Executing ESP-IDF command: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; + } + + return `Executing ESP-IDF command: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; +} + +function getTaskDescription(commandName: string): string { + switch (commandName) { + case "build": + return "This will compile your ESP-IDF project and generate the necessary binary files."; + case "flash": + return "This will upload the compiled firmware to your ESP-IDF device."; + case "monitor": + return "This will open a serial monitor to view device output and send commands."; + case "buildFlashMonitor": + return "This will build the project, flash it to the device, and start monitoring in sequence."; + case "fullClean": + return "This will clean all build artifacts and temporary files from your project."; + case "eraseFlash": + return "This will completely erase the flash memory of your ESP-IDF device."; + case "apptrace": + return "This will start application tracing to analyze performance and behavior."; + case "heaptrace": + return "This will start heap tracing to monitor memory allocation and usage."; + default: + return "This task will be executed in the background."; + } +} + +function getWebviewDescription(commandName: string): string { + switch (commandName) { + case "menuconfig": + return "SDK Configuration Editor - Configure your ESP-IDF project settings"; + case "size": + return "Size Analysis Tool - Analyze memory usage and optimize your application"; + case "newProject": + return "New Project Wizard - Create a new ESP-IDF project from templates"; + case "partitionTable": + return "Partition Table Editor - Configure flash memory layout"; + case "componentManager": + return "Component Manager - Browse and install ESP-IDF components"; + default: + return "interface"; + } +} + export function deactivateLanguageTool() { if (disposable) { disposable.dispose(); From 31d41706bb20413e751be68f5809f18525311c24 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Fri, 15 Aug 2025 14:43:00 +0800 Subject: [PATCH 07/15] fix set target with target string to show idf targer mismatch msg --- src/espIdf/setTarget/index.ts | 8 +++- src/extension.ts | 70 ++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/espIdf/setTarget/index.ts b/src/espIdf/setTarget/index.ts index b5e7fdd8c..3a55d37fb 100644 --- a/src/espIdf/setTarget/index.ts +++ b/src/espIdf/setTarget/index.ts @@ -53,6 +53,10 @@ export interface ISetTargetQuickPickItems { kind?: QuickPickItemKind; } +export function setIsSettingIDFTarget(value: boolean) { + isSettingIDFTarget = value; +} + export async function setIdfTarget( placeHolderMsg: string, workspaceFolder: WorkspaceFolder @@ -65,7 +69,7 @@ export async function setIdfTarget( Logger.info("setTargetInIDF is already running."); return; } - isSettingIDFTarget = true; + setIsSettingIDFTarget(true); const notificationMode = readParameter( "idf.notificationMode", @@ -220,7 +224,7 @@ export async function setIdfTarget( OutputChannel.appendLine(errMsg); } } finally { - isSettingIDFTarget = false; + setIsSettingIDFTarget(false); } } ); diff --git a/src/extension.ts b/src/extension.ts index b4382225e..566895836 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -129,8 +129,13 @@ import { TaskManager } from "./taskManager"; import { WelcomePanel } from "./welcome/panel"; import { getWelcomePageInitialValues } from "./welcome/welcomeInit"; import { getEspMatter } from "./espMatter/espMatterDownload"; -import { setIdfTarget } from "./espIdf/setTarget"; +import { + setIdfTarget, + setIsSettingIDFTarget, + isSettingIDFTarget, +} from "./espIdf/setTarget"; import { setTargetInIDF } from "./espIdf/setTarget/setTargetInIdf"; +import { updateCurrentProfileIdfTarget } from "./project-conf"; import { PeripheralTreeView } from "./espIdf/debugAdapter/peripheralTreeView"; import { PeripheralBaseNode } from "./espIdf/debugAdapter/nodes/base"; import { ExtensionConfigStore } from "./common/store"; @@ -2200,21 +2205,60 @@ export async function activate(context: vscode.ExtensionContext) { ); if (target) { - // If a target is provided, set it directly - const targetsFromIdf = await getTargetsFromEspIdf(workspaceFolder.uri); - const selectedTarget = targetsFromIdf.find((t) => t.target === target); + // Check if target setting is already in progress + if (isSettingIDFTarget) { + Logger.info("setTargetInIDF is already running."); + return; + } + setIsSettingIDFTarget(true); - if (selectedTarget) { - await setTargetInIDF(workspaceFolder, selectedTarget); - await getIdfTargetFromSdkconfig( - workspaceRoot, - statusBarItems["target"] + try { + // If a target is provided, set it directly + const targetsFromIdf = await getTargetsFromEspIdf( + workspaceFolder.uri ); - } else { - const listOfTargets = targetsFromIdf.map((t) => t.target).join(", "); - vscode.window.showErrorMessage( - `Invalid target: ${target}. Please use one of the supported targets: ${listOfTargets}.` + const selectedTarget = targetsFromIdf.find( + (t) => t.target === target ); + + if (selectedTarget) { + await setTargetInIDF(workspaceFolder, selectedTarget); + + // Update configuration like setIdfTarget does + const configurationTarget = + vscode.ConfigurationTarget.WorkspaceFolder; + const customExtraVars = idfConf.readParameter( + "idf.customExtraVars", + workspaceFolder + ) as { [key: string]: string }; + customExtraVars["IDF_TARGET"] = selectedTarget.target; + await idfConf.writeParameter( + "idf.customExtraVars", + customExtraVars, + configurationTarget, + workspaceFolder.uri + ); + await updateCurrentProfileIdfTarget( + selectedTarget.target, + workspaceFolder.uri + ); + + await getIdfTargetFromSdkconfig( + workspaceRoot, + statusBarItems["target"] + ); + } else { + const listOfTargets = targetsFromIdf + .map((t) => t.target) + .join(", "); + vscode.window.showErrorMessage( + `Invalid target: ${target}. Please use one of the supported targets: ${listOfTargets}.` + ); + } + } catch (error) { + Logger.errorNotify(error.message, error, "espIdf.setTarget command"); + } finally { + setIsSettingIDFTarget(false); } } else { // If no target is provided, show the selection dialog From 20ad5b6ff4e75bf299ac7945515aa9c5fd9d5af2 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Wed, 20 Aug 2025 14:07:49 +0800 Subject: [PATCH 08/15] use functions directly for language tool better feedback --- package.json | 40 +- src/build/buildCmd.ts | 114 ++-- src/build/buildTask.ts | 16 +- .../debugAdapter/debugAdapterManager.ts | 21 +- src/extension.ts | 86 +-- src/flash/eraseFlashTask.ts | 147 ++++++ src/flash/jtagCmd.ts | 40 +- src/flash/uartFlash.ts | 74 ++- src/idfConfiguration.ts | 14 +- src/langTools/index.ts | 493 +++++++++++++++++- src/utils.ts | 32 ++ 11 files changed, 875 insertions(+), 202 deletions(-) create mode 100644 src/flash/eraseFlashTask.ts diff --git a/package.json b/package.json index f531e4fd2..ba1f64953 100644 --- a/package.json +++ b/package.json @@ -2511,7 +2511,7 @@ { "name": "espIdfCommands", "displayName": "ESP-IDF Commands", - "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', 'set target to esp32s3', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, target setting, and more. For setTarget command, you can specify a target parameter (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) to set it directly. This tool provides detailed feedback about command execution status, automatically focuses the terminal for task commands, and shows the ESP-IDF output channel for other operations. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", + "modelDescription": "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', 'set target to esp32s3', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, target setting, and more. For setTarget command, you can specify a target parameter (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) to set it directly. For build, flash, and buildFlashMonitor commands, you can optionally specify partitionToUse (app, bootloader, partition-table) and flashType (UART, JTAG, DFU) parameters. This tool provides detailed feedback about command execution status, automatically focuses the terminal for task commands, and shows the ESP-IDF output channel for other operations. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks.", "canBeReferencedInPrompt": true, "toolReferenceName": "espIdfCommands", "tags": [ @@ -2563,7 +2563,16 @@ "edit partition table", "manage components", "start app trace", - "start heap trace" + "start heap trace", + "build app", + "build bootloader", + "build partition table", + "flash app", + "flash bootloader", + "flash partition table", + "build and flash app", + "build and flash bootloader", + "build and flash partition table" ], "icon": "$(run-view-icon)", "inputSchema": { @@ -2610,6 +2619,33 @@ "esp32h21", "esp32h4" ] + }, + "partitionToUse": { + "oneOf": [ + { + "type": "string", + "description": "Specifies which partition to build or flash. This parameter is optional and only used for build, flash, and buildFlashMonitor commands.", + "enum": [ + "app", + "bootloader", + "partition-table" + ] + }, + { + "type": "null", + "description": "Use undefined to explicitly use the default configuration" + } + ], + "description": "Specifies which partition to build or flash. Use undefined to explicitly use the default configuration. This parameter is optional and only used for build, flash, and buildFlashMonitor commands." + }, + "flashType": { + "type": "string", + "description": "Specifies the flash method to use. This parameter is optional and only used for flash and buildFlashMonitor commands.", + "enum": [ + "UART", + "JTAG", + "DFU" + ] } }, "required": [ diff --git a/src/build/buildCmd.ts b/src/build/buildCmd.ts index 753b0cf94..ffc9d1b0f 100644 --- a/src/build/buildCmd.ts +++ b/src/build/buildCmd.ts @@ -34,7 +34,19 @@ import { ESP } from "../config"; import { createFlashModel } from "../flash/flashModelBuilder"; import { OutputChannel } from "../logger/outputChannel"; -export async function buildCommand( +/** + * Build the project with the given parameters. + * + * This function is used to build the project with the given parameters. + * It will build the project, run the size task, and flash the project if the flash type is set. + * + * @param workspace - The workspace folder URI + * @param cancelToken - The cancellation token + * @param flashType - The flash type + * @param buildType - The build type + * @returns true if the build is successful, false otherwise + */ +export async function buildCommandMain( workspace: vscode.Uri, cancelToken: vscode.CancellationToken, flashType: ESP.FlashType, @@ -52,55 +64,72 @@ export async function buildCommand( new Error("One_Task_At_A_Time"), "buildCmd buildCommand" ); - return; + continueFlag = false; + return continueFlag; } cancelToken.onCancellationRequested(() => { TaskManager.cancelTasks(); TaskManager.disposeListeners(); buildTask.building(false); }); - try { - await customTask.addCustomTask(CustomTaskType.PreBuild); - await buildTask.build(buildType); - await TaskManager.runTasks(); - const enableSizeTask = (await readParameter( - "idf.enableSizeTaskAfterBuildTask", - workspace - )) as boolean; - if (enableSizeTask && typeof buildType === "undefined") { - const sizeTask = new IdfSizeTask(workspace); - await sizeTask.getSizeInfo(); - } - await customTask.addCustomTask(CustomTaskType.PostBuild); - await TaskManager.runTasks(); - if (flashType === ESP.FlashType.DFU) { - const buildPath = readParameter("idf.buildPath", workspace) as string; - if (!(await pathExists(join(buildPath, "flasher_args.json")))) { - return Logger.warnNotify( - "flasher_args.json file is missing from the build directory, can't proceed, please build properly!" - ); - } - const adapterTargetName = await getIdfTargetFromSdkconfig(workspace); - if ( - adapterTargetName && - adapterTargetName !== "esp32s2" && - adapterTargetName !== "esp32s3" - ) { - return Logger.warnNotify( - `The selected device target "${adapterTargetName}" is not compatible for DFU, as a result the DFU.bin was not created.` - ); - } else { - await buildTask.buildDfu(); - await TaskManager.runTasks(); - } + await customTask.addCustomTask(CustomTaskType.PreBuild); + await buildTask.build(buildType); + await TaskManager.runTasks(); + const enableSizeTask = (await readParameter( + "idf.enableSizeTaskAfterBuildTask", + workspace + )) as boolean; + if (enableSizeTask && typeof buildType === "undefined") { + const sizeTask = new IdfSizeTask(workspace); + await sizeTask.getSizeInfo(); + } + await customTask.addCustomTask(CustomTaskType.PostBuild); + await TaskManager.runTasks(); + if (flashType === ESP.FlashType.DFU) { + const buildPath = readParameter("idf.buildPath", workspace) as string; + if (!(await pathExists(join(buildPath, "flasher_args.json")))) { + Logger.warnNotify( + "flasher_args.json file is missing from the build directory, can't proceed, please build properly!" + ); + continueFlag = false; + return continueFlag; } - if (!cancelToken.isCancellationRequested) { - updateIdfComponentsTree(workspace); - Logger.infoNotify("Build Successful"); - const flashCmd = await buildFinishFlashCmd(workspace); - OutputChannel.appendLine(flashCmd, "Build"); - TaskManager.disposeListeners(); + const adapterTargetName = await getIdfTargetFromSdkconfig(workspace); + if ( + adapterTargetName && + adapterTargetName !== "esp32s2" && + adapterTargetName !== "esp32s3" + ) { + Logger.warnNotify( + `The selected device target "${adapterTargetName}" is not compatible for DFU, as a result the DFU.bin was not created.` + ); + continueFlag = false; + return continueFlag; + } else { + await buildTask.buildDfu(); + await TaskManager.runTasks(); } + } + if (!cancelToken.isCancellationRequested) { + updateIdfComponentsTree(workspace); + Logger.infoNotify("Build Successful"); + const flashCmd = await buildFinishFlashCmd(workspace); + OutputChannel.appendLine(flashCmd, "Build"); + TaskManager.disposeListeners(); + } + buildTask.building(false); + return continueFlag; +} + +export async function buildCommand( + workspace: vscode.Uri, + cancelToken: vscode.CancellationToken, + flashType: ESP.FlashType, + buildType?: ESP.BuildType +) { + let continueFlag = true; + try { + continueFlag = await buildCommandMain(workspace, cancelToken, flashType, buildType); } catch (error) { if (error.message === "ALREADY_BUILDING") { return Logger.errorNotify( @@ -121,7 +150,6 @@ export async function buildCommand( ); continueFlag = false; } - buildTask.building(false); return continueFlag; } diff --git a/src/build/buildTask.ts b/src/build/buildTask.ts index fb590effb..278d8e1b4 100644 --- a/src/build/buildTask.ts +++ b/src/build/buildTask.ts @@ -41,14 +41,6 @@ export class BuildTask { constructor(workspaceUri: vscode.Uri) { this.currentWorkspace = workspaceUri; - this.idfPathDir = idfConf.readParameter( - "idf.espIdfPath", - workspaceUri - ) as string; - this.buildDirPath = idfConf.readParameter( - "idf.buildPath", - workspaceUri - ) as string; } public building(flag: boolean) { @@ -78,6 +70,14 @@ export class BuildTask { throw new Error("ALREADY_BUILDING"); } this.building(true); + this.idfPathDir = idfConf.readParameter( + "idf.espIdfPath", + this.currentWorkspace + ) as string; + this.buildDirPath = idfConf.readParameter( + "idf.buildPath", + this.currentWorkspace + ) as string; await ensureDir(this.buildDirPath); const modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); const processOptions = { diff --git a/src/espIdf/debugAdapter/debugAdapterManager.ts b/src/espIdf/debugAdapter/debugAdapterManager.ts index 8cee16da0..6fd5e52eb 100644 --- a/src/espIdf/debugAdapter/debugAdapterManager.ts +++ b/src/espIdf/debugAdapter/debugAdapterManager.ts @@ -112,11 +112,18 @@ export class DebugAdapterManager extends EventEmitter { const logFile = path.join(this.currentWorkspace.fsPath, "debug") + ".log"; if (!this.appOffset) { - const serialPort = idfConf.readParameter( - "idf.port", - this.currentWorkspace - ); - const flashBaudRate = idfConf.readParameter( + const serialPort = await idfConf.readSerialPort(this.currentWorkspace, false); + if (!serialPort) { + return reject( + new Error( + vscode.l10n.t( + "No serial port found for current IDF_TARGET: {0}", + this.target + ) + ) + ); + } + const flashBaudRate = await idfConf.readParameter( "idf.flashBaudRate", this.currentWorkspace ); @@ -140,7 +147,9 @@ export class DebugAdapterManager extends EventEmitter { ); this.appOffset = model.app.address; } - const pythonBinPath = await getVirtualEnvPythonPath(this.currentWorkspace); + const pythonBinPath = await getVirtualEnvPythonPath( + this.currentWorkspace + ); const toolchainPrefix = getToolchainToolName(this.target, ""); const adapterArgs = [ diff --git a/src/extension.ts b/src/extension.ts index 566895836..ec9b0695f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -45,7 +45,7 @@ import { showInfoNotificationWithMultipleActions, } from "./logger/utils"; import * as utils from "./utils"; -import { PreCheck } from "./utils"; +import { PreCheck, shouldDisableMonitorReset } from "./utils"; import { getIdfTargetFromSdkconfig, getProjectName, @@ -69,6 +69,7 @@ import { WSServer } from "./espIdf/communications/ws"; import { IDFMonitor } from "./espIdf/monitor"; import { BuildTask } from "./build/buildTask"; import { FlashTask } from "./flash/flashTask"; +import { EraseFlashTask } from "./flash/eraseFlashTask"; import { ESPCoreDumpPyTool, InfoCoreFileFormat } from "./espIdf/core-dump"; import { ArduinoComponentInstaller } from "./espIdf/arduino/addArduinoComponent"; import { PartitionTableEditorPanel } from "./espIdf/partition-table"; @@ -689,36 +690,6 @@ export async function activate(context: vscode.ExtensionContext) { registerIDFCommand("espIdf.eraseFlash", async () => { PreCheck.perform([webIdeCheck, openFolderCheck], async () => { - if (IDFMonitor.terminal) { - IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); - const monitorDelay = idfConf.readParameter( - "idf.monitorDelay", - workspaceRoot - ) as number; - await utils.sleep(monitorDelay); - } - const pythonBinPath = await getVirtualEnvPythonPath(workspaceRoot); - const idfPathDir = idfConf.readParameter( - "idf.espIdfPath", - workspaceRoot - ) as string; - const port = await idfConf.readSerialPort(workspaceRoot, false); - if (!port) { - return Logger.warnNotify( - vscode.l10n.t( - "No serial port found for current IDF_TARGET: {0}", - await getIdfTargetFromSdkconfig(workspaceRoot) - ) - ); - } - const flashScriptPath = path.join( - idfPathDir, - "components", - "esptool_py", - "esptool", - "esptool.py" - ); - const notificationMode = idfConf.readParameter( "idf.notificationMode", workspaceRoot @@ -728,7 +699,6 @@ export async function activate(context: vscode.ExtensionContext) { notificationMode === idfConf.NotificationMode.Notifications ? vscode.ProgressLocation.Notification : vscode.ProgressLocation.Window; - vscode.window.withProgress( { cancellable: true, @@ -745,17 +715,16 @@ export async function activate(context: vscode.ExtensionContext) { cancelToken: vscode.CancellationToken ) => { try { - const args = [flashScriptPath, "-p", port, "erase_flash"]; - const result = await utils.execChildProcess( - pythonBinPath, - args, - process.cwd(), - OutputChannel.init(), - null, - cancelToken - ); - OutputChannel.appendLine(result); - Logger.infoNotify("Flash memory content has been erased."); + const eraseFlashTask = new EraseFlashTask(workspaceRoot); + await eraseFlashTask.eraseFlash(); + await TaskManager.runTasks(); + if (!cancelToken.isCancellationRequested) { + EraseFlashTask.isErasing = false; + const msg = "Erase flash done"; + OutputChannel.appendLineAndShow(msg, "Erase flash"); + Logger.infoNotify(msg); + } + TaskManager.disposeListeners(); } catch (error) { Logger.errorNotify(error.message, error, "extension eraseFlash"); } @@ -4614,40 +4583,11 @@ async function createMonitor() { vscode.commands.executeCommand(IDFWebCommandKeys.Monitor); return; } - const noReset = await shouldDisableMonitorReset(); + const noReset = await shouldDisableMonitorReset(workspaceRoot); await createNewIdfMonitor(workspaceRoot, noReset); }); } -/** - * Determines if the monitor reset should be disabled. - * If flash encryption is enabled for release mode, we add --no-reset flag for monitoring - * because by default monitoring command resets the device which is not recommended. - * Reset should happen by Bootloader itself once it completes encrypting all artifacts. - * - * @returns {Promise} True if monitor reset should be disabled, false otherwise. - */ -const shouldDisableMonitorReset = async (): Promise => { - const configNoReset = idfConf.readParameter( - "idf.monitorNoReset", - workspaceRoot - ); - - if (configNoReset === true) { - return true; - } - - if (isFlashEncryptionEnabled(workspaceRoot)) { - const valueReleaseModeEnabled = await utils.getConfigValueFromSDKConfig( - "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE", - workspaceRoot - ); - return valueReleaseModeEnabled === "y"; - } - - return false; -}; - /** * Checks if the clangd extension is installed and prompts the user to install it if not. * This is specifically for VS Code fork users (like Cursor, VSCodium, etc.) to ensure they have the best C/C++ development experience. diff --git a/src/flash/eraseFlashTask.ts b/src/flash/eraseFlashTask.ts new file mode 100644 index 000000000..23dbcc7cc --- /dev/null +++ b/src/flash/eraseFlashTask.ts @@ -0,0 +1,147 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Friday, 27th September 2019 9:59:57 pm + * Copyright 2019 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + l10n, + ProcessExecution, + ProcessExecutionOptions, + TaskPanelKind, + TaskPresentationOptions, + TaskRevealKind, + TaskScope, + Uri, + workspace, +} from "vscode"; +import { join } from "path"; +import { constants } from "fs"; +import { NotificationMode, readParameter, readSerialPort } from "../idfConfiguration"; +import { appendIdfAndToolsToPath, canAccessFile } from "../utils"; +import { sleep } from "../utils"; +import { TaskManager } from "../taskManager"; +import { ESP } from "../config"; +import { getVirtualEnvPythonPath } from "../pythonManager"; +import { IDFMonitor } from "../espIdf/monitor"; +import { Logger } from "../logger/logger"; +import { OutputChannel } from "../logger/outputChannel"; +import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; + +export class EraseFlashTask { + public static isErasing: boolean; + private currentWorkspace: Uri; + private flashScriptPath: string; + private idfPathDir: string; + private modifiedEnv: { [key: string]: string }; + private processOptions: ProcessExecutionOptions; + + constructor(workspaceUri: Uri) { + this.currentWorkspace = workspaceUri; + this.idfPathDir = readParameter("idf.espIdfPath", workspaceUri) as string; + this.flashScriptPath = join( + this.idfPathDir, + "components", + "esptool_py", + "esptool", + "esptool.py" + ); + } + + public erasing(flag: boolean) { + EraseFlashTask.isErasing = flag; + } + + private verifyArgs() { + if (!canAccessFile(this.flashScriptPath, constants.R_OK)) { + throw new Error("SCRIPT_PERMISSION_ERROR"); + } + } + + public async eraseFlash() { + if (EraseFlashTask.isErasing) { + throw new Error("ALREADY_ERASING"); + } + + this.verifyArgs(); + + // Stop monitor if running + if (IDFMonitor.terminal) { + IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); + const monitorDelay = readParameter( + "idf.monitorDelay", + this.currentWorkspace + ) as number; + await sleep(monitorDelay); + } + + const notificationMode = readParameter( + "idf.notificationMode", + this.currentWorkspace + ) as string; + const pythonBinPath = await getVirtualEnvPythonPath(this.currentWorkspace); + const currentWorkspaceFolder = workspace.workspaceFolders.find( + (w) => w.uri === this.currentWorkspace + ); + const showTaskOutput = + notificationMode === NotificationMode.All || + notificationMode === NotificationMode.Output + ? TaskRevealKind.Always + : TaskRevealKind.Silent; + + this.modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); + this.processOptions = { + cwd: process.cwd(), + env: this.modifiedEnv, + }; + + const eraseExecution = this._eraseExecution(pythonBinPath); + const erasePresentationOptions = { + reveal: showTaskOutput, + showReuseMessage: false, + clear: false, + panel: TaskPanelKind.Shared, + } as TaskPresentationOptions; + + await TaskManager.addTask( + { + type: "esp-idf", + command: "ESP-IDF Erase Flash", + taskId: "idf-erase-flash-task", + }, + currentWorkspaceFolder || TaskScope.Workspace, + "ESP-IDF Erase Flash", + eraseExecution, + ["espIdf"], + erasePresentationOptions + ); + OutputChannel.appendLine("Flash memory content has been erased."); + Logger.infoNotify("Flash memory content has been erased."); + } + + private async _eraseExecution(pythonBinPath: string) { + this.erasing(true); + const port = await readSerialPort(this.currentWorkspace, false); + if (!port) { + return Logger.warnNotify( + l10n.t( + "No serial port found for current IDF_TARGET: {0}", + await getIdfTargetFromSdkconfig(this.currentWorkspace) + ) + ); + } + const args = [this.flashScriptPath, "-p", port, "erase_flash"]; + return new ProcessExecution(pythonBinPath, args, this.processOptions); + } +} diff --git a/src/flash/jtagCmd.ts b/src/flash/jtagCmd.ts index 5e0bebb17..7d9eb208c 100644 --- a/src/flash/jtagCmd.ts +++ b/src/flash/jtagCmd.ts @@ -26,8 +26,7 @@ import { CustomTask, CustomTaskType } from "../customTasks/customTaskProvider"; import { Uri } from "vscode"; import { OutputChannel } from "../logger/outputChannel"; -export async function jtagFlashCommand(workspace: Uri): Promise { - let continueFlag = true; +export async function jtagFlashCommandMain(workspace: Uri) { const isOpenOCDLaunched = await OpenOCDManager.init().promptUserToLaunchOpenOCDServer(); if (!isOpenOCDLaunched) { const errStr = @@ -69,23 +68,28 @@ export async function jtagFlashCommand(workspace: Uri): Promise { if (forceUNIXPathSeparator === true) { buildPath = buildPath.replace(/\\/g, "/"); } + await customTask.addCustomTask(CustomTaskType.PreFlash); + await customTask.runTasks(CustomTaskType.PreFlash); + await jtag.flash( + "program_esp_bins", + buildPath, + "flasher_args.json", + ...openOCDJTagFlashArguments + ); + await customTask.addCustomTask(CustomTaskType.PostFlash); + await customTask.runTasks(CustomTaskType.PostFlash); + const msg = "⚡️ Flashed Successfully (JTAG)"; + OutputChannel.appendLineAndShow(msg, "Flash"); + Logger.infoNotify(msg); + const closingOpenOCDMsg = "Closing OpenOCD server connection..."; + OutputChannel.appendLineAndShow(closingOpenOCDMsg, "Flash"); + OpenOCDManager.init().stop(); +} + +export async function jtagFlashCommand(workspace: Uri) { + let continueFlag = true; try { - await customTask.addCustomTask(CustomTaskType.PreFlash); - await customTask.runTasks(CustomTaskType.PreFlash); - await jtag.flash( - "program_esp_bins", - buildPath, - "flasher_args.json", - ...openOCDJTagFlashArguments - ); - await customTask.addCustomTask(CustomTaskType.PostFlash); - await customTask.runTasks(CustomTaskType.PostFlash); - const msg = "⚡️ Flashed Successfully (JTAG)"; - OutputChannel.appendLineAndShow(msg, "Flash"); - Logger.infoNotify(msg); - const closingOpenOCDMsg = "Closing OpenOCD server connection..."; - OutputChannel.appendLineAndShow(closingOpenOCDMsg, "Flash"); - OpenOCDManager.init().stop(); + await jtagFlashCommandMain(workspace); } catch (msg) { OpenOCDManager.init().showOutputChannel(true); OutputChannel.appendLine(msg, "Flash"); diff --git a/src/flash/uartFlash.ts b/src/flash/uartFlash.ts index 11dedd10b..fb077bfd2 100644 --- a/src/flash/uartFlash.ts +++ b/src/flash/uartFlash.ts @@ -27,7 +27,7 @@ import { readParameter } from "../idfConfiguration"; import { ESP } from "../config"; import { OutputChannel } from "../logger/outputChannel"; -export async function flashCommand( +export async function uartFlashCommandMain( cancelToken: CancellationToken, flashBaudRate: string, idfPathDir: string, @@ -36,8 +36,7 @@ export async function flashCommand( flashType: ESP.FlashType, encryptPartitions: boolean, partitionToUse?: ESP.BuildType -): Promise { - let continueFlag = true; +) { const buildPath = readParameter("idf.buildPath", workspace) as string; const buildFiles = await readdir(buildPath); const binFiles = buildFiles.filter( @@ -59,33 +58,56 @@ export async function flashCommand( TaskManager.cancelTasks(); TaskManager.disposeListeners(); }); + const model = await createFlashModel( + flasherArgsJsonPath, + port, + flashBaudRate + ); + let flashTask = new FlashTask( + workspace, + idfPathDir, + model, + encryptPartitions + ); + const customTask = new CustomTask(workspace); + cancelToken.onCancellationRequested(() => { + FlashTask.isFlashing = false; + }); + await customTask.addCustomTask(CustomTaskType.PreFlash); + await flashTask.flash(flashType, partitionToUse); + await customTask.addCustomTask(CustomTaskType.PostFlash); + await TaskManager.runTasks(); + if (!cancelToken.isCancellationRequested) { + FlashTask.isFlashing = false; + const msg = "Flash Done ⚡️"; + OutputChannel.appendLineAndShow(msg, "Flash"); + Logger.infoNotify(msg); + } + TaskManager.disposeListeners(); +} + +export async function flashCommand( + cancelToken: CancellationToken, + flashBaudRate: string, + idfPathDir: string, + port: string, + workspace: Uri, + flashType: ESP.FlashType, + encryptPartitions: boolean, + partitionToUse?: ESP.BuildType +) { + let continueFlag = true; try { - const model = await createFlashModel( - flasherArgsJsonPath, + await uartFlashCommandMain( + cancelToken, + flashBaudRate, + idfPathDir, port, - flashBaudRate - ); - let flashTask = new FlashTask( workspace, - idfPathDir, - model, - encryptPartitions + flashType, + encryptPartitions, + partitionToUse ); - const customTask = new CustomTask(workspace); - cancelToken.onCancellationRequested(() => { - FlashTask.isFlashing = false; - }); - await customTask.addCustomTask(CustomTaskType.PreFlash); - await flashTask.flash(flashType, partitionToUse); - await customTask.addCustomTask(CustomTaskType.PostFlash); - await TaskManager.runTasks(); - if (!cancelToken.isCancellationRequested) { - FlashTask.isFlashing = false; - const msg = "Flash Done ⚡️"; - OutputChannel.appendLineAndShow(msg, "Flash"); - Logger.infoNotify(msg); - } - TaskManager.disposeListeners(); } catch (error) { if (error.message === "ALREADY_FLASHING") { const errStr = "Already one flash process is running!"; diff --git a/src/idfConfiguration.ts b/src/idfConfiguration.ts index fb52d55f3..8f4da0710 100644 --- a/src/idfConfiguration.ts +++ b/src/idfConfiguration.ts @@ -351,7 +351,19 @@ export function resolveVariables( return process.env[envVariable]; } if (scope && match.indexOf("workspaceFolder") > 0) { - return scope instanceof vscode.Uri ? scope.fsPath : scope.uri.fsPath; + let folderFsPath: string | undefined; + if ("fsPath" in scope && typeof scope.fsPath === "string") { + folderFsPath = scope.fsPath; + } else if ( + "uri" in scope && + scope.uri && + typeof scope.uri.fsPath === "string" + ) { + folderFsPath = scope.uri.fsPath; + } else { + folderFsPath = match; + } + return folderFsPath; } if (match.indexOf("execPath") > 0) { return process.execPath; diff --git a/src/langTools/index.ts b/src/langTools/index.ts index b77cd9042..3b864588a 100644 --- a/src/langTools/index.ts +++ b/src/langTools/index.ts @@ -1,5 +1,35 @@ import * as vscode from "vscode"; import { OutputChannel } from "../logger/outputChannel"; +import { ESP } from "../config"; +import { buildCommandMain } from "../build/buildCmd"; +import { + readParameter, + readSerialPort, + writeParameter, +} from "../idfConfiguration"; +import { OpenOCDManager } from "../espIdf/openOcd/openOcdManager"; +import { + getEspIdfFromCMake, + PreCheck, + shouldDisableMonitorReset, + sleep, +} from "../utils"; +import { Logger } from "../logger/logger"; +import { jtagFlashCommandMain } from "../flash/jtagCmd"; +import { verifyCanFlash } from "../flash/flashCmd"; +import { uartFlashCommandMain } from "../flash/uartFlash"; +import { IDFMonitor } from "../espIdf/monitor"; +import { IDFWebCommandKeys } from "../cmdTreeView/cmdStore"; +import { createNewIdfMonitor } from "../espIdf/monitor/command"; +import { isFlashEncryptionEnabled } from "../flash/verifyFlashEncryption"; +import { EraseFlashTask } from "../flash/eraseFlashTask"; +import { TaskManager } from "../taskManager"; +import { getTargetsFromEspIdf } from "../espIdf/setTarget/getTargets"; +import { updateCurrentProfileIdfTarget } from "../project-conf"; +import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { setTargetInIDF } from "../espIdf/setTarget/setTargetInIdf"; +import { statusBarItems } from "../statusBar"; +import { isSettingIDFTarget, setIsSettingIDFTarget } from "../espIdf/setTarget"; // Map of command names to their corresponding VS Code command IDs const COMMAND_MAP: Record = { @@ -58,28 +88,398 @@ let disposable: vscode.Disposable | undefined; export function activateLanguageTool(context: vscode.ExtensionContext) { disposable = vscode.lm.registerTool("espIdfCommands", { async invoke( - options: { input: { command: string; target?: string } }, + options: { + input: { + command: string; + target?: string; + partitionToUse?: string; + flashType?: string; + }; + }, token: vscode.CancellationToken ) { const commandName = options.input.command; const target = options.input.target; const commandId = COMMAND_MAP[commandName]; + const defaultWorkspace = vscode.workspace.workspaceFolders?.[0]; + + const workspaceURI = ESP.GlobalConfiguration.store.get( + ESP.GlobalConfiguration.SELECTED_WORKSPACE_FOLDER, + defaultWorkspace?.uri + ); + + // Check if we have a valid workspace + if (!workspaceURI) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "No ESP-IDF workspace found. Please open an ESP-IDF project folder first." + ), + ]); + } + + let flashType = options.input.flashType as ESP.FlashType; + if (!flashType) { + flashType = readParameter( + "idf.flashType", + workspaceURI + ) as ESP.FlashType; + if (!flashType) { + flashType = ESP.FlashType.UART; + } + } + + // Validate flash type + if (flashType && !["UART", "JTAG", "DFU"].includes(flashType)) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `Invalid flash type: ${flashType}. Valid options are: UART, JTAG, DFU` + ), + ]); + } + + let encryptPartitions = await isFlashEncryptionEnabled(workspaceURI); + + let partitionToUse = options.input.partitionToUse as + | ESP.BuildType + | undefined; + + // If partitionToUse is explicitly set to undefined, keep it undefined + // If it's not provided (null/undefined), use the default from configuration + if (options.input.partitionToUse === undefined) { + partitionToUse = readParameter( + "idf.flashPartitionToUse", + workspaceURI + ) as ESP.BuildType; + } + + if ( + partitionToUse && + !["app", "bootloader", "partition-table"].includes(partitionToUse) + ) { + partitionToUse = undefined; + } + + let continueFlag = true; if (commandId) { try { - if (commandName === "setTarget" && target) { - await vscode.commands.executeCommand(commandId, target); + await focusOnAppropriateOutput(commandName); + if (commandName === "build") { + continueFlag = await buildCommandMain( + workspaceURI, + token, + flashType, + partitionToUse + ); + } else if (commandName === "flash") { + if (IDFMonitor.terminal) { + IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); + const monitorDelay = readParameter( + "idf.monitorDelay", + workspaceURI + ) as number; + await sleep(monitorDelay); + } + const port = await readSerialPort(workspaceURI, false); + if (!port) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + vscode.l10n.t( + "No serial port found for current IDF_TARGET: {0}", + await getIdfTargetFromSdkconfig(workspaceURI) + ) + ), + ]); + } + const flashBaudRate = readParameter( + "idf.flashBaudRate", + workspaceURI + ); + const canFlash = await verifyCanFlash( + flashBaudRate, + port, + flashType, + workspaceURI + ); + if (!canFlash) { + return; + } + if (flashType === ESP.FlashType.JTAG) { + const openOCDManager = OpenOCDManager.init(); + const currOpenOcdVersion = await openOCDManager.version(); + const openOCDVersionIsValid = PreCheck.openOCDVersionValidator( + "v0.10.0-esp32-20201125", + currOpenOcdVersion + ); + if (!openOCDVersionIsValid) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `Minimum OpenOCD version v0.10.0-esp32-20201125 is required while you have ${currOpenOcdVersion} version installed` + ), + ]); + } + await jtagFlashCommandMain(workspaceURI); + } else { + const idfPathDir = readParameter( + "idf.espIdfPath", + workspaceURI + ) as string; + await uartFlashCommandMain( + token, + flashBaudRate, + idfPathDir, + port, + workspaceURI, + flashType, + encryptPartitions, + partitionToUse + ); + } + } else if (commandName === "monitor") { + if (IDFMonitor.terminal) { + IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); + } + if (vscode.env.uiKind === vscode.UIKind.Web) { + vscode.commands.executeCommand(IDFWebCommandKeys.Monitor); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Redirecting to ESP-IDF Web Monitor command" + ), + ]); + } + const noReset = await shouldDisableMonitorReset(workspaceURI); + await createNewIdfMonitor(workspaceURI, noReset); + } else if (commandName === "buildFlashMonitor") { + continueFlag = await buildCommandMain( + workspaceURI, + token, + flashType, + partitionToUse + ); + if (!continueFlag) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Build ended without success." + ), + ]); + } + if (vscode.env.uiKind === vscode.UIKind.Web) { + vscode.commands.executeCommand(IDFWebCommandKeys.FlashAndMonitor); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Redirecting to ESP-IDF Web Flash and Monitor command" + ), + ]); + } + if (IDFMonitor.terminal) { + IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); + const monitorDelay = readParameter( + "idf.monitorDelay", + workspaceURI + ) as number; + await sleep(monitorDelay); + } + const port = await readSerialPort(workspaceURI, false); + if (!port) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + vscode.l10n.t( + "No serial port found for current IDF_TARGET: {0}", + await getIdfTargetFromSdkconfig(workspaceURI) + ) + ), + ]); + } + const flashBaudRate = readParameter( + "idf.flashBaudRate", + workspaceURI + ); + const canFlash = await verifyCanFlash( + flashBaudRate, + port, + flashType, + workspaceURI + ); + if (!canFlash) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Flash verification has failed" + ), + ]); + } + if (flashType === ESP.FlashType.JTAG) { + const openOCDManager = OpenOCDManager.init(); + const currOpenOcdVersion = await openOCDManager.version(); + const openOCDVersionIsValid = PreCheck.openOCDVersionValidator( + "v0.10.0-esp32-20201125", + currOpenOcdVersion + ); + if (!openOCDVersionIsValid) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `Minimum OpenOCD version v0.10.0-esp32-20201125 is required while you have ${currOpenOcdVersion} version installed` + ), + ]); + } + await jtagFlashCommandMain(workspaceURI); + } else { + const idfPathDir = readParameter( + "idf.espIdfPath", + workspaceURI + ) as string; + await uartFlashCommandMain( + token, + flashBaudRate, + idfPathDir, + port, + workspaceURI, + flashType, + encryptPartitions, + partitionToUse + ); + } + if (IDFMonitor.terminal) { + IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); + } + const noReset = await shouldDisableMonitorReset(workspaceURI); + await createNewIdfMonitor(workspaceURI, noReset); + } else if (commandName === "eraseFlash") { + const eraseFlashTask = new EraseFlashTask(workspaceURI); + await eraseFlashTask.eraseFlash(); + await TaskManager.runTasks(); + if (!token.isCancellationRequested) { + EraseFlashTask.isErasing = false; + const msg = "Erase flash done"; + OutputChannel.appendLineAndShow(msg, "Erase flash"); + Logger.infoNotify(msg); + } + TaskManager.disposeListeners(); + } else if (commandName === "setTarget") { + if (!target) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Please provide a ESP-IDF target name (esp32, esp32s2, etc.)" + ), + ]); + } + const targetsFromIdf = await getTargetsFromEspIdf(workspaceURI); + const selectedTarget = targetsFromIdf.find( + (t) => t.target === target + ); + + if (!selectedTarget) { + const espIdfPath = readParameter( + "idf.espIdfPath", + workspaceURI + ) as string; + const espIdfVersion = await getEspIdfFromCMake(espIdfPath); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `${target} is not a valid target for ESP-IDF ${espIdfVersion}` + ), + ]); + } + const workspaceFolder = vscode.workspace.getWorkspaceFolder( + workspaceURI + ); + if (isSettingIDFTarget) { + Logger.info("setTargetInIDF is already running."); + return; + } + setIsSettingIDFTarget(true); + await setTargetInIDF(workspaceFolder, selectedTarget); + + // Update configuration like setIdfTarget does + const configurationTarget = + vscode.ConfigurationTarget.WorkspaceFolder; + const customExtraVars = readParameter( + "idf.customExtraVars", + workspaceURI + ) as { [key: string]: string }; + customExtraVars["IDF_TARGET"] = selectedTarget.target; + await writeParameter( + "idf.customExtraVars", + customExtraVars, + configurationTarget, + workspaceFolder.uri + ); + await updateCurrentProfileIdfTarget( + selectedTarget.target, + workspaceFolder.uri + ); + + await getIdfTargetFromSdkconfig( + workspaceFolder.uri, + statusBarItems["target"] + ); + + setIsSettingIDFTarget(false); } else { await vscode.commands.executeCommand(commandId); } - await focusOnAppropriateOutput(commandName); - const feedback = await getCommandFeedback(commandName, target); + const feedback = await getCommandFeedback( + commandName, + target, + options.input.partitionToUse, + options.input.flashType + ); return new vscode.LanguageModelToolResult([ new vscode.LanguageModelTextPart(feedback), ]); } catch (error) { - const errorMessage = `Failed to execute command "${commandName}": ${error.message}`; + if (error.message === "ALREADY_BUILDING") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart("Already a build is running!"), + ]); + } + if (error.message === "BUILD_TERMINATED") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart("Build is Terminated"), + ]); + } + if (error.message === "ALREADY_FLASHING") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Already one flash process is running!" + ), + ]); + } + if (error.message === "NO_DFU_DEVICE_SELECTED") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart("No DFU was selected"), + ]); + } + if (error.message === "Task ESP-IDF Flash exited with code 74") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "No DFU capable USB device available found" + ), + ]); + } + if (error.message === "FLASH_TERMINATED") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart("Flashing has been stopped!"), + ]); + } + if (error.message === "SECTION_BIN_FILE_NOT_ACCESSIBLE") { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Flash (.bin) files don't exists or can't be accessed!" + ), + ]); + } + if ( + error.code === "ENOENT" || + error.message === "SCRIPT_PERMISSION_ERROR" + ) { + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + "Make sure you have the esptool.py installed and set in $PATH with proper permission" + ), + ]); + } + const errorMessage = `Failed to execute command "${commandName}": ${error.message}\n${error.stack}`; return new vscode.LanguageModelToolResult([ new vscode.LanguageModelTextPart(errorMessage), ]); @@ -90,16 +490,35 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { }, async prepareInvocation( - options: { input: { command: string; target?: string } }, + options: { + input: { + command: string; + target?: string; + partitionToUse?: string; + flashType?: string; + }; + }, token: vscode.CancellationToken ) { const commandName = options.input.command; const target = options.input.target; if (CONFIRMATION_COMMANDS.has(commandName)) { - const message = target - ? `Are you sure you want to run the "${commandName}" command with target "${target}"? This may affect your ESP-IDF project or device.` - : `Are you sure you want to run the "${commandName}" command? This may affect your ESP-IDF project or device.`; + const params = []; + if (target) params.push(`target "${target}"`); + if (options.input.partitionToUse !== undefined) { + if (options.input.partitionToUse === null) { + params.push(`partition "undefined (use default)"`); + } else { + params.push(`partition "${options.input.partitionToUse}"`); + } + } + if (options.input.flashType) + params.push(`flash type "${options.input.flashType}"`); + + const paramString = + params.length > 0 ? ` with ${params.join(", ")}` : ""; + const message = `Are you sure you want to run the "${commandName}" command${paramString}? This may affect your ESP-IDF project or device.`; return { confirmationMessages: { @@ -109,7 +528,12 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { }; } - const invocationMessage = getInvocationMessage(commandName, target); + const invocationMessage = getInvocationMessage( + commandName, + target, + options.input.partitionToUse, + options.input.flashType + ); return { invocationMessage, @@ -137,18 +561,26 @@ async function focusOnTerminal(): Promise { async function getCommandFeedback( commandName: string, - target?: string + target?: string, + partitionToUse?: string | null, + flashType?: string ): Promise { - const targetInfo = target ? ` with target "${target}"` : ""; + const params = []; + if (target) params.push(`target "${target}"`); + if (partitionToUse !== undefined) { + params.push(`partition "${partitionToUse}"`); + } + if (flashType) params.push(`flash type "${flashType}"`); + const paramString = params.length > 0 ? ` with ${params.join(", ")}` : ""; if (TASK_COMMANDS.has(commandName)) { const taskDescription = getTaskDescription(commandName); - return `Command "${commandName}"${targetInfo} has been started successfully. ${taskDescription} The task is now running in the background. The terminal tab has been focused for you to monitor the task progress. You can also check the output panel or status bar for additional information.`; + return `Command "${commandName}"${paramString} has been started successfully. ${taskDescription} The task is now running in the background. The terminal tab has been focused for you to monitor the task progress. You can also check the output panel or status bar for additional information.`; } if (WEBVIEW_COMMANDS.has(commandName)) { const webviewDescription = getWebviewDescription(commandName); - return `Command "${commandName}"${targetInfo} has been executed successfully. A webview panel has been opened for you to interact with the ${webviewDescription}. The ESP-IDF output channel has been focused to show any relevant information.`; + return `Command "${commandName}"${paramString} has been executed successfully. A webview panel has been opened for you to interact with the ${webviewDescription}. The ESP-IDF output channel has been focused to show any relevant information.`; } if (DIALOG_COMMANDS.has(commandName)) { @@ -156,36 +588,47 @@ async function getCommandFeedback( return `Command "${commandName}" with target "${target}" is now running. The ESP-IDF target is being set to ${target}. The ESP-IDF output channel has been focused to show the operation details. You can verify this in the status bar or by checking your project configuration.`; } if (commandName === "selectPort") { - return `Command "${commandName}"${targetInfo} is now running. A port selection dialog has been opened. The ESP-IDF output channel has been focused to show any relevant information. Please select the appropriate serial port for your ESP-IDF device.`; + return `Command "${commandName}"${paramString} is now running. A port selection dialog has been opened. The ESP-IDF output channel has been focused to show any relevant information. Please select the appropriate serial port for your ESP-IDF device.`; } if (commandName === "doctor") { - return `Command "${commandName}"${targetInfo} is now running. The ESP-IDF doctor diagnostic tool is now running. The ESP-IDF output channel has been focused to show detailed information about your ESP-IDF setup and any potential issues.`; + return `Command "${commandName}"${paramString} is now running. The ESP-IDF doctor diagnostic tool is now running. The ESP-IDF output channel has been focused to show detailed information about your ESP-IDF setup and any potential issues.`; } - return `Command "${commandName}"${targetInfo} is now running. A dialog or interface has been opened for you to complete the operation. The ESP-IDF output channel has been focused to show any relevant information.`; + return `Command "${commandName}"${paramString} is now running. A dialog or interface has been opened for you to complete the operation. The ESP-IDF output channel has been focused to show any relevant information.`; } - return `Command "${commandName}"${targetInfo} is now running. The ESP-IDF output channel has been focused to show any relevant information.`; + return `Command "${commandName}"${paramString} is now running. The ESP-IDF output channel has been focused to show any relevant information.`; } -function getInvocationMessage(commandName: string, target?: string): string { - const targetInfo = target ? ` with target ${target}` : ""; +function getInvocationMessage( + commandName: string, + target?: string, + partitionToUse?: string | null, + flashType?: string +): string { + const params = []; + if (target) params.push(`target ${target}`); + if (partitionToUse) { + params.push(`partition ${partitionToUse}`); + } + if (flashType) params.push(`flash type ${flashType}`); + const paramString = params.length > 0 ? ` with ${params.join(", ")}` : ""; if (TASK_COMMANDS.has(commandName)) { const taskDescription = getTaskDescription(commandName); - return `Starting ESP-IDF task: ${commandName}${targetInfo}. ${taskDescription} This may take some time to complete. The terminal will be focused to show task progress.`; + return `Starting ESP-IDF task: ${commandName}${paramString}. ${taskDescription} This may take some time to complete. The terminal will be focused to show task progress.`; } if (WEBVIEW_COMMANDS.has(commandName)) { return `Opening ${getWebviewDescription( commandName - )}: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; + )}: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; } if (DIALOG_COMMANDS.has(commandName)) { - return `Executing ESP-IDF command: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; + return `Executing ESP-IDF command: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; } - return `Executing ESP-IDF command: ${commandName}${targetInfo}. The ESP-IDF output channel will be focused.`; + return `Executing ESP-IDF command: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; } function getTaskDescription(commandName: string): string { diff --git a/src/utils.ts b/src/utils.ts index 5b43a362c..8343397d8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -46,6 +46,7 @@ import { getVirtualEnvPythonPath, } from "./pythonManager"; import { IdfToolsManager } from "./idfToolsManager"; +import { isFlashEncryptionEnabled } from "./flash/verifyFlashEncryption"; const currentFolderMsg = vscode.l10n.t("ESP-IDF: Current Project"); @@ -1619,3 +1620,34 @@ export async function getConfigValueFromBuild( throw new Error(`Failed to read or parse the JSON file: ${error.message}`); } } + +/** + * Determines if the monitor reset should be disabled. + * If flash encryption is enabled for release mode, we add --no-reset flag for monitoring + * because by default monitoring command resets the device which is not recommended. + * Reset should happen by Bootloader itself once it completes encrypting all artifacts. + * + * @returns {Promise} True if monitor reset should be disabled, false otherwise. + */ +export async function shouldDisableMonitorReset( + workspaceUri: vscode.Uri +): Promise { + const configNoReset = idfConf.readParameter( + "idf.monitorNoReset", + workspaceUri + ); + + if (configNoReset === true) { + return true; + } + + if (isFlashEncryptionEnabled(workspaceUri)) { + const valueReleaseModeEnabled = await getConfigValueFromSDKConfig( + "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE", + workspaceUri + ); + return valueReleaseModeEnabled === "y"; + } + + return false; +} From de9adcf37f605471803ec93b80b766226431f10c Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Wed, 20 Aug 2025 15:08:54 +0800 Subject: [PATCH 09/15] fix hints theme icon --- src/statusBar/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/statusBar/index.ts b/src/statusBar/index.ts index 8ed1f5669..3844b928d 100644 --- a/src/statusBar/index.ts +++ b/src/statusBar/index.ts @@ -26,7 +26,7 @@ import { Uri, window, l10n, - ThemeIcon, + ThemeColor, } from "vscode"; import { getCurrentIdfSetup } from "../versionSwitcher"; import { readParameter } from "../idfConfiguration"; @@ -278,7 +278,7 @@ export function updateHintsStatusBarItem(hasHints: boolean) { statusBarItems["hints"].tooltip = l10n.t( "ESP-IDF: Hints available. Click to view." ); - statusBarItems["hints"].backgroundColor = new ThemeIcon( + statusBarItems["hints"].backgroundColor = new ThemeColor( "statusBarItem.warningBackground" ); statusBarItems["hints"].show(); From c7e8c2cff881508a7f712486ea27143ccad2ddcb Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Mon, 1 Sep 2025 19:32:16 +0800 Subject: [PATCH 10/15] use customExecution --- src/build/buildCmd.ts | 100 +++++--- src/build/buildTask.ts | 36 ++- src/customTasks/customTaskProvider.ts | 4 +- src/espIdf/setTarget/setTargetInIdf.ts | 1 + src/espIdf/size/idfSizeTask.ts | 12 +- src/extension.ts | 21 +- src/flash/eraseFlashTask.ts | 31 ++- src/flash/flashTask.ts | 21 +- src/flash/uartFlash.ts | 33 ++- src/langTools/index.ts | 108 +++++---- src/taskManager.ts | 59 ++++- src/taskManager/customExecution.ts | 306 +++++++++++++++++++++++++ 12 files changed, 605 insertions(+), 127 deletions(-) create mode 100644 src/taskManager/customExecution.ts diff --git a/src/build/buildCmd.ts b/src/build/buildCmd.ts index ffc9d1b0f..eb0cad528 100644 --- a/src/build/buildCmd.ts +++ b/src/build/buildCmd.ts @@ -29,17 +29,22 @@ import { } from "../workspaceConfig"; import { IdfSizeTask } from "../espIdf/size/idfSizeTask"; import { CustomTask, CustomTaskType } from "../customTasks/customTaskProvider"; -import { readParameter, readSerialPort } from "../idfConfiguration"; +import { readParameter } from "../idfConfiguration"; import { ESP } from "../config"; import { createFlashModel } from "../flash/flashModelBuilder"; import { OutputChannel } from "../logger/outputChannel"; +import { + CustomExecutionTaskResult, + OutputCapturingExecution, + ShellOutputCapturingExecution, +} from "../taskManager/customExecution"; /** * Build the project with the given parameters. - * + * * This function is used to build the project with the given parameters. * It will build the project, run the size task, and flash the project if the flash type is set. - * + * * @param workspace - The workspace folder URI * @param cancelToken - The cancellation token * @param flashType - The flash type @@ -51,8 +56,7 @@ export async function buildCommandMain( cancelToken: vscode.CancellationToken, flashType: ESP.FlashType, buildType?: ESP.BuildType -) { - let continueFlag = true; +): Promise { const buildTask = new BuildTask(workspace); const customTask = new CustomTask(workspace); if (BuildTask.isBuilding || FlashTask.isFlashing) { @@ -64,35 +68,45 @@ export async function buildCommandMain( new Error("One_Task_At_A_Time"), "buildCmd buildCommand" ); - continueFlag = false; - return continueFlag; + return { continueFlag: false, executions: [] }; } cancelToken.onCancellationRequested(() => { TaskManager.cancelTasks(); TaskManager.disposeListeners(); buildTask.building(false); + return { continueFlag: false, executions: [] }; }); - await customTask.addCustomTask(CustomTaskType.PreBuild); - await buildTask.build(buildType); - await TaskManager.runTasks(); + let preBuildExecution = await customTask.addCustomTask( + CustomTaskType.PreBuild + ); + let executions: ( + | OutputCapturingExecution + | ShellOutputCapturingExecution + )[] = []; + const [compileExecution, buildExecution] = await buildTask.build(buildType); + executions.push(compileExecution, buildExecution, preBuildExecution); const enableSizeTask = (await readParameter( "idf.enableSizeTaskAfterBuildTask", workspace )) as boolean; if (enableSizeTask && typeof buildType === "undefined") { const sizeTask = new IdfSizeTask(workspace); - await sizeTask.getSizeInfo(); + let sizeInfoExecution = await sizeTask.getSizeInfo(); + executions.push(sizeInfoExecution); } - await customTask.addCustomTask(CustomTaskType.PostBuild); - await TaskManager.runTasks(); + + const postBuildExecution = await customTask.addCustomTask( + CustomTaskType.PostBuild + ); + executions.push(postBuildExecution); + if (flashType === ESP.FlashType.DFU) { const buildPath = readParameter("idf.buildPath", workspace) as string; if (!(await pathExists(join(buildPath, "flasher_args.json")))) { Logger.warnNotify( "flasher_args.json file is missing from the build directory, can't proceed, please build properly!" ); - continueFlag = false; - return continueFlag; + return { continueFlag: false, executions: [] }; } const adapterTargetName = await getIdfTargetFromSdkconfig(workspace); if ( @@ -103,13 +117,13 @@ export async function buildCommandMain( Logger.warnNotify( `The selected device target "${adapterTargetName}" is not compatible for DFU, as a result the DFU.bin was not created.` ); - continueFlag = false; - return continueFlag; + return { continueFlag: false, executions: [] }; } else { - await buildTask.buildDfu(); - await TaskManager.runTasks(); + const dfuExecution = await buildTask.buildDfu(); + executions.push(dfuExecution); } } + const buildResult = await TaskManager.runTasksWithOutput(); if (!cancelToken.isCancellationRequested) { updateIdfComponentsTree(workspace); Logger.infoNotify("Build Successful"); @@ -118,7 +132,12 @@ export async function buildCommandMain( TaskManager.disposeListeners(); } buildTask.building(false); - return continueFlag; + + return { + continueFlag: buildResult.success, + executions, + results: buildResult.results, + }; } export async function buildCommand( @@ -126,30 +145,39 @@ export async function buildCommand( cancelToken: vscode.CancellationToken, flashType: ESP.FlashType, buildType?: ESP.BuildType -) { +): Promise { let continueFlag = true; try { - continueFlag = await buildCommandMain(workspace, cancelToken, flashType, buildType); + let buildCmdResults = await buildCommandMain( + workspace, + cancelToken, + flashType, + buildType + ); + continueFlag = buildCmdResults.continueFlag; + const taskExecutions = buildCmdResults.executions; + if (!continueFlag) { + throw buildCmdResults.results[0].error; + } + // const compileOutput = taskExecutions[1].getOutput(); + // const buildOutput = taskExecutions[2].getOutput(); } catch (error) { if (error.message === "ALREADY_BUILDING") { - return Logger.errorNotify( - "Already a build is running!", - error, - "buildCommand" - ); + Logger.errorNotify("Already a build is running!", error, "buildCommand"); } if (error.message === "BUILD_TERMINATED") { - return Logger.warnNotify(`Build is Terminated`); + Logger.warnNotify(`Build is Terminated`); + } else { + Logger.errorNotify( + "Something went wrong while trying to build the project", + error, + "buildCommand", + undefined, + false + ); } - Logger.errorNotify( - "Something went wrong while trying to build the project", - error, - "buildCommand", - undefined, - false - ); - continueFlag = false; } + continueFlag = false; return continueFlag; } diff --git a/src/build/buildTask.ts b/src/build/buildTask.ts index 278d8e1b4..c24c20e28 100644 --- a/src/build/buildTask.ts +++ b/src/build/buildTask.ts @@ -33,6 +33,12 @@ import { getVirtualEnvPythonPath } from "../pythonManager"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; import { ESP } from "../config"; +import { + OutputCapturingExecution, + ShellOutputCapturingExecution, +} from "../taskManager/customExecution"; +import { ProcessExecutionOptions } from "vscode"; + export class BuildTask { public static isBuilding: boolean; private buildDirPath: string; @@ -80,18 +86,12 @@ export class BuildTask { ) as string; await ensureDir(this.buildDirPath); const modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); - const processOptions = { + const processOptions: ProcessExecutionOptions = { cwd: this.buildDirPath, env: modifiedEnv, }; - const canAccessCMake = await isBinInPath( - "cmake", - modifiedEnv - ); - const canAccessNinja = await isBinInPath( - "ninja", - modifiedEnv - ); + const canAccessCMake = await isBinInPath("cmake", modifiedEnv); + const canAccessNinja = await isBinInPath("ninja", modifiedEnv); const cmakeCachePath = join(this.buildDirPath, "CMakeCache.txt"); const cmakeCacheExists = await pathExists(cmakeCachePath); @@ -114,6 +114,9 @@ export class BuildTask { ? vscode.TaskRevealKind.Always : vscode.TaskRevealKind.Silent; + let compileExecution: OutputCapturingExecution; + let buildExecution: OutputCapturingExecution; + if (!cmakeCacheExists) { const espIdfVersion = await getEspIdfFromCMake(this.idfPathDir); let defaultCompilerArgs; @@ -173,7 +176,7 @@ export class BuildTask { compilerArgs.push("-DCCACHE_ENABLE=1"); } } - const compileExecution = new vscode.ProcessExecution( + compileExecution = OutputCapturingExecution.create( canAccessCMake, compilerArgs, processOptions @@ -207,7 +210,7 @@ export class BuildTask { buildArgs.push(buildType); } const ninjaCommand = "ninja"; - const buildExecution = new vscode.ProcessExecution( + buildExecution = OutputCapturingExecution.create( ninjaCommand, buildArgs, processOptions @@ -226,6 +229,14 @@ export class BuildTask { ["espIdf", "espIdfLd"], buildPresentationOptions ); + let results: OutputCapturingExecution[] = []; + if (compileExecution) { + results.push(compileExecution); + } + if (buildExecution) { + results.push(buildExecution); + } + return results; } public async buildDfu() { @@ -266,7 +277,7 @@ export class BuildTask { cwd: this.buildDirPath, env: modifiedEnv, }; - const writeExecution = new vscode.ProcessExecution( + const writeExecution = OutputCapturingExecution.create( pythonBinPath, args, processOptions @@ -289,5 +300,6 @@ export class BuildTask { ["espIdf"], buildPresentationOptions ); + return writeExecution; } } diff --git a/src/customTasks/customTaskProvider.ts b/src/customTasks/customTaskProvider.ts index 5ebda1f67..6456ca0c0 100644 --- a/src/customTasks/customTaskProvider.ts +++ b/src/customTasks/customTaskProvider.ts @@ -29,6 +29,7 @@ import { import { NotificationMode, readParameter } from "../idfConfiguration"; import { TaskManager } from "../taskManager"; import { appendIdfAndToolsToPath } from "../utils"; +import { ShellOutputCapturingExecution } from "../taskManager/customExecution"; export enum CustomTaskType { Custom = "custom", @@ -51,7 +52,7 @@ export class CustomTask { cmdString: string, options: ShellExecutionOptions ) { - return new ShellExecution(`${cmdString}`, options); + return new ShellOutputCapturingExecution(cmdString, options); } public async addCustomTask(taskType: CustomTaskType) { @@ -133,6 +134,7 @@ export class CustomTask { ["espIdf", "espIdfLd"], customTaskPresentationOptions ); + return customExecution; } public async runTasks(taskType: CustomTaskType) { diff --git a/src/espIdf/setTarget/setTargetInIdf.ts b/src/espIdf/setTarget/setTargetInIdf.ts index 010e5a6aa..f42aac1d4 100644 --- a/src/espIdf/setTarget/setTargetInIdf.ts +++ b/src/espIdf/setTarget/setTargetInIdf.ts @@ -89,6 +89,7 @@ export async function setTargetInIDF( OutputChannel.appendLineAndShow(msg, "Set Target"); Logger.infoNotify(msg); setCCppPropertiesJsonCompilerPath(workspaceFolder.uri); + return setTargetResult.toString(); } catch (error) { throw new Error( `Failed to set target ${selectedTarget.target}: ${error.message}.` diff --git a/src/espIdf/size/idfSizeTask.ts b/src/espIdf/size/idfSizeTask.ts index e2fb730cc..3dc254cce 100644 --- a/src/espIdf/size/idfSizeTask.ts +++ b/src/espIdf/size/idfSizeTask.ts @@ -26,13 +26,14 @@ import { TaskScope, Uri, workspace, - ProcessExecution + ProcessExecution, } from "vscode"; import { NotificationMode, readParameter } from "../../idfConfiguration"; import { TaskManager } from "../../taskManager"; import { appendIdfAndToolsToPath } from "../../utils"; import { getProjectName } from "../../workspaceConfig"; import { getVirtualEnvPythonPath } from "../../pythonManager"; +import { OutputCapturingExecution } from "../../taskManager/customExecution"; export class IdfSizeTask { private currentWorkspace: Uri; @@ -53,7 +54,7 @@ export class IdfSizeTask { public async getSizeInfo() { await ensureDir(this.buildDirPath); - const pythonCommand = await getVirtualEnvPythonPath(this.currentWorkspace);; + const pythonCommand = await getVirtualEnvPythonPath(this.currentWorkspace); const mapFilePath = await this.mapFilePath(); const args = [this.idfSizePath, mapFilePath]; @@ -63,7 +64,11 @@ export class IdfSizeTask { env: modifiedEnv, }; - const sizeExecution = new ProcessExecution(pythonCommand, args, processOptions); + const sizeExecution = OutputCapturingExecution.create( + pythonCommand, + args, + processOptions + ); const notificationMode = readParameter( "idf.notificationMode", this.currentWorkspace @@ -90,5 +95,6 @@ export class IdfSizeTask { ["espIdf"], sizePresentationOptions ); + return sizeExecution; } } diff --git a/src/extension.ts b/src/extension.ts index ec9b0695f..6513d4c18 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -195,6 +195,7 @@ import { configureClangSettings } from "./clang"; import { OpenOCDErrorMonitor } from "./espIdf/hints/openocdhint"; import { updateHintsStatusBarItem } from "./statusBar"; import { activateLanguageTool, deactivateLanguageTool } from "./langTools"; +import { readSerialPort } from "./idfConfiguration"; // Global variables shared by commands let workspaceRoot: vscode.Uri; @@ -715,8 +716,17 @@ export async function activate(context: vscode.ExtensionContext) { cancelToken: vscode.CancellationToken ) => { try { + const port = await readSerialPort(this.currentWorkspace, false); + if (!port) { + return Logger.warnNotify( + vscode.l10n.t( + "No serial port found for current IDF_TARGET: {0}", + await getIdfTargetFromSdkconfig(this.currentWorkspace) + ) + ); + } const eraseFlashTask = new EraseFlashTask(workspaceRoot); - await eraseFlashTask.eraseFlash(); + await eraseFlashTask.eraseFlash(port); await TaskManager.runTasks(); if (!cancelToken.isCancellationRequested) { EraseFlashTask.isErasing = false; @@ -725,6 +735,8 @@ export async function activate(context: vscode.ExtensionContext) { Logger.infoNotify(msg); } TaskManager.disposeListeners(); + OutputChannel.appendLine("Flash memory content has been erased."); + Logger.infoNotify("Flash memory content has been erased."); } catch (error) { Logger.errorNotify(error.message, error, "extension eraseFlash"); } @@ -4376,7 +4388,7 @@ async function startFlashing( flashType: ESP.FlashType, encryptPartitions: boolean, partitionToUse?: ESP.BuildType -) { +): Promise { if (!flashType) { flashType = await selectFlashMethod(); } @@ -4409,12 +4421,13 @@ async function startFlashing( const port = await idfConf.readSerialPort(workspaceRoot, false); if (!port) { - return Logger.warnNotify( + Logger.warnNotify( vscode.l10n.t( "No serial port found for current IDF_TARGET: {0}", await getIdfTargetFromSdkconfig(workspaceRoot) ) ); + return false; } const flashBaudRate = idfConf.readParameter( "idf.flashBaudRate", @@ -4427,7 +4440,7 @@ async function startFlashing( workspaceRoot ); if (!canFlash) { - return; + return false; } if (flashType === ESP.FlashType.JTAG) { diff --git a/src/flash/eraseFlashTask.ts b/src/flash/eraseFlashTask.ts index 23dbcc7cc..4b3bb88e8 100644 --- a/src/flash/eraseFlashTask.ts +++ b/src/flash/eraseFlashTask.ts @@ -28,7 +28,11 @@ import { } from "vscode"; import { join } from "path"; import { constants } from "fs"; -import { NotificationMode, readParameter, readSerialPort } from "../idfConfiguration"; +import { + NotificationMode, + readParameter, + readSerialPort, +} from "../idfConfiguration"; import { appendIdfAndToolsToPath, canAccessFile } from "../utils"; import { sleep } from "../utils"; import { TaskManager } from "../taskManager"; @@ -38,6 +42,7 @@ import { IDFMonitor } from "../espIdf/monitor"; import { Logger } from "../logger/logger"; import { OutputChannel } from "../logger/outputChannel"; import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; +import { OutputCapturingExecution } from "../taskManager/customExecution"; export class EraseFlashTask { public static isErasing: boolean; @@ -69,7 +74,7 @@ export class EraseFlashTask { } } - public async eraseFlash() { + public async eraseFlash(port: string) { if (EraseFlashTask.isErasing) { throw new Error("ALREADY_ERASING"); } @@ -106,7 +111,7 @@ export class EraseFlashTask { env: this.modifiedEnv, }; - const eraseExecution = this._eraseExecution(pythonBinPath); + const eraseExecution = this._eraseExecution(pythonBinPath, port); const erasePresentationOptions = { reveal: showTaskOutput, showReuseMessage: false, @@ -126,22 +131,16 @@ export class EraseFlashTask { ["espIdf"], erasePresentationOptions ); - OutputChannel.appendLine("Flash memory content has been erased."); - Logger.infoNotify("Flash memory content has been erased."); + return eraseExecution; } - private async _eraseExecution(pythonBinPath: string) { + private async _eraseExecution(pythonBinPath: string, port: string) { this.erasing(true); - const port = await readSerialPort(this.currentWorkspace, false); - if (!port) { - return Logger.warnNotify( - l10n.t( - "No serial port found for current IDF_TARGET: {0}", - await getIdfTargetFromSdkconfig(this.currentWorkspace) - ) - ); - } const args = [this.flashScriptPath, "-p", port, "erase_flash"]; - return new ProcessExecution(pythonBinPath, args, this.processOptions); + return OutputCapturingExecution.create( + pythonBinPath, + args, + this.processOptions + ); } } diff --git a/src/flash/flashTask.ts b/src/flash/flashTask.ts index 82f791894..ef08f9bc1 100644 --- a/src/flash/flashTask.ts +++ b/src/flash/flashTask.ts @@ -26,6 +26,7 @@ import { TaskManager } from "../taskManager"; import { getDfuList, selectDfuDevice, selectedDFUAdapterId } from "./dfu"; import { ESP } from "../config"; import { getVirtualEnvPythonPath } from "../pythonManager"; +import { OutputCapturingExecution } from "../taskManager/customExecution"; export class FlashTask { public static isFlashing: boolean; @@ -102,7 +103,7 @@ export class FlashTask { notificationMode === idfConf.NotificationMode.Output ? vscode.TaskRevealKind.Always : vscode.TaskRevealKind.Silent; - let flashExecution: vscode.ProcessExecution; + let flashExecution: OutputCapturingExecution; this.modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); this.processOptions = { cwd: this.buildDirPath, @@ -155,16 +156,22 @@ export class FlashTask { ["espIdf"], flashPresentationOptions ); + return flashExecution; } public _flashExecution(pythonBinPath: string) { this.flashing(true); const flasherArgs = this.getFlasherArgs(this.flashScriptPath); - return new vscode.ProcessExecution( + return OutputCapturingExecution.create( pythonBinPath, flasherArgs, this.processOptions ); + // return new vscode.ProcessExecution( + // pythonBinPath, + // flasherArgs, + // this.processOptions + // ); } public async _dfuFlashing(pythonBinPath: string) { @@ -191,7 +198,8 @@ export class FlashTask { join(this.buildDirPath, "dfu.bin"), ]; } - return new vscode.ProcessExecution(cmd, args, this.processOptions); + return OutputCapturingExecution.create(cmd, args, this.processOptions); + // return new vscode.ProcessExecution(cmd, args, this.processOptions); } public _partitionFlashExecution( @@ -203,11 +211,16 @@ export class FlashTask { this.flashScriptPath, sectionToUse ); - return new vscode.ProcessExecution( + return OutputCapturingExecution.create( pythonBinPath, flasherArgs, this.processOptions ); + // return new vscode.ProcessExecution( + // pythonBinPath, + // flasherArgs, + // this.processOptions + // ); } public getSingleBinFlasherArgs( diff --git a/src/flash/uartFlash.ts b/src/flash/uartFlash.ts index fb077bfd2..2aecc347c 100644 --- a/src/flash/uartFlash.ts +++ b/src/flash/uartFlash.ts @@ -26,6 +26,7 @@ import { CustomTask, CustomTaskType } from "../customTasks/customTaskProvider"; import { readParameter } from "../idfConfiguration"; import { ESP } from "../config"; import { OutputChannel } from "../logger/outputChannel"; +import { CustomExecutionTaskResult } from "../taskManager/customExecution"; export async function uartFlashCommandMain( cancelToken: CancellationToken, @@ -36,7 +37,7 @@ export async function uartFlashCommandMain( flashType: ESP.FlashType, encryptPartitions: boolean, partitionToUse?: ESP.BuildType -) { +): Promise { const buildPath = readParameter("idf.buildPath", workspace) as string; const buildFiles = await readdir(buildPath); const binFiles = buildFiles.filter( @@ -51,7 +52,7 @@ export async function uartFlashCommandMain( new Error("BIN_FILE_ACCESS_ERROR"), "flashCommand binfile access error" ); - return false; + return { continueFlag: false, executions: [] }; } const flasherArgsJsonPath = join(buildPath, "flasher_args.json"); cancelToken.onCancellationRequested(() => { @@ -73,17 +74,27 @@ export async function uartFlashCommandMain( cancelToken.onCancellationRequested(() => { FlashTask.isFlashing = false; }); - await customTask.addCustomTask(CustomTaskType.PreFlash); - await flashTask.flash(flashType, partitionToUse); - await customTask.addCustomTask(CustomTaskType.PostFlash); - await TaskManager.runTasks(); + const preFlashExecution = await customTask.addCustomTask( + CustomTaskType.PreFlash + ); + const flashExecution = await flashTask.flash(flashType, partitionToUse); + const postFlashExecution = await customTask.addCustomTask( + CustomTaskType.PostFlash + ); + const flashResult = await TaskManager.runTasksWithOutput(); + if (!cancelToken.isCancellationRequested) { - FlashTask.isFlashing = false; const msg = "Flash Done ⚡️"; OutputChannel.appendLineAndShow(msg, "Flash"); Logger.infoNotify(msg); + TaskManager.disposeListeners(); } - TaskManager.disposeListeners(); + FlashTask.isFlashing = false; + return { + continueFlag: flashResult.success, + executions: [preFlashExecution, flashExecution, postFlashExecution], + results: flashResult.results, + }; } export async function flashCommand( @@ -98,7 +109,7 @@ export async function flashCommand( ) { let continueFlag = true; try { - await uartFlashCommandMain( + let flashCmdResult = await uartFlashCommandMain( cancelToken, flashBaudRate, idfPathDir, @@ -108,6 +119,10 @@ export async function flashCommand( encryptPartitions, partitionToUse ); + continueFlag = flashCmdResult.continueFlag; + if (!continueFlag) { + throw flashCmdResult.results[0].error; + } } catch (error) { if (error.message === "ALREADY_FLASHING") { const errStr = "Already one flash process is running!"; diff --git a/src/langTools/index.ts b/src/langTools/index.ts index 3b864588a..d49efa600 100644 --- a/src/langTools/index.ts +++ b/src/langTools/index.ts @@ -30,6 +30,11 @@ import { getIdfTargetFromSdkconfig } from "../workspaceConfig"; import { setTargetInIDF } from "../espIdf/setTarget/setTargetInIdf"; import { statusBarItems } from "../statusBar"; import { isSettingIDFTarget, setIsSettingIDFTarget } from "../espIdf/setTarget"; +import { + OutputCapturingExecution, + ShellOutputCapturingExecution, +} from "../taskManager/customExecution"; +import { l10n } from "vscode"; // Map of command names to their corresponding VS Code command IDs const COMMAND_MAP: Record = { @@ -66,10 +71,7 @@ const TASK_COMMANDS = new Set([ "flash", "monitor", "buildFlashMonitor", - "fullClean", "eraseFlash", - "apptrace", - "heaptrace", ]); const WEBVIEW_COMMANDS = new Set([ @@ -80,9 +82,6 @@ const WEBVIEW_COMMANDS = new Set([ "componentManager", ]); -// Commands that show dialogs or simple operations -const DIALOG_COMMANDS = new Set(["selectPort", "setTarget", "doctor"]); - let disposable: vscode.Disposable | undefined; export function activateLanguageTool(context: vscode.ExtensionContext) { @@ -161,16 +160,22 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { } let continueFlag = true; + let taskExecutions: ( + | OutputCapturingExecution + | ShellOutputCapturingExecution + )[] = []; if (commandId) { try { await focusOnAppropriateOutput(commandName); if (commandName === "build") { - continueFlag = await buildCommandMain( + let buildCmdResults = await buildCommandMain( workspaceURI, token, flashType, partitionToUse ); + continueFlag = buildCmdResults.continueFlag; + taskExecutions.push(...buildCmdResults.executions); } else if (commandName === "flash") { if (IDFMonitor.terminal) { IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); @@ -224,7 +229,7 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { "idf.espIdfPath", workspaceURI ) as string; - await uartFlashCommandMain( + const flashCmdResult = await uartFlashCommandMain( token, flashBaudRate, idfPathDir, @@ -234,6 +239,8 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { encryptPartitions, partitionToUse ); + continueFlag = flashCmdResult.continueFlag; + taskExecutions.push(...flashCmdResult.executions); } } else if (commandName === "monitor") { if (IDFMonitor.terminal) { @@ -250,12 +257,14 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { const noReset = await shouldDisableMonitorReset(workspaceURI); await createNewIdfMonitor(workspaceURI, noReset); } else if (commandName === "buildFlashMonitor") { - continueFlag = await buildCommandMain( + let buildCmdResults = await buildCommandMain( workspaceURI, token, flashType, partitionToUse ); + continueFlag = buildCmdResults.continueFlag; + taskExecutions.push(...buildCmdResults.executions); if (!continueFlag) { return new vscode.LanguageModelToolResult([ new vscode.LanguageModelTextPart( @@ -327,7 +336,7 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { "idf.espIdfPath", workspaceURI ) as string; - await uartFlashCommandMain( + let flashCmdResult = await uartFlashCommandMain( token, flashBaudRate, idfPathDir, @@ -337,6 +346,8 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { encryptPartitions, partitionToUse ); + continueFlag = flashCmdResult.continueFlag; + taskExecutions.push(...flashCmdResult.executions); } if (IDFMonitor.terminal) { IDFMonitor.terminal.sendText(ESP.CTRL_RBRACKET); @@ -344,9 +355,19 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { const noReset = await shouldDisableMonitorReset(workspaceURI); await createNewIdfMonitor(workspaceURI, noReset); } else if (commandName === "eraseFlash") { + const port = await readSerialPort(this.currentWorkspace, false); + if (!port) { + Logger.warnNotify( + l10n.t( + "No serial port found for current IDF_TARGET: {0}", + await getIdfTargetFromSdkconfig(this.currentWorkspace) + ) + ); + } const eraseFlashTask = new EraseFlashTask(workspaceURI); - await eraseFlashTask.eraseFlash(); - await TaskManager.runTasks(); + const eraseFlashExecution = await eraseFlashTask.eraseFlash(port); + const eraseFlashResult = await TaskManager.runTasksWithOutput(); + taskExecutions.push(eraseFlashExecution); if (!token.isCancellationRequested) { EraseFlashTask.isErasing = false; const msg = "Erase flash done"; @@ -354,6 +375,8 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { Logger.infoNotify(msg); } TaskManager.disposeListeners(); + OutputChannel.appendLine("Flash memory content has been erased."); + Logger.infoNotify("Flash memory content has been erased."); } else if (commandName === "setTarget") { if (!target) { return new vscode.LanguageModelToolResult([ @@ -383,11 +406,17 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { workspaceURI ); if (isSettingIDFTarget) { - Logger.info("setTargetInIDF is already running."); - return; + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `"setTargetInIDF is already running."` + ), + ]); } setIsSettingIDFTarget(true); - await setTargetInIDF(workspaceFolder, selectedTarget); + const setTargetResult = await setTargetInIDF( + workspaceFolder, + selectedTarget + ); // Update configuration like setIdfTarget does const configurationTarget = @@ -414,10 +443,25 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { ); setIsSettingIDFTarget(false); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart(setTargetResult), + ]); } else { await vscode.commands.executeCommand(commandId); } + let outputs: vscode.LanguageModelTextPart[] = []; + if (TASK_COMMANDS.has(commandName)) { + for (const execution of taskExecutions) { + if (execution) { + const output = await execution.getOutput(); + outputs.push(new vscode.LanguageModelTextPart(output.stdout)); + outputs.push(new vscode.LanguageModelTextPart(output.stderr)); + } + } + return new vscode.LanguageModelToolResult(outputs); + } + const feedback = await getCommandFeedback( commandName, target, @@ -573,27 +617,19 @@ async function getCommandFeedback( if (flashType) params.push(`flash type "${flashType}"`); const paramString = params.length > 0 ? ` with ${params.join(", ")}` : ""; - if (TASK_COMMANDS.has(commandName)) { - const taskDescription = getTaskDescription(commandName); - return `Command "${commandName}"${paramString} has been started successfully. ${taskDescription} The task is now running in the background. The terminal tab has been focused for you to monitor the task progress. You can also check the output panel or status bar for additional information.`; - } - if (WEBVIEW_COMMANDS.has(commandName)) { const webviewDescription = getWebviewDescription(commandName); return `Command "${commandName}"${paramString} has been executed successfully. A webview panel has been opened for you to interact with the ${webviewDescription}. The ESP-IDF output channel has been focused to show any relevant information.`; } - if (DIALOG_COMMANDS.has(commandName)) { - if (commandName === "setTarget" && target) { - return `Command "${commandName}" with target "${target}" is now running. The ESP-IDF target is being set to ${target}. The ESP-IDF output channel has been focused to show the operation details. You can verify this in the status bar or by checking your project configuration.`; - } - if (commandName === "selectPort") { - return `Command "${commandName}"${paramString} is now running. A port selection dialog has been opened. The ESP-IDF output channel has been focused to show any relevant information. Please select the appropriate serial port for your ESP-IDF device.`; - } - if (commandName === "doctor") { - return `Command "${commandName}"${paramString} is now running. The ESP-IDF doctor diagnostic tool is now running. The ESP-IDF output channel has been focused to show detailed information about your ESP-IDF setup and any potential issues.`; - } - return `Command "${commandName}"${paramString} is now running. A dialog or interface has been opened for you to complete the operation. The ESP-IDF output channel has been focused to show any relevant information.`; + if (commandName === "setTarget" && target) { + return `Command "${commandName}" with target "${target}" is now running. The ESP-IDF target is being set to ${target}. The ESP-IDF output channel has been focused to show the operation details. You can verify this in the status bar or by checking your project configuration.`; + } + if (commandName === "selectPort") { + return `Command "${commandName}"${paramString} is now running. A port selection dialog has been opened. The ESP-IDF output channel has been focused to show any relevant information. Please select the appropriate serial port for your ESP-IDF device.`; + } + if (commandName === "doctor") { + return `Command "${commandName}"${paramString} is now running. The ESP-IDF doctor diagnostic tool is now running. The ESP-IDF output channel has been focused to show detailed information about your ESP-IDF setup and any potential issues.`; } return `Command "${commandName}"${paramString} is now running. The ESP-IDF output channel has been focused to show any relevant information.`; @@ -624,10 +660,6 @@ function getInvocationMessage( )}: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; } - if (DIALOG_COMMANDS.has(commandName)) { - return `Executing ESP-IDF command: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; - } - return `Executing ESP-IDF command: ${commandName}${paramString}. The ESP-IDF output channel will be focused.`; } @@ -641,14 +673,8 @@ function getTaskDescription(commandName: string): string { return "This will open a serial monitor to view device output and send commands."; case "buildFlashMonitor": return "This will build the project, flash it to the device, and start monitoring in sequence."; - case "fullClean": - return "This will clean all build artifacts and temporary files from your project."; case "eraseFlash": return "This will completely erase the flash memory of your ESP-IDF device."; - case "apptrace": - return "This will start application tracing to analyze performance and behavior."; - case "heaptrace": - return "This will start heap tracing to monitor memory allocation and usage."; default: return "This task will be executed in the background."; } diff --git a/src/taskManager.ts b/src/taskManager.ts index e05ddc15f..ad46db0e3 100644 --- a/src/taskManager.ts +++ b/src/taskManager.ts @@ -27,6 +27,7 @@ export interface IdfTaskDefinition extends vscode.TaskDefinition { export class TaskManager { private static tasks: vscode.Task[] = []; private static disposables: vscode.Disposable[] = []; + private static taskResults: Array<{ taskId: string; output?: any; error?: Error }> = []; public static addTask( taskDefinition: IdfTaskDefinition, @@ -50,7 +51,7 @@ export class TaskManager { newTask.presentationOptions = presentationOptions; TaskManager.tasks.push(newTask); return new Promise((resolve, reject) => { - vscode.tasks.onDidEndTask((e) => { + const taskEndListener = vscode.tasks.onDidEndTask(async (e) => { if ( e.execution && e.execution.task.definition.taskId.indexOf( @@ -60,6 +61,7 @@ export class TaskManager { return resolve(); } }); + TaskManager.disposables.push(taskEndListener); }); } @@ -95,6 +97,14 @@ export class TaskManager { lastExecution.task.definition.taskId ) !== -1 ) { + // Store the result regardless of success/failure + const taskResult = { + taskId: lastExecution.task.definition.taskId, + exitCode: e.exitCode, + taskName: lastExecution.task.name + }; + TaskManager.taskResults.push(taskResult); + if (e.exitCode !== 0) { this.cancelTasks(); this.disposeListeners(); @@ -118,4 +128,51 @@ export class TaskManager { TaskManager.disposables.push(taskDisposable); }); } + + public static getTaskResults() { + return TaskManager.taskResults; + } + + public static clearTaskResults() { + TaskManager.taskResults = []; + } + + public static async runTasksWithOutput() { + const results: Array<{ taskId: string; output?: any; error?: Error; exitCode: number }> = []; + + try { + await TaskManager.runTasks(); + // If we get here, all tasks succeeded + return { success: true, results }; + } catch (error) { + // Tasks failed, but we can still get output from custom executions + const customExecutions = TaskManager.tasks.filter(task => + task.execution && + (task.execution as any).getOutput && + typeof (task.execution as any).getOutput === 'function' + ); + + for (const task of customExecutions) { + try { + const execution = task.execution as any; + const output = await execution.getOutput(); + results.push({ + taskId: task.definition.taskId, + output, + error: undefined, + exitCode: output.exitCode + }); + } catch (execError) { + results.push({ + taskId: task.definition.taskId, + output: undefined, + error: execError as Error, + exitCode: -1 + }); + } + } + + return { success: false, results }; + } + } } diff --git a/src/taskManager/customExecution.ts b/src/taskManager/customExecution.ts new file mode 100644 index 000000000..c3c15bbc9 --- /dev/null +++ b/src/taskManager/customExecution.ts @@ -0,0 +1,306 @@ +/* + * Project: ESP-IDF VSCode Extension + * File Created: Friday, 27th September 2019 9:59:57 pm + * Copyright 2019 Espressif Systems (Shanghai) CO LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import * as childProcess from "child_process"; + +export interface CustomExecutionTaskResult { + continueFlag: boolean; + executions: (OutputCapturingExecution | ShellOutputCapturingExecution)[]; + results?: Array<{ + taskId: string; + output?: any; + error?: Error; + exitCode: number; + }>; +} + +export interface CapturedTaskOutput { + stdout: string; + stderr: string; + exitCode: number; + success: boolean; +} + +class OutputCapturingPseudoterminal implements vscode.Pseudoterminal { + private writeEmitter = new vscode.EventEmitter(); + private closeEmitter = new vscode.EventEmitter(); + + constructor(private outputPromise: Promise) { + this.outputPromise + .then((output) => { + // Don't write output again since it's already been streamed in real-time + // Just close the terminal with the exit code + this.closeEmitter.fire(output.exitCode); + }) + .catch((error) => { + // Write error to terminal only if it wasn't already streamed + this.writeEmitter.fire(`Error: ${error.message}\n`); + // Use the error's exit code if available, otherwise default to 1 + const exitCode = (error as any).code || 1; + this.closeEmitter.fire(exitCode); + }); + } + + onDidWrite: vscode.Event = this.writeEmitter.event; + onDidClose: vscode.Event = this.closeEmitter.event; + + open(): void { + // Terminal is opened, but we don't need to do anything special + } + + close(): void { + // Terminal is closed - this is called when VS Code wants to terminate the task + // We can't directly terminate the child process from here, but the execution class + // should handle termination through its terminate() method + } + + handleInput(data: string): void { + // Handle any input if needed + } + + getWriteEmitter(): vscode.EventEmitter { + return this.writeEmitter; + } +} + +export class OutputCapturingExecution extends vscode.CustomExecution { + private outputPromise: Promise | undefined; + private resolveOutput: ((output: CapturedTaskOutput) => void) | undefined; + private rejectOutput: ((error: Error) => void) | undefined; + private writeEmitter: vscode.EventEmitter | undefined; + private childProcess: childProcess.ChildProcess | undefined; + + constructor( + private command: string, + private args: string[], + private options: childProcess.ExecFileOptions + ) { + super(async (resolvedDefinition) => { + this.outputPromise = new Promise( + (resolve, reject) => { + this.resolveOutput = resolve; + this.rejectOutput = reject; + } + ); + + const pseudoterminal = new OutputCapturingPseudoterminal( + this.outputPromise! + ); + this.writeEmitter = pseudoterminal.getWriteEmitter(); + + this.executeCommand(); + return pseudoterminal; + }); + } + + private executeCommand(): void { + let stdout = ""; + let stderr = ""; + let exitCode = 0; + + this.childProcess = childProcess.execFile( + this.command, + this.args, + this.options + ); + + // Stream stdout in real-time + this.childProcess.stdout?.on("data", (data: Buffer) => { + const output = data.toString(); + const formattedOutput = output.replace(/\r/g, "").replace(/\n/g, "\r\n"); + stdout += formattedOutput; + if (this.writeEmitter) { + this.writeEmitter.fire(formattedOutput); + } + }); + + // Stream stderr in real-time + this.childProcess.stderr?.on("data", (data: Buffer) => { + const output = data.toString(); + const formattedOutput = output.replace(/\r/g, "").replace(/\n/g, "\r\n"); + stderr += formattedOutput; + if (this.writeEmitter) { + this.writeEmitter.fire(formattedOutput); + } + }); + + this.childProcess.on("close", (code) => { + exitCode = code || 0; + const output: CapturedTaskOutput = { + stdout, + stderr, + exitCode, + success: exitCode === 0, + }; + + if (this.resolveOutput) { + this.resolveOutput(output); + } + }); + + this.childProcess.on("error", (error) => { + if (this.rejectOutput) { + this.rejectOutput(error); + } + }); + } + + public terminate(): void { + if (this.childProcess) { + this.childProcess.kill(); + } + } + + public async getOutput(): Promise { + if (!this.outputPromise) { + throw new Error("Task has not been executed yet"); + } + return this.outputPromise; + } + + public static create( + command: string, + args: string[], + options: childProcess.ExecFileOptions + ): OutputCapturingExecution { + return new OutputCapturingExecution(command, args, options); + } +} + +export class ShellOutputCapturingExecution extends vscode.CustomExecution { + private outputPromise: Promise | undefined; + private resolveOutput: ((output: CapturedTaskOutput) => void) | undefined; + private rejectOutput: ((error: Error) => void) | undefined; + private writeEmitter: vscode.EventEmitter | undefined; + private childProcess: childProcess.ChildProcess | undefined; + + constructor( + private command: string, + private options: vscode.ShellExecutionOptions + ) { + super(async (resolvedDefinition) => { + this.outputPromise = new Promise( + (resolve, reject) => { + this.resolveOutput = resolve; + this.rejectOutput = reject; + } + ); + + const pseudoterminal = new OutputCapturingPseudoterminal( + this.outputPromise! + ); + this.writeEmitter = pseudoterminal.getWriteEmitter(); + + this.executeShellCommand(); + return pseudoterminal; + }); + } + + private executeShellCommand(): void { + let stdout = ""; + let stderr = ""; + let exitCode = 0; + + const execOptions: any = { + cwd: this.options.cwd, + env: this.options.env, + }; + + if (this.options.executable) { + execOptions.shell = this.options.executable; + } + + if (this.options.shellArgs) { + execOptions.shellArgs = this.options.shellArgs; + } + + this.childProcess = childProcess.spawn( + this.options.executable || process.env.SHELL || "sh", + this.options.shellArgs || [], + { + ...execOptions, + stdio: ["pipe", "pipe", "pipe"], + } + ); + + // Send the command to the shell + this.childProcess.stdin?.write(this.command + "\n"); + this.childProcess.stdin?.end(); + + // Stream stdout in real-time + this.childProcess.stdout?.on("data", (data: Buffer) => { + const output = data.toString(); + const formattedOutput = output.replace(/\r/g, "").replace(/\n/g, "\r\n"); + stdout += formattedOutput; + if (this.writeEmitter) { + this.writeEmitter.fire(formattedOutput); + } + }); + + // Stream stderr in real-time + this.childProcess.stderr?.on("data", (data: Buffer) => { + const output = data.toString(); + const formattedOutput = output.replace(/\r/g, "").replace(/\n/g, "\r\n"); + stderr += formattedOutput; + if (this.writeEmitter) { + this.writeEmitter.fire(formattedOutput); + } + }); + + this.childProcess.on("close", (code) => { + exitCode = code || 0; + const output: CapturedTaskOutput = { + stdout, + stderr, + exitCode, + success: exitCode === 0, + }; + + if (this.resolveOutput) { + this.resolveOutput(output); + } + }); + + this.childProcess.on("error", (error) => { + if (this.rejectOutput) { + this.rejectOutput(error); + } + }); + } + + public terminate(): void { + if (this.childProcess) { + this.childProcess.kill(); + } + } + + public async getOutput(): Promise { + if (!this.outputPromise) { + throw new Error("Task has not been executed yet"); + } + return this.outputPromise; + } + + public static create( + command: string, + options: vscode.ShellExecutionOptions + ): ShellOutputCapturingExecution { + return new ShellOutputCapturingExecution(command, options); + } +} From 8a9f52d235cb0bbe8a2b502d1b39e3114019119d Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Mon, 1 Sep 2025 19:32:40 +0800 Subject: [PATCH 11/15] fix lint --- src/taskManager.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/taskManager.ts b/src/taskManager.ts index ad46db0e3..fc38bdd3d 100644 --- a/src/taskManager.ts +++ b/src/taskManager.ts @@ -27,7 +27,11 @@ export interface IdfTaskDefinition extends vscode.TaskDefinition { export class TaskManager { private static tasks: vscode.Task[] = []; private static disposables: vscode.Disposable[] = []; - private static taskResults: Array<{ taskId: string; output?: any; error?: Error }> = []; + private static taskResults: Array<{ + taskId: string; + output?: any; + error?: Error; + }> = []; public static addTask( taskDefinition: IdfTaskDefinition, @@ -101,7 +105,7 @@ export class TaskManager { const taskResult = { taskId: lastExecution.task.definition.taskId, exitCode: e.exitCode, - taskName: lastExecution.task.name + taskName: lastExecution.task.name, }; TaskManager.taskResults.push(taskResult); @@ -138,18 +142,24 @@ export class TaskManager { } public static async runTasksWithOutput() { - const results: Array<{ taskId: string; output?: any; error?: Error; exitCode: number }> = []; - + const results: Array<{ + taskId: string; + output?: any; + error?: Error; + exitCode: number; + }> = []; + try { await TaskManager.runTasks(); // If we get here, all tasks succeeded return { success: true, results }; } catch (error) { // Tasks failed, but we can still get output from custom executions - const customExecutions = TaskManager.tasks.filter(task => - task.execution && - (task.execution as any).getOutput && - typeof (task.execution as any).getOutput === 'function' + const customExecutions = TaskManager.tasks.filter( + (task) => + task.execution && + (task.execution as any).getOutput && + typeof (task.execution as any).getOutput === "function" ); for (const task of customExecutions) { @@ -160,14 +170,14 @@ export class TaskManager { taskId: task.definition.taskId, output, error: undefined, - exitCode: output.exitCode + exitCode: output.exitCode, }); } catch (execError) { results.push({ taskId: task.definition.taskId, output: undefined, error: execError as Error, - exitCode: -1 + exitCode: -1, }); } } From 235549a509e240d1f4b7d3e98db58cf9a8d1733b Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Tue, 2 Sep 2025 14:19:46 +0800 Subject: [PATCH 12/15] fix idf size task --- src/espIdf/size/idfSizeTask.ts | 17 +++++++---------- src/test/project.test.ts | 4 ++-- src/utils.ts | 8 ++++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/espIdf/size/idfSizeTask.ts b/src/espIdf/size/idfSizeTask.ts index 3dc254cce..f3087acdf 100644 --- a/src/espIdf/size/idfSizeTask.ts +++ b/src/espIdf/size/idfSizeTask.ts @@ -2,13 +2,13 @@ * Project: ESP-IDF VSCode Extension * File Created: Wednesday, 3rd November 2021 4:56:23 pm * Copyright 2021 Espressif Systems (Shanghai) CO LTD - *  + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *  + * * http://www.apache.org/licenses/LICENSE-2.0 - *  + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,18 +20,15 @@ import { ensureDir } from "fs-extra"; import { join } from "path"; import { TaskPanelKind, - ProcessExecutionOptions, TaskPresentationOptions, TaskRevealKind, TaskScope, Uri, workspace, - ProcessExecution, } from "vscode"; import { NotificationMode, readParameter } from "../../idfConfiguration"; import { TaskManager } from "../../taskManager"; -import { appendIdfAndToolsToPath } from "../../utils"; -import { getProjectName } from "../../workspaceConfig"; +import { appendIdfAndToolsToPath, readProjectCMakeLists } from "../../utils"; import { getVirtualEnvPythonPath } from "../../pythonManager"; import { OutputCapturingExecution } from "../../taskManager/customExecution"; @@ -47,15 +44,15 @@ export class IdfSizeTask { this.buildDirPath = readParameter("idf.buildPath", workspaceUri) as string; } - private async mapFilePath() { - const projectName = await getProjectName(this.buildDirPath); + private mapFilePath() { + const projectName = readProjectCMakeLists(this.currentWorkspace.fsPath); return join(this.buildDirPath, `${projectName}.map`); } public async getSizeInfo() { await ensureDir(this.buildDirPath); const pythonCommand = await getVirtualEnvPythonPath(this.currentWorkspace); - const mapFilePath = await this.mapFilePath(); + const mapFilePath = this.mapFilePath(); const args = [this.idfSizePath, mapFilePath]; const modifiedEnv = await appendIdfAndToolsToPath(this.currentWorkspace); diff --git a/src/test/project.test.ts b/src/test/project.test.ts index 83eb3dc16..30c7f3481 100644 --- a/src/test/project.test.ts +++ b/src/test/project.test.ts @@ -117,8 +117,8 @@ suite("Project tests", () => { const newProjectName = readProjectCMakeLists(projectPath); assert.notEqual(currProjectName, undefined); assert.notEqual(newProjectName, undefined); - assert.equal(currProjectName[0], `project(${prevName})`); - assert.equal(newProjectName[0], `project(${newName})`); + assert.equal(currProjectName, `${prevName}`); + assert.equal(newProjectName, `${newName}`); }); test("get templates projects", async () => { diff --git a/src/utils.ts b/src/utils.ts index 8343397d8..48bdcb68f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -185,7 +185,7 @@ export function spawn( sendToTelemetry: true, } ): Promise { - let buff = Buffer.alloc(0); + let buff: Buffer = Buffer.alloc(0); const sendToOutputChannel = (data: any) => { buff = Buffer.concat([buff, data]); options.outputString += buff.toString(); @@ -803,9 +803,9 @@ export function readProjectCMakeLists(dirPath: string) { const cmakeListFile = path.join(dirPath, "CMakeLists.txt"); if (fileExists(cmakeListFile)) { const content = fs.readFileSync(cmakeListFile, "utf-8"); - const projectMatches = content.match(/(project\(.*?\))/g); - if (projectMatches && projectMatches.length > 0) { - return projectMatches; + const projectMatches = content.match(/project\(([^)\s]+)/i); + if (projectMatches && projectMatches[1]) { + return projectMatches[1]; } } } From 1edc74ff3853e870f9e21468ac7b65f492aae48b Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Thu, 16 Oct 2025 14:15:56 +0800 Subject: [PATCH 13/15] rm commented code, update docs --- .../_static/confirm-idf-cmd-message.png | Bin 0 -> 14118 bytes .../en/additionalfeatures/language-tools.rst | 124 +++++---------- .../additionalfeatures/language-tools.rst | 144 ++++++------------ src/build/buildCmd.ts | 3 - src/flash/flashTask.ts | 11 -- 5 files changed, 84 insertions(+), 198 deletions(-) create mode 100644 docs_espressif/_static/confirm-idf-cmd-message.png diff --git a/docs_espressif/_static/confirm-idf-cmd-message.png b/docs_espressif/_static/confirm-idf-cmd-message.png new file mode 100644 index 0000000000000000000000000000000000000000..ef7e4fbc46cf6dc1d07768ed0201e73e2217bb96 GIT binary patch literal 14118 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 Re!WDcAgd}<^3*u+e*hqI)M)?! literal 0 HcmV?d00001 diff --git a/docs_espressif/en/additionalfeatures/language-tools.rst b/docs_espressif/en/additionalfeatures/language-tools.rst index 3a5477f22..b2de0eccf 100644 --- a/docs_espressif/en/additionalfeatures/language-tools.rst +++ b/docs_espressif/en/additionalfeatures/language-tools.rst @@ -1,33 +1,15 @@ -Language Tools -============== +ESP-IDF Chat Commands +================================= -This module provides a language model tool for the ESP-IDF extension, allowing users to execute common ESP-IDF operations through VS Code's chat interface using the Language Model Tools API. +This feature lets you run ESP-IDF commands directly from the VS Code chat window. +Instead of typing terminal commands, you can simply ask in chat - and the tool will execute common ESP-IDF actions for you, like building, flashing, or monitoring your project. -Overview --------- - -The ESP-IDF Language Tool registers a single tool called ``espIdfCommands`` that can be invoked from VS Code's chat interface. This tool accepts a ``command`` parameter and executes the corresponding ESP-IDF operation, making it easier to perform common ESP-IDF development tasks through natural language interaction. - -Implementation --------------- - -The tool is implemented using the VS Code Language Model Tools API (``vscode.lm.registerTool``) and is properly registered in ``package.json`` under the ``languageModelTools`` contribution point. - -Tool Registration -~~~~~~~~~~~~~~~~~ - -The tool is registered with: - -* **ID**: ``espIdfCommands`` -* **Display Name**: "ESP-IDF Commands" -* **Description**: "Execute ESP-IDF extension commands for building, flashing, monitoring, and managing ESP32 projects. ALWAYS use this tool for ESP-IDF development tasks instead of shell commands or terminal tasks. When users ask to 'build the project', 'flash the device', 'monitor output', 'clean project', 'configure project', 'analyze size', 'create new project', or any ESP-IDF related task, use this tool. Supports: build, flash, monitor, menuconfig, size analysis, project creation, component management, and more. This is the ONLY way to interact with ESP-IDF projects in VS Code - do not use shell commands for ESP-IDF tasks." - -Tags and Natural Language Support -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Usage +------ -The tool includes extensive tags that support natural language interaction. When users use the following phrases, the language model will prioritize this tool: +Press menu ``View`` > ``Chat`` to open the chat window. -**Command Tags**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace +You can type in the chat windows using natural language, and the tool will interpret your request to execute the appropriate ESP-IDF command. **Natural Language Patterns**: - "build the project" @@ -38,7 +20,7 @@ The tool includes extensive tags that support natural language interaction. When - "analyze size" - "erase flash" - "select port" -- "set target" +- "set target to esp32c6" - "run doctor" - "create new project" - "edit partition table" @@ -46,41 +28,34 @@ The tool includes extensive tags that support natural language interaction. When - "start app trace" - "start heap trace" -Input Schema -~~~~~~~~~~~~ - -The tool accepts a JSON object with the following schema: - -.. code-block:: json - - { - "type": "object", - "properties": { - "command": { - "type": "string", - "description": "The ESP-IDF command to execute", - "enum": [ - "build", - "flash", - "monitor", - "buildFlashMonitor", - "fullClean", - "menuconfig", - "size", - "eraseFlash", - "selectPort", - "setTarget", - "doctor", - "newProject", - "partitionTable", - "componentManager", - "apptrace", - "heaptrace" - ] - } - }, - "required": ["command"] - } +You can alternatively type ``#espIdfCommands `` to invoke the command directly. Replace ```` with one of the supported command tags listed below. + +**#espIdfCommands Tags**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace + +.. note:: + + * While the tool can understand natural language, using the specific ``#espIdfCommands `` format ensures accurate command execution. + * The tool is designed to handle one command at a time. For multiple actions, please enter them separately. + * Ensure your ESP-IDF environment is properly set up in VS Code for the commands to work correctly. Review the documentation to :ref:`Install ESP-IDF and Tools `. + +For example, to build the project, you can type: + +.. code-block:: text + + build the project + +.. code-block:: text + + #espIdfCommands build + +A dialog will appear to ``Confirm ESP-IDF Command``. Click ``Allow`` to proceed. + +.. figure:: ../../_static/confirm-idf-cmd-message.png + :align: center + :alt: Confirm ESP-IDF Command + :figclass: align-center + +The command will execute, and the output will be displayed in the terminal (if the command uses a terminal) and the chat window. Some commands may not produce output and launch a UI (like ``newProject``). Available Commands ------------------ @@ -118,28 +93,3 @@ Development Commands * **``componentManager``** - Open the ESP component manager (``esp.component-manager.ui.show``) * **``apptrace``** - Start application tracing (``espIdf.apptrace``) * **``heaptrace``** - Start heap tracing (``espIdf.heaptrace``) - -Usage ------ - -Users can invoke the tool through VS Code's chat interface by referencing it with the ``#espIdfCommands`` syntax and providing the desired command: - -.. code-block:: text - - #espIdfCommands {"command": "build"} - -The tool will execute the specified ESP-IDF command and return a confirmation message. - -Integration ------------ - -The language tool is automatically activated when the extension starts and is properly disposed when the extension deactivates. It uses the ``onLanguageModelTool:espIdfCommands`` activation event to ensure it's available when needed. - -Error Handling --------------- - -The tool includes proper error handling: - -* Validates that the provided command exists in the supported command list -* Returns descriptive error messages for unknown commands -* Provides confirmation messages for successful command execution diff --git a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst index d52ba1332..95f212440 100644 --- a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst +++ b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst @@ -1,86 +1,61 @@ -语言工具 -======== +ESP-IDF 聊天命令 +================ -本模块为 ESP-IDF 扩展提供了语言模型工具,允许用户通过 VS Code 的聊天界面使用语言模型工具 API 执行常见的 ESP-IDF 操作。 +此功能允许您直接从 VS Code 聊天窗口运行 ESP-IDF 命令。 +无需输入终端命令,您只需在聊天中询问 - 工具将为您执行常见的 ESP-IDF 操作,如构建、烧录或监控您的项目。 -概述 ----- +使用方法 +-------- -ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以通过 VS Code 的聊天界面调用。该工具接受一个 ``command`` 参数并执行相应的 ESP-IDF 操作,使通过自然语言交互执行常见 ESP-IDF 开发任务变得更加容易。 +按菜单 ``视图`` > ``聊天`` 打开聊天窗口。 -实现 ----- +您可以在聊天窗口中使用自然语言输入,工具将解释您的请求以执行相应的 ESP-IDF 命令。 -该工具使用 VS Code 语言模型工具 API (``vscode.lm.registerTool``) 实现,并在 ``package.json`` 中的 ``languageModelTools`` 贡献点下正确注册。 +**自然语言模式**: +- "构建项目" +- "烧录设备" +- "监控输出" +- "清理项目" +- "配置项目" +- "分析大小" +- "擦除闪存" +- "选择端口" +- "设置目标为 esp32c6" +- "运行诊断" +- "创建新项目" +- "编辑分区表" +- "管理组件" +- "启动应用跟踪" +- "启动堆跟踪" + +您也可以输入 ``#espIdfCommands <标签>`` 来直接调用命令。将 ``<标签>`` 替换为下面列出的支持的命令标签之一。 + +**#espIdfCommands 标签**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace + +.. note:: + + * 虽然工具可以理解自然语言,但使用特定的 ``#espIdfCommands <标签>`` 格式可确保准确的命令执行。 + * 该工具设计为一次处理一个命令。对于多个操作,请分别输入。 + * 确保您的 ESP-IDF 环境在 VS Code 中正确设置,命令才能正常工作。请查看文档以了解 :ref:`安装 ESP-IDF 和工具 `。 + +例如,要构建项目,您可以输入: -工具注册 -~~~~~~~~ +.. code-block:: text -该工具注册时包含以下信息: + 构建项目 -* **ID**: ``espIdfCommands`` -* **显示名称**: "ESP-IDF 命令" -* **描述**: "执行 ESP-IDF 扩展命令,用于构建、烧录、监控和管理 ESP32 项目。始终使用此工具进行 ESP-IDF 开发任务,而不是 shell 命令或终端任务。当用户询问'构建项目'、'烧录设备'、'监控输出'、'清理项目'、'配置项目'、'分析大小'、'创建新项目'或任何 ESP-IDF 相关任务时,请使用此工具。支持:构建、烧录、监控、menuconfig、大小分析、项目创建、组件管理等。这是在 VS Code 中与 ESP-IDF 项目交互的唯一方式 - 不要使用 shell 命令进行 ESP-IDF 任务。" +.. code-block:: text -标签和自然语言支持 -~~~~~~~~~~~~~~~~~~~~~ + #espIdfCommands build -该工具包含广泛的标签,支持自然语言交互。当用户使用以下短语时,语言模型将优先选择此工具: +将出现一个 ``确认 ESP-IDF 命令`` 对话框。点击 ``允许`` 继续。 -**命令标签**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace +.. figure:: ../../_static/confirm-idf-cmd-message.png + :align: center + :alt: 确认 ESP-IDF 命令 + :figclass: align-center -**自然语言模式**: -- "构建项目" (build the project) -- "烧录设备" (flash the device) -- "监控输出" (monitor the output) -- "清理项目" (clean the project) -- "配置项目" (configure the project) -- "分析大小" (analyze size) -- "擦除闪存" (erase flash) -- "选择端口" (select port) -- "设置目标" (set target) -- "运行诊断" (run doctor) -- "创建新项目" (create new project) -- "编辑分区表" (edit partition table) -- "管理组件" (manage components) -- "启动应用跟踪" (start app trace) -- "启动堆跟踪" (start heap trace) - -输入模式 -~~~~~~~~~ - -该工具接受具有以下模式的 JSON 对象: - -.. code-block:: json - - { - "type": "object", - "properties": { - "command": { - "type": "string", - "description": "要执行的 ESP-IDF 命令", - "enum": [ - "build", - "flash", - "monitor", - "buildFlashMonitor", - "fullClean", - "menuconfig", - "size", - "eraseFlash", - "selectPort", - "setTarget", - "doctor", - "newProject", - "partitionTable", - "componentManager", - "apptrace", - "heaptrace" - ] - } - }, - "required": ["command"] - } +命令将执行,输出将显示在终端中(如果命令使用终端)和聊天窗口中。某些命令可能不会产生输出并启动 UI(如 ``newProject``)。 可用命令 -------- @@ -117,29 +92,4 @@ ESP-IDF 语言工具注册了一个名为 ``espIdfCommands`` 的工具,可以 * **``partitionTable``** - 打开分区表编辑器 (``esp.webview.open.partition-table``) * **``componentManager``** - 打开 ESP 组件管理器 (``esp.component-manager.ui.show``) * **``apptrace``** - 启动应用程序跟踪 (``espIdf.apptrace``) -* **``heaptrace``** - 启动堆跟踪 (``espIdf.heaptrace``) - -使用方法 --------- - -用户可以通过 VS Code 的聊天界面使用 ``#espIdfCommands`` 语法调用该工具,并提供所需的命令: - -.. code-block:: text - - #espIdfCommands {"command": "build"} - -该工具将执行指定的 ESP-IDF 命令并返回确认消息。 - -集成 ----- - -语言工具在扩展启动时自动激活,在扩展停用时正确释放。它使用 ``onLanguageModelTool:espIdfCommands`` 激活事件确保在需要时可用。 - -错误处理 --------- - -该工具包含适当的错误处理: - -* 验证提供的命令是否存在于支持的命令列表中 -* 为未知命令返回描述性错误消息 -* 为成功的命令执行提供确认消息 \ No newline at end of file +* **``heaptrace``** - 启动堆跟踪 (``espIdf.heaptrace``) \ No newline at end of file diff --git a/src/build/buildCmd.ts b/src/build/buildCmd.ts index eb0cad528..85375b417 100644 --- a/src/build/buildCmd.ts +++ b/src/build/buildCmd.ts @@ -155,12 +155,9 @@ export async function buildCommand( buildType ); continueFlag = buildCmdResults.continueFlag; - const taskExecutions = buildCmdResults.executions; if (!continueFlag) { throw buildCmdResults.results[0].error; } - // const compileOutput = taskExecutions[1].getOutput(); - // const buildOutput = taskExecutions[2].getOutput(); } catch (error) { if (error.message === "ALREADY_BUILDING") { Logger.errorNotify("Already a build is running!", error, "buildCommand"); diff --git a/src/flash/flashTask.ts b/src/flash/flashTask.ts index ef08f9bc1..1fe8f61bb 100644 --- a/src/flash/flashTask.ts +++ b/src/flash/flashTask.ts @@ -167,11 +167,6 @@ export class FlashTask { flasherArgs, this.processOptions ); - // return new vscode.ProcessExecution( - // pythonBinPath, - // flasherArgs, - // this.processOptions - // ); } public async _dfuFlashing(pythonBinPath: string) { @@ -199,7 +194,6 @@ export class FlashTask { ]; } return OutputCapturingExecution.create(cmd, args, this.processOptions); - // return new vscode.ProcessExecution(cmd, args, this.processOptions); } public _partitionFlashExecution( @@ -216,11 +210,6 @@ export class FlashTask { flasherArgs, this.processOptions ); - // return new vscode.ProcessExecution( - // pythonBinPath, - // flasherArgs, - // this.processOptions - // ); } public getSingleBinFlasherArgs( From d76bd717b942a42c36d4ee1960abd0ac2822f576 Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Mon, 20 Oct 2025 20:12:14 +0800 Subject: [PATCH 14/15] fix Task has not been executed yet issue --- src/build/buildCmd.ts | 20 +++++++++++--- src/flash/uartFlash.ts | 20 +++++++++++--- src/langTools/index.ts | 16 +++++++++--- src/taskManager.ts | 42 +++--------------------------- src/taskManager/customExecution.ts | 8 +----- 5 files changed, 48 insertions(+), 58 deletions(-) diff --git a/src/build/buildCmd.ts b/src/build/buildCmd.ts index 85375b417..08320a1c1 100644 --- a/src/build/buildCmd.ts +++ b/src/build/buildCmd.ts @@ -123,7 +123,7 @@ export async function buildCommandMain( executions.push(dfuExecution); } } - const buildResult = await TaskManager.runTasksWithOutput(); + const buildResult = await TaskManager.runTasksWithBoolean(); if (!cancelToken.isCancellationRequested) { updateIdfComponentsTree(workspace); Logger.infoNotify("Build Successful"); @@ -134,9 +134,8 @@ export async function buildCommandMain( buildTask.building(false); return { - continueFlag: buildResult.success, + continueFlag: buildResult, executions, - results: buildResult.results, }; } @@ -156,7 +155,20 @@ export async function buildCommand( ); continueFlag = buildCmdResults.continueFlag; if (!continueFlag) { - throw buildCmdResults.results[0].error; + for (let i = 0; i < buildCmdResults.executions.length; i++) { + if (buildCmdResults.executions[i]) { + const executionOutput = await buildCmdResults.executions[ + i + ].getOutput(); + if ( + executionOutput && + !executionOutput.success && + executionOutput.stderr + ) { + throw executionOutput.stderr; + } + } + } } } catch (error) { if (error.message === "ALREADY_BUILDING") { diff --git a/src/flash/uartFlash.ts b/src/flash/uartFlash.ts index 2aecc347c..6d2365574 100644 --- a/src/flash/uartFlash.ts +++ b/src/flash/uartFlash.ts @@ -81,7 +81,7 @@ export async function uartFlashCommandMain( const postFlashExecution = await customTask.addCustomTask( CustomTaskType.PostFlash ); - const flashResult = await TaskManager.runTasksWithOutput(); + const flashResult = await TaskManager.runTasksWithBoolean(); if (!cancelToken.isCancellationRequested) { const msg = "Flash Done ⚡️"; @@ -91,9 +91,8 @@ export async function uartFlashCommandMain( } FlashTask.isFlashing = false; return { - continueFlag: flashResult.success, + continueFlag: flashResult, executions: [preFlashExecution, flashExecution, postFlashExecution], - results: flashResult.results, }; } @@ -121,7 +120,20 @@ export async function flashCommand( ); continueFlag = flashCmdResult.continueFlag; if (!continueFlag) { - throw flashCmdResult.results[0].error; + for (let i = 0; i < flashCmdResult.executions.length; i++) { + if (flashCmdResult.executions[i]) { + const executionOutput = await flashCmdResult.executions[ + i + ].getOutput(); + if ( + executionOutput && + !executionOutput.success && + executionOutput.stderr + ) { + throw executionOutput.stderr; + } + } + } } } catch (error) { if (error.message === "ALREADY_FLASHING") { diff --git a/src/langTools/index.ts b/src/langTools/index.ts index d49efa600..b6934eb2d 100644 --- a/src/langTools/index.ts +++ b/src/langTools/index.ts @@ -137,7 +137,10 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { ]); } - let encryptPartitions = await isFlashEncryptionEnabled(workspaceURI); + let encryptPartitions: boolean = false; + if (commandName === "flash" || commandName === "buildFlashMonitor") { + encryptPartitions = await isFlashEncryptionEnabled(workspaceURI); + } let partitionToUse = options.input.partitionToUse as | ESP.BuildType @@ -165,6 +168,7 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { | ShellOutputCapturingExecution )[] = []; if (commandId) { + let outputs: vscode.LanguageModelTextPart[] = []; try { await focusOnAppropriateOutput(commandName); if (commandName === "build") { @@ -366,7 +370,7 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { } const eraseFlashTask = new EraseFlashTask(workspaceURI); const eraseFlashExecution = await eraseFlashTask.eraseFlash(port); - const eraseFlashResult = await TaskManager.runTasksWithOutput(); + const eraseFlashResult = await TaskManager.runTasksWithBoolean(); taskExecutions.push(eraseFlashExecution); if (!token.isCancellationRequested) { EraseFlashTask.isErasing = false; @@ -450,7 +454,6 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { await vscode.commands.executeCommand(commandId); } - let outputs: vscode.LanguageModelTextPart[] = []; if (TASK_COMMANDS.has(commandName)) { for (const execution of taskExecutions) { if (execution) { @@ -525,11 +528,16 @@ export function activateLanguageTool(context: vscode.ExtensionContext) { } const errorMessage = `Failed to execute command "${commandName}": ${error.message}\n${error.stack}`; return new vscode.LanguageModelToolResult([ + ...outputs, new vscode.LanguageModelTextPart(errorMessage), ]); } } else { - throw new Error(`Unknown ESP-IDF command: ${commandName}`); + return new vscode.LanguageModelToolResult([ + new vscode.LanguageModelTextPart( + `Unknown ESP-IDF command: ${commandName}` + ), + ]); } }, diff --git a/src/taskManager.ts b/src/taskManager.ts index fc38bdd3d..efddbbfcf 100644 --- a/src/taskManager.ts +++ b/src/taskManager.ts @@ -141,48 +141,12 @@ export class TaskManager { TaskManager.taskResults = []; } - public static async runTasksWithOutput() { - const results: Array<{ - taskId: string; - output?: any; - error?: Error; - exitCode: number; - }> = []; - + public static async runTasksWithBoolean() { try { await TaskManager.runTasks(); - // If we get here, all tasks succeeded - return { success: true, results }; + return true; } catch (error) { - // Tasks failed, but we can still get output from custom executions - const customExecutions = TaskManager.tasks.filter( - (task) => - task.execution && - (task.execution as any).getOutput && - typeof (task.execution as any).getOutput === "function" - ); - - for (const task of customExecutions) { - try { - const execution = task.execution as any; - const output = await execution.getOutput(); - results.push({ - taskId: task.definition.taskId, - output, - error: undefined, - exitCode: output.exitCode, - }); - } catch (execError) { - results.push({ - taskId: task.definition.taskId, - output: undefined, - error: execError as Error, - exitCode: -1, - }); - } - } - - return { success: false, results }; + return false; } } } diff --git a/src/taskManager/customExecution.ts b/src/taskManager/customExecution.ts index c3c15bbc9..1bbe4ab49 100644 --- a/src/taskManager/customExecution.ts +++ b/src/taskManager/customExecution.ts @@ -22,12 +22,6 @@ import * as childProcess from "child_process"; export interface CustomExecutionTaskResult { continueFlag: boolean; executions: (OutputCapturingExecution | ShellOutputCapturingExecution)[]; - results?: Array<{ - taskId: string; - output?: any; - error?: Error; - exitCode: number; - }>; } export interface CapturedTaskOutput { @@ -169,7 +163,7 @@ export class OutputCapturingExecution extends vscode.CustomExecution { public async getOutput(): Promise { if (!this.outputPromise) { - throw new Error("Task has not been executed yet"); + return { stdout: "", stderr: "", exitCode: -1, success: false } as CapturedTaskOutput; } return this.outputPromise; } From 0dd5a8400e0c6c9c4559c6da9c1011f7f81d94da Mon Sep 17 00:00:00 2001 From: Brian Ignacio Date: Tue, 21 Oct 2025 12:52:28 +0800 Subject: [PATCH 15/15] update docs --- .../en/additionalfeatures/language-tools.rst | 122 ++++++++++++------ .../additionalfeatures/language-tools.rst | 122 ++++++++++++------ package.json | 11 +- 3 files changed, 176 insertions(+), 79 deletions(-) diff --git a/docs_espressif/en/additionalfeatures/language-tools.rst b/docs_espressif/en/additionalfeatures/language-tools.rst index b2de0eccf..cde9a27f2 100644 --- a/docs_espressif/en/additionalfeatures/language-tools.rst +++ b/docs_espressif/en/additionalfeatures/language-tools.rst @@ -4,6 +4,57 @@ ESP-IDF Chat Commands This feature lets you run ESP-IDF commands directly from the VS Code chat window. Instead of typing terminal commands, you can simply ask in chat - and the tool will execute common ESP-IDF actions for you, like building, flashing, or monitoring your project. +.. _available-language-commands: +Available Commands +------------------ + +The tool supports the following ESP-IDF commands: + +Command Parameters +~~~~~~~~~~~~~~~~~~ + +Some commands support additional parameters to customize their behavior: + +* **``target``** - Specifies the ESP32 target device (for ``setTarget`` command). Supported values: esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux +* **``partitionToUse``** - Specifies which partition to build or flash (for ``build``, ``flash``, and ``buildFlashMonitor`` commands). Supported values: app, bootloader, partition-table +* **``flashType``** - Specifies the flash method to use (for ``flash`` and ``buildFlashMonitor`` commands). Supported values: UART, JTAG, DFU + +Build and Flash Commands +~~~~~~~~~~~~~~~~~~~~~~~~ + +* **``build``** - Build the ESP-IDF project (``espIdf.buildDevice``) + * Optional parameter: ``partitionToUse`` (app, bootloader, partition-table) +* **``flash``** - Flash the built application to the device (``espIdf.flashDevice``) + * Optional parameters: ``partitionToUse`` (app, bootloader, partition-table), ``flashType`` (UART, JTAG, DFU) +* **``monitor``** - Monitor the device output (``espIdf.monitorDevice``) +* **``buildFlashMonitor``** - Build, flash, and monitor the project in one command (``espIdf.buildFlashMonitor``) + * Optional parameters: ``partitionToUse`` (app, bootloader, partition-table), ``flashType`` (UART, JTAG, DFU) + +Project Management Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* **``fullClean``** - Perform a full clean of the project (``espIdf.fullClean``) +* **``menuconfig``** - Open the ESP-IDF menuconfig interface (``espIdf.menuconfig.start``) +* **``size``** - Analyze the application size (``espIdf.size``) +* **``eraseFlash``** - Erase the device flash memory (``espIdf.eraseFlash``) + +Configuration Commands +~~~~~~~~~~~~~~~~~~~~~~ + +* **``selectPort``** - Select the serial port for communication (``espIdf.selectPort``) +* **``setTarget``** - Set the ESP32 target device (``espIdf.setTarget``) + * Optional parameter: ``target`` (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) +* **``doctor``** - Run the ESP-IDF doctor command to diagnose issues (``espIdf.doctorCommand``) + +Development Commands +~~~~~~~~~~~~~~~~~~~~ + +* **``newProject``** - Create a new ESP-IDF project (``espIdf.newProject.start``) +* **``partitionTable``** - Open the partition table editor (``esp.webview.open.partition-table``) +* **``componentManager``** - Open the ESP component manager (``esp.component-manager.ui.show``) +* **``apptrace``** - Start application tracing (``espIdf.apptrace``) +* **``heaptrace``** - Start heap tracing (``espIdf.heaptrace``) + Usage ------ @@ -28,13 +79,11 @@ You can type in the chat windows using natural language, and the tool will inter - "start app trace" - "start heap trace" -You can alternatively type ``#espIdfCommands `` to invoke the command directly. Replace ```` with one of the supported command tags listed below. - -**#espIdfCommands Tags**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace +You can alternatively type ``#espIdfCommands `` to invoke the command directly. Replace ```` with one of the supported command tags from :ref:`Available Commands `. .. note:: - * While the tool can understand natural language, using the specific ``#espIdfCommands `` format ensures accurate command execution. + * While the Chat can understand natural language, using the specific ``#espIdfCommands `` command format ensures accurate command execution. * The tool is designed to handle one command at a time. For multiple actions, please enter them separately. * Ensure your ESP-IDF environment is properly set up in VS Code for the commands to work correctly. Review the documentation to :ref:`Install ESP-IDF and Tools `. @@ -57,39 +106,34 @@ A dialog will appear to ``Confirm ESP-IDF Command``. Click ``Allow`` to proceed. The command will execute, and the output will be displayed in the terminal (if the command uses a terminal) and the chat window. Some commands may not produce output and launch a UI (like ``newProject``). -Available Commands ------------------- - -The tool supports the following ESP-IDF commands: - -Build and Flash Commands -~~~~~~~~~~~~~~~~~~~~~~~~ - -* **``build``** - Build the ESP-IDF project (``espIdf.buildDevice``) -* **``flash``** - Flash the built application to the device (``espIdf.flashDevice``) -* **``monitor``** - Monitor the device output (``espIdf.monitorDevice``) -* **``buildFlashMonitor``** - Build, flash, and monitor the project in one command (``espIdf.buildFlashMonitor``) - -Project Management Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* **``fullClean``** - Perform a full clean of the project (``espIdf.fullClean``) -* **``menuconfig``** - Open the ESP-IDF menuconfig interface (``espIdf.menuconfig.start``) -* **``size``** - Analyze the application size (``espIdf.size``) -* **``eraseFlash``** - Erase the device flash memory (``espIdf.eraseFlash``) - -Configuration Commands -~~~~~~~~~~~~~~~~~~~~~~ -* **``selectPort``** - Select the serial port for communication (``espIdf.selectPort``) -* **``setTarget``** - Set the ESP32 target device (``espIdf.setTarget``) -* **``doctor``** - Run the ESP-IDF doctor command to diagnose issues (``espIdf.doctorCommand``) - -Development Commands -~~~~~~~~~~~~~~~~~~~~ - -* **``newProject``** - Create a new ESP-IDF project (``espIdf.newProject.start``) -* **``partitionTable``** - Open the partition table editor (``esp.webview.open.partition-table``) -* **``componentManager``** - Open the ESP component manager (``esp.component-manager.ui.show``) -* **``apptrace``** - Start application tracing (``espIdf.apptrace``) -* **``heaptrace``** - Start heap tracing (``espIdf.heaptrace``) +Examples +~~~~~~~~~~~~~~ + +Here are some examples of how to use the commands with parameters: + +**Setting Target:** +* "set target to esp32s3" +* "#espIdfCommands setTarget esp32c6" + +**Building Specific Partitions:** +* "build app" +* "#espIdfCommands build bootloader" +* "build bootloader" +* "#espIdfCommands build partition-table" + +**Flashing with Different Methods:** +* "flash with UART" +* "#espIdfCommands flash UART" +* "flash with JTAG" +* "#espIdfCommands flash JTAG" +* "flash with DFU" +* "#espIdfCommands flash DFU" + +**Combined Operations:** +* "build and flash app with UART" +* "#espIdfCommands buildFlashMonitor app UART" +* "build and flash bootloader with JTAG" +* "#espIdfCommands buildFlashMonitor bootloader JTAG" +* "build and flash partition table with DFU" +* "#espIdfCommands buildFlashMonitor partition-table DFU" diff --git a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst index 95f212440..0812d2f83 100644 --- a/docs_espressif/zh_CN/additionalfeatures/language-tools.rst +++ b/docs_espressif/zh_CN/additionalfeatures/language-tools.rst @@ -4,6 +4,57 @@ ESP-IDF 聊天命令 此功能允许您直接从 VS Code 聊天窗口运行 ESP-IDF 命令。 无需输入终端命令,您只需在聊天中询问 - 工具将为您执行常见的 ESP-IDF 操作,如构建、烧录或监控您的项目。 +.. _available-language-commands: +可用命令 +-------- + +该工具支持以下 ESP-IDF 命令: + +命令参数 +~~~~~~~~ + +某些命令支持附加参数来自定义其行为: + +* **``target``** - 指定 ESP32 目标设备(用于 ``setTarget`` 命令)。支持的值:esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux +* **``partitionToUse``** - 指定要构建或烧录的分区(用于 ``build``、``flash`` 和 ``buildFlashMonitor`` 命令)。支持的值:app, bootloader, partition-table +* **``flashType``** - 指定要使用的烧录方法(用于 ``flash`` 和 ``buildFlashMonitor`` 命令)。支持的值:UART, JTAG, DFU + +构建和烧录命令 +~~~~~~~~~~~~~~~ + +* **``build``** - 构建 ESP-IDF 项目 (``espIdf.buildDevice``) + * 可选参数:``partitionToUse`` (app, bootloader, partition-table) +* **``flash``** - 将构建的应用程序烧录到设备 (``espIdf.flashDevice``) + * 可选参数:``partitionToUse`` (app, bootloader, partition-table), ``flashType`` (UART, JTAG, DFU) +* **``monitor``** - 监控设备输出 (``espIdf.monitorDevice``) +* **``buildFlashMonitor``** - 在一个命令中构建、烧录和监控项目 (``espIdf.buildFlashMonitor``) + * 可选参数:``partitionToUse`` (app, bootloader, partition-table), ``flashType`` (UART, JTAG, DFU) + +项目管理命令 +~~~~~~~~~~~~~ + +* **``fullClean``** - 执行项目的完全清理 (``espIdf.fullClean``) +* **``menuconfig``** - 打开 ESP-IDF menuconfig 界面 (``espIdf.menuconfig.start``) +* **``size``** - 分析应用程序大小 (``espIdf.size``) +* **``eraseFlash``** - 擦除设备闪存 (``espIdf.eraseFlash``) + +配置命令 +~~~~~~~~~ + +* **``selectPort``** - 选择用于通信的串口 (``espIdf.selectPort``) +* **``setTarget``** - 设置 ESP32 目标设备 (``espIdf.setTarget``) + * 可选参数:``target`` (esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32h2, esp32p4, esp32c2, esp32c5, esp32c61, esp32h21, esp32h4, linux) +* **``doctor``** - 运行 ESP-IDF doctor 命令诊断问题 (``espIdf.doctorCommand``) + +开发命令 +~~~~~~~~~ + +* **``newProject``** - 创建新的 ESP-IDF 项目 (``espIdf.newProject.start``) +* **``partitionTable``** - 打开分区表编辑器 (``esp.webview.open.partition-table``) +* **``componentManager``** - 打开 ESP 组件管理器 (``esp.component-manager.ui.show``) +* **``apptrace``** - 启动应用程序跟踪 (``espIdf.apptrace``) +* **``heaptrace``** - 启动堆跟踪 (``espIdf.heaptrace``) + 使用方法 -------- @@ -28,13 +79,11 @@ ESP-IDF 聊天命令 - "启动应用跟踪" - "启动堆跟踪" -您也可以输入 ``#espIdfCommands <标签>`` 来直接调用命令。将 ``<标签>`` 替换为下面列出的支持的命令标签之一。 - -**#espIdfCommands 标签**: build, flash, monitor, buildFlashMonitor, fullClean, menuconfig, size, eraseFlash, selectPort, setTarget, doctor, newProject, partitionTable, componentManager, apptrace, heaptrace +您也可以输入 ``#espIdfCommands <标签>`` 来直接调用命令。将 ``<标签>`` 替换为 :ref:`可用命令 ` 中列出的支持的命令标签之一。 .. note:: - * 虽然工具可以理解自然语言,但使用特定的 ``#espIdfCommands <标签>`` 格式可确保准确的命令执行。 + * 虽然聊天可以理解自然语言,但使用特定的 ``#espIdfCommands <标签>`` 命令格式可确保准确的命令执行。 * 该工具设计为一次处理一个命令。对于多个操作,请分别输入。 * 确保您的 ESP-IDF 环境在 VS Code 中正确设置,命令才能正常工作。请查看文档以了解 :ref:`安装 ESP-IDF 和工具 `。 @@ -57,39 +106,34 @@ ESP-IDF 聊天命令 命令将执行,输出将显示在终端中(如果命令使用终端)和聊天窗口中。某些命令可能不会产生输出并启动 UI(如 ``newProject``)。 -可用命令 --------- - -该工具支持以下 ESP-IDF 命令: - -构建和烧录命令 -~~~~~~~~~~~~~~~ -* **``build``** - 构建 ESP-IDF 项目 (``espIdf.buildDevice``) -* **``flash``** - 将构建的应用程序烧录到设备 (``espIdf.flashDevice``) -* **``monitor``** - 监控设备输出 (``espIdf.monitorDevice``) -* **``buildFlashMonitor``** - 在一个命令中构建、烧录和监控项目 (``espIdf.buildFlashMonitor``) - -项目管理命令 -~~~~~~~~~~~~~ - -* **``fullClean``** - 执行项目的完全清理 (``espIdf.fullClean``) -* **``menuconfig``** - 打开 ESP-IDF menuconfig 界面 (``espIdf.menuconfig.start``) -* **``size``** - 分析应用程序大小 (``espIdf.size``) -* **``eraseFlash``** - 擦除设备闪存 (``espIdf.eraseFlash``) - -配置命令 -~~~~~~~~~ - -* **``selectPort``** - 选择用于通信的串口 (``espIdf.selectPort``) -* **``setTarget``** - 设置 ESP32 目标设备 (``espIdf.setTarget``) -* **``doctor``** - 运行 ESP-IDF doctor 命令诊断问题 (``espIdf.doctorCommand``) - -开发命令 -~~~~~~~~~ - -* **``newProject``** - 创建新的 ESP-IDF 项目 (``espIdf.newProject.start``) -* **``partitionTable``** - 打开分区表编辑器 (``esp.webview.open.partition-table``) -* **``componentManager``** - 打开 ESP 组件管理器 (``esp.component-manager.ui.show``) -* **``apptrace``** - 启动应用程序跟踪 (``espIdf.apptrace``) -* **``heaptrace``** - 启动堆跟踪 (``espIdf.heaptrace``) \ No newline at end of file +示例 +~~~~ + +以下是一些使用带参数命令的示例: + +**设置目标:** +* "设置目标为 esp32s3" +* "#espIdfCommands setTarget esp32c6" + +**构建特定分区:** +* "构建应用" +* "#espIdfCommands build bootloader" +* "构建引导加载程序" +* "#espIdfCommands build partition-table" + +**使用不同方法烧录:** +* "使用 UART 烧录" +* "#espIdfCommands flash UART" +* "使用 JTAG 烧录" +* "#espIdfCommands flash JTAG" +* "使用 DFU 烧录" +* "#espIdfCommands flash DFU" + +**组合操作:** +* "构建并烧录应用使用 UART" +* "#espIdfCommands buildFlashMonitor app UART" +* "构建并烧录引导加载程序使用 JTAG" +* "#espIdfCommands buildFlashMonitor bootloader JTAG" +* "构建并烧录分区表使用 DFU" +* "#espIdfCommands buildFlashMonitor partition-table DFU" \ No newline at end of file diff --git a/package.json b/package.json index ba1f64953..3adcbc3fd 100644 --- a/package.json +++ b/package.json @@ -2572,7 +2572,16 @@ "flash partition table", "build and flash app", "build and flash bootloader", - "build and flash partition table" + "build and flash partition table", + "flash with UART", + "flash with JTAG", + "flash with DFU", + "flash device with UART", + "flash device with JTAG", + "flash device with DFU", + "build and flash with UART", + "build and flash with JTAG", + "build and flash with DFU" ], "icon": "$(run-view-icon)", "inputSchema": {