From ecdfabdd17b4df70bcc5320f0936fd76c914b7c8 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 19 Mar 2026 11:01:20 -0700 Subject: [PATCH 01/31] Update changelog and TPN. (#14298) --- Extension/CHANGELOG.md | 3 +++ Extension/ThirdPartyNotices.txt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index b77d45c17..e6edcbad5 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,8 +1,10 @@ # C/C++ for Visual Studio Code Changelog ## Version 1.31.3: March 23, 2026 +### Enhancements * Add support for `program` in attach debug configurations. [#14046](https://github.com/microsoft/vscode-cpptools/issues/14046) * Thanks for the contribution. [@Subham-KRLX (Subham)](https://github.com/Subham-KRLX) [PR #14108](https://github.com/microsoft/vscode-cpptools/pull/14108) +* Remove unnecessary `console.debug` logging. [#14294](https://github.com/microsoft/vscode-cpptools/issues/14294) * Update clang-tidy and clang-format from 21.1.4 to 22.1.1. * Update support for the latest compiler versions. * Update SQLite to the latest version. @@ -12,6 +14,7 @@ * Fix `embed-dir` compiler arguments not being handled for IntelliSense. [#14154](https://github.com/microsoft/vscode-cpptools/issues/14154) * Fix workspace symbol search with `scope::variable` not working after symbols are deleted and then added back. [#14200](https://github.com/microsoft/vscode-cpptools/issues/14200) * Fix bugs where a path was checked for existence but not whether it was a file or a folder. [#14257](https://github.com/microsoft/vscode-cpptools/issues/14257) +* Fix the Call Hierarchy Callers Of progress UI not showing. [#14287](https://github.com/microsoft/vscode-cpptools/issues/14287) * Add IntelliSense support for `__builtin_is_implicit_lifetime`. * Fix three IntelliSense process crashes. * Fix some missing translations. diff --git a/Extension/ThirdPartyNotices.txt b/Extension/ThirdPartyNotices.txt index 48635c5a6..b2b6d038b 100644 --- a/Extension/ThirdPartyNotices.txt +++ b/Extension/ThirdPartyNotices.txt @@ -1765,6 +1765,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. core-util-is 1.0.3 - MIT https://github.com/isaacs/core-util-is#readme +Copyright Node.js contributors Copyright Joyent, Inc. and other Node contributors Copyright Node.js contributors. All rights reserved. @@ -2598,6 +2599,7 @@ merge2 1.4.1 - MIT https://github.com/teambition/merge2 Copyright (c) 2014-2020 Teambition +(c) Teambition (https://www.teambition.com) The MIT License (MIT) From 0608ecd86fdd7824eada93257f0a407a44315c57 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 20 Mar 2026 11:12:36 -0700 Subject: [PATCH 02/31] Update flatted and fast-xml-parser. (#14303) --- .github/actions/package-lock.json | 14 +++++++------- .github/actions/package.json | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index 8faef0f0f..10f442ce2 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -4019,9 +4019,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.5.6", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/fast-xml-parser/-/fast-xml-parser-5.5.6.tgz", - "integrity": "sha1-b8YfWuBqVaHwWKvWpPS10+mXLNA=", + "version": "5.5.7", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", + "integrity": "sha1-4d3IZmLYCEUKGc8vtszJw8mTPF0=", "dev": true, "funding": [ { @@ -4033,7 +4033,7 @@ "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.1.3", - "strnum": "^2.1.2" + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -4118,9 +4118,9 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha1-hMzZV5526cwNJGwR2L4L6wGRQ+Y=", + "version": "3.4.2", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha1-9cI8EH8PN96NvfJPE3IrO5jVJyY=", "dev": true, "license": "ISC" }, diff --git a/.github/actions/package.json b/.github/actions/package.json index 51797750e..847c77094 100644 --- a/.github/actions/package.json +++ b/.github/actions/package.json @@ -41,7 +41,7 @@ }, "overrides": { "serialize-javascript": "^7.0.4", - "flatted": "^3.4.1", - "fast-xml-parser": "^5.5.6" + "flatted": "^3.4.2", + "fast-xml-parser": "^5.5.7" } } From 61421ea4fe6552603d8cd2b93fb1646b4558d25a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:34:04 -0700 Subject: [PATCH 03/31] Bump flatted from 3.4.1 to 3.4.2 in /Extension (#14304) Bumps [flatted](https://github.com/WebReflection/flatted) from 3.4.1 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.4.1...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Extension/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Extension/yarn.lock b/Extension/yarn.lock index f9954bfba..5ec7cad55 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -3037,9 +3037,9 @@ flat@^5.0.2: integrity sha1-jKb+MyBp/6nTJMMnGYxZglnOskE= flatted@^3.2.9, flatted@^3.4.1: - version "3.4.1" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/flatted/-/flatted-3.4.1.tgz#84ccd9579e76e9cc0d246c11d8be0beb019143e6" - integrity sha1-hMzZV5526cwNJGwR2L4L6wGRQ+Y= + version "3.4.2" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity sha1-9cI8EH8PN96NvfJPE3IrO5jVJyY= flush-write-stream@^1.0.2: version "1.1.1" From e08377a353f12c7575452a2226ff1fb93f6335ec Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 23 Mar 2026 12:01:04 -0700 Subject: [PATCH 04/31] Update changelog with recent fixes. (#14313) --- Extension/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index e6edcbad5..4600171ba 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,6 +1,6 @@ # C/C++ for Visual Studio Code Changelog -## Version 1.31.3: March 23, 2026 +## Version 1.31.3: March 24, 2026 ### Enhancements * Add support for `program` in attach debug configurations. [#14046](https://github.com/microsoft/vscode-cpptools/issues/14046) * Thanks for the contribution. [@Subham-KRLX (Subham)](https://github.com/Subham-KRLX) [PR #14108](https://github.com/microsoft/vscode-cpptools/pull/14108) @@ -15,6 +15,8 @@ * Fix workspace symbol search with `scope::variable` not working after symbols are deleted and then added back. [#14200](https://github.com/microsoft/vscode-cpptools/issues/14200) * Fix bugs where a path was checked for existence but not whether it was a file or a folder. [#14257](https://github.com/microsoft/vscode-cpptools/issues/14257) * Fix the Call Hierarchy Callers Of progress UI not showing. [#14287](https://github.com/microsoft/vscode-cpptools/issues/14287) +* Fix an IntelliSense crash when using GCC 16 `span` and `string`. [#14309](https://github.com/microsoft/vscode-cpptools/issues/14309) +* Fix `_CONTROL_FLOW_GUARD` not being defined when `/guard:cf` is used in MSVC mode. [#14310](https://github.com/microsoft/vscode-cpptools/issues/14310) * Add IntelliSense support for `__builtin_is_implicit_lifetime`. * Fix three IntelliSense process crashes. * Fix some missing translations. From 37a558260d980e64c4b36d2ac627b7570c383419 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:27:38 -0700 Subject: [PATCH 05/31] Bump picomatch from 2.3.1 to 2.3.2 in /ExtensionPack (#14318) Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ExtensionPack/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ExtensionPack/package-lock.json b/ExtensionPack/package-lock.json index 09e6d2016..5ecbd639a 100644 --- a/ExtensionPack/package-lock.json +++ b/ExtensionPack/package-lock.json @@ -2904,9 +2904,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha1-O6ODNzNkbZ0+SZWUbBNlpn+wekI=", + "version": "2.3.2", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE=", "dev": true, "license": "MIT", "engines": { From 5c16227c40520d789369f56bb44a6c32fbcca232 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:44:08 -0700 Subject: [PATCH 06/31] Bump picomatch from 2.3.1 to 2.3.2 in /.github/actions (#14319) Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sean McManus --- .github/actions/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index 10f442ce2..64f79a5e3 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -5277,9 +5277,9 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha1-O6ODNzNkbZ0+SZWUbBNlpn+wekI=", + "version": "2.3.2", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE=", "dev": true, "license": "MIT", "engines": { From 6e3a7b2b7ee6f7d1429586f89734670a66a205a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:01:33 -0700 Subject: [PATCH 07/31] Bump picomatch from 2.3.1 to 2.3.2 in /Themes (#14323) Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Themes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Themes/package-lock.json b/Themes/package-lock.json index c9a006175..fa9fb98b0 100644 --- a/Themes/package-lock.json +++ b/Themes/package-lock.json @@ -2904,9 +2904,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha1-O6ODNzNkbZ0+SZWUbBNlpn+wekI=", + "version": "2.3.2", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE=", "dev": true, "license": "MIT", "engines": { From 65beaebaf96fa499f39def3704a6bb93decd4019 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 26 Mar 2026 18:50:45 -0700 Subject: [PATCH 08/31] Update picomatch. (#14324) --- Extension/yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 5ec7cad55..8e61aa60f 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -4974,14 +4974,14 @@ picocolors@^1.1.1: integrity sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s= picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha1-O6ODNzNkbZ0+SZWUbBNlpn+wekI= + version "2.3.2" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha1-WpQpFeJrNy3A8OZ1MUmhbmscVgE= picomatch@^4.0.3: - version "4.0.3" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" - integrity sha1-eWx2E20e6tcV2x57rXhd7daVoEI= + version "4.0.4" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha1-/W9eAKFDCG4HTf/kySS4+yk7BYk= pkg-dir@^4.2.0: version "4.2.0" From 4884af8c9492484f4aeaeea3d92012d94f33511f Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 27 Mar 2026 10:20:36 -0700 Subject: [PATCH 09/31] Update brace-expansion v5. (#14325) --- Extension/yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 8e61aa60f..45f5fd6a2 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -1761,10 +1761,10 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -brace-expansion@^5.0.2: - version "5.0.3" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" - integrity sha1-apxsJo+FtTlZ7FJ66v4PcwAlju8= +brace-expansion@^5.0.2, brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha1-3MOjcRa3nz4bRtuZTO1dVw6TD9s= dependencies: balanced-match "^4.0.2" From 97943a9d676e937564acc861d929b2ded5525bfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:09:26 -0700 Subject: [PATCH 10/31] Bump serialize-javascript from 7.0.4 to 7.0.5 in /.github/actions (#14332) Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 7.0.4 to 7.0.5. - [Release notes](https://github.com/yahoo/serialize-javascript/releases) - [Commits](https://github.com/yahoo/serialize-javascript/compare/v7.0.4...v7.0.5) --- updated-dependencies: - dependency-name: serialize-javascript dependency-version: 7.0.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/package-lock.json | 6 +++--- .github/actions/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index 64f79a5e3..4f07ae542 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -5556,9 +5556,9 @@ } }, "node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha1-xRdzW9W3Yx3R/Bke4Zy7cT/44Fw=", + "version": "7.0.5", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha1-x5jMBVL/uwiYGRSkKodW4znQ1bE=", "dev": true, "license": "BSD-3-Clause", "engines": { diff --git a/.github/actions/package.json b/.github/actions/package.json index 847c77094..f824ebd03 100644 --- a/.github/actions/package.json +++ b/.github/actions/package.json @@ -40,7 +40,7 @@ "yargs": "^17.5.1" }, "overrides": { - "serialize-javascript": "^7.0.4", + "serialize-javascript": "^7.0.5", "flatted": "^3.4.2", "fast-xml-parser": "^5.5.7" } From 5451e32bd806061298f48939b03d8e544105b80d Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 30 Mar 2026 10:14:13 -0700 Subject: [PATCH 11/31] Update brace-expansion and serialize-javascript. (#14328) --- Extension/package.json | 2 +- Extension/yarn.lock | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 5cd6cb4f5..9f75d96c9 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -6883,7 +6883,7 @@ "resolutions": { "postcss": "^8.4.31", "gulp-typescript/**/glob-parent": "^5.1.2", - "serialize-javascript": "^7.0.4", + "serialize-javascript": "^7.0.5", "@azure/msal-browser": "^4.29.1", "flatted": "^3.4.1" } diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 45f5fd6a2..086050806 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -1747,21 +1747,21 @@ boundary@^2.0.0: integrity sha1-FpyLHw1Ezywlk4lnoyjzfgpOXvw= brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha1-q5tFRGblqMw6GHvqrVgEEqnFuEM= + version "1.1.13" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.13.tgz#d37875c01dc9eff988dd49d112a57cb67b54efe6" + integrity sha1-03h1wB3J7/mI3UnREqV8tntU7+Y= dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.1: - version "2.0.2" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc= + version "2.0.3" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.3.tgz#0493338bdd58e319b1039c67cf7ee439892c01d9" + integrity sha1-BJMzi91Y4xmxA5xnz37kOYksAdk= dependencies: balanced-match "^1.0.0" -brace-expansion@^5.0.2, brace-expansion@^5.0.5: +brace-expansion@^5.0.2: version "5.0.5" resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" integrity sha1-3MOjcRa3nz4bRtuZTO1dVw6TD9s= @@ -5448,10 +5448,10 @@ semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4, semve resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" integrity sha1-KEZONgYOmR+noR0CedLT87V6foo= -serialize-javascript@^6.0.2, serialize-javascript@^7.0.4: - version "7.0.4" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/serialize-javascript/-/serialize-javascript-7.0.4.tgz#c517735bd5b7631dd1fc191ee19cbb713ff8e05c" - integrity sha1-xRdzW9W3Yx3R/Bke4Zy7cT/44Fw= +serialize-javascript@^6.0.2, serialize-javascript@^7.0.5: + version "7.0.5" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/serialize-javascript/-/serialize-javascript-7.0.5.tgz#c798cc0552ffbb08981914a42a8756e339d0d5b1" + integrity sha1-x5jMBVL/uwiYGRSkKodW4znQ1bE= set-function-length@^1.2.2: version "1.2.2" From 21a8e34c915cebc91417898e2ca3b2bb1759ccad Mon Sep 17 00:00:00 2001 From: Luca <681992+lukka@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:41:03 -0700 Subject: [PATCH 12/31] minimize the calls to lm.selectChatModels (#14327) --- .../Providers/CopilotHoverProvider.ts | 44 ++++++++++++++++--- Extension/src/LanguageServer/extension.ts | 10 +++-- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts index b04b8e302..b04e17cd3 100644 --- a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts +++ b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts @@ -26,10 +26,45 @@ export class CopilotHoverProvider implements vscode.HoverProvider { private cancelledDocument: vscode.TextDocument | undefined; private cancelledPosition: vscode.Position | undefined; private content: string | undefined; + private chatModel: vscode.LanguageModelChat | undefined; + // Flag to avoid querying the LanguageModelChat repeatedly if no model is found + private checkedChatModel: boolean = false; constructor(client: DefaultClient) { this.client = client; } + public async getCachedChatModel(): Promise { + if (this.checkedChatModel) { + return this.chatModel; + } + + const vscodelm = getVSCodeLanguageModel(); + if (vscodelm) { + try { + const [model] = await vscodelm.selectChatModels(modelSelector); + if (!model) { + telemetry.logLanguageServerEvent('CopilotHoverNoModelSelected', { remoteName: vscode.env.remoteName || 'local' }); + } else { + this.chatModel = model; + } + } catch (e: any) { + const exceptionType = e?.name || e?.constructor?.name || typeof e; + telemetry.logLanguageServerEvent('CopilotHoverSelectModelFailed', { + remoteName: vscode.env.remoteName || 'local', + exceptionType: String(exceptionType) + }); + } + } + this.checkedChatModel = true; + return this.chatModel; + } + + public async refreshCachedChatModel(): Promise { + this.chatModel = undefined; + this.checkedChatModel = false; + return this.getCachedChatModel(); + } + public async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { await this.client.ready; @@ -43,12 +78,9 @@ export class CopilotHoverProvider implements vscode.HoverProvider { } // Ensure the user has access to Copilot. - const vscodelm = getVSCodeLanguageModel(); - if (vscodelm) { - const [model] = await vscodelm.selectChatModels(modelSelector); - if (!model) { - return undefined; - } + const model = await this.getCachedChatModel(); + if (!model) { + return undefined; } const newHover = this.isNewHover(document, position); diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index 2c072eff3..f4d3417b2 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -17,7 +17,7 @@ import { TargetPopulation } from 'vscode-tas-client'; import * as which from 'which'; import { logAndReturn } from '../Utility/Async/returns'; import * as util from '../common'; -import { isWindows, modelSelector } from '../constants'; +import { isWindows } from '../constants'; import { instrument } from '../instrumentation'; import { getCrashCallStacksChannel } from '../logger'; import { PlatformInformation } from '../platform'; @@ -1471,8 +1471,12 @@ async function onCopilotHover(): Promise { let chatResponse: vscode.LanguageModelChatResponse | undefined; try { - // Select the chat model. - const [model] = await vscodelm.selectChatModels(modelSelector); + // Select the chat model (refresh from cache). + const model = await copilotHoverProvider.refreshCachedChatModel(); + if (!model) { + await reportCopilotFailure(copilotHoverProvider, hoverDocument, hoverPosition, "Copilot model not available."); + return; + } chatResponse = await model.sendRequest( messages, From 5f56194febf21d857330fb70bbf5261de08bff27 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 30 Mar 2026 11:08:36 -0700 Subject: [PATCH 13/31] Update brace-expansion for other folders (#14333) * Update brace-expansion for other folders. --- .github/actions/package-lock.json | 18 +++++++++--------- ExtensionPack/package-lock.json | 12 ++++++------ Themes/package-lock.json | 14 +++++++------- Themes/package.json | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index 4f07ae542..af2dead5b 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -3166,9 +3166,9 @@ "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha1-q5tFRGblqMw6GHvqrVgEEqnFuEM=", + "version": "1.1.13", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha1-03h1wB3J7/mI3UnREqV8tntU7+Y=", "dev": true, "license": "MIT", "dependencies": { @@ -4282,9 +4282,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", + "version": "2.0.3", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha1-BJMzi91Y4xmxA5xnz37kOYksAdk=", "dev": true, "license": "MIT", "dependencies": { @@ -4925,9 +4925,9 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha1-VPxTI3phPYVMe9N0Y6rRffhyFOc=", + "version": "2.0.3", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha1-BJMzi91Y4xmxA5xnz37kOYksAdk=", "dev": true, "license": "MIT", "dependencies": { diff --git a/ExtensionPack/package-lock.json b/ExtensionPack/package-lock.json index 5ecbd639a..ff471e417 100644 --- a/ExtensionPack/package-lock.json +++ b/ExtensionPack/package-lock.json @@ -956,9 +956,9 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha1-q5tFRGblqMw6GHvqrVgEEqnFuEM=", + "version": "1.1.13", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha1-03h1wB3J7/mI3UnREqV8tntU7+Y=", "dev": true, "license": "MIT", "dependencies": { @@ -1809,9 +1809,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha1-YU2q7NCmiPZgu7yQmodIw9gNQzY=", + "version": "5.0.5", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha1-3MOjcRa3nz4bRtuZTO1dVw6TD9s=", "dev": true, "license": "MIT", "dependencies": { diff --git a/Themes/package-lock.json b/Themes/package-lock.json index fa9fb98b0..310e5c69b 100644 --- a/Themes/package-lock.json +++ b/Themes/package-lock.json @@ -9,7 +9,7 @@ "version": "2.1.0", "license": "SEE LICENSE IN LICENSE.txt", "devDependencies": { - "@vscode/vsce": "3.7.1" + "@vscode/vsce": "^3.7.1" }, "engines": { "vscode": "^1.30.0" @@ -956,9 +956,9 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha1-q5tFRGblqMw6GHvqrVgEEqnFuEM=", + "version": "1.1.13", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha1-03h1wB3J7/mI3UnREqV8tntU7+Y=", "dev": true, "license": "MIT", "dependencies": { @@ -1809,9 +1809,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha1-YU2q7NCmiPZgu7yQmodIw9gNQzY=", + "version": "5.0.5", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha1-3MOjcRa3nz4bRtuZTO1dVw6TD9s=", "dev": true, "license": "MIT", "dependencies": { diff --git a/Themes/package.json b/Themes/package.json index 69a60f779..03eafba21 100644 --- a/Themes/package.json +++ b/Themes/package.json @@ -63,7 +63,7 @@ ] }, "devDependencies": { - "@vscode/vsce": "3.7.1" + "@vscode/vsce": "^3.7.1" }, "overrides": { "@azure/msal-browser": "^4.29.1" From 6770b0a25a98dacef7a28750d2e07765c9248cff Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 31 Mar 2026 15:16:52 -0700 Subject: [PATCH 14/31] Update changelog for 1.31.4. (#14338) --- Extension/CHANGELOG.md | 4 ++++ Extension/package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 4600171ba..09ef7ed93 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,9 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.31.4: March 31, 2026 +### Bug Fix +* Debugging cpptools and cpptools-srv processes on macOS (to get call stacks) is now blocked when SIP is enabled (due to a potential security issue). + ## Version 1.31.3: March 24, 2026 ### Enhancements * Add support for `program` in attach debug configurations. [#14046](https://github.com/microsoft/vscode-cpptools/issues/14046) diff --git a/Extension/package.json b/Extension/package.json index 9f75d96c9..49701bccf 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "1.31.3-main", + "version": "1.31.4-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md", From fa097e4966a8d319e7e6d6fac4aaf9fc131ad320 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:09:05 -0700 Subject: [PATCH 15/31] Bump @xmldom/xmldom from 0.8.11 to 0.8.12 in /Extension (#14340) Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.11 to 0.8.12. - [Release notes](https://github.com/xmldom/xmldom/releases) - [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md) - [Commits](https://github.com/xmldom/xmldom/compare/0.8.11...0.8.12) --- updated-dependencies: - dependency-name: "@xmldom/xmldom" dependency-version: 0.8.12 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Extension/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 086050806..1d665116d 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -1310,9 +1310,9 @@ integrity sha1-Ml20I5XNSf5sFAV/mpAOQn34gQ4= "@xmldom/xmldom@^0.8.8": - version "0.8.11" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608" - integrity sha1-t53i1nOJc0xXxSWV96cwXjDC1gg= + version "0.8.12" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/@xmldom/xmldom/-/xmldom-0.8.12.tgz#cf488a5435fa06c7374ad1449c69cea0f823624b" + integrity sha1-z0iKVDX6Bsc3StFEnGnOoPgjYks= "@xtuc/ieee754@^1.2.0": version "1.2.0" From f309f90a4f20794829f2915b63b2b8ccb21c2c1a Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 2 Apr 2026 11:10:56 -0700 Subject: [PATCH 16/31] Update TPN. (#14343) --- Extension/ThirdPartyNotices.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Extension/ThirdPartyNotices.txt b/Extension/ThirdPartyNotices.txt index b2b6d038b..1d3df8562 100644 --- a/Extension/ThirdPartyNotices.txt +++ b/Extension/ThirdPartyNotices.txt @@ -1250,7 +1250,7 @@ SOFTWARE. --------------------------------------------------------- -@xmldom/xmldom 0.8.11 - MIT +@xmldom/xmldom 0.8.12 - MIT https://github.com/xmldom/xmldom Copyright 2019 - present Christopher J. Brody and other contributors @@ -1510,7 +1510,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --------------------------------------------------------- -brace-expansion 1.1.12 - MIT +brace-expansion 1.1.13 - MIT https://github.com/juliangruber/brace-expansion Copyright (c) 2013 Julian Gruber @@ -1542,7 +1542,7 @@ SOFTWARE. --------------------------------------------------------- -brace-expansion 2.0.2 - MIT +brace-expansion 2.0.3 - MIT https://github.com/juliangruber/brace-expansion Copyright (c) 2013 Julian Gruber @@ -1642,7 +1642,7 @@ SOFTWARE. chokidar 3.6.0 - MIT https://github.com/paulmillr/chokidar -(c) Paul Miller +(c) Paul Miller Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker The MIT License (MIT) @@ -3018,7 +3018,7 @@ THE SOFTWARE. --------------------------------------------------------- -picomatch 2.3.1 - MIT +picomatch 2.3.2 - MIT https://github.com/micromatch/picomatch Copyright (c) 2017-present, Jon Schlinkert @@ -3183,7 +3183,7 @@ readdirp 3.6.0 - MIT https://github.com/paulmillr/readdirp Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) -Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller +Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller ( https://paulmillr.com ) MIT License From 61815d8869519298dafb5648d09771f4be9dd302 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 2 Apr 2026 11:23:56 -0700 Subject: [PATCH 17/31] Update npmrc. (#14346) --- .github/actions/.npmrc | 4 +++- Extension/.npmrc | 4 +++- ExtensionPack/.npmrc | 4 +++- Themes/.npmrc | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/actions/.npmrc b/.github/actions/.npmrc index d8324806f..0446fca08 100644 --- a/.github/actions/.npmrc +++ b/.github/actions/.npmrc @@ -1,2 +1,4 @@ registry=https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/ -always-auth=true \ No newline at end of file +always-auth=true +# Disable postinstall scripts for supply chain security. Allowlist exceptions with npm trust: https://docs.npmjs.com/cli/v11/commands/npm-trust +ignore-scripts=true diff --git a/Extension/.npmrc b/Extension/.npmrc index d8324806f..0446fca08 100644 --- a/Extension/.npmrc +++ b/Extension/.npmrc @@ -1,2 +1,4 @@ registry=https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/ -always-auth=true \ No newline at end of file +always-auth=true +# Disable postinstall scripts for supply chain security. Allowlist exceptions with npm trust: https://docs.npmjs.com/cli/v11/commands/npm-trust +ignore-scripts=true diff --git a/ExtensionPack/.npmrc b/ExtensionPack/.npmrc index d8324806f..0446fca08 100644 --- a/ExtensionPack/.npmrc +++ b/ExtensionPack/.npmrc @@ -1,2 +1,4 @@ registry=https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/ -always-auth=true \ No newline at end of file +always-auth=true +# Disable postinstall scripts for supply chain security. Allowlist exceptions with npm trust: https://docs.npmjs.com/cli/v11/commands/npm-trust +ignore-scripts=true diff --git a/Themes/.npmrc b/Themes/.npmrc index d8324806f..0446fca08 100644 --- a/Themes/.npmrc +++ b/Themes/.npmrc @@ -1,2 +1,4 @@ registry=https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/ -always-auth=true \ No newline at end of file +always-auth=true +# Disable postinstall scripts for supply chain security. Allowlist exceptions with npm trust: https://docs.npmjs.com/cli/v11/commands/npm-trust +ignore-scripts=true From 34d375a6161cd9918b08407506bb955347ae3871 Mon Sep 17 00:00:00 2001 From: Ben McMorran Date: Fri, 3 Apr 2026 10:42:25 -0700 Subject: [PATCH 18/31] Prefer GPT-5-mini for Copilot Hover (#14349) --- .../src/LanguageServer/Providers/CopilotHoverProvider.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts index b04e17cd3..6e4954370 100644 --- a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts +++ b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts @@ -41,7 +41,13 @@ export class CopilotHoverProvider implements vscode.HoverProvider { const vscodelm = getVSCodeLanguageModel(); if (vscodelm) { try { - const [model] = await vscodelm.selectChatModels(modelSelector); + // First look for GPT-5-mini which should be available to all + // users and have a 0x multiplier on paid plans. + let [model] = await vscodelm.selectChatModels({ ...modelSelector, id: 'gpt-5-mini' }); + if (!model) { + // If GPT-5-mini is not available, fall back to the first available model + [model] = await vscodelm.selectChatModels(modelSelector); + } if (!model) { telemetry.logLanguageServerEvent('CopilotHoverNoModelSelected', { remoteName: vscode.env.remoteName || 'local' }); } else { From 61b53bcda81e17ade6d3ed859b7c8f3e765a0b96 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 3 Apr 2026 16:02:22 -0700 Subject: [PATCH 19/31] Update lodash. (#14350) --- Extension/yarn.lock | 6 +++--- ExtensionPack/package-lock.json | 6 +++--- Themes/package-lock.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 1d665116d..52bf6dd26 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -4271,9 +4271,9 @@ lodash.truncate@^4.4.2: integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= lodash@^4.17.23: - version "4.17.23" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" - integrity sha1-8ROwN4OGEDvk9okziMc9C95/LFo= + version "4.18.1" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha1-/ytmwfYybVlRPeJAe/iBQ5gSdxw= log-symbols@^4.1.0: version "4.1.0" diff --git a/ExtensionPack/package-lock.json b/ExtensionPack/package-lock.json index ff471e417..fe77d2eba 100644 --- a/ExtensionPack/package-lock.json +++ b/ExtensionPack/package-lock.json @@ -2353,9 +2353,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha1-8ROwN4OGEDvk9okziMc9C95/LFo=", + "version": "4.18.1", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c", + "integrity": "sha1-/ytmwfYybVlRPeJAe/iBQ5gSdxw=", "dev": true, "license": "MIT" }, diff --git a/Themes/package-lock.json b/Themes/package-lock.json index 310e5c69b..7f89e7fb1 100644 --- a/Themes/package-lock.json +++ b/Themes/package-lock.json @@ -2353,9 +2353,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha1-8ROwN4OGEDvk9okziMc9C95/LFo=", + "version": "4.18.1", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c", + "integrity": "sha1-/ytmwfYybVlRPeJAe/iBQ5gSdxw=", "dev": true, "license": "MIT" }, From 900aa75527d943b3c3af1c855c30297c59c6da81 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 7 Apr 2026 13:03:03 -0700 Subject: [PATCH 20/31] Fix npx @vscode/vsce. (#14354) * Fix npx @vscode/vsce. --- Build/cg/cg.yml | 18 ++++++++++++++---- Build/package/jobs_package_vsix.yml | 19 +++++++++++++++---- Build/publish/jobs_publish_vsix.yml | 12 ++++++++++-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Build/cg/cg.yml b/Build/cg/cg.yml index 7f1b47176..dc4dd0c49 100644 --- a/Build/cg/cg.yml +++ b/Build/cg/cg.yml @@ -107,8 +107,18 @@ extends: fi retryCountOnTaskFailure: 3 - - script: | - cd $(Build.SourcesDirectory)\Extension - npx @vscode/vsce@3.7.1 package --yarn -o $(Build.ArtifactStagingDirectory)\Extension\cpptools.vsix - name: ProcessRunner_12 + - script: npm install --no-save --ignore-scripts=false --include=optional --force @vscode/vsce@3.7.1 + displayName: Install vsce + workingDirectory: $(Build.SourcesDirectory)\Extension + + - script: npm rebuild @vscode/vsce-sign --ignore-scripts=false + displayName: Rebuild vsce-sign binary + workingDirectory: $(Build.SourcesDirectory)\Extension + + - script: if not exist node_modules\@vscode\vsce-sign\bin\vsce-sign.exe (echo Missing vsce-sign.exe && exit 1) + displayName: Verify vsce-sign binary exists + workingDirectory: $(Build.SourcesDirectory)\Extension + + - script: npx vsce package --yarn -o $(Build.ArtifactStagingDirectory)\Extension\cpptools.vsix displayName: Run VSCE to package vsix + workingDirectory: $(Build.SourcesDirectory)\Extension diff --git a/Build/package/jobs_package_vsix.yml b/Build/package/jobs_package_vsix.yml index 95d65b430..b5a73b174 100644 --- a/Build/package/jobs_package_vsix.yml +++ b/Build/package/jobs_package_vsix.yml @@ -59,13 +59,24 @@ jobs: - script: mkdir $(Build.ArtifactStagingDirectory)\vsix displayName: Create Staging Directory - - script: | - cd $(Build.SourcesDirectory)\${{ parameters.srcDir }} - npx @vscode/vsce@3.7.1 package -o $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.vsix + - script: npm install --no-save --ignore-scripts=false --include=optional --force @vscode/vsce@3.7.1 + displayName: Install vsce + workingDirectory: $(Build.SourcesDirectory)\${{ parameters.srcDir }} + + - script: npm rebuild @vscode/vsce-sign --ignore-scripts=false + displayName: Rebuild vsce-sign binary + workingDirectory: $(Build.SourcesDirectory)\${{ parameters.srcDir }} + + - script: if not exist node_modules\@vscode\vsce-sign\bin\vsce-sign.exe (echo Missing vsce-sign.exe && exit 1) + displayName: Verify vsce-sign binary exists + workingDirectory: $(Build.SourcesDirectory)\${{ parameters.srcDir }} + + - script: npx vsce package -o $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.vsix displayName: Run VSCE to package vsix + workingDirectory: $(Build.SourcesDirectory)\${{ parameters.srcDir }} # sign the vsix - - script: npx @vscode/vsce@3.7.1 generate-manifest -i $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.vsix -o $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.manifest + - script: npx vsce generate-manifest -i $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.vsix -o $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.manifest displayName: generate manifest workingDirectory: $(Build.SourcesDirectory)\${{ parameters.srcDir }} - script: copy $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.manifest $(Build.ArtifactStagingDirectory)\vsix\${{ parameters.vsixName }}.signature.p7s diff --git a/Build/publish/jobs_publish_vsix.yml b/Build/publish/jobs_publish_vsix.yml index 9cab81201..9decb93f8 100644 --- a/Build/publish/jobs_publish_vsix.yml +++ b/Build/publish/jobs_publish_vsix.yml @@ -31,8 +31,16 @@ jobs: $aadToken = az account get-access-token --query accessToken --resource $(AzureGuid) -o tsv Write-Host "##vso[task.setvariable variable=AAD_TOKEN;issecret=true]$aadToken" - - script: | - npx @vscode/vsce@3.7.1 publish --skip-duplicate -i $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.vsix --manifestPath $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.manifest --signaturePath $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.signature.p7s + - script: npm install --no-save --ignore-scripts=false --include=optional --force @vscode/vsce@3.7.1 + displayName: Install vsce + + - script: npm rebuild @vscode/vsce-sign --ignore-scripts=false + displayName: Rebuild vsce-sign binary + + - script: if not exist node_modules\@vscode\vsce-sign\bin\vsce-sign.exe (echo Missing vsce-sign.exe && exit 1) + displayName: Verify vsce-sign binary exists + + - script: npx vsce publish --skip-duplicate -i $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.vsix --manifestPath $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.manifest --signaturePath $(Build.StagingDirectory)\vsix\${{ parameters.vsixName }}.signature.p7s displayName: Publish to Marketplace env: VSCE_PAT: $(AAD_TOKEN) From 8e820040172b82ff6e91580114a146e1451be75c Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Wed, 8 Apr 2026 08:49:30 -0700 Subject: [PATCH 21/31] Fix cg.yml. (#14355) --- Build/cg/cg.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Build/cg/cg.yml b/Build/cg/cg.yml index dc4dd0c49..415874be5 100644 --- a/Build/cg/cg.yml +++ b/Build/cg/cg.yml @@ -107,12 +107,8 @@ extends: fi retryCountOnTaskFailure: 3 - - script: npm install --no-save --ignore-scripts=false --include=optional --force @vscode/vsce@3.7.1 - displayName: Install vsce - workingDirectory: $(Build.SourcesDirectory)\Extension - - - script: npm rebuild @vscode/vsce-sign --ignore-scripts=false - displayName: Rebuild vsce-sign binary + - script: yarn install --frozen-lockfile + displayName: Install dependencies with yarn workingDirectory: $(Build.SourcesDirectory)\Extension - script: if not exist node_modules\@vscode\vsce-sign\bin\vsce-sign.exe (echo Missing vsce-sign.exe && exit 1) From 69d4585eb273ce09ab9d09d66be57d860f01d695 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Wed, 8 Apr 2026 16:13:33 -0700 Subject: [PATCH 22/31] UCRTVersion is not required for a developer environment (#14358) --- Extension/src/common.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/Extension/src/common.ts b/Extension/src/common.ts index 849f4d6f3..4fc413604 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -1549,7 +1549,6 @@ export function hasMsvcEnvironment(): boolean { 'INCLUDE', 'LIB', 'LIBPATH', - 'UCRTVersion', 'UniversalCRTSdkDir', 'VCIDEInstallDir', 'VCINSTALLDIR', From bb5943c7551f62e0b47d6e7291291ed954187206 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Thu, 9 Apr 2026 13:37:29 -0700 Subject: [PATCH 23/31] Update instructions for installing MSVC (#14361) --- .../installcompiler/install-compiler-windows10.md | 5 ++--- .../installcompiler/install-compiler-windows11.md | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Extension/walkthrough/installcompiler/install-compiler-windows10.md b/Extension/walkthrough/installcompiler/install-compiler-windows10.md index 7e215fd65..305842637 100644 --- a/Extension/walkthrough/installcompiler/install-compiler-windows10.md +++ b/Extension/walkthrough/installcompiler/install-compiler-windows10.md @@ -1,9 +1,8 @@

Install a C++ compiler on Windows

If you're doing C++ development for Windows, we recommend installing the Microsoft Visual C++ (MSVC) compiler.

    -
  1. To install MSVC, open the VS Code terminal (CTRL + `) and paste in the following command: -

    winget install Microsoft.VisualStudio.2022.BuildTools --force --override "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.20348"
    -
  2. +
  3. To install MSVC, open the VS Code terminal (CTRL + `) and paste in the following command:

    +

    winget install Microsoft.VisualStudio.2022.BuildTools --force --override "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.19041"

    Note: You can use the C++ toolset from Visual Studio Build Tools along with Visual Studio Code to compile, build, and verify any C++ codebase as long as you also have a valid Visual Studio license (either Community, Pro, or Enterprise) that you are actively using to develop that C++ codebase.

    diff --git a/Extension/walkthrough/installcompiler/install-compiler-windows11.md b/Extension/walkthrough/installcompiler/install-compiler-windows11.md index cf9c39f1b..8d79b7eb7 100644 --- a/Extension/walkthrough/installcompiler/install-compiler-windows11.md +++ b/Extension/walkthrough/installcompiler/install-compiler-windows11.md @@ -1,9 +1,8 @@

    Install a C++ compiler on Windows

    If you're doing C++ development for Windows, we recommend installing the Microsoft Visual C++ (MSVC) compiler.

      -
    1. To install MSVC, open the VS Code terminal (CTRL + `) and paste in the following command: -

      winget install Microsoft.VisualStudio.2022.BuildTools --force --override "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100"
      -
    2. +
    3. To install MSVC, open the VS Code terminal (CTRL + `) and paste in the following command:

      +

      winget install Microsoft.VisualStudio.2022.BuildTools --force --override "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100"

      Note: You can use the C++ toolset from Visual Studio Build Tools along with Visual Studio Code to compile, build, and verify any C++ codebase as long as you also have a valid Visual Studio license (either Community, Pro, or Enterprise) that you are actively using to develop that C++ codebase.

      From 55ef430b968b2b6396201ef50218ca4a5264d2a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:49:49 -0700 Subject: [PATCH 24/31] Bump axios from 1.13.6 to 1.15.0 in /.github/actions (#14365) Bumps [axios](https://github.com/axios/axios) from 1.13.6 to 1.15.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.13.6...v1.15.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/package-lock.json | 21 ++++++++++++--------- .github/actions/package.json | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/actions/package-lock.json b/.github/actions/package-lock.json index af2dead5b..8a3524cf5 100644 --- a/.github/actions/package-lock.json +++ b/.github/actions/package-lock.json @@ -13,7 +13,7 @@ "@octokit/rest": "^21.1.1", "@slack/web-api": "^6.9.1", "applicationinsights": "^2.5.1", - "axios": "^1.13.5", + "axios": "^1.15.0", "uuid": "^8.3.2" }, "devDependencies": { @@ -3084,14 +3084,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/axios/-/axios-1.13.6.tgz", - "integrity": "sha1-w/ktqRfcIJoV3SmTbSDVCJtrbJg=", + "version": "1.15.0", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/axios/-/axios-1.15.0.tgz", + "integrity": "sha1-D87pHvA9OGUUR0kEsnhjssaDv08=", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "proxy-from-env": "^2.1.0" } }, "node_modules/axios/node_modules/form-data": { @@ -5339,10 +5339,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha1-4QLxbKNVQkhldV0sno6k8k1Yw+I=", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha1-p0h1aK2tV3z6qn6IxJyrOrMIGro=", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", diff --git a/.github/actions/package.json b/.github/actions/package.json index f824ebd03..ed71c72c8 100644 --- a/.github/actions/package.json +++ b/.github/actions/package.json @@ -15,7 +15,7 @@ "@octokit/rest": "^21.1.1", "@slack/web-api": "^6.9.1", "applicationinsights": "^2.5.1", - "axios": "^1.13.5", + "axios": "^1.15.0", "uuid": "^8.3.2" }, "devDependencies": { From 7774dafb1fb990a57a0390ebc6c1db2685995493 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 13 Apr 2026 12:19:54 -0700 Subject: [PATCH 25/31] Add setting doxygen.generateOnCodeAction. (#14342) --- Extension/package.json | 6 ++++++ Extension/package.nls.json | 1 + Extension/src/LanguageServer/client.ts | 1 + Extension/src/LanguageServer/settings.ts | 2 ++ 4 files changed, 10 insertions(+) diff --git a/Extension/package.json b/Extension/package.json index 49701bccf..e07af7649 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -1703,6 +1703,12 @@ "description": "%c_cpp.configuration.doxygen.generateOnType.description%", "scope": "resource" }, + "C_Cpp.doxygen.generateOnCodeAction": { + "type": "boolean", + "default": true, + "description": "%c_cpp.configuration.doxygen.generateOnCodeAction.description%", + "scope": "resource" + }, "C_Cpp.doxygen.generatedStyle": { "type": "string", "enum": [ diff --git a/Extension/package.nls.json b/Extension/package.nls.json index ca0f42449..9adf8c517 100644 --- a/Extension/package.nls.json +++ b/Extension/package.nls.json @@ -580,6 +580,7 @@ ] }, "c_cpp.configuration.doxygen.generateOnType.description": "Controls whether to automatically insert the Doxygen comment after typing the chosen comment style.", + "c_cpp.configuration.doxygen.generateOnCodeAction.description": "Controls whether the code action to generate a Doxygen comment is enabled.", "c_cpp.configuration.doxygen.generatedStyle.description": "The string of characters used as the starting line of the Doxygen comment.", "c_cpp.configuration.doxygen.sectionTags.description": "Select the Doxygen section tags that you would like to appear on hover in the tooltip area when the 'Simplify Structured Comments' setting is enabled. ", "c_cpp.configuration.commentContinuationPatterns.items.anyof.string.markdownDescription": { diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 9fd268013..680c14749 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -1537,6 +1537,7 @@ export class DefaultClient implements Client { vcFormatSpaceAroundTernaryOperator: settings.vcFormatSpaceAroundTernaryOperator, vcFormatWrapPreserveBlocks: settings.vcFormatWrapPreserveBlocks, doxygenGenerateOnType: settings.doxygenGenerateOnType, + doxygenGenerateOnCodeAction: settings.doxygenGenerateOnCodeAction, doxygenGeneratedStyle: settings.doxygenGeneratedCommentStyle, doxygenSectionTags: settings.doxygenSectionTags, filesExclude: otherSettings.filesExclude, diff --git a/Extension/src/LanguageServer/settings.ts b/Extension/src/LanguageServer/settings.ts index 881423789..a80d54347 100644 --- a/Extension/src/LanguageServer/settings.ts +++ b/Extension/src/LanguageServer/settings.ts @@ -126,6 +126,7 @@ export interface WorkspaceFolderSettingsParams { vcFormatSpaceAroundTernaryOperator: string; vcFormatWrapPreserveBlocks: string; doxygenGenerateOnType: boolean; + doxygenGenerateOnCodeAction: boolean; doxygenGeneratedStyle: string; doxygenSectionTags: string[]; filesExclude: Excludes; @@ -383,6 +384,7 @@ export class CppSettings extends Settings { public get simplifyStructuredComments(): boolean { return this.getAsBoolean("simplifyStructuredComments"); } public get doxygenGeneratedCommentStyle(): string { return this.getAsString("doxygen.generatedStyle"); } public get doxygenGenerateOnType(): boolean { return this.getAsBoolean("doxygen.generateOnType"); } + public get doxygenGenerateOnCodeAction(): boolean { return this.getAsBoolean("doxygen.generateOnCodeAction"); } public get commentContinuationPatterns(): (string | CommentPattern)[] { const value: any = super.Section.get("commentContinuationPatterns"); if (this.isArrayOfCommentContinuationPatterns(value)) { From 291e5a322822131e8ad1b99c1d67112513ae6bf7 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Mon, 13 Apr 2026 12:38:30 -0700 Subject: [PATCH 26/31] Run without debugging (#14351) --- .github/workflows/job-compile-and-test.yml | 12 + Extension/.vscode/launch.json | 4 + .../src/Debugger/configurationProvider.ts | 2 + Extension/src/Debugger/configurations.ts | 6 +- .../Debugger/debugAdapterDescriptorFactory.ts | 47 ++- .../Debugger/runWithoutDebuggingAdapter.ts | 302 ++++++++++++++++++ Extension/src/common.ts | 43 ++- .../RunWithoutDebugging/assets/argsTest.cpp | 21 ++ .../RunWithoutDebugging/assets/debugTest.cpp | 11 + .../tests/compileProgram.ts | 72 +++++ .../runWithoutDebugging.integration.test.ts | 267 ++++++++++++++++ .../runWithoutDebugging.terminals.test.ts | 190 +++++++++++ 12 files changed, 969 insertions(+), 8 deletions(-) create mode 100644 Extension/src/Debugger/runWithoutDebuggingAdapter.ts create mode 100644 Extension/test/scenarios/RunWithoutDebugging/assets/argsTest.cpp create mode 100644 Extension/test/scenarios/RunWithoutDebugging/assets/debugTest.cpp create mode 100644 Extension/test/scenarios/RunWithoutDebugging/tests/compileProgram.ts create mode 100644 Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.integration.test.ts create mode 100644 Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.terminals.test.ts diff --git a/.github/workflows/job-compile-and-test.yml b/.github/workflows/job-compile-and-test.yml index a9a1b8621..884c61c91 100644 --- a/.github/workflows/job-compile-and-test.yml +++ b/.github/workflows/job-compile-and-test.yml @@ -66,6 +66,11 @@ jobs: # run: yarn test --scenario=MultirootDeadlockTest # working-directory: Extension + # - name: Run E2E IntelliSense features tests + # if: ${{ inputs.platform == 'windows' }} + # run: yarn test --scenario=RunWithoutDebugging + # working-directory: Extension + # NOTE: For mac/linux run the tests with xvfb-action for UI support. # Another way to start xvfb https://github.com/microsoft/vscode-test/blob/master/sample/azure-pipelines.yml @@ -83,3 +88,10 @@ jobs: # run: yarn test --scenario=MultirootDeadlockTest # working-directory: Extension + # - name: Run E2E IntelliSense features tests (xvfb) + # if: ${{ inputs.platform == 'mac' || inputs.platform == 'linux' }} + # uses: coactions/setup-xvfb@v1 + # with: + # run: yarn test --scenario=RunWithoutDebugging + # working-directory: Extension + diff --git a/Extension/.vscode/launch.json b/Extension/.vscode/launch.json index 4323f133d..a110f3407 100644 --- a/Extension/.vscode/launch.json +++ b/Extension/.vscode/launch.json @@ -97,6 +97,10 @@ "label": "MultirootDeadlockTest ", "value": "${workspaceFolder}/test/scenarios/MultirootDeadlockTest/assets/test.code-workspace" }, + { + "label": "RunWithoutDebugging ", + "value": "${workspaceFolder}/test/scenarios/RunWithoutDebugging/assets/" + }, { "label": "SimpleCppProject ", "value": "${workspaceFolder}/test/scenarios/SimpleCppProject/assets/simpleCppProject.code-workspace" diff --git a/Extension/src/Debugger/configurationProvider.ts b/Extension/src/Debugger/configurationProvider.ts index 8f0ae0c7c..5bc427759 100644 --- a/Extension/src/Debugger/configurationProvider.ts +++ b/Extension/src/Debugger/configurationProvider.ts @@ -147,12 +147,14 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile, "configMode": ConfigMode.noLaunchConfig, "cancelled": "true", "succeeded": "true" }); return undefined; // aborts debugging silently } else { + const noDebug = config.noDebug ?? false; // Preserve the noDebug value from the config if it exists. // Currently, we expect only one debug config to be selected. console.assert(configs.length === 1, "More than one debug config is selected."); config = configs[0]; // Keep track of the entry point where the debug config has been selected, for telemetry purposes. config.debuggerEvent = DebuggerEvent.debugPanel; config.configSource = folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile; + config.noDebug = noDebug; } } diff --git a/Extension/src/Debugger/configurations.ts b/Extension/src/Debugger/configurations.ts index 96895c6da..d3c0acaf6 100644 --- a/Extension/src/Debugger/configurations.ts +++ b/Extension/src/Debugger/configurations.ts @@ -97,7 +97,7 @@ function createLaunchString(name: string, type: string, executable: string): str "stopAtEntry": false, "cwd": "$\{fileDirname\}", "environment": [], -${ type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"` } +${type === "cppdbg" ? `"externalConsole": false` : `"console": "internalConsole"`} `; } @@ -164,7 +164,7 @@ export class MIConfigurations extends Configuration { \t${indentJsonString(createLaunchString(name, this.miDebugger, this.executable))}, \t"MIMode": "${this.MIMode}"{0}{1} }`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "", - this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]); + this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]); return { "label": configPrefix + name, @@ -182,7 +182,7 @@ export class MIConfigurations extends Configuration { \t${indentJsonString(createAttachString(name, this.miDebugger, this.executable))} \t"MIMode": "${this.MIMode}"{0}{1} }`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "", - this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]); + this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]); return { "label": configPrefix + name, diff --git a/Extension/src/Debugger/debugAdapterDescriptorFactory.ts b/Extension/src/Debugger/debugAdapterDescriptorFactory.ts index d43d71bc3..90657bb90 100644 --- a/Extension/src/Debugger/debugAdapterDescriptorFactory.ts +++ b/Extension/src/Debugger/debugAdapterDescriptorFactory.ts @@ -7,11 +7,13 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from "vscode"; import * as nls from 'vscode-nls'; +import { getOutputChannel } from '../logger'; +import { RunWithoutDebuggingAdapter } from './runWithoutDebuggingAdapter'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); -// Registers DebugAdapterDescriptorFactory for `cppdbg` and `cppvsdbg`. If it is not ready, it will prompt a wait for the download dialog. +// Registers DebugAdapterDescriptorFactory for `cppdbg` and `cppvsdbg`. // NOTE: This file is not automatically tested. abstract class AbstractDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory { @@ -26,8 +28,15 @@ abstract class AbstractDebugAdapterDescriptorFactory implements vscode.DebugAdap } export class CppdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDescriptorFactory { + async createDebugAdapterDescriptor(session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise { + if (session.configuration.noDebug) { + if (noDebugSupported(session.configuration)) { + return new vscode.DebugAdapterInlineImplementation(new RunWithoutDebuggingAdapter()); + } + // If the configuration is not supported, gracefully fall back to a regular debug session and log a message to the user. + logReasonForNoDebugNotSupported(session.configuration); + } - async createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise { const adapter: string = "./debugAdapters/bin/OpenDebugAD7" + (os.platform() === 'win32' ? ".exe" : ""); const command: string = path.join(this.context.extensionPath, adapter); @@ -37,8 +46,15 @@ export class CppdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDes } export class CppvsdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDescriptorFactory { + async createDebugAdapterDescriptor(session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise { + if (session.configuration.noDebug) { + if (noDebugSupported(session.configuration)) { + return new vscode.DebugAdapterInlineImplementation(new RunWithoutDebuggingAdapter()); + } + // If the configuration is not supported, gracefully fall back to a regular debug session and log a message to the user. + logReasonForNoDebugNotSupported(session.configuration); + } - async createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise { if (os.platform() !== 'win32') { void vscode.window.showErrorMessage(localize("debugger.not.available", "Debugger type '{0}' is not available for non-Windows machines.", "cppvsdbg")); return null; @@ -50,3 +66,28 @@ export class CppvsdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterD } } } + +function noDebugSupported(configuration: vscode.DebugConfiguration): boolean { + // Don't attempt to start a noDebug session if the configuration has any of these properties, which require a debug adapter to function. + return configuration.request === 'launch' && !configuration.pipeTransport && !configuration.debugServerPath && !configuration.miDebuggerServerAddress && !configuration.coreDumpPath; +} + +function logReasonForNoDebugNotSupported(configuration: vscode.DebugConfiguration): void { + const outputChannel = getOutputChannel(); + if (configuration.request !== 'launch') { + outputChannel.appendLine(localize("debugger.noDebug.requestType.not.supported", "Run Without Debugging is only supported for launch configurations.")); + } + if (configuration.pipeTransport) { + outputChannel.appendLine(localize("debugger.noDebug.pipeTransport.not.supported", "Run Without Debugging is not supported for configurations with 'pipeTransport' set.")); + } + if (configuration.debugServerPath) { + outputChannel.appendLine(localize("debugger.noDebug.debugServerPath.not.supported", "Run Without Debugging is not supported for configurations with 'debugServerPath' set.")); + } + if (configuration.miDebuggerServerAddress) { + outputChannel.appendLine(localize("debugger.noDebug.miDebuggerServerAddress.not.supported", "Run Without Debugging is not supported for configurations with 'miDebuggerServerAddress' set.")); + } + if (configuration.coreDumpPath) { + outputChannel.appendLine(localize("debugger.noDebug.coreDumpPath.not.supported", "Run Without Debugging is not supported for configurations with 'coreDumpPath' set.")); + } + outputChannel.show(true); +} diff --git a/Extension/src/Debugger/runWithoutDebuggingAdapter.ts b/Extension/src/Debugger/runWithoutDebuggingAdapter.ts new file mode 100644 index 000000000..0d4eda2c6 --- /dev/null +++ b/Extension/src/Debugger/runWithoutDebuggingAdapter.ts @@ -0,0 +1,302 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as cp from 'child_process'; +import * as os from 'os'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { buildShellCommandLine, sessionIsWsl } from '../common'; +import { isWindows } from '../constants'; + +nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); +const localize = nls.loadMessageBundle(); + +/** + * A minimal inline Debug Adapter that runs the target program directly without a debug adapter + * when the user invokes "Run Without Debugging". + */ +export class RunWithoutDebuggingAdapter implements vscode.DebugAdapter { + private readonly sendMessageEmitter = new vscode.EventEmitter(); + public readonly onDidSendMessage: vscode.Event = this.sendMessageEmitter.event; + private readonly terminalListeners: vscode.Disposable[] = []; + + private seq: number = 1; + private childProcess?: cp.ChildProcess; + private terminal?: vscode.Terminal; + private terminalExecution?: vscode.TerminalShellExecution; + private hasTerminated: boolean = false; + + public handleMessage(message: vscode.DebugProtocolMessage): void { + const msg = message as { type: string; command: string; seq: number; arguments?: any; }; + if (msg.type === 'request') { + void this.handleRequest(msg); + } + } + + private async handleRequest(request: { command: string; seq: number; arguments?: any; }): Promise { + switch (request.command) { + case 'initialize': + this.sendResponse(request, {}); + this.sendEvent('initialized'); + break; + case 'launch': + await this.launch(request); + break; + case 'configurationDone': + this.sendResponse(request, {}); + break; + case 'disconnect': + case 'terminate': + this.sendResponse(request, {}); + break; + default: + this.sendResponse(request, {}); + break; + } + } + + private async launch(request: { command: string; seq: number; arguments?: any; }): Promise { + const config = request.arguments as { + program?: string; + args?: string[]; + cwd?: string; + environment?: { name: string; value: string; }[]; + console?: string; + externalConsole?: boolean; + }; + + const program: string = config.program ?? ''; + const args: string[] = config.args ?? []; + const cwd: string | undefined = config.cwd; + const environment: { name: string; value: string; }[] = config.environment ?? []; + const consoleMode: string = config.console ?? (config.externalConsole ? 'externalTerminal' : 'integratedTerminal'); + + // Merge the launch config's environment variables on top of the inherited process environment. + const env: NodeJS.ProcessEnv = { ...process.env }; + for (const e of environment) { + env[e.name] = e.value; + } + + this.sendResponse(request, {}); + + if (consoleMode === 'integratedTerminal' || consoleMode === 'internalConsole') { + await this.launchIntegratedTerminal(program, args, cwd, env); + } else if (consoleMode === 'externalTerminal') { + this.launchExternalTerminal(program, args, cwd, env); + } + } + + /** + * Launch the program in a VS Code integrated terminal. + * The terminal will remain open after the program exits and be reused for the next session, if applicable. + */ + private async launchIntegratedTerminal(program: string, args: string[], cwd: string | undefined, env: NodeJS.ProcessEnv): Promise { + const terminalName = path.normalize(program); + const existingTerminal = vscode.window.terminals.find(t => t.name === terminalName); + this.terminal = existingTerminal ?? vscode.window.createTerminal({ + name: terminalName, + cwd, + env: env as Record + }); + this.terminal.show(true); + + const shellIntegration: vscode.TerminalShellIntegration | undefined = + this.terminal.shellIntegration ?? await this.waitForShellIntegration(this.terminal, 3000); + + // Not all terminals support shell integration. If it's not available, we'll just send the command as text though we won't be able to monitor its execution. + if (shellIntegration) { + this.monitorIntegratedTerminal(this.terminal); + let executable: string = program; + let executableArgs: string[] = args; + if (!program.match(/["']/) && program.match(/\s/)) { + // VS Code does not automatically quote the program path if it has spaces. + const shellPath: string | undefined = 'shellPath' in this.terminal.creationOptions + ? this.terminal.creationOptions.shellPath?.toLowerCase() + : undefined; + const terminalShell: string | undefined = this.terminal.state.shell?.toLowerCase(); + const defaultTerminalProfile: string | undefined = isWindows + ? vscode.workspace.getConfiguration('terminal.integrated').get('defaultProfile.windows')?.toLowerCase() + : undefined; + const isPowerShell: boolean | undefined = + shellPath?.endsWith('pwsh.exe') || shellPath?.endsWith('powershell.exe') || shellPath?.endsWith('pwsh') || + terminalShell?.includes('powershell') || terminalShell?.includes('pwsh') || + defaultTerminalProfile?.includes('powershell') || defaultTerminalProfile?.includes('pwsh'); + + if (isPowerShell || (isWindows && isPowerShell === undefined)) { // PowerShell is the default on Windows if we can't determine the shell. + executable = '&'; + executableArgs = [program, ...args]; + } else { + executable = `"${program}"`; + } + } + this.terminalExecution = shellIntegration.executeCommand(executable, executableArgs); + } else { + const cmdLine: string = buildShellCommandLine('', program, args, true); + this.terminal.sendText(cmdLine); + + // The terminal manages its own lifecycle; notify VS Code the "debug" session is done. + this.sendEvent('terminated'); + } + } + + /** + * Launch the program in an external terminal. We do not keep track of this terminal or the spawned process. + */ + private launchExternalTerminal(program: string, args: string[], cwd: string | undefined, env: NodeJS.ProcessEnv): void { + const cmdLine: string = buildShellCommandLine('', program, args, true); + const platform: string = os.platform(); + if (platform === 'win32') { + cp.spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K', `"${cmdLine}"`], { cwd, env, windowsVerbatimArguments: true, detached: true, stdio: 'ignore' }).unref(); + } else if (platform === 'darwin') { + cp.spawn('osascript', ['-e', `tell application "Terminal" to do script "${this.escapeQuotes(cmdLine)}"`], { cwd, env, detached: true, stdio: 'ignore' }).unref(); + } else if (platform === 'linux' && sessionIsWsl()) { + cp.spawn('/mnt/c/Windows/System32/cmd.exe', ['/c', 'start', 'bash', '-c', `${cmdLine};read -p 'Press enter to continue...'`], { env, detached: true, stdio: 'ignore' }).unref(); + } else { // platform === 'linux' + this.launchLinuxExternalTerminal(cmdLine, cwd, env); + } + this.sendEvent('terminated'); + } + + /** + * On Linux, find and launch an available terminal emulator to run the command. + */ + private launchLinuxExternalTerminal(cmdLine: string, cwd: string | undefined, env: NodeJS.ProcessEnv): void { + const bashCmd = `${cmdLine}; echo; read -p 'Press enter to continue...'`; + const bashArgs = ['bash', '-c', bashCmd]; + + // Terminal emulators in order of preference, with the correct flag style for each. + const candidates: { cmd: string; buildArgs(): string[] }[] = [ + { cmd: 'x-terminal-emulator', buildArgs: () => ['-e', ...bashArgs] }, + { cmd: 'gnome-terminal', buildArgs: () => ['-e', ...bashArgs] }, + { cmd: 'konsole', buildArgs: () => ['-e', ...bashArgs] }, + { cmd: 'xterm', buildArgs: () => ['-e', ...bashArgs] } + ]; + + // Honor the $TERMINAL environment variable if set. + const terminalEnv = process.env['TERMINAL']; + if (terminalEnv) { + candidates.unshift({ cmd: terminalEnv, buildArgs: () => ['-e', ...bashArgs] }); + } + + for (const candidate of candidates) { + try { + const result = cp.spawnSync('which', [candidate.cmd], { stdio: 'pipe' }); + if (result.status === 0) { + cp.spawn(candidate.cmd, candidate.buildArgs(), { cwd, env, detached: true, stdio: 'ignore' }).unref(); + return; + } + } catch { + continue; + } + } + + const message = localize('no.terminal.emulator', 'No terminal emulator found. Please set the $TERMINAL environment variable to your terminal emulator of choice, or install one of the following: x-terminal-emulator, gnome-terminal, konsole, xterm.'); + vscode.window.showErrorMessage(message); + } + + private escapeQuotes(arg: string): string { + return arg.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + } + + private waitForShellIntegration(terminal: vscode.Terminal, timeoutMs: number): Promise { + return new Promise(resolve => { + let resolved: boolean = false; + const done = (shellIntegration: vscode.TerminalShellIntegration | undefined): void => { + if (resolved) { + return; + } + + resolved = true; + clearTimeout(timeout); + shellIntegrationChanged.dispose(); + terminalClosed.dispose(); + resolve(shellIntegration); + }; + + const timeout = setTimeout(() => done(undefined), timeoutMs); + const shellIntegrationChanged = vscode.window.onDidChangeTerminalShellIntegration(event => { + if (event.terminal === terminal) { + done(event.shellIntegration); + } + }); + const terminalClosed = vscode.window.onDidCloseTerminal(closedTerminal => { + if (closedTerminal === terminal) { + done(undefined); + } + }); + }); + } + + private monitorIntegratedTerminal(terminal: vscode.Terminal): void { + this.disposeTerminalListeners(); + this.terminalListeners.push( + vscode.window.onDidEndTerminalShellExecution(event => { + if (event.terminal !== terminal || event.execution !== this.terminalExecution || this.hasTerminated) { + return; + } + + if (event.exitCode !== undefined) { + this.sendEvent('exited', { exitCode: event.exitCode }); + } + + this.sendEvent('terminated'); + }), + vscode.window.onDidCloseTerminal(closedTerminal => { + if (closedTerminal !== terminal || this.hasTerminated) { + return; + } + + this.sendEvent('terminated'); + }) + ); + } + + private disposeTerminalListeners(): void { + while (this.terminalListeners.length > 0) { + this.terminalListeners.pop()?.dispose(); + } + } + + private sendResponse(request: { command: string; seq: number; }, body: object): void { + this.sendMessageEmitter.fire({ + type: 'response', + seq: this.seq++, + request_seq: request.seq, + success: true, + command: request.command, + body + } as vscode.DebugProtocolMessage); + } + + private sendEvent(event: string, body?: object): void { + if (event === 'terminated') { + if (this.hasTerminated) { + return; + } + + this.hasTerminated = true; + this.disposeTerminalListeners(); + } + + this.sendMessageEmitter.fire({ + type: 'event', + seq: this.seq++, + event, + body + } as vscode.DebugProtocolMessage); + } + + public dispose(): void { + this.terminateProcess(); + this.disposeTerminalListeners(); + this.sendMessageEmitter.dispose(); + } + + private terminateProcess(): void { + this.childProcess?.kill(); + this.childProcess = undefined; + } +} diff --git a/Extension/src/common.ts b/Extension/src/common.ts index 4fc413604..c84f94290 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -1567,6 +1567,34 @@ export function hasMsvcEnvironment(): boolean { ); } +export function getMissingMsvcEnvironmentVariables(): string[] { + const msvcEnvVars: string[] = [ + 'DevEnvDir', + 'Framework40Version', + 'FrameworkDir', + 'FrameworkVersion', + 'INCLUDE', + 'LIB', + 'LIBPATH', + 'UCRTVersion', + 'UniversalCRTSdkDir', + 'VCIDEInstallDir', + 'VCINSTALLDIR', + 'VCToolsRedistDir', + 'VisualStudioVersion', + 'VSINSTALLDIR', + 'WindowsLibPath', + 'WindowsSdkBinPath', + 'WindowsSdkDir', + 'WindowsSDKLibVersion', + 'WindowsSDKVersion' + ]; + return msvcEnvVars.filter(envVarName => + (process.env[envVarName] === undefined || process.env[envVarName] === '') && + extensionContext?.environmentVariableCollection?.get(envVarName) === undefined + ); +} + function isIntegral(str: string): boolean { const regex = /^-?\d+$/; return regex.test(str); @@ -1667,7 +1695,7 @@ export interface IQuotedString { export type CommandString = string | IQuotedString; -export function buildShellCommandLine(originalCommand: CommandString, command: CommandString, args: CommandString[]): string { +export function buildShellCommandLine(originalCommand: CommandString, command: CommandString, args: CommandString[], singleCommandOnly: boolean = false): string { let shellQuoteOptions: IShellQuotingOptions; const isWindows: boolean = os.platform() === 'win32'; @@ -1781,7 +1809,7 @@ export function buildShellCommandLine(originalCommand: CommandString, command: C let commandLine = result.join(' '); // There are special rules quoted command line in cmd.exe - if (isWindows) { + if (isWindows && !singleCommandOnly) { commandLine = `chcp 65001>nul && ${commandLine}`; if (commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; @@ -1849,3 +1877,14 @@ export function getVSCodeLanguageModel(): any | undefined { } return vscodelm; } + +export function sessionIsWsl(): boolean { + if (process.env.WSL_DISTRO_NAME) { + return true; + } + try { + return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); + } catch { + return false; + } +} diff --git a/Extension/test/scenarios/RunWithoutDebugging/assets/argsTest.cpp b/Extension/test/scenarios/RunWithoutDebugging/assets/argsTest.cpp new file mode 100644 index 000000000..aef910aef --- /dev/null +++ b/Extension/test/scenarios/RunWithoutDebugging/assets/argsTest.cpp @@ -0,0 +1,21 @@ +#include + +int main(int argc, char *argv[]) { + if (argc < 2) { + return 1; + } + + std::ofstream resultFile(argv[1]); + if (!resultFile) { + return 2; + } + + for (int i = 2; i < argc; ++i) { + resultFile << argv[i]; + if (i + 1 < argc) { + resultFile << '\n'; + } + } + + return 0; +} diff --git a/Extension/test/scenarios/RunWithoutDebugging/assets/debugTest.cpp b/Extension/test/scenarios/RunWithoutDebugging/assets/debugTest.cpp new file mode 100644 index 000000000..77020e3e5 --- /dev/null +++ b/Extension/test/scenarios/RunWithoutDebugging/assets/debugTest.cpp @@ -0,0 +1,11 @@ +#include + +int main() { + std::ofstream resultFile("runWithoutDebuggingResult.txt"); + if (!resultFile) { + return 1; + } + + resultFile << 37; + return 0; +} diff --git a/Extension/test/scenarios/RunWithoutDebugging/tests/compileProgram.ts b/Extension/test/scenarios/RunWithoutDebugging/tests/compileProgram.ts new file mode 100644 index 000000000..4c71ad62c --- /dev/null +++ b/Extension/test/scenarios/RunWithoutDebugging/tests/compileProgram.ts @@ -0,0 +1,72 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +/* eslint-disable @typescript-eslint/triple-slash-reference */ +/// +import * as assert from 'assert'; +import * as cp from 'child_process'; +import * as vscode from 'vscode'; +import * as util from '../../../../src/common'; +import { isLinux, isMacOS, isWindows } from '../../../../src/constants'; +import { getEffectiveEnvironment } from '../../../../src/LanguageServer/devcmd'; + +interface ProcessResult { + code: number | null; + stdout: string; + stderr: string; +} + +function runProcess(command: string, args: string[], cwd: string, env?: NodeJS.ProcessEnv): Promise { + return new Promise((resolve, reject) => { + const child = cp.spawn(command, args, { cwd, env }); + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (data: Buffer) => { + stdout += data.toString(); + }); + + child.stderr.on('data', (data: Buffer) => { + stderr += data.toString(); + }); + + child.on('error', reject); + child.on('close', (code) => resolve({ code, stdout, stderr })); + }); +} + +async function setWindowsBuildEnvironment(): Promise { + const promise = vscode.commands.executeCommand('C_Cpp.SetVsDeveloperEnvironment', 'test'); + const timer = setInterval(() => { + void vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + }, 1000); + await promise; + clearInterval(timer); + const missingVars = util.getMissingMsvcEnvironmentVariables(); + assert.strictEqual(missingVars.length, 0, `MSVC environment missing: ${missingVars.join(', ')}`); +} + +export async function compileProgram(workspacePath: string, sourcePath: string, outputPath: string): Promise { + if (isWindows) { + await setWindowsBuildEnvironment(); + const env = getEffectiveEnvironment(); + const result = await runProcess('cl.exe', ['/nologo', '/EHsc', '/Zi', '/std:c++17', `/Fe:${outputPath}`, sourcePath], workspacePath, env); + assert.strictEqual(result.code, 0, `MSVC compilation failed. stdout: ${result.stdout}\nstderr: ${result.stderr}`); + return; + } + + if (isMacOS) { + const result = await runProcess('clang++', ['-std=c++17', '-g', sourcePath, '-o', outputPath], workspacePath); + assert.strictEqual(result.code, 0, `clang++ compilation failed. stdout: ${result.stdout}\nstderr: ${result.stderr}`); + return; + } + + if (isLinux) { + const result = await runProcess('g++', ['-std=c++17', '-g', sourcePath, '-o', outputPath], workspacePath); + assert.strictEqual(result.code, 0, `g++ compilation failed. stdout: ${result.stdout}\nstderr: ${result.stderr}`); + return; + } + + assert.fail(`Unsupported test platform: ${process.platform}`); +} diff --git a/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.integration.test.ts b/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.integration.test.ts new file mode 100644 index 000000000..53542e36e --- /dev/null +++ b/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.integration.test.ts @@ -0,0 +1,267 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +/* eslint-disable @typescript-eslint/triple-slash-reference */ +/// +import * as assert from 'assert'; +import { suite } from 'mocha'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as util from '../../../../src/common'; +import { isMacOS, isWindows } from '../../../../src/constants'; +import { compileProgram } from './compileProgram'; + +interface TrackerState { + setBreakpointsRequestReceived: boolean; + stoppedEventReceived: boolean; + exitedEventReceived: boolean; + exitedBeforeStop: boolean; +} + +interface TrackerController { + state: TrackerState; + lastEvent: Promise<'stopped' | 'exited'>; + dispose(): void; +} + +async function createBreakpointAtResultWriteStatement(sourceUri: vscode.Uri): Promise { + const document = await vscode.workspace.openTextDocument(sourceUri); + const resultWriteLine = document.getText().split(/\r?\n/).findIndex((line) => line.includes('resultFile << 37;')); + assert.notStrictEqual(resultWriteLine, -1, 'Unable to find expected result-write statement for breakpoint placement.'); + const breakpoint = new vscode.SourceBreakpoint(new vscode.Location(sourceUri, new vscode.Position(resultWriteLine, 0)), true); + vscode.debug.addBreakpoints([breakpoint]); + return breakpoint; +} + +function createSessionTerminatedPromise(sessionName: string): Promise { + return new Promise((resolve) => { + const terminateSubscription = vscode.debug.onDidTerminateDebugSession((session) => { + if (session.name === sessionName) { + terminateSubscription.dispose(); + resolve(); + } + }); + }); +} + +function createTracker(debugType: string, sessionName: string, timeoutMs: number, timeoutMessage: string): TrackerController { + const state: TrackerState = { + setBreakpointsRequestReceived: false, + stoppedEventReceived: false, + exitedEventReceived: false, + exitedBeforeStop: false + }; + + let trackerRegistration: vscode.Disposable | undefined; + let timeoutHandle: NodeJS.Timeout | undefined; + + const lastEvent = new Promise<'stopped' | 'exited'>((resolve, reject) => { + timeoutHandle = setTimeout(() => { + trackerRegistration?.dispose(); + trackerRegistration = undefined; + reject(new Error(timeoutMessage)); + }, timeoutMs); + + trackerRegistration = vscode.debug.registerDebugAdapterTrackerFactory(debugType, { + createDebugAdapterTracker: (session: vscode.DebugSession): vscode.DebugAdapterTracker | undefined => { + if (session.name !== sessionName) { + return undefined; + } + + return { + onWillReceiveMessage: (message: any): void => { + if (message?.type === 'request' && message?.command === 'setBreakpoints') { + state.setBreakpointsRequestReceived = true; + } + }, + onDidSendMessage: (message: any): void => { + if (message?.type !== 'event') { + return; + } + + if (message.event === 'stopped') { + state.stoppedEventReceived = true; + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = undefined; + } + resolve('stopped'); + } + + if ((message.event === 'terminated' || message.event === 'exited') && !state.exitedEventReceived) { + state.exitedEventReceived = true; + if (!state.stoppedEventReceived) { + state.exitedBeforeStop = true; + } + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = undefined; + } + resolve('exited'); + } + } + }; + } + }); + }); + + return { + state, + lastEvent, + dispose(): void { + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = undefined; + } + trackerRegistration?.dispose(); + trackerRegistration = undefined; + } + }; +} + +async function waitForResultFileValue(filePath: string, timeoutMs: number): Promise { + const deadline = Date.now() + timeoutMs; + let lastContents = ''; + + while (Date.now() < deadline) { + try { + lastContents = await util.readFileText(filePath, 'utf8'); + const trimmedContents = lastContents.trim(); + if (trimmedContents.length > 0) { + const value = Number.parseInt(trimmedContents, 10); + if (!Number.isNaN(value)) { + return value; + } + } + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + + await new Promise(resolve => setTimeout(resolve, 100)); + } + + assert.fail(`Timed out waiting for numeric result in ${filePath}. Last contents: ${lastContents}`); +} + +suite('Run Without Debugging Test', function (): void { + const expectedResultValue = 37; + const workspaceFolder = vscode.workspace.workspaceFolders?.[0] ?? assert.fail('No workspace folder available'); + const workspacePath = workspaceFolder.uri.fsPath; + const sourceFile = path.join(workspacePath, 'debugTest.cpp'); + const sourceUri = vscode.Uri.file(sourceFile); + const resultFilePath = path.join(workspacePath, 'runWithoutDebuggingResult.txt'); + const executableName = isWindows ? 'debugTestProgram.exe' : 'debugTestProgram'; + const executablePath = path.join(workspacePath, executableName); + const sessionName = 'Run Without Debugging Result File'; + const debugType = isWindows ? 'cppvsdbg' : 'cppdbg'; + const miMode = isMacOS ? 'lldb' : 'gdb'; + + suiteSetup(async function (): Promise { + const extension: vscode.Extension = vscode.extensions.getExtension('ms-vscode.cpptools') || assert.fail('Extension not found'); + if (!extension.isActive) { + await extension.activate(); + } + await compileProgram(workspacePath, sourceFile, executablePath); + }); + + suiteTeardown(async function (): Promise { + if (isWindows) { + await vscode.commands.executeCommand('C_Cpp.ClearVsDeveloperEnvironment'); + } + }); + + setup(async function (): Promise { + await util.deleteFile(resultFilePath); + }); + + test('Run Without Debugging should not break on breakpoints and write the expected result file', async () => { + const breakpoint = await createBreakpointAtResultWriteStatement(sourceUri); + const tracker = createTracker(debugType, sessionName, 30000, 'Timed out waiting for debugger event.'); + const debugSessionTerminated = createSessionTerminatedPromise(sessionName); + + try { + const started = await vscode.debug.startDebugging( + workspaceFolder, + { + name: sessionName, + type: debugType, + request: 'launch', + program: executablePath, + args: [], + cwd: workspacePath, + externalConsole: debugType === 'cppdbg' ? false : undefined, + console: debugType === 'cppvsdbg' ? 'internalConsole' : undefined + }, + { noDebug: true }); + + assert.strictEqual(started, true, 'The noDebug launch did not start successfully.'); + + const lastEvent = await tracker.lastEvent; + await debugSessionTerminated; + const actualResultValue = await waitForResultFileValue(resultFilePath, 10000); + + assert.strictEqual(lastEvent, 'exited', 'No-debug launch should exit rather than stop on a breakpoint.'); + assert.strictEqual(tracker.state.setBreakpointsRequestReceived, false, 'a "no debug" session should not send setBreakpoints requests.'); + assert.strictEqual(tracker.state.stoppedEventReceived, false, 'a "no debug" session should not emit stopped events.'); + assert.strictEqual(actualResultValue, expectedResultValue, 'Unexpected result value from run without debugging launch.'); + } finally { + tracker.dispose(); + vscode.debug.removeBreakpoints([breakpoint]); + await util.deleteFile(resultFilePath); + } + }); + + test('Debug launch should bind and stop at the breakpoint', async () => { + const breakpoint = await createBreakpointAtResultWriteStatement(sourceUri); + const tracker = createTracker(debugType, sessionName, 30000, 'Timed out waiting for debugger event in normal debug mode.'); + const debugSessionTerminated = createSessionTerminatedPromise(sessionName); + + let launchedSession: vscode.DebugSession | undefined; + const startedSubscription = vscode.debug.onDidStartDebugSession((session) => { + if (session.name === sessionName) { + launchedSession = session; + } + }); + + try { + const started = await vscode.debug.startDebugging( + workspaceFolder, + { + name: sessionName, + type: debugType, + request: 'launch', + program: executablePath, + args: [], + cwd: workspacePath, + MIMode: debugType === 'cppdbg' ? miMode : undefined, + externalConsole: debugType === 'cppdbg' ? false : undefined, + console: debugType === 'cppvsdbg' ? 'internalConsole' : undefined + }, + { noDebug: false }); + + assert.strictEqual(started, true, 'The debug launch did not start successfully.'); + + const lastEvent = await tracker.lastEvent; + + assert.strictEqual(lastEvent, 'stopped', 'Debug launch should stop at the breakpoint before exit.'); + assert.strictEqual(tracker.state.setBreakpointsRequestReceived, true, 'Debug mode should send setBreakpoints requests.'); + assert.strictEqual(tracker.state.stoppedEventReceived, true, 'Debug mode should emit a stopped event at the breakpoint.'); + assert.strictEqual(tracker.state.exitedBeforeStop, false, 'Program exited before stopping at breakpoint in debug mode.'); + assert.strictEqual(vscode.debug.activeDebugSession?.name, sessionName, 'Debug session should still be active at breakpoint.'); + + const stoppedSession = launchedSession ?? vscode.debug.activeDebugSession; + assert.ok(stoppedSession, 'Unable to identify the running debug session for termination.'); + await vscode.debug.stopDebugging(stoppedSession); + await debugSessionTerminated; + } finally { + startedSubscription.dispose(); + tracker.dispose(); + vscode.debug.removeBreakpoints([breakpoint]); + await util.deleteFile(resultFilePath); + } + }); + +}); diff --git a/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.terminals.test.ts b/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.terminals.test.ts new file mode 100644 index 000000000..57b27d975 --- /dev/null +++ b/Extension/test/scenarios/RunWithoutDebugging/tests/runWithoutDebugging.terminals.test.ts @@ -0,0 +1,190 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +/* eslint-disable @typescript-eslint/triple-slash-reference */ +/// +import * as assert from 'assert'; +import { suite } from 'mocha'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as util from '../../../../src/common'; +import { isWindows } from '../../../../src/constants'; +import { compileProgram } from './compileProgram'; + +type ConsoleMode = 'integratedTerminal' | 'externalTerminal'; +type WindowsTerminalProfile = 'Command Prompt' | 'PowerShell'; + +/** + * Waits for the output of a program to be written to a file and returns the lines of output. + * @param filePath The path to the file containing the program output. + * @param timeoutMs The maximum time to wait for the output, in milliseconds. + * @returns A promise that resolves to an array of output lines. + * @throws An error if the output is not available within the specified timeout. + */ +async function waitForOutput(filePath: string, timeoutMs: number): Promise { + const deadline = Date.now() + timeoutMs; + let lastContents = ''; + + while (Date.now() < deadline) { + try { + lastContents = await util.readFileText(filePath, 'utf8'); + const trimmedContents = lastContents.trimEnd(); + if (trimmedContents.length > 0) { + return trimmedContents.split(/\r?\n/); + } + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + + await new Promise(resolve => setTimeout(resolve, 100)); + } + + assert.fail(`Timed out waiting for argument output in ${filePath}. Last contents: ${lastContents}`); +} + +/** + * Disposes of any terminals whose names match the given program paths. These are the terminals that were created to run the specified programs. + * @param programs An array of program paths whose corresponding terminals should be disposed. + */ +function disposeTerminals(programs: string[]): void { + const terminalNames = new Set(programs.map(program => path.normalize(program))); + for (const terminal of vscode.window.terminals) { + if (terminalNames.has(terminal.name)) { + terminal.dispose(); + } + } +} + +/** + * Sets or clears the setting for the default Windows terminal profile. + * @param profile The terminal profile to set as the default, or undefined to clear the setting. + */ +async function setWindowsDefaultTerminalProfile(profile?: WindowsTerminalProfile): Promise { + if (!isWindows) { + return; + } + + const config = vscode.workspace.getConfiguration('terminal.integrated'); + await config.update('defaultProfile.windows', profile, vscode.ConfigurationTarget.Workspace); +} + +async function runNoDebugLaunch(workspaceFolder: vscode.WorkspaceFolder, sessionName: string, program: string, consoleMode: ConsoleMode, resultFilePath: string, args: string[]): Promise { + const debugType = isWindows ? 'cppvsdbg' : 'cppdbg'; + const launchConfig: vscode.DebugConfiguration = { + name: sessionName, + type: debugType, + request: 'launch', + program: program, + args: [resultFilePath, ...args], + cwd: workspaceFolder.uri.fsPath + }; + + if (debugType === 'cppvsdbg') { + launchConfig.console = consoleMode; + } else { + launchConfig.externalConsole = consoleMode === 'externalTerminal'; + } + + const started = await vscode.debug.startDebugging(workspaceFolder, launchConfig, { noDebug: true }); + assert.strictEqual(started, true, `The ${consoleMode} noDebug launch did not start successfully.`); + return waitForOutput(resultFilePath, 15000); +} + +suite('Run Without Debugging Terminal and Arguments Test', function (this: Mocha.Suite): void { + this.timeout(120000); + + const workspaceFolder = vscode.workspace.workspaceFolders?.[0] ?? assert.fail('No workspace folder available'); + const workspacePath = workspaceFolder.uri.fsPath; + const sourceFile = path.join(workspacePath, 'argsTest.cpp'); + const resultFilePath = path.join(workspacePath, 'test_output.txt'); + const executablePath = path.join(workspacePath, isWindows ? 'argsTestProgram.exe' : 'argsTestProgram'); + const spacedExecutablePath = path.join(workspacePath, isWindows ? 'args Test Program.exe' : 'args Test Program'); + const executablePaths = [executablePath, spacedExecutablePath]; + const expectedArgs = [ + 'alpha', + 'two words', + path.join(workspacePath, 'input folder', 'three words.txt') + ]; + + suiteSetup(async function (): Promise { + const extension: vscode.Extension = vscode.extensions.getExtension('ms-vscode.cpptools') || assert.fail('Extension not found'); + if (!extension.isActive) { + await extension.activate(); + } + + await compileProgram(workspacePath, sourceFile, executablePath); + await compileProgram(workspacePath, sourceFile, spacedExecutablePath); + }); + + teardown(async function (): Promise { + await util.deleteFile(resultFilePath); + disposeTerminals(executablePaths); + }); + + setup(async function (): Promise { + if (await util.checkFileExists(resultFilePath)) { + await util.deleteFile(resultFilePath); + } + }); + + suiteTeardown(async function (): Promise { + await util.deleteFile(resultFilePath); + disposeTerminals(executablePaths); + + if (isWindows) { + await vscode.commands.executeCommand('C_Cpp.ClearVsDeveloperEnvironment'); + await util.deleteFile(path.join(workspacePath, '.vscode', 'settings.json')); + } + }); + + const consoleCases: { label: string; consoleMode: ConsoleMode; windowsProfiles?: (WindowsTerminalProfile | undefined)[] }[] = [ + { + label: 'integrated terminal', + consoleMode: 'integratedTerminal', + windowsProfiles: [undefined, 'Command Prompt', 'PowerShell'] + }, + { + label: 'external terminal', + consoleMode: 'externalTerminal' + } + ]; + + const programCases = [ + { + label: 'a program name without spaces', + programPath: executablePath + }, + { + label: 'a program name with spaces', + programPath: spacedExecutablePath + } + ]; + + for (const consoleCase of consoleCases) { + for (const programCase of programCases) { + const profiles: (WindowsTerminalProfile | undefined)[] = isWindows && consoleCase.consoleMode === 'integratedTerminal' ? consoleCase.windowsProfiles ?? [undefined] : [undefined]; + + for (const profile of profiles) { + const profileSuffix = profile ? ` with ${profile} as the default terminal` : consoleCase.consoleMode === 'integratedTerminal' ? ' with default terminal' : ''; + test(`No-debug launch via ${consoleCase.label} handles ${programCase.label}${profileSuffix}`, async () => { + await setWindowsDefaultTerminalProfile(profile); + + disposeTerminals(executablePaths); + const sessionName = `Run Without Debugging Args (${consoleCase.consoleMode}, ${path.basename(programCase.programPath)}${profile ? `, ${profile}` : ''})`; + const actualArgs = await runNoDebugLaunch( + workspaceFolder, + sessionName, + programCase.programPath, + consoleCase.consoleMode, + resultFilePath, + expectedArgs); + + assert.deepStrictEqual(actualArgs, expectedArgs, `Unexpected arguments received for ${consoleCase.label} using ${programCase.label}${profileSuffix}.`); + }); + } + } + } +}); From bc9a72cf2f0f3030abf6ac609faec6c91632a678 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 13 Apr 2026 13:07:02 -0700 Subject: [PATCH 27/31] Switch to GPT-4o. (#14364) --- .../src/LanguageServer/Providers/CopilotHoverProvider.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts index 6e4954370..3098e9a02 100644 --- a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts +++ b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts @@ -41,11 +41,12 @@ export class CopilotHoverProvider implements vscode.HoverProvider { const vscodelm = getVSCodeLanguageModel(); if (vscodelm) { try { - // First look for GPT-5-mini which should be available to all + // First look for GPT-4o which should be available to all // users and have a 0x multiplier on paid plans. - let [model] = await vscodelm.selectChatModels({ ...modelSelector, id: 'gpt-5-mini' }); + // GTP-4o is faster than GPT-5-mini (which seems too slow for hover, e.g. 10+ seconds). + let [model] = await vscodelm.selectChatModels({ ...modelSelector, id: 'gpt-4o' }); if (!model) { - // If GPT-5-mini is not available, fall back to the first available model + // If GPT-4o is not available, fall back to the first available model. [model] = await vscodelm.selectChatModels(modelSelector); } if (!model) { From 2db5c05c37ca56ecedc7d3f91089e985e231d610 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 13 Apr 2026 14:50:38 -0700 Subject: [PATCH 28/31] Fallback to GPT-5.4-mini and cache the model used. (#14369) * Fallback to GPT-5.4-mini and cache the model used. --- .../Providers/CopilotHoverProvider.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts index 3098e9a02..7469fd570 100644 --- a/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts +++ b/Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts @@ -27,6 +27,7 @@ export class CopilotHoverProvider implements vscode.HoverProvider { private cancelledPosition: vscode.Position | undefined; private content: string | undefined; private chatModel: vscode.LanguageModelChat | undefined; + private chatModelId: string | undefined; // Save the selected model ID to avoid trying the same unavailable model repeatedly. // Flag to avoid querying the LanguageModelChat repeatedly if no model is found private checkedChatModel: boolean = false; constructor(client: DefaultClient) { @@ -41,13 +42,29 @@ export class CopilotHoverProvider implements vscode.HoverProvider { const vscodelm = getVSCodeLanguageModel(); if (vscodelm) { try { - // First look for GPT-4o which should be available to all - // users and have a 0x multiplier on paid plans. - // GTP-4o is faster than GPT-5-mini (which seems too slow for hover, e.g. 10+ seconds). - let [model] = await vscodelm.selectChatModels({ ...modelSelector, id: 'gpt-4o' }); - if (!model) { - // If GPT-4o is not available, fall back to the first available model. - [model] = await vscodelm.selectChatModels(modelSelector); + let model: vscode.LanguageModelChat | undefined; + if (this.chatModelId === undefined) { + // First look for GPT-4o which should be available to all + // users and have a 0x multiplier on paid plans. + // GPT-4o is faster than the x0 GPT-5-mini (which seems too slow for hover, e.g. 10+ seconds). + this.chatModelId = 'gpt-4o'; + [model] = await vscodelm.selectChatModels({ ...modelSelector, id: this.chatModelId }); + if (!model) { + // If GPT-4o is not available, fallback to GPT-5.4-mini (x0.33 and fast). + this.chatModelId = 'gpt-5.4-mini'; + [model] = await vscodelm.selectChatModels({ ...modelSelector, id: this.chatModelId }); + } + if (!model) { + // If GPT-5.4-mini is not available, fallback to the first available model. + this.chatModelId = 'default'; + [model] = await vscodelm.selectChatModels(modelSelector); + } + } else { + if (this.chatModelId === 'default') { + [model] = await vscodelm.selectChatModels(modelSelector); + } else { + [model] = await vscodelm.selectChatModels({ ...modelSelector, id: this.chatModelId }); + } } if (!model) { telemetry.logLanguageServerEvent('CopilotHoverNoModelSelected', { remoteName: vscode.env.remoteName || 'local' }); From ddb54454e32ab8c3d7b8f8e4c0e2759dc38d7e4f Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Mon, 13 Apr 2026 15:48:40 -0700 Subject: [PATCH 29/31] Add a script to copy language server and debugger binaries to the Extension folder (#14370) --- Extension/.scripts/copyExtensionBinaries.ts | 113 ++++++++++++++++++++ Extension/package.json | 1 + 2 files changed, 114 insertions(+) create mode 100644 Extension/.scripts/copyExtensionBinaries.ts diff --git a/Extension/.scripts/copyExtensionBinaries.ts b/Extension/.scripts/copyExtensionBinaries.ts new file mode 100644 index 000000000..0ebf078be --- /dev/null +++ b/Extension/.scripts/copyExtensionBinaries.ts @@ -0,0 +1,113 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { cp, readdir, rm, stat } from 'node:fs/promises'; +import { homedir } from 'node:os'; +import { join } from 'node:path'; +import { $args, $root, green, heading, note } from './common'; + +const extensionPrefix = 'ms-vscode.cpptools-'; +const foldersToCopy = ['bin', 'debugAdapters', 'LLVM'] as const; + +type InstalledExtension = { + path: string; + version: number[]; + modified: number; +}; + +function compareVersions(left: number[], right: number[]): number { + const maxLength: number = Math.max(left.length, right.length); + for (let i = 0; i < maxLength; i++) { + const diff: number = (left[i] ?? 0) - (right[i] ?? 0); + if (diff !== 0) { + return diff; + } + } + return 0; +} + +function tryParseVersion(folderName: string): number[] | undefined { + if (!folderName.startsWith(extensionPrefix)) { + return undefined; + } + + const versionText: string | undefined = folderName.substring(extensionPrefix.length).match(/^\d+\.\d+\.\d+/)?.[0]; + return versionText?.split('.').map(each => Number(each)); +} + +async function getInstalledExtensions(root: string): Promise { + try { + const entries = await readdir(root, { withFileTypes: true }); + const candidates: Promise[] = entries.map(async (entry) => { + if (!entry.isDirectory()) { + return undefined; + } + + const version: number[] | undefined = tryParseVersion(entry.name); + if (!version) { + return undefined; + } + + const extensionPath: string = join(root, entry.name); + for (const folder of foldersToCopy) { + const info = await stat(join(extensionPath, folder)).catch(() => undefined); + if (!info?.isDirectory()) { + return undefined; + } + } + + const info = await stat(extensionPath); + return { + path: extensionPath, + version, + modified: info.mtimeMs + }; + }); + + const found = await Promise.all(candidates); + return found.filter((entry): entry is InstalledExtension => entry !== undefined); + } catch { + return []; + } +} + +async function findLatestInstalledExtension(providedPath?: string): Promise { + if (providedPath) { + return providedPath; + } + + const searchRoots: string[] = [ + join(homedir(), '.vscode', 'extensions'), + join(homedir(), '.vscode-insiders', 'extensions'), + join(homedir(), '.vscode-server', 'extensions'), + join(homedir(), '.vscode-server-insiders', 'extensions') + ]; + + const installed: InstalledExtension[] = (await Promise.all(searchRoots.map(each => getInstalledExtensions(each)))).flat(); + if (!installed.length) { + throw new Error(`Unable to find an installed C/C++ extension under ${searchRoots.join(' or ')}.`); + } + + installed.sort((left, right) => compareVersions(right.version, left.version) || right.modified - left.modified); + return installed[0].path; +} + +export async function main(sourcePath = $args[0]) { + console.log(heading('Copy installed extension binaries')); + + const installedExtensionPath: string = await findLatestInstalledExtension(sourcePath); + note(`Using installed extension at ${installedExtensionPath}`); + + for (const folder of foldersToCopy) { + const source: string = join(installedExtensionPath, folder); + const destination: string = join($root, folder); + + console.log(`Copying ${green(folder)} from ${source}`); + await rm(destination, { recursive: true, force: true }); + await cp(source, destination, { recursive: true, force: true }); + } + + note(`Copied installed binaries into ${$root}`); +} diff --git a/Extension/package.json b/Extension/package.json index e07af7649..338d299ff 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -6805,6 +6805,7 @@ "generate-native-strings": "ts-node -T ./.scripts/generateNativeStrings.ts", "generate-options-schema": "ts-node -T ./.scripts/generateOptionsSchema.ts", "copy-walkthrough-media": "ts-node -T ./.scripts/copyWalkthruMedia.ts", + "copy-extension-binaries": "ts-node -T ./.scripts/copyExtensionBinaries.ts", "translations-export": "yarn install && yarn prep && yarn generate-native-strings && gulp translations-export", "translations-generate": "gulp translations-generate", "translations-import": "gulp translations-import", From 775fd692eaad807087476e61b6f693ad222edfb4 Mon Sep 17 00:00:00 2001 From: Carson Radtke Date: Mon, 13 Apr 2026 22:00:46 -0600 Subject: [PATCH 30/31] Add internal silent find-all-references command (#14281) * Add internal silent find-all-references command Introduce an internal C_Cpp.FindAllReferences command that issues the existing cpptools/findAllReferences request without joining the workspaceReferences single-flight cancellation path. Extract the shared request and confirmed-location mapping logic from FindAllReferencesProvider so the existing vscode.executeReferenceProvider flow and the new silent command use the same request translation and cancellation handling for server-side cancel responses. Keep the interactive provider behavior unchanged: user-invoked references still cancel prior work, reset reference progress state, and update the ReferencesManager UI. The new command resolves the owning client from the target URI and returns locations without progress UI, preview notifications, or references panel updates, enabling concurrent silent callers such as Copilot. * Add internal quiet definition and call hierarchy commands Align cpptools with the companion changes that now prefer internal C_Cpp.* navigation commands over the generic vscode.* provider commands when running extension-driven symbol queries. Add C_Cpp.GoToDefinition, C_Cpp.PrepareCallHierarchy, C_Cpp.CallHierarchyCallsTo, and C_Cpp.CallHierarchyCallsFrom as internal commands that resolve the owning DefaultClient from the target URI and send requests directly to cpptools without joining the workspaceReferences UI and single-flight cancellation path. Extract shared call hierarchy request and conversion logic from CallHierarchyProvider so the existing interactive provider flow and the new silent commands share the same request translation and server-cancellation handling. Add a dedicated go-to-definition helper that sends the standard definition request through the language client and normalizes both Location and DefinitionLink responses to Location[] so companion callers can consume a stable result shape. Keep interactive behavior unchanged: user-invoked providers continue to use the existing VS Code registrations, progress handling, and workspaceReferences-driven cancellation semantics, while extension callers such as the devtools companion can use the new internal command surface without canceling overlapping work. --- .../Providers/callHierarchyProvider.ts | 256 ++++++++++-------- .../Providers/findAllReferencesProvider.ts | 80 ++++-- .../Providers/goToDefinitionProvider.ts | 57 ++++ Extension/src/LanguageServer/extension.ts | 79 ++++++ 4 files changed, 330 insertions(+), 142 deletions(-) create mode 100644 Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts diff --git a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts index 6c6d39f07..61b7b110b 100644 --- a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts +++ b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts @@ -88,6 +88,129 @@ const CallHierarchyCallsToRequest: RequestType = new RequestType('cpptools/callHierarchyCallsFrom'); +function makeVscodeCallHierarchyItem(client: DefaultClient, item: CallHierarchyItem): vscode.CallHierarchyItem { + const containerDetail: string = (item.detail !== "") ? `${item.detail} - ` : ""; + const itemUri: vscode.Uri = vscode.Uri.file(item.file); + + // Get file detail + const isInWorkspace: boolean = client.RootUri !== undefined && + itemUri.fsPath.startsWith(client.RootUri.fsPath); + const dirPath: string = isInWorkspace ? + path.relative(client.RootPath, path.dirname(item.file)) : path.dirname(item.file); + const fileDetail: string = dirPath.length === 0 ? + `${path.basename(item.file)}` : `${path.basename(item.file)} (${dirPath})`; + + return new vscode.CallHierarchyItem( + item.kind, + item.name, + containerDetail + fileDetail, + itemUri, + makeVscodeRange(item.range), + makeVscodeRange(item.selectionRange)); +} + +function createIncomingCalls(client: DefaultClient, calls: CallHierarchyCallsItem[]): vscode.CallHierarchyIncomingCall[] { + const result: vscode.CallHierarchyIncomingCall[] = []; + + for (const call of calls) { + const item: vscode.CallHierarchyItem = makeVscodeCallHierarchyItem(client, call.item); + const ranges: vscode.Range[] = []; + call.fromRanges.forEach(r => { + ranges.push(makeVscodeRange(r)); + }); + + const incomingCall: vscode.CallHierarchyIncomingCall = + new vscode.CallHierarchyIncomingCall(item, ranges); + result.push(incomingCall); + } + + return result; +} + +function createOutgoingCalls(client: DefaultClient, calls: CallHierarchyCallsItem[]): vscode.CallHierarchyOutgoingCall[] { + const result: vscode.CallHierarchyOutgoingCall[] = []; + + for (const call of calls) { + const item: vscode.CallHierarchyItem = makeVscodeCallHierarchyItem(client, call.item); + const ranges: vscode.Range[] = []; + call.fromRanges.forEach(r => { + ranges.push(makeVscodeRange(r)); + }); + + const outgoingCall: vscode.CallHierarchyOutgoingCall = + new vscode.CallHierarchyOutgoingCall(item, ranges); + result.push(outgoingCall); + } + + return result; +} + +export async function sendPrepareCallHierarchyRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: uri.toString() }, + position: Position.create(position.line, position.character) + }; + let response: CallHierarchyItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyItemRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.item === undefined ? [] : [makeVscodeCallHierarchyItem(client, response.item)]; +} + +export async function sendCallHierarchyCallsToRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: item.uri.toString() }, + position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) + }; + let response: CallHierarchyCallsItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.calls.length !== 0 ? createIncomingCalls(client, response.calls) : []; +} + +export async function sendCallHierarchyCallsFromRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: item.uri.toString() }, + position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) + }; + let response: CallHierarchyCallsItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.calls.length !== 0 ? createOutgoingCalls(client, response.calls) : []; +} + export class CallHierarchyProvider implements vscode.CallHierarchyProvider { // Indicates whether a request is from an entry root node (e.g. top function in the call tree). private isEntryRootNodeTelemetry: boolean = false; @@ -118,20 +241,10 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { cancelSource.cancel(); }); - const params: CallHierarchyParams = { - textDocument: { uri: document.uri.toString() }, - position: Position.create(position.line, position.character) - }; - let response: CallHierarchyItemResult; + let result: vscode.CallHierarchyItem[] | undefined; try { - response = await this.client.languageClient.sendRequest(CallHierarchyItemRequest, params, cancelSource.token); - } catch (e: any) { - if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { - return undefined; - } - throw e; - } - finally { + result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token); + } finally { cancellationTokenListener.dispose(); requestCanceledListener.dispose(); } @@ -139,12 +252,12 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { if (cancelSource.token.isCancellationRequested) { throw new vscode.CancellationError(); } - if (response.item === undefined) { + if (!result || result.length === 0) { return undefined; } this.isEntryRootNodeTelemetry = true; - return this.makeVscodeCallHierarchyItem(response.item); + return result[0]; } public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { @@ -172,39 +285,28 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { // Send the request to the language server. let result: vscode.CallHierarchyIncomingCall[] | undefined; - const params: CallHierarchyParams = { - textDocument: { uri: item.uri.toString() }, - position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) - }; - let response: CallHierarchyCallsItemResult | undefined; - let cancelled: boolean = false; + let progressBarDuration: number | undefined; try { - response = await this.client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, cancelSource.token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } + result = await sendCallHierarchyCallsToRequest(this.client, item, cancelSource.token); + } finally { + // Reset anything that can be cleared before processing the result. + progressBarDuration = workspaceReferences.getCallHierarchyProgressBarDuration(); + workspaceReferences.resetProgressBar(); + workspaceReferences.resetReferences(); + cancellationTokenListener.dispose(); + requestCanceledListener.dispose(); } - // Reset anything that can be cleared before processing the result. - const progressBarDuration: number | undefined = workspaceReferences.getCallHierarchyProgressBarDuration(); - workspaceReferences.resetProgressBar(); - workspaceReferences.resetReferences(); - cancellationTokenListener.dispose(); - requestCanceledListener.dispose(); // Process the result. - if (cancelSource.token.isCancellationRequested || cancelled || requestCanceled !== undefined) { + if (cancelSource.token.isCancellationRequested || result === undefined || requestCanceled !== undefined) { const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ? CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled; this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration); throw new vscode.CancellationError(); - } else if (response && response.calls.length !== 0) { - result = this.createIncomingCalls(response.calls); } this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration); - return result; + return result.length !== 0 ? result : undefined; } public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { @@ -216,87 +318,15 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { await this.client.ready; - let result: vscode.CallHierarchyOutgoingCall[] | undefined; - const params: CallHierarchyParams = { - textDocument: { uri: item.uri.toString() }, - position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) - }; - let response: CallHierarchyCallsItemResult | undefined; - let cancelled: boolean = false; - try { - response = await this.client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } - } - if (token.isCancellationRequested || cancelled) { + const result: vscode.CallHierarchyOutgoingCall[] | undefined = + await sendCallHierarchyCallsFromRequest(this.client, item, token); + if (token.isCancellationRequested || result === undefined) { this.logTelemetry(CallHierarchyCallsFromEvent, CallHierarchyRequestStatus.Canceled); throw new vscode.CancellationError(); - } else if (response && response.calls.length !== 0) { - result = this.createOutgoingCalls(response.calls); } this.logTelemetry(CallHierarchyCallsFromEvent, CallHierarchyRequestStatus.Succeeded); - return result; - } - - private makeVscodeCallHierarchyItem(item: CallHierarchyItem): vscode.CallHierarchyItem { - const containerDetail: string = (item.detail !== "") ? `${item.detail} - ` : ""; - const itemUri: vscode.Uri = vscode.Uri.file(item.file); - - // Get file detail - const isInWorkspace: boolean = this.client.RootUri !== undefined && - itemUri.fsPath.startsWith(this.client.RootUri?.fsPath); - const dirPath: string = isInWorkspace ? - path.relative(this.client.RootPath, path.dirname(item.file)) : path.dirname(item.file); - const fileDetail: string = dirPath.length === 0 ? - `${path.basename(item.file)}` : `${path.basename(item.file)} (${dirPath})`; - - return new vscode.CallHierarchyItem( - item.kind, - item.name, - containerDetail + fileDetail, - itemUri, - makeVscodeRange(item.range), - makeVscodeRange(item.selectionRange)); - } - - private createIncomingCalls(calls: CallHierarchyCallsItem[]): vscode.CallHierarchyIncomingCall[] { - const result: vscode.CallHierarchyIncomingCall[] = []; - - for (const call of calls) { - const item: vscode.CallHierarchyItem = this.makeVscodeCallHierarchyItem(call.item); - const ranges: vscode.Range[] = []; - call.fromRanges.forEach(r => { - ranges.push(makeVscodeRange(r)); - }); - - const incomingCall: vscode.CallHierarchyIncomingCall = - new vscode.CallHierarchyIncomingCall(item, ranges); - result.push(incomingCall); - } - - return result; - } - - private createOutgoingCalls(calls: CallHierarchyCallsItem[]): vscode.CallHierarchyOutgoingCall[] { - const result: vscode.CallHierarchyOutgoingCall[] = []; - - for (const call of calls) { - const item: vscode.CallHierarchyItem = this.makeVscodeCallHierarchyItem(call.item); - const ranges: vscode.Range[] = []; - call.fromRanges.forEach(r => { - ranges.push(makeVscodeRange(r)); - }); - - const outgoingCall: vscode.CallHierarchyOutgoingCall = - new vscode.CallHierarchyOutgoingCall(item, ranges); - result.push(outgoingCall); - } - - return result; + return result.length !== 0 ? result : undefined; } private logTelemetry(eventName: string, requestStatus: CallHierarchyRequestStatus, progressBarDuration?: number): void { diff --git a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts index ffdb1cf9e..664cf80c6 100644 --- a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts +++ b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts @@ -11,6 +11,50 @@ import { CancellationSender, ReferenceInfo, ReferenceType, ReferencesParams, Ref const FindAllReferencesRequest: RequestType = new RequestType('cpptools/findAllReferences'); +export interface FindAllReferencesResult { + referencesResult: ReferencesResult; + locations: vscode.Location[]; +} + +function convertConfirmedReferencesToLocations(referencesResult: ReferencesResult): vscode.Location[] { + const locationsResult: vscode.Location[] = []; + referencesResult.referenceInfos.forEach((referenceInfo: ReferenceInfo) => { + if (referenceInfo.type === ReferenceType.Confirmed) { + const uri: vscode.Uri = vscode.Uri.file(referenceInfo.file); + const range: vscode.Range = new vscode.Range(referenceInfo.position.line, referenceInfo.position.character, + referenceInfo.position.line, referenceInfo.position.character + referencesResult.text.length); + locationsResult.push(new vscode.Location(uri, range)); + } + }); + return locationsResult; +} + +export async function sendFindAllReferencesRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: ReferencesParams = { + newName: "", + position: Position.create(position.line, position.character), + textDocument: { uri: uri.toString() } + }; + let response: ReferencesResult; + try { + response = await client.languageClient.sendRequest(FindAllReferencesRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested || response.isCanceled) { + return undefined; + } + + return { + referencesResult: response, + locations: convertConfirmedReferencesToLocations(response) + }; +} + export class FindAllReferencesProvider implements vscode.ReferenceProvider { private client: DefaultClient; @@ -29,23 +73,10 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); // Send the request to the language server. - const locationsResult: vscode.Location[] = []; - const params: ReferencesParams = { - newName: "", - position: Position.create(position.line, position.character), - textDocument: { uri: document.uri.toString() } - }; - let response: ReferencesResult | undefined; - let cancelled: boolean = false; + let result: FindAllReferencesResult | undefined; try { - response = await this.client.languageClient.sendRequest(FindAllReferencesRequest, params, cancelSource.token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } - } - finally { + result = await sendFindAllReferencesRequest(this.client, document.uri, position, cancelSource.token); + } finally { // Reset anything that can be cleared before processing the result. workspaceReferences.resetProgressBar(); cancellationTokenListener.dispose(); @@ -53,30 +84,21 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { } // Process the result. - if (cancelSource.token.isCancellationRequested || cancelled || (response && response.isCanceled)) { + if (cancelSource.token.isCancellationRequested || !result) { // Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code: // "Cannot destructure property 'range' of 'e.location' as it is undefined." // TODO: per issue https://github.com/microsoft/vscode/issues/169698 // vscode.CancellationError is expected, so when VS Code fixes the error use vscode.CancellationError again. workspaceReferences.resetReferences(); return undefined; - } else if (response && response.referenceInfos.length > 0) { - response.referenceInfos.forEach((referenceInfo: ReferenceInfo) => { - if (referenceInfo.type === ReferenceType.Confirmed) { - const uri: vscode.Uri = vscode.Uri.file(referenceInfo.file); - const range: vscode.Range = new vscode.Range(referenceInfo.position.line, referenceInfo.position.character, - referenceInfo.position.line, referenceInfo.position.character + response.text.length); - locationsResult.push(new vscode.Location(uri, range)); - } - }); - + } else if (result.referencesResult.referenceInfos.length > 0) { // Display other reference types in panel or channel view. // Note: ReferencesManager.resetReferences is called in ReferencesManager.showResultsInPanelView - workspaceReferences.showResultsInPanelView(response); + workspaceReferences.showResultsInPanelView(result.referencesResult); } else { workspaceReferences.resetReferences(); } - return locationsResult; + return result.locations; } } diff --git a/Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts b/Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts new file mode 100644 index 000000000..80fb17c34 --- /dev/null +++ b/Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts @@ -0,0 +1,57 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import * as vscode from 'vscode'; +import { Definition, DefinitionLink, DefinitionRequest, Position, ResponseError, TextDocumentPositionParams } from 'vscode-languageclient'; +import { DefaultClient } from '../client'; +import { RequestCancelled, ServerCancelled } from '../protocolFilter'; + +function convertDefinitionsToLocations(definitionsResult: vscode.Definition | vscode.DefinitionLink[] | undefined): vscode.Location[] { + if (!definitionsResult) { + return []; + } + + if (!Array.isArray(definitionsResult)) { + return [definitionsResult]; + } + + const result: vscode.Location[] = []; + for (const definition of definitionsResult) { + if (definition instanceof vscode.Location) { + result.push(definition); + } else { + result.push(new vscode.Location(definition.targetUri, definition.targetSelectionRange ?? definition.targetRange)); + } + } + + return result; +} + +export async function sendGoToDefinitionRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: TextDocumentPositionParams = { + position: Position.create(position.line, position.character), + textDocument: { uri: uri.toString() } + }; + let response: Definition | DefinitionLink[] | null; + try { + response = await client.languageClient.sendRequest(DefinitionRequest.type, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + const result: vscode.Definition | vscode.DefinitionLink[] | undefined = + await client.languageClient.protocol2CodeConverter.asDefinitionResult(response, token); + if (token.isCancellationRequested) { + return undefined; + } + + return convertDefinitionsToLocations(result); +} diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index f4d3417b2..fd8267b42 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -23,6 +23,9 @@ import { getCrashCallStacksChannel } from '../logger'; import { PlatformInformation } from '../platform'; import * as telemetry from '../telemetry'; import { CopilotHoverProvider } from './Providers/CopilotHoverProvider'; +import { sendCallHierarchyCallsFromRequest, sendCallHierarchyCallsToRequest, sendPrepareCallHierarchyRequest } from './Providers/callHierarchyProvider'; +import { sendFindAllReferencesRequest } from './Providers/findAllReferencesProvider'; +import { sendGoToDefinitionRequest } from './Providers/goToDefinitionProvider'; import { Client, DefaultClient, DoxygenCodeActionCommandArguments, openFileVersions } from './client'; import { ClientCollection } from './clientCollection'; import { CodeActionDiagnosticInfo, CodeAnalysisDiagnosticIdentifiersAndUri, codeAnalysisAllFixes, codeAnalysisCodeToFixes, codeAnalysisFileToCodeActions } from './codeAnalysis'; @@ -397,6 +400,11 @@ export async function registerCommands(enabled: boolean): Promise { commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowActiveCodeAnalysisCommands', enabled ? onShowActiveCodeAnalysisCommands : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowIdleCodeAnalysisCommands', enabled ? onShowIdleCodeAnalysisCommands : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowReferencesProgress', enabled ? onShowReferencesProgress : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.FindAllReferences', enabled ? onFindAllReferences : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.GoToDefinition', enabled ? onGoToDefinition : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.PrepareCallHierarchy', enabled ? onPrepareCallHierarchy : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyCallsTo', enabled ? onCallHierarchyCallsTo : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyCallsFrom', enabled ? onCallHierarchyCallsFrom : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.TakeSurvey', enabled ? onTakeSurvey : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.LogDiagnostics', enabled ? onLogDiagnostics : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.RescanWorkspace', enabled ? onRescanWorkspace : onDisabledCommand)); @@ -808,6 +816,77 @@ function onShowReferencesProgress(): void { void clients.ActiveClient.handleReferencesIcon().catch(logAndReturn.undefined); } +async function onFindAllReferences(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.FindAllReferences requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + const result = await sendFindAllReferencesRequest(client, uri, position, token ?? CancellationToken.None); + return result?.locations; +} + +async function onGoToDefinition(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.GoToDefinition requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendGoToDefinitionRequest(client, uri, position, token ?? CancellationToken.None); +} + +async function onPrepareCallHierarchy(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.PrepareCallHierarchy requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendPrepareCallHierarchyRequest(client, uri, position, token ?? CancellationToken.None); +} + +async function onCallHierarchyCallsTo(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyCallsTo requires an item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendCallHierarchyCallsToRequest(client, item, token ?? CancellationToken.None); +} + +async function onCallHierarchyCallsFrom(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyCallsFrom requires an item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendCallHierarchyCallsFromRequest(client, item, token ?? CancellationToken.None); +} + function onToggleRefGroupView(): void { // Set context to switch icons const client: Client = getActiveClient(); From a6b12a8c10eacefc06851238b82f600514912ed5 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 14 Apr 2026 10:54:17 -0700 Subject: [PATCH 31/31] Update changelog for 1.32.0. (#14373) * Update changelog for 1.32.0. --- Extension/CHANGELOG.md | 18 ++++++++++++++++++ Extension/package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 09ef7ed93..fe483aa03 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,23 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.32.0: April 14, 2026 +### New Feature +* Add support for run without debugging. [#1201](https://github.com/microsoft/vscode-cpptools/issues/1201) + +### Enhancements +* Add a `C_Cpp.doxygen.generateOnCodeAction` setting to allow disabling of Doxygen generation code actions. [#14341](https://github.com/microsoft/vscode-cpptools/issues/14341) +* Improve wildcard matching with the debugger natvis. [MIEngine#1162](https://github.com/microsoft/MIEngine/issues/1162) +* Add support for `HideRawView` with the debugger natvis. [MIEngine#1458](https://github.com/microsoft/MIEngine/issues/1458) + +### Bug Fixes +* Fix high CPU usage caused by repeated calls to `selectChatModels`. [#14168](https://github.com/microsoft/vscode-cpptools/issues/14168), [#14211](https://github.com/microsoft/vscode-cpptools/issues/14211), [#14241](https://github.com/microsoft/vscode-cpptools/issues/14241) +* Fix the MSVC developer environment not working if `UCRTVersion` isn't found, and update the walkthrough instructions for installing MSVC. [#14352](https://github.com/microsoft/vscode-cpptools/issues/14352) +* Fix Copilot hover taking too many premium requests. [#14372](https://github.com/microsoft/vscode-cpptools/issues/14372) +* Fix null pointers being expandable for variables in the debugger. [MIEngine#698](https://github.com/microsoft/MIEngine/issues/698) +* Fix recursive `{this}` evaluation with the debugger natvis. [MIEngine#1391](https://github.com/microsoft/MIEngine/issues/1391) +* Update clang-tidy and clang-format from 22.1.1 to 22.1.3 (bug fixes). +* Fix a bug with semantic colorization of operators. + ## Version 1.31.4: March 31, 2026 ### Bug Fix * Debugging cpptools and cpptools-srv processes on macOS (to get call stacks) is now blocked when SIP is enabled (due to a potential security issue). diff --git a/Extension/package.json b/Extension/package.json index 338d299ff..8f2cca3f5 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "1.31.4-main", + "version": "1.32.0-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md",