From e8c18686026d194d4f9aa0ace28a56925d1631d9 Mon Sep 17 00:00:00 2001 From: Glen Chung <105310954+kuchungmsft@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:36:51 -0700 Subject: [PATCH 1/8] Add mscppls repeated-crash diagnostic (#14798) Define the localized native string used when mscppls --check can no longer restart the C++ analysis process after repeated crashes. --- Extension/src/nativeStrings.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Extension/src/nativeStrings.json b/Extension/src/nativeStrings.json index ffe091980..8b1a93276 100644 --- a/Extension/src/nativeStrings.json +++ b/Extension/src/nativeStrings.json @@ -785,5 +785,9 @@ "selected_translation_unit_not_include_file_header_only": { "text": "The selected translation unit '{0}' does not include '{1}'. No previous translation unit is available, so IntelliSense will use '{1}' as a header-only translation unit.", "hint": "{0} is the selected translation unit path. {1} is the active file path. {Locked=\"IntelliSense\"} {Locked=\"{0}\"} {Locked=\"{1}\"}" + }, + "check_analysis_process_unavailable": { + "text": "The C++ analysis process was unavailable while checking \"{0}\".", + "hint": "{0} is the source file path. {Locked=\"C++\"} {Locked=\"{0}\"}" } } From ccef2ab11765ecde6d5aeb1c6651ad46a4e19bde Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 25 Sep 2026 17:33:58 -0700 Subject: [PATCH 2/8] Update localized IntelliSense diagnostics (#14799) --- Extension/bin/messages/cs/messages.json | 3 ++- Extension/bin/messages/de/messages.json | 3 ++- Extension/bin/messages/es/messages.json | 3 ++- Extension/bin/messages/fr/messages.json | 3 ++- Extension/bin/messages/it/messages.json | 3 ++- Extension/bin/messages/ja/messages.json | 3 ++- Extension/bin/messages/ko/messages.json | 3 ++- Extension/bin/messages/pl/messages.json | 3 ++- Extension/bin/messages/pt-br/messages.json | 3 ++- Extension/bin/messages/ru/messages.json | 3 ++- Extension/bin/messages/tr/messages.json | 3 ++- Extension/bin/messages/zh-cn/messages.json | 3 ++- Extension/bin/messages/zh-tw/messages.json | 5 +++-- 13 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Extension/bin/messages/cs/messages.json b/Extension/bin/messages/cs/messages.json index f2ec27691..b7334c92e 100644 --- a/Extension/bin/messages/cs/messages.json +++ b/Extension/bin/messages/cs/messages.json @@ -3782,5 +3782,6 @@ "Typ není podobný jazyku C kvůli této deklaraci členu", "Typ není podobný jazyku C kvůli tomuto lambda výrazu", "Kvalifikátor názvu výčtového typu je funkce jazyka C++11", - "Neomezené sjednocení je funkce jazyka C++11" + "Neomezené sjednocení je funkce jazyka C++11", + "%sq už je v aktuálním rozsahu deklarovaný" ] diff --git a/Extension/bin/messages/de/messages.json b/Extension/bin/messages/de/messages.json index 26c553580..588885286 100644 --- a/Extension/bin/messages/de/messages.json +++ b/Extension/bin/messages/de/messages.json @@ -3782,5 +3782,6 @@ "Der Typ ist aufgrund dieser Memberdeklaration nicht C-ähnlich.", "Der Typ ist aufgrund dieses Lambdaausdrucks nicht C-ähnlich.", "Ein Namensqualifizierer für einen Enumerationstyp ist ein C++11-Feature.", - "Unbeschränkte Unions sind ein C++11-Feature." + "Unbeschränkte Unions sind ein C++11-Feature.", + "%sq wurde bereits im aktuellen Bereich deklariert." ] diff --git a/Extension/bin/messages/es/messages.json b/Extension/bin/messages/es/messages.json index 1780f12c7..25532bdca 100644 --- a/Extension/bin/messages/es/messages.json +++ b/Extension/bin/messages/es/messages.json @@ -3782,5 +3782,6 @@ "el tipo no es similar a C debido a esta declaración de miembro", "el tipo no es similar a C debido a esta expresión lambda", "un calificador de nombre de tipo de enumeración es una característica de C++11", - "las uniones sin restricciones son una característica de C++11" + "las uniones sin restricciones son una característica de C++11", + "%sq ya se ha declarado en el ámbito actual" ] diff --git a/Extension/bin/messages/fr/messages.json b/Extension/bin/messages/fr/messages.json index 126273175..15217cf88 100644 --- a/Extension/bin/messages/fr/messages.json +++ b/Extension/bin/messages/fr/messages.json @@ -3782,5 +3782,6 @@ "le type n’est pas similaire à C en raison de cette déclaration de membre", "le type n’est pas similaire à C en raison de cette expression lambda", "un qualificateur de nom de type énumération est une fonctionnalité C++11", - "les unions sans restriction sont une fonctionnalité C++11" + "les unions sans restriction sont une fonctionnalité C++11", + "%sq a déjà été déclaré dans la portée actuelle" ] diff --git a/Extension/bin/messages/it/messages.json b/Extension/bin/messages/it/messages.json index 82f75976e..84047e643 100644 --- a/Extension/bin/messages/it/messages.json +++ b/Extension/bin/messages/it/messages.json @@ -3782,5 +3782,6 @@ "il tipo non è simile a C a causa di questa dichiarazione di membro", "il tipo non è simile a C a causa di questa espressione lambda", "un qualificatore del nome di tipo enumerazione è una funzionalità di C++11", - "le unioni senza restrizioni sono una funzionalità di C++11" + "le unioni senza restrizioni sono una funzionalità di C++11", + "%sq già dichiarato nell'ambito corrente" ] diff --git a/Extension/bin/messages/ja/messages.json b/Extension/bin/messages/ja/messages.json index fa9d10284..352ed9c04 100644 --- a/Extension/bin/messages/ja/messages.json +++ b/Extension/bin/messages/ja/messages.json @@ -3782,5 +3782,6 @@ "このメンバー宣言のため、型は C-like ではありません", "このラムダ式のため、型は C-like ではありません", "列挙型名修飾子は C++11 の機能です", - "無制限の共用体は C++11 機能です" + "無制限の共用体は C++11 機能です", + "%sq は現在のスコープで既に宣言されています" ] diff --git a/Extension/bin/messages/ko/messages.json b/Extension/bin/messages/ko/messages.json index 828b00b6b..7263448f6 100644 --- a/Extension/bin/messages/ko/messages.json +++ b/Extension/bin/messages/ko/messages.json @@ -3782,5 +3782,6 @@ "이 멤버 선언 때문에 형식이 C와 유사하지 않음", "이 람다 식 때문에 형식이 C와 유사하지 않음", "열거형 형식 이름 한정자는 C++11 기능임", - "제한 없는 공용 구조체는 C++11 기능임" + "제한 없는 공용 구조체는 C++11 기능임", + "현재 범위에 이미 %sq이(가) 선언되어 있습니다." ] diff --git a/Extension/bin/messages/pl/messages.json b/Extension/bin/messages/pl/messages.json index bf5161221..1c5db291b 100644 --- a/Extension/bin/messages/pl/messages.json +++ b/Extension/bin/messages/pl/messages.json @@ -3782,5 +3782,6 @@ "typ nie jest C-podobny z powodu tej deklaracji składowej", "typ nie jest C-podobny z powodu tego wyrażenia lambda", "kwalifikator nazwy typu wyliczeniowego jest funkcją języka C++11", - "nieograniczone unie są funkcją języka C++11" + "nieograniczone unie są funkcją języka C++11", + "element %sq został już zadeklarowany w bieżącym zakresie" ] diff --git a/Extension/bin/messages/pt-br/messages.json b/Extension/bin/messages/pt-br/messages.json index 0b732be7f..f607f807f 100644 --- a/Extension/bin/messages/pt-br/messages.json +++ b/Extension/bin/messages/pt-br/messages.json @@ -3782,5 +3782,6 @@ "o tipo não é semelhante a C devido a esta declaração de membro", "o tipo não é semelhante a C devido a esta expressão lambda", "um qualificador de nome de tipo de enumeração é um recurso do C++11", - "uniões irrestritas são um recurso do C++11" + "uniões irrestritas são um recurso do C++11", + "%sq já foi declarado no escopo atual" ] diff --git a/Extension/bin/messages/ru/messages.json b/Extension/bin/messages/ru/messages.json index 7e3072f00..a3db081fe 100644 --- a/Extension/bin/messages/ru/messages.json +++ b/Extension/bin/messages/ru/messages.json @@ -3782,5 +3782,6 @@ "тип не является C-подобным из-за этого объявления элемента", "тип не является C-подобным из-за этого лямбда-выражения", "квалификатор имени типа перечисления — это функция C++11", - "неограниченные объединения — это функция C++11" + "неограниченные объединения — это функция C++11", + "%sq уже объявлено в текущей области" ] diff --git a/Extension/bin/messages/tr/messages.json b/Extension/bin/messages/tr/messages.json index d04768feb..1b3c8999e 100644 --- a/Extension/bin/messages/tr/messages.json +++ b/Extension/bin/messages/tr/messages.json @@ -3782,5 +3782,6 @@ "bu üye bildirimi nedeniyle tür C benzeri değil", "bu lambda ifadesi nedeniyle tür C benzeri değil", "sabit listesi türü ad niteleyicisi bir C++11 özelliğidir", - "kısıtlanmamış birleşimler bir C++11 özelliğidir" + "kısıtlanmamış birleşimler bir C++11 özelliğidir", + "geçerli kapsamda, %sq önceden de tanımlanmış" ] diff --git a/Extension/bin/messages/zh-cn/messages.json b/Extension/bin/messages/zh-cn/messages.json index 2818c6b89..bccbe9be2 100644 --- a/Extension/bin/messages/zh-cn/messages.json +++ b/Extension/bin/messages/zh-cn/messages.json @@ -3782,5 +3782,6 @@ "由于此成员声明,类型不是 C 风格", "由于此 lambda 表达式,该类型不是 C 风格", "枚举类型名称限定符是 C++11 功能", - "无限制联合是 C++11 功能" + "无限制联合是 C++11 功能", + "已在当前范围内声明 %sq" ] diff --git a/Extension/bin/messages/zh-tw/messages.json b/Extension/bin/messages/zh-tw/messages.json index 1af9e0f9f..cc2a900d1 100644 --- a/Extension/bin/messages/zh-tw/messages.json +++ b/Extension/bin/messages/zh-tw/messages.json @@ -1697,7 +1697,7 @@ "向量運算不適用於非整數類資料類型的向量", "無法開啟 %s 檔案 %sq2", "無法開啟 %s 檔案 %sq2: %s3", - "先行編譯標頭檔", + "預編譯標頭", "預先定義巨集", "已產生 C", "已產生 C++", @@ -3782,5 +3782,6 @@ "因為這個成員宣告,類型不是 C 樣式", "因為這個 Lambda 運算式,類型不是 C 樣式", "列舉型別名稱限定詞是 C++11 功能", - "不受限的等位是 C++11 功能" + "不受限的聯合是 C++11 功能", + "已經在目前的範圍宣告 %sq" ] From 2f9e2a52283462eb152096b690425b513652a1bf Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 28 Sep 2026 17:24:04 -0700 Subject: [PATCH 3/8] Update fast-uri. (#14804) --- Extension/package.json | 2 +- Extension/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 92d7ed6ef..b6eb1bfac 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -7269,7 +7269,7 @@ "resolutions": { "gulp-typescript/**/glob-parent": "^5.1.2", "serialize-javascript": "^7.0.5", - "fast-uri": "^3.1.6", + "fast-uri": "^3.1.8", "@humanfs/node": "^0.16.8" } } diff --git a/Extension/yarn.lock b/Extension/yarn.lock index 66c71267d..6baca69d1 100644 --- a/Extension/yarn.lock +++ b/Extension/yarn.lock @@ -2530,10 +2530,10 @@ fast-levenshtein@^3.0.0: dependencies: fastest-levenshtein "^1.0.7" -fast-uri@^3.0.1, fast-uri@^3.1.6: - version "3.1.6" - resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/fast-uri/-/fast-uri-3.1.6.tgz#bfdd308ef763f835fed059f3c6c925f708e33e3a" - integrity sha1-v90wjvdj+DX+0Fnzxskl9wjjPjo= +fast-uri@^3.0.1, fast-uri@^3.1.8: + version "3.1.8" + resolved "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/fast-uri/-/fast-uri-3.1.8.tgz#f7db8d942e20eead3cfe93b0beeb4eb0169e938b" + integrity sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg== fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.7: version "1.0.16" From 26f1df0a67636903064fd345d7ab03956897056f Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson <49173979+Colengms@users.noreply.github.com> Date: Mon, 28 Sep 2026 18:19:33 -0700 Subject: [PATCH 4/8] Add localized C++ hover size and alignment text (#14797) --- Extension/src/nativeStrings.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Extension/src/nativeStrings.json b/Extension/src/nativeStrings.json index 8b1a93276..5ad20f459 100644 --- a/Extension/src/nativeStrings.json +++ b/Extension/src/nativeStrings.json @@ -789,5 +789,13 @@ "check_analysis_process_unavailable": { "text": "The C++ analysis process was unavailable while checking \"{0}\".", "hint": "{0} is the source file path. {Locked=\"C++\"} {Locked=\"{0}\"}" + }, + "quick_info_size": { + "text": "**Size:** %s B", + "hint": "Size of a C or C++ type in bytes. %s is the numeric size. Translate B to a count-invariant byte unit symbol or abbreviation. {Locked=\"**\"} {Locked=\"%s\"}" + }, + "quick_info_alignment": { + "text": "**Alignment:** %s B", + "hint": "Alignment of a C or C++ type in bytes. %s is the numeric alignment. Translate B to a count-invariant byte unit symbol or abbreviation. {Locked=\"**\"} {Locked=\"%s\"}" } } From 0e056e52ec885a7bd82b31681cbe8ac9b4d38d59 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson <49173979+Colengms@users.noreply.github.com> Date: Thu, 1 Oct 2026 12:18:18 -0700 Subject: [PATCH 5/8] Automatically fold inactive regions (#14796) --- Extension/package.json | 24 ++ Extension/package.nls.json | 3 + Extension/src/LanguageServer/client.ts | 189 ++++++++-- Extension/src/LanguageServer/extension.ts | 12 + .../src/LanguageServer/inactiveRegions.ts | 57 +++ .../src/LanguageServer/languageClient.ts | 9 +- Extension/src/LanguageServer/settings.ts | 10 + Extension/test/minBinaryVersion.json | 2 +- .../tests/inactiveFolding.test.ts | 139 +++++++ .../tests/inactiveFolding.test.ts | 338 ++++++++++++++++++ Extension/test/unit/inactiveRegions.test.ts | 75 ++++ 11 files changed, 822 insertions(+), 36 deletions(-) create mode 100644 Extension/src/LanguageServer/inactiveRegions.ts create mode 100644 Extension/test/scenarios/MultirootDeadlockTest/tests/inactiveFolding.test.ts create mode 100644 Extension/test/scenarios/SingleRootProject/tests/inactiveFolding.test.ts create mode 100644 Extension/test/unit/inactiveRegions.test.ts diff --git a/Extension/package.json b/Extension/package.json index b6eb1bfac..dcce0856c 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -1072,6 +1072,12 @@ "description": "%c_cpp.configuration.dimInactiveRegions.description%", "scope": "resource" }, + "C_Cpp.autoFoldInactiveRegions": { + "type": "boolean", + "default": false, + "markdownDescription": "%c_cpp.configuration.autoFoldInactiveRegions.description%", + "scope": "resource" + }, "C_Cpp.inactiveRegionOpacity": { "type": "number", "default": 0.55, @@ -3999,6 +4005,16 @@ "title": "%c_cpp.command.toggleDimInactiveRegions.title%", "category": "C/C++" }, + { + "command": "C_Cpp.FoldAllInactiveRegions", + "title": "%c_cpp.command.foldAllInactiveRegions.title%", + "category": "C/C++" + }, + { + "command": "C_Cpp.UnfoldAllInactiveRegions", + "title": "%c_cpp.command.unfoldAllInactiveRegions.title%", + "category": "C/C++" + }, { "command": "C_Cpp.ResetDatabase", "title": "%c_cpp.command.resetDatabase.title%", @@ -6552,6 +6568,14 @@ "command": "C_Cpp.ToggleDimInactiveRegions", "when": "config.C_Cpp.intelliSenseEngine =~ /^[dD]efault$/" }, + { + "command": "C_Cpp.FoldAllInactiveRegions", + "when": "editorLangId =~ /^(c|(cuda-)?cpp)$/ && config.C_Cpp.intelliSenseEngine =~ /^[dD]efault$/ && config.C_Cpp.codeFolding =~ /^[eE]nabled$/" + }, + { + "command": "C_Cpp.UnfoldAllInactiveRegions", + "when": "editorLangId =~ /^(c|(cuda-)?cpp)$/ && config.C_Cpp.intelliSenseEngine =~ /^[dD]efault$/ && config.C_Cpp.codeFolding =~ /^[eE]nabled$/" + }, { "command": "C_Cpp.ResetDatabase", "when": "!(config.C_Cpp.intelliSenseEngine =~ /^[dD]isabled$/)" diff --git a/Extension/package.nls.json b/Extension/package.nls.json index d10af1bcd..57f924d7e 100644 --- a/Extension/package.nls.json +++ b/Extension/package.nls.json @@ -19,6 +19,8 @@ "c_cpp.command.enableErrorSquiggles.title": "Enable Error Squiggles", "c_cpp.command.disableErrorSquiggles.title": "Disable Error Squiggles", "c_cpp.command.toggleDimInactiveRegions.title": "Toggle Inactive Region Colorization", + "c_cpp.command.foldAllInactiveRegions.title": "Fold All Inactive Regions", + "c_cpp.command.unfoldAllInactiveRegions.title": "Unfold All Inactive Regions", "c_cpp.command.resetDatabase.title": "Reset IntelliSense Database", "c_cpp.command.takeSurvey.title": "Take Survey", "c_cpp.command.restartIntelliSenseForFile.title": "Restart IntelliSense for Active File", @@ -486,6 +488,7 @@ "c_cpp.configuration.autocomplete.disabled.description": "Uses the word-based completion provided by Visual Studio Code.", "c_cpp.configuration.errorSquiggles.description": "Controls whether suspected compile errors detected by the IntelliSense engine will be reported back to the editor. It also controls whether code analysis warnings are reported if includes can't be found. This setting is ignored by the Tag Parser engine.", "c_cpp.configuration.dimInactiveRegions.description": "Controls whether inactive preprocessor blocks are colored differently than active code. This setting has no effect if IntelliSense is disabled or if using the Default High Contrast theme.", + "c_cpp.configuration.autoFoldInactiveRegions.description": "Controls whether inactive preprocessor blocks are automatically folded when a file is opened. This setting only applies when `#C_Cpp.intelliSenseEngine#` is `default` and `#C_Cpp.codeFolding#` is `enabled`.", "c_cpp.configuration.inactiveRegionOpacity.markdownDescription": { "message": "Controls the opacity of inactive preprocessor blocks. Scales between `0.1` and `1.0`. This setting only applies when inactive region dimming is enabled.", "comment": [ diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 77ac15102..22ccc7d96 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -55,6 +55,7 @@ import { CustomConfigurationProvider1, getCustomConfigProviders, isSameProviderE import { DataBinding } from './dataBinding'; import { cachedEditorConfigSettings, getEditorConfigSettings } from './editorConfig'; import { CppSourceStr, clients, configPrefix, initializeIntervalTimer, isWritingCrashCallStack, updateLanguageConfigurations, usesCrashHandler, watchForCrashes } from './extension'; +import { InactiveRegion, InactiveRegionStore, getInactiveRegionStartLines } from './inactiveRegions'; import { LanguageClient } from './languageClient'; import { LocalizeStringParams, getLocaleId, getLocalizedString } from './localization'; import { PersistentFolderState, PersistentState, PersistentWorkspaceState } from './persistentState'; @@ -227,18 +228,13 @@ interface FileChangedParams extends WorkspaceFolderParams { uri: string; } -interface InputRegion { - startLine: number; - startColumn: number; - endLine: number; - endColumn: number; -} - interface DecorationRangesPair { decoration: vscode.TextEditorDecorationType; ranges: vscode.Range[]; } +type InactiveRegionFoldingOperation = 'fold' | 'unfold'; + interface InternalSourceFileConfiguration extends SourceFileConfiguration { compilerArgsLegacy?: string[]; } @@ -362,7 +358,7 @@ export interface IntelliSenseResult { uri: string; fileVersion: number; diagnostics: IntelliSenseDiagnostic[]; - inactiveRegions: InputRegion[]; + inactiveRegions: InactiveRegion[]; semanticTokens: SemanticToken[]; inlayHints: CppInlayHint[]; clearExistingDiagnostics: boolean; @@ -806,6 +802,8 @@ export interface Client { selectTranslationUnit(uri: vscode.Uri, translationUnit: string): Promise; updateActiveDocumentTextOptions(): void; didChangeActiveEditor(editor?: vscode.TextEditor, selection?: Range): Promise; + foldAllInactiveRegions(): Promise; + unfoldAllInactiveRegions(): Promise; restartIntelliSenseForFile(document: vscode.TextDocument): Promise; activate(): void; selectionChanged(selection: Range): Promise; @@ -872,6 +870,7 @@ export function createNullClient(): Client { } export class DefaultClient implements Client { + private static readonly autoFoldedEditors = new WeakSet(); private disposables: vscode.Disposable[] = []; private documentFormattingProviderDisposable: vscode.Disposable | undefined; private formattingRangeProviderDisposable: vscode.Disposable | undefined; @@ -886,6 +885,9 @@ export class DefaultClient implements Client { private workspaceStoragePath: string; private trackedDocuments = new Map(); private inactiveRegionsDecorations = new Map(); + private inactiveRegions = new InactiveRegionStore(); + private pendingInactiveRegionFoldingOperations = new WeakMap(); + private inactiveRegionReportingEnabled: boolean; private settingsTracker: SettingsTracker; private loggingLevel: number = 1; private configurationProvider?: string; @@ -1327,6 +1329,7 @@ export class DefaultClient implements Client { } const rootUri: vscode.Uri | undefined = this.RootUri; + this.inactiveRegionReportingEnabled = new CppSettings(rootUri).inactiveRegionReportingEnabled; this.settingsTracker = new SettingsTracker(rootUri); try { @@ -1477,6 +1480,7 @@ export class DefaultClient implements Client { intelliSenseCacheSize: settings.intelliSenseCacheSize, intelliSenseMemoryLimit: settings.intelliSenseMemoryLimit, dimInactiveRegions: settings.dimInactiveRegions, + codeFolding: settings.codeFolding, suggestSnippets: settings.suggestSnippets, legacyCompilerArgsBehavior: settings.legacyCompilerArgsBehavior, defaultSystemIncludePath: settings.defaultSystemIncludePath, @@ -1849,6 +1853,11 @@ export class DefaultClient implements Client { await this.ready; if (Object.keys(changedSettings).length > 0) { + const inactiveRegionReportingEnabled: boolean = new CppSettings(this.RootUri).inactiveRegionReportingEnabled; + if (inactiveRegionReportingEnabled !== this.inactiveRegionReportingEnabled) { + this.inactiveRegionReportingEnabled = inactiveRegionReportingEnabled; + this.inactiveRegions.clear(); + } if (this === defaultClient) { if (changedSettings.commentContinuationPatterns2 !== undefined) { updateLanguageConfigurations(); @@ -1903,6 +1912,20 @@ export class DefaultClient implements Client { void ui.ShowConfigureIntelliSenseButton(false, this, ConfigurationType.CompilerPath, showButtonSender); } } + if ((changedSettings.autoFoldInactiveRegions !== undefined || changedSettings.codeFolding !== undefined) + && vscode.window.activeTextEditor + && clients.getClientFor(vscode.window.activeTextEditor.document.uri) === this) { + if (changedSettings.codeFolding !== undefined) { + await this.languageClient.refreshFoldingRanges(vscode.window.activeTextEditor.document); + } + await this.tryApplyInactiveRegionFolding(vscode.window.activeTextEditor); + } + if (["dimInactiveRegions", + "inactiveRegionOpacity", + "inactiveRegionForegroundColor", + "inactiveRegionBackgroundColor"].some(setting => setting in changedSettings)) { + this.refreshInactiveRegionDecorations(); + } if (changedSettings.legacyCompilerArgsBehavior !== undefined) { this.configuration.handleConfigurationChange(); } @@ -1997,7 +2020,9 @@ export class DefaultClient implements Client { const oldVersion: number | undefined = openFileVersions.get(textDocumentChangeEvent.document.uri.toString()); const newVersion: number = textDocumentChangeEvent.document.version; if (oldVersion === undefined || newVersion > oldVersion) { - openFileVersions.set(textDocumentChangeEvent.document.uri.toString(), newVersion); + const uri: string = textDocumentChangeEvent.document.uri.toString(); + openFileVersions.set(uri, newVersion); + this.inactiveRegions.delete(uri); } } } @@ -2006,6 +2031,7 @@ export class DefaultClient implements Client { if (document.uri.scheme === "file") { const uri: string = document.uri.toString(); openFileVersions.set(uri, document.version); + this.inactiveRegions.delete(uri); } } @@ -2017,6 +2043,8 @@ export class DefaultClient implements Client { if (this.inlayHintsProvider) { this.inlayHintsProvider.removeFile(uri); } + this.inactiveRegions.delete(uri); + this.inactiveRegionsDecorations.get(uri)?.decoration.dispose(); this.inactiveRegionsDecorations.delete(uri); if (diagnosticsCollectionIntelliSense) { diagnosticsCollectionIntelliSense.delete(document.uri); @@ -2615,7 +2643,8 @@ export class DefaultClient implements Client { this.inlayHintsProvider.deliverInlayHints(intelliSenseResult.uri, intelliSenseResult.inlayHints, intelliSenseResult.clearExistingInlayHint); } - this.updateInactiveRegions(intelliSenseResult.uri, intelliSenseResult.inactiveRegions, intelliSenseResult.clearExistingInactiveRegions); + this.updateInactiveRegions(intelliSenseResult.uri, intelliSenseResult.inactiveRegions, + intelliSenseResult.clearExistingInactiveRegions, intelliSenseResult.isCompletePass); if (intelliSenseResult.clearExistingDiagnostics || intelliSenseResult.diagnostics.length > 0) { this.updateSquiggles(intelliSenseResult.uri, intelliSenseResult.diagnostics, intelliSenseResult.clearExistingDiagnostics); } @@ -2927,12 +2956,24 @@ export class DefaultClient implements Client { this.model.isParsingWorkspacePaused.Value = tagParseStatus.isPaused; } - private updateInactiveRegions(uriString: string, inactiveRegions: InputRegion[], startNewSet: boolean): void { + private updateInactiveRegions(uriString: string, inactiveRegions: InactiveRegion[], startNewSet: boolean, isCompletePass: boolean): void { const client: Client = clients.getClientFor(vscode.Uri.parse(uriString)); - if (!(client instanceof DefaultClient) || (!startNewSet && inactiveRegions.length === 0)) { + if (!(client instanceof DefaultClient) || (!startNewSet && inactiveRegions.length === 0 && !isCompletePass)) { return; } + client.inactiveRegions.update(uriString, inactiveRegions, startNewSet, isCompletePass); + const settings: CppSettings = new CppSettings(client.RootUri); + client.updateInactiveRegionDecorations(uriString, inactiveRegions, startNewSet, settings); + + const activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; + if (isCompletePass && activeEditor?.document.uri.toString() === uriString) { + void client.tryApplyInactiveRegionFolding(activeEditor).catch(logAndReturn.undefined); + } + } + + private updateInactiveRegionDecorations(uriString: string, inactiveRegions: readonly InactiveRegion[], + startNewSet: boolean, settings: CppSettings): void { const dimInactiveRegions: boolean = settings.dimInactiveRegions; let currentSet: DecorationRangesPair | undefined = this.inactiveRegionsDecorations.get(uriString); if (startNewSet || !dimInactiveRegions) { @@ -2940,32 +2981,109 @@ export class DefaultClient implements Client { currentSet.decoration.dispose(); this.inactiveRegionsDecorations.delete(uriString); } - if (!dimInactiveRegions) { - return; - } currentSet = undefined; } - if (currentSet === undefined) { - const opacity: number | undefined = settings.inactiveRegionOpacity; - currentSet = { - decoration: vscode.window.createTextEditorDecorationType({ - opacity: (opacity === undefined) ? "0.55" : opacity.toString(), - backgroundColor: settings.inactiveRegionBackgroundColor, - color: settings.inactiveRegionForegroundColor, - rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen - }), - ranges: [] - }; - this.inactiveRegionsDecorations.set(uriString, currentSet); + if (dimInactiveRegions) { + if (currentSet === undefined) { + const opacity: number | undefined = settings.inactiveRegionOpacity; + currentSet = { + decoration: vscode.window.createTextEditorDecorationType({ + opacity: (opacity === undefined) ? "0.55" : opacity.toString(), + backgroundColor: settings.inactiveRegionBackgroundColor, + color: settings.inactiveRegionForegroundColor, + rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen + }), + ranges: [] + }; + this.inactiveRegionsDecorations.set(uriString, currentSet); + } + + Array.prototype.push.apply(currentSet.ranges, inactiveRegions.map(element => + new vscode.Range(element.startLine, element.startColumn, element.endLine, element.endColumn))); + + // Apply the decorations to all *visible* text editors + const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === uriString); + for (const e of editors) { + e.setDecorations(currentSet.decoration, currentSet.ranges); + } + } + } + + private refreshInactiveRegionDecorations(): void { + const settings: CppSettings = new CppSettings(this.RootUri); + if (!settings.dimInactiveRegions) { + this.inactiveRegionsDecorations.forEach(currentSet => currentSet.decoration.dispose()); + this.inactiveRegionsDecorations.clear(); + return; + } + + this.inactiveRegions.forEach((inactiveRegions, uriString) => + this.updateInactiveRegionDecorations(uriString, inactiveRegions, true, settings)); + } + + public foldAllInactiveRegions(): Promise { + return this.applyInactiveRegionFolding('fold'); + } + + public unfoldAllInactiveRegions(): Promise { + return this.applyInactiveRegionFolding('unfold'); + } + + private async applyInactiveRegionFolding(operation: InactiveRegionFoldingOperation): Promise { + const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; + if (!editor || !util.isCpp(editor.document)) { + return; + } + + this.pendingInactiveRegionFoldingOperations.set(editor, operation); + await this.tryApplyInactiveRegionFolding(editor); + } + + private async tryApplyInactiveRegionFolding(editor: vscode.TextEditor): Promise { + if (vscode.window.activeTextEditor !== editor || !util.isCpp(editor.document)) { + return; + } + + const uri: string = editor.document.uri.toString(); + const inactiveRegions: readonly InactiveRegion[] | undefined = this.inactiveRegions.getComplete(uri); + if (inactiveRegions === undefined) { + return; + } + + const settings: CppSettings = new CppSettings(editor.document.uri); + const autoFold: boolean = settings.autoFoldInactiveRegions && !DefaultClient.autoFoldedEditors.has(editor); + const pendingOperation: InactiveRegionFoldingOperation | undefined = + this.pendingInactiveRegionFoldingOperations.get(editor); + const operation: InactiveRegionFoldingOperation | undefined = pendingOperation ?? (autoFold ? 'fold' : undefined); + if (operation === undefined) { + return; + } + + this.pendingInactiveRegionFoldingOperations.delete(editor); + + if (autoFold) { + DefaultClient.autoFoldedEditors.add(editor); } - Array.prototype.push.apply(currentSet.ranges, inactiveRegions.map(element => - new vscode.Range(element.startLine, element.startColumn, element.endLine, element.endColumn))); + const selectionLines: number[] = getInactiveRegionStartLines(inactiveRegions); + if (selectionLines.length === 0) { + return; + } - // Apply the decorations to all *visible* text editors - const editors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => e.document.uri.toString() === uriString); - for (const e of editors) { - e.setDecorations(currentSet.decoration, currentSet.ranges); + try { + await this.languageClient.refreshFoldingRanges(editor.document); + await vscode.commands.executeCommand(`editor.${operation}`, { + selectionLines, + direction: 'down' + }); + } catch (error) { + if (autoFold) { + DefaultClient.autoFoldedEditors.delete(editor); + } + if (pendingOperation) { + this.pendingInactiveRegionFoldingOperations.set(editor, pendingOperation); + } + throw error; } } @@ -3136,6 +3254,7 @@ export class DefaultClient implements Client { return; } + void this.tryApplyInactiveRegionFolding(editor).catch(logAndReturn.undefined); this.updateActiveDocumentTextOptions(); const params: DidChangeActiveEditorParams = { @@ -4232,6 +4351,8 @@ export class DefaultClient implements Client { } public dispose(): void { + this.inactiveRegionsDecorations.forEach(currentSet => currentSet.decoration.dispose()); + this.inactiveRegionsDecorations.clear(); this.pendingTagParsingCalls.forEach(pendingCall => { if (pendingCall.timer) { clearTimeout(pendingCall.timer); @@ -4447,6 +4568,8 @@ class NullClient implements Client { selectTranslationUnit(uri: vscode.Uri, translationUnit: string): Promise { return Promise.resolve(); } updateActiveDocumentTextOptions(): void { } didChangeActiveEditor(editor?: vscode.TextEditor): Promise { return Promise.resolve(); } + foldAllInactiveRegions(): Promise { return Promise.resolve(); } + unfoldAllInactiveRegions(): Promise { return Promise.resolve(); } restartIntelliSenseForFile(document: vscode.TextDocument): Promise { return Promise.resolve(); } activate(): void { } selectionChanged(selection: Range): Promise { return Promise.resolve(); } diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index 8ae2125f4..653f620e5 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -396,6 +396,8 @@ export async function registerCommands(enabled: boolean): Promise { commandDisposables.push(vscode.commands.registerCommand('C_Cpp.EnableErrorSquiggles', enabled ? onEnableSquiggles : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.DisableErrorSquiggles', enabled ? onDisableSquiggles : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ToggleDimInactiveRegions', enabled ? onToggleDimInactiveRegions : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.FoldAllInactiveRegions', enabled ? onFoldAllInactiveRegions : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.UnfoldAllInactiveRegions', enabled ? onUnfoldAllInactiveRegions : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.PauseParsing', enabled ? onPauseParsing : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ResumeParsing', enabled ? onResumeParsing : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.PauseCodeAnalysis', enabled ? onPauseCodeAnalysis : onDisabledCommand)); @@ -925,6 +927,16 @@ function onToggleDimInactiveRegions(): void { settings.update("dimInactiveRegions", !settings.dimInactiveRegions); } +function onFoldAllInactiveRegions(): Promise { + const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; + return editor ? clients.getClientFor(editor.document.uri).foldAllInactiveRegions() : Promise.resolve(); +} + +function onUnfoldAllInactiveRegions(): Promise { + const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; + return editor ? clients.getClientFor(editor.document.uri).unfoldAllInactiveRegions() : Promise.resolve(); +} + function onPauseParsing(): void { void clients.ActiveClient.pauseParsing().catch(logAndReturn.undefined); } diff --git a/Extension/src/LanguageServer/inactiveRegions.ts b/Extension/src/LanguageServer/inactiveRegions.ts new file mode 100644 index 000000000..b05d23397 --- /dev/null +++ b/Extension/src/LanguageServer/inactiveRegions.ts @@ -0,0 +1,57 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +export interface InactiveRegion { + startLine: number; + startColumn: number; + endLine: number; + endColumn: number; +} + +interface InactiveRegionSet { + regions: InactiveRegion[]; + isComplete: boolean; +} + +export class InactiveRegionStore { + private readonly regionSets = new Map(); + + public update(uri: string, regions: InactiveRegion[], startNewSet: boolean, isComplete: boolean): void { + let regionSet: InactiveRegionSet | undefined = this.regionSets.get(uri); + if (startNewSet || regionSet === undefined) { + regionSet = { + regions: [], + isComplete: false + }; + this.regionSets.set(uri, regionSet); + } + + for (const region of regions) { + regionSet.regions.push(region); + } + regionSet.isComplete = isComplete; + } + + public getComplete(uri: string): readonly InactiveRegion[] | undefined { + const regionSet: InactiveRegionSet | undefined = this.regionSets.get(uri); + return regionSet?.isComplete ? regionSet.regions : undefined; + } + + public forEach(callback: (regions: readonly InactiveRegion[], uri: string) => void): void { + this.regionSets.forEach((regionSet, uri) => callback(regionSet.regions, uri)); + } + + public clear(): void { + this.regionSets.clear(); + } + + public delete(uri: string): void { + this.regionSets.delete(uri); + } +} + +export function getInactiveRegionStartLines(regions: readonly InactiveRegion[]): number[] { + return [...new Set(regions.map(region => region.startLine))].sort((a, b) => a - b); +} diff --git a/Extension/src/LanguageServer/languageClient.ts b/Extension/src/LanguageServer/languageClient.ts index 255490fdd..63dd88b9c 100644 --- a/Extension/src/LanguageServer/languageClient.ts +++ b/Extension/src/LanguageServer/languageClient.ts @@ -4,8 +4,8 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; -import { Disposable } from 'vscode'; -import { CancellationToken, NotificationHandler, NotificationType, RequestType } from 'vscode-languageclient'; +import { Disposable, TextDocument } from 'vscode'; +import { CancellationToken, FoldingRangeRequest, NotificationHandler, NotificationType, RequestType } from 'vscode-languageclient'; import * as rpc from 'vscode-languageclient/node'; import { ManualSignal } from '../Utility/Async/manualSignal'; @@ -71,6 +71,11 @@ export class LanguageClient { return this.rpcClient.code2ProtocolConverter; } + public async refreshFoldingRanges(document: TextDocument): Promise { + await this.ready; + this.rpcClient.getFeature(FoldingRangeRequest.method).getProvider(document)?.onDidChangeFoldingRange.fire(); + } + public async sendRequest(type: RequestType, params: P, token?: CancellationToken): Promise { await this.ready; return this.rpcClient.sendRequest(type, params, token); diff --git a/Extension/src/LanguageServer/settings.ts b/Extension/src/LanguageServer/settings.ts index a18b2cd61..2a394cd62 100644 --- a/Extension/src/LanguageServer/settings.ts +++ b/Extension/src/LanguageServer/settings.ts @@ -49,6 +49,7 @@ export interface WorkspaceFolderSettingsParams { intelliSenseCacheSize: number; intelliSenseMemoryLimit: number; dimInactiveRegions: boolean; + codeFolding: boolean; suggestSnippets: boolean; legacyCompilerArgsBehavior: boolean; defaultSystemIncludePath: string[] | undefined; @@ -377,6 +378,15 @@ export class CppSettings extends Settings { public get inactiveRegionOpacity(): number { return this.getAsNumber("inactiveRegionOpacity"); } public get inactiveRegionForegroundColor(): string | undefined { return changeBlankStringToUndefined(this.getAsStringOrUndefined("inactiveRegionForegroundColor")); } public get inactiveRegionBackgroundColor(): string | undefined { return changeBlankStringToUndefined(this.getAsStringOrUndefined("inactiveRegionBackgroundColor")); } + public get autoFoldInactiveRegions(): boolean { + return this.getAsBoolean("autoFoldInactiveRegions") + && this.codeFolding + && this.intelliSenseEngine.toLowerCase() === "default"; + } + public get inactiveRegionReportingEnabled(): boolean { + return this.dimInactiveRegions + || (this.codeFolding && this.intelliSenseEngine.toLowerCase() === "default"); + } public get autocomplete(): string { return this.getAsString("autocomplete"); } public get autocompleteAddParentheses(): boolean { return this.getAsBoolean("autocompleteAddParentheses"); } public get loggingLevel(): string { return this.getAsString("loggingLevel"); } diff --git a/Extension/test/minBinaryVersion.json b/Extension/test/minBinaryVersion.json index 1a1f20762..26f354be5 100644 --- a/Extension/test/minBinaryVersion.json +++ b/Extension/test/minBinaryVersion.json @@ -1,3 +1,3 @@ { - "minBinaryVersion": "1.33.0" + "minBinaryVersion": "1.35.3" } diff --git a/Extension/test/scenarios/MultirootDeadlockTest/tests/inactiveFolding.test.ts b/Extension/test/scenarios/MultirootDeadlockTest/tests/inactiveFolding.test.ts new file mode 100644 index 000000000..f97597c64 --- /dev/null +++ b/Extension/test/scenarios/MultirootDeadlockTest/tests/inactiveFolding.test.ts @@ -0,0 +1,139 @@ +/* -------------------------------------------------------------------------------------------- + * 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 sinon from 'sinon'; +import * as vscode from 'vscode'; +import * as api from 'vscode-cpptools'; +import * as apit from 'vscode-cpptools/out/testApi'; +import * as extension from '../../../../src/LanguageServer/extension'; +import * as testHelpers from '../../../common/testHelpers'; + +suite("Inactive region folding in a multi-root workspace", function (): void { + let testHook: apit.CppToolsTestHook; + let firstWorkspaceFolder: vscode.WorkspaceFolder; + let workspaceFolder: vscode.WorkspaceFolder; + + suiteSetup(async function (): Promise { + await testHelpers.activateCppExtension(); + const cpptools: apit.CppToolsTestApi = await apit.getCppToolsTestApi(api.Version.latest) + ?? assert.fail("Could not get CppToolsTestApi"); + testHook = cpptools.getTestHook(); + firstWorkspaceFolder = vscode.workspace.workspaceFolders?.[0] + ?? assert.fail("First workspace folder is unavailable"); + workspaceFolder = vscode.workspace.workspaceFolders?.[1] + ?? assert.fail("Second workspace folder is unavailable"); + }); + + suiteTeardown(function (): void { + testHook.dispose(); + }); + + test("routes manual folding operations by URI during a workspace client switch", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + let releaseEditorChange: (() => void) | undefined; + let editorChangeStub: sinon.SinonStub | undefined; + let editorChangePromise: Promise | undefined; + let editor: vscode.TextEditor | undefined; + let firstEditor: vscode.TextEditor | undefined; + try { + editor = await openFileAndWaitForIntelliSense(); + await vscode.commands.executeCommand("editor.unfoldAll"); + + const firstDocument: vscode.TextDocument = await vscode.workspace.openTextDocument( + path.join(firstWorkspaceFolder.uri.fsPath, "test.cpp")); + firstEditor = await vscode.window.showTextDocument(firstDocument); + await extension.clients.didChangeActiveEditor(firstEditor); + const firstClient = extension.clients.ActiveClient; + const owner = extension.clients.getClientFor(editor.document.uri); + assert.notStrictEqual(firstClient, owner); + + const pendingEditorChange: Promise = new Promise(resolve => releaseEditorChange = resolve); + editorChangeStub = sinon.stub(owner, "didChangeActiveEditor").returns(pendingEditorChange); + const activeEditor: vscode.TextEditor = await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + editorChangePromise = extension.clients.didChangeActiveEditor(activeEditor); + assert.strictEqual(extension.clients.ActiveClient, firstClient); + + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + await assertInactiveBranchIsFolded(editor); + + await vscode.commands.executeCommand("C_Cpp.UnfoldAllInactiveRegions"); + await assertInactiveBranchIsUnfolded(editor); + } finally { + releaseEditorChange?.(); + await editorChangePromise; + editorChangeStub?.restore(); + if (editor) { + await closeEditor(editor); + } + if (firstEditor) { + await closeEditor(firstEditor); + } + await testHelpers.delay(150); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + } + }); + + async function closeEditor(editor: vscode.TextEditor): Promise { + await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + } + + async function assertInactiveBranchIsFolded(editor: vscode.TextEditor): Promise { + let line: number = 24; + for (let i: number = 0; i < 20; ++i) { + const activeEditor: vscode.TextEditor = await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + activeEditor.selection = new vscode.Selection(24, 0, 24, 0); + await vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine", value: 1 }); + line = activeEditor.selection.active.line; + if (line === 28) { + return; + } + await testHelpers.delay(50); + } + + assert.strictEqual(line, 28); + } + + async function assertInactiveBranchIsUnfolded(editor: vscode.TextEditor): Promise { + const activeEditor: vscode.TextEditor = await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + activeEditor.selection = new vscode.Selection(24, 0, 24, 0); + await vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine", value: 1 }); + assert.strictEqual(activeEditor.selection.active.line, 25); + } + + function waitForIntelliSenseReady(fileName: string): Promise { + return new Promise((resolve, reject) => { + const timeout: NodeJS.Timeout = setTimeout(() => { + listener.dispose(); + reject(new Error(`Timed out waiting for IntelliSense for ${fileName}`)); + }, testHelpers.defaultTimeout); + const listener: vscode.Disposable = testHook.IntelliSenseStatusChanged(result => { + if (result.filename === fileName && result.status === apit.Status.IntelliSenseReady) { + clearTimeout(timeout); + listener.dispose(); + resolve(); + } + }); + }); + } + + async function openFileAndWaitForIntelliSense(): Promise { + const fileName: string = "code_folding.cpp"; + const filePath: string = path.join(workspaceFolder.uri.fsPath, fileName); + const ready: Promise = waitForIntelliSenseReady(fileName); + const document: vscode.TextDocument = await vscode.workspace.openTextDocument(filePath); + const editor: vscode.TextEditor = await vscode.window.showTextDocument(document); + await ready; + return editor; + } +}); diff --git a/Extension/test/scenarios/SingleRootProject/tests/inactiveFolding.test.ts b/Extension/test/scenarios/SingleRootProject/tests/inactiveFolding.test.ts new file mode 100644 index 000000000..f623ef848 --- /dev/null +++ b/Extension/test/scenarios/SingleRootProject/tests/inactiveFolding.test.ts @@ -0,0 +1,338 @@ +/* -------------------------------------------------------------------------------------------- + * 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 api from 'vscode-cpptools'; +import * as apit from 'vscode-cpptools/out/testApi'; +import * as extension from '../../../../src/LanguageServer/extension'; +import * as testHelpers from '../../../common/testHelpers'; + +suite("Inactive region folding", function (): void { + let testHook: apit.CppToolsTestHook; + let workspaceFolder: vscode.WorkspaceFolder; + + suiteSetup(async function (): Promise { + await testHelpers.activateCppExtension(); + const cpptools: apit.CppToolsTestApi = await apit.getCppToolsTestApi(api.Version.latest) + ?? assert.fail("Could not get CppToolsTestApi"); + testHook = cpptools.getTestHook(); + workspaceFolder = vscode.workspace.workspaceFolders?.[0] + ?? assert.fail("No workspace folder available"); + }); + + suiteTeardown(function (): void { + testHook.dispose(); + }); + + test("folds an inactive preprocessor branch instead of its nested function", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousValue: boolean | undefined = configuration.inspect("dimInactiveRegions")?.globalValue; + await configuration.update("dimInactiveRegions", false, vscode.ConfigurationTarget.Global); + try { + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + await vscode.commands.executeCommand("editor.unfoldAll"); + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + + await assertInactiveBranchIsFolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("dimInactiveRegions", previousValue, vscode.ConfigurationTarget.Global); + } + }); + + test("automatically folds inactive regions when enabled", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + await configuration.update("autoFoldInactiveRegions", true, vscode.ConfigurationTarget.Global); + try { + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + await testHelpers.delay(100); + + await assertInactiveBranchIsFolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("autoFoldInactiveRegions", previousValue, vscode.ConfigurationTarget.Global); + } + }); + + test("manual folding does not suppress later automatic folding", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousAutoFoldValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + await configuration.update("autoFoldInactiveRegions", false, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + try { + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + await vscode.commands.executeCommand("editor.unfoldAll"); + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + await assertInactiveBranchIsFolded(editor); + + await vscode.commands.executeCommand("editor.unfoldAll"); + await assertInactiveBranchIsUnfolded(editor); + await configuration.update("autoFoldInactiveRegions", true, vscode.ConfigurationTarget.Global); + + await assertInactiveBranchIsFolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("autoFoldInactiveRegions", previousAutoFoldValue, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + } + }); + + test("unfolds all inactive regions", async () => { + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + try { + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + await assertInactiveBranchIsFolded(editor); + + await vscode.commands.executeCommand("C_Cpp.UnfoldAllInactiveRegions"); + await assertInactiveBranchIsUnfolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + } + }); + + test("applies a fold command queued before IntelliSense is ready", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp"); + const previousUpdateDelay: number | undefined = configuration.inspect("intelliSenseUpdateDelay")?.globalValue; + await configuration.update("intelliSenseUpdateDelay", 3000, vscode.ConfigurationTarget.Global); + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + try { + await vscode.commands.executeCommand("editor.unfoldAll"); + let intelliSenseReady: boolean = false; + const ready: Promise = waitForIntelliSenseReady(path.basename(editor.document.fileName)) + .then(() => { + intelliSenseReady = true; + }); + const editApplied: boolean = await editor.edit(editBuilder => + editBuilder.insert(new vscode.Position(0, 0), " ")); + assert.strictEqual(editApplied, true); + assert.strictEqual(intelliSenseReady, false); + + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + await ready; + await assertInactiveBranchIsFolded(editor); + } finally { + if (editor.document.isDirty) { + await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + await vscode.commands.executeCommand("undo"); + } + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("intelliSenseUpdateDelay", previousUpdateDelay, vscode.ConfigurationTarget.Global); + } + }); + + test("applies a queued fold only to the editor that requested it", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp"); + const previousAutoFoldValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + const previousUpdateDelay: number | undefined = configuration.inspect("intelliSenseUpdateDelay")?.globalValue; + await configuration.update("autoFoldInactiveRegions", false, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + await configuration.update("intelliSenseUpdateDelay", 3000, vscode.ConfigurationTarget.Global); + const firstEditor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + const secondEditor: vscode.TextEditor = await vscode.window.showTextDocument(firstEditor.document, { + viewColumn: vscode.ViewColumn.Beside, + preserveFocus: false, + preview: false + }); + try { + await vscode.window.showTextDocument(firstEditor.document, firstEditor.viewColumn, false); + await vscode.commands.executeCommand("editor.unfoldAll"); + firstEditor.revealRange(new vscode.Range(24, 0, 25, 0), vscode.TextEditorRevealType.InCenter); + await vscode.window.showTextDocument(secondEditor.document, secondEditor.viewColumn, false); + await vscode.commands.executeCommand("editor.unfoldAll"); + secondEditor.revealRange(new vscode.Range(24, 0, 25, 0), vscode.TextEditorRevealType.InCenter); + await assertLineVisibility(firstEditor, 25, true); + await assertLineVisibility(secondEditor, 25, true); + await vscode.window.showTextDocument(firstEditor.document, firstEditor.viewColumn, false); + + const ready: Promise = waitForIntelliSenseReady(path.basename(firstEditor.document.fileName)); + const editApplied: boolean = await firstEditor.edit(editBuilder => + editBuilder.insert(new vscode.Position(0, 0), " ")); + assert.strictEqual(editApplied, true); + await vscode.commands.executeCommand("C_Cpp.FoldAllInactiveRegions"); + await vscode.window.showTextDocument(secondEditor.document, secondEditor.viewColumn, false); + + await ready; + await assertLineVisibility(secondEditor, 25, true); + await vscode.window.showTextDocument(firstEditor.document, firstEditor.viewColumn, false); + await assertLineVisibility(firstEditor, 25, false); + await assertLineVisibility(secondEditor, 25, true); + } finally { + if (firstEditor.document.isDirty) { + await vscode.window.showTextDocument(firstEditor.document, firstEditor.viewColumn, false); + await vscode.commands.executeCommand("undo"); + } + await closeEditor(firstEditor); + await closeEditor(secondEditor); + await testHelpers.delay(150); + await configuration.update("autoFoldInactiveRegions", previousAutoFoldValue, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + await configuration.update("intelliSenseUpdateDelay", previousUpdateDelay, vscode.ConfigurationTarget.Global); + } + }); + + test("does not automatically refold an editor after client recovery", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousAutoFoldValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + await configuration.update("autoFoldInactiveRegions", true, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + try { + await assertInactiveBranchIsFolded(editor); + await vscode.commands.executeCommand("editor.unfoldAll"); + await assertInactiveBranchIsUnfolded(editor); + + const ready: Promise = waitForIntelliSenseReady(path.basename(editor.document.fileName)); + await extension.clients.recreateClients(); + await ready; + + await assertInactiveBranchIsUnfolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("autoFoldInactiveRegions", previousAutoFoldValue, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + } + }); + + test("does not automatically fold regions introduced after an empty initial result", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousAutoFoldValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + await configuration.update("autoFoldInactiveRegions", true, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense("main.cpp"); + try { + const ready: Promise = waitForIntelliSenseReady(path.basename(editor.document.fileName)); + const editApplied: boolean = await editor.edit(editBuilder => editBuilder.insert( + new vscode.Position(0, 0), + "#if 0\nint inactive()\n{\n return 0;\n}\n#endif\n\n")); + assert.strictEqual(editApplied, true); + await ready; + await testHelpers.delay(100); + + const activeEditor: vscode.TextEditor = await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + activeEditor.selection = new vscode.Selection(0, 0, 0, 0); + await vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine", value: 1 }); + assert.strictEqual(activeEditor.selection.active.line, 1); + } finally { + if (editor.document.isDirty) { + await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + await vscode.commands.executeCommand("undo"); + } + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await configuration.update("autoFoldInactiveRegions", previousAutoFoldValue, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + } + }); + + test("automatically folds when code folding is enabled later", async () => { + const configuration: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp", workspaceFolder.uri); + const previousAutoFoldValue: boolean | undefined = configuration.inspect("autoFoldInactiveRegions")?.globalValue; + const previousCodeFoldingValue: string | undefined = configuration.inspect("codeFolding")?.globalValue; + await configuration.update("autoFoldInactiveRegions", true, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", "disabled", vscode.ConfigurationTarget.Global); + try { + const editor: vscode.TextEditor = await openFileAndWaitForIntelliSense(); + await vscode.commands.executeCommand("editor.unfoldAll"); + await testHelpers.delay(100); + + await assertInactiveBranchIsUnfolded(editor); + await configuration.update("codeFolding", "enabled", vscode.ConfigurationTarget.Global); + await assertInactiveBranchIsFolded(editor); + } finally { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + await testHelpers.delay(150); + await configuration.update("autoFoldInactiveRegions", previousAutoFoldValue, vscode.ConfigurationTarget.Global); + await configuration.update("codeFolding", previousCodeFoldingValue, vscode.ConfigurationTarget.Global); + } + }); + + async function assertInactiveBranchIsFolded(editor: vscode.TextEditor): Promise { + let line: number = 24; + for (let i: number = 0; i < 20; ++i) { + line = await moveCursorDown(editor); + if (line === 28) { + return; + } + await testHelpers.delay(50); + } + + assert.strictEqual(line, 28); + } + + async function assertInactiveBranchIsUnfolded(editor: vscode.TextEditor): Promise { + let line: number = 24; + for (let i: number = 0; i < 20; ++i) { + line = await moveCursorDown(editor); + if (line === 25) { + return; + } + await testHelpers.delay(50); + } + + assert.strictEqual(line, 25); + } + + async function moveCursorDown(editor: vscode.TextEditor): Promise { + const activeEditor: vscode.TextEditor = await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + await vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup"); + activeEditor.selection = new vscode.Selection(24, 0, 24, 0); + await vscode.commands.executeCommand("cursorMove", { to: "down", by: "wrappedLine", value: 1 }); + return activeEditor.selection.active.line; + } + + async function assertLineVisibility(editor: vscode.TextEditor, line: number, expected: boolean): Promise { + const position: vscode.Position = new vscode.Position(line, 0); + let visible: boolean = false; + for (let i: number = 0; i < 20; ++i) { + visible = editor.visibleRanges.some(range => range.contains(position)); + if (visible === expected) { + return; + } + await testHelpers.delay(50); + } + + assert.strictEqual(visible, expected); + } + + async function closeEditor(editor: vscode.TextEditor): Promise { + await vscode.window.showTextDocument(editor.document, editor.viewColumn, false); + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + } + + function waitForIntelliSenseReady(fileName: string): Promise { + return new Promise((resolve, reject) => { + const timeout: NodeJS.Timeout = setTimeout(() => { + listener.dispose(); + reject(new Error(`Timed out waiting for IntelliSense for ${fileName}`)); + }, testHelpers.defaultTimeout); + const listener: vscode.Disposable = testHook.IntelliSenseStatusChanged(result => { + if (result.filename === fileName && result.status === apit.Status.IntelliSenseReady) { + clearTimeout(timeout); + listener.dispose(); + resolve(); + } + }); + }); + } + + async function openFileAndWaitForIntelliSense(fileName: string = "code_folding.cpp"): Promise { + const filePath: string = path.join(workspaceFolder.uri.fsPath, fileName); + const ready: Promise = waitForIntelliSenseReady(fileName); + const document: vscode.TextDocument = await vscode.workspace.openTextDocument(filePath); + const editor: vscode.TextEditor = await vscode.window.showTextDocument(document); + await ready; + return editor; + } +}); diff --git a/Extension/test/unit/inactiveRegions.test.ts b/Extension/test/unit/inactiveRegions.test.ts new file mode 100644 index 000000000..bd4e81c9f --- /dev/null +++ b/Extension/test/unit/inactiveRegions.test.ts @@ -0,0 +1,75 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as assert from 'assert'; +import { suite, test } from 'mocha'; +import { getInactiveRegionStartLines, InactiveRegion, InactiveRegionStore } from '../../src/LanguageServer/inactiveRegions'; + +suite("InactiveRegionStore", () => { + const uri: string = "file:///source.cpp"; + const firstRegion: InactiveRegion = { + startLine: 2, + startColumn: 0, + endLine: 4, + endColumn: 0 + }; + const secondRegion: InactiveRegion = { + startLine: 8, + startColumn: 0, + endLine: 10, + endColumn: 0 + }; + + test("returns regions only after a complete pass", () => { + const store: InactiveRegionStore = new InactiveRegionStore(); + + store.update(uri, [firstRegion], true, false); + assert.strictEqual(store.getComplete(uri), undefined); + + store.update(uri, [secondRegion], false, true); + assert.deepStrictEqual(store.getComplete(uri), [firstRegion, secondRegion]); + }); + + test("replaces regions when a new pass starts", () => { + const store: InactiveRegionStore = new InactiveRegionStore(); + store.update(uri, [firstRegion], true, true); + + store.update(uri, [secondRegion], true, true); + + assert.deepStrictEqual(store.getComplete(uri), [secondRegion]); + }); + + test("deletes cached regions", () => { + const store: InactiveRegionStore = new InactiveRegionStore(); + store.update(uri, [firstRegion], true, true); + + store.delete(uri); + + assert.strictEqual(store.getComplete(uri), undefined); + }); + + test("iterates cached regions", () => { + const store: InactiveRegionStore = new InactiveRegionStore(); + store.update(uri, [firstRegion, secondRegion], true, true); + const entries: [string, readonly InactiveRegion[]][] = []; + + store.forEach((regions, entryUri) => entries.push([entryUri, regions])); + + assert.deepStrictEqual(entries, [[uri, [firstRegion, secondRegion]]]); + }); + + test("clears all cached regions", () => { + const store: InactiveRegionStore = new InactiveRegionStore(); + store.update(uri, [firstRegion], true, true); + + store.clear(); + + assert.strictEqual(store.getComplete(uri), undefined); + }); + + test("returns sorted unique start lines", () => { + assert.deepStrictEqual(getInactiveRegionStartLines([secondRegion, firstRegion, secondRegion]), [2, 8]); + }); +}); From 333cfadab82727e550ec42efc81727875db047dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 12:54:41 -0700 Subject: [PATCH 6/8] Bump brace-expansion from 5.0.9 to 5.0.12 in /ExtensionPack (#14809) Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.9 to 5.0.12. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.9...v5.0.12) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 5.0.12 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 330bfe832..deb508073 100644 --- a/ExtensionPack/package-lock.json +++ b/ExtensionPack/package-lock.json @@ -777,9 +777,9 @@ "license": "BSD-2-Clause" }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha1-fHJDiAm1+lur9UGZofHCgaaYT88=", + "version": "5.0.12", + "resolved": "https://pkgs.dev.azure.com/azure-public/VisualCpp/_packaging/cpp_PublicPackages/npm/registry/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { From 32893ee9fb10f01629fa3007dfa7aea4e5d83a04 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 1 Oct 2026 16:26:51 -0700 Subject: [PATCH 7/8] Update changelog and version for 1.35.3 (#14813) --- Extension/CHANGELOG.md | 29 +++++++++++++++++++++++++++++ Extension/package.json | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 308197abe..b8c8b62db 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,34 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.35.3: October 2, 2026 +### New Features +* Add 'Fold All Inactive Regions' and 'Unfold All Inactive Regions' commands and the opt-in `C_Cpp.autoFoldInactiveRegions` setting. [#8992](https://github.com/microsoft/vscode-cpptools/issues/8992) +* Dim unreachable code when `editor.showUnused` is enabled. [#12902](https://github.com/microsoft/vscode-cpptools/issues/12902) +* Show C++ type size and alignment in hovers. [#13034](https://github.com/microsoft/vscode-cpptools/issues/13034) + +### Enhancements +* Increase the per-file IntelliSense diagnostic limit from 250 to 1,000, prioritize errors over unused-code hints, and reduce processing of over-limit diagnostics. +* Improve IntelliSense performance in large include graphs and classes with many inherited constructors. +* Reduce custom-configuration processing overhead and memory retained from `compile_commands.json`. +* Improve code folding performance in files with many inactive preprocessor regions. +* Improve loading performance for Windows-1255 and Windows-1258 source files. +* Reduce CPU usage when resolving headers in large Windows workspaces. + +### Bug Fixes +* Fix 'Go to Declaration' and 'Go to Definition' returning the current location instead of an available counterpart. [#14509](https://github.com/microsoft/vscode-cpptools/issues/14509) +* Update MIEngine to fix debugger failures with synthetic GDB stack frames. [#14741](https://github.com/microsoft/vscode-cpptools/issues/14741) + * Thanks for the contribution. [@tzcnt](https://github.com/tzcnt) [PR MIEngine#1605](https://github.com/microsoft/MIEngine/pull/1605) +* Fix incorrect IntelliSense errors for alias-template deduction guides using C++26 pack indexing. [#14794](https://github.com/microsoft/vscode-cpptools/issues/14794) +* Fix incorrect unused-parameter warnings and dimming for assignments through reference parameters. [#14801](https://github.com/microsoft/vscode-cpptools/issues/14801) +* Fix missing IntelliSense headers with Clang `-cxx-isystem` and `-stdlib++-isystem` options and split clang-cl `-imsvc` and `/imsvc` arguments. +* Fix IntelliSense crashes involving deduction guides, user-defined conversions, and lambdas in macros. +* Fix IntelliSense handling of GCC `--embed-directory=` and `--embed-directory ` arguments. +* Fix a tag parser stack-overflow crash when loading cyclic or excessively deep cached symbol data. +* Fix unwanted completion popups when typing incomplete floating-point exponents. +* Fix configuration-provider requests delaying language server shutdown. +* Various localization updates. +* Update dependencies. + ## Version 1.35.2: September 22, 2026 ### New Feature * Dim unused local variables and parameters when `editor.showUnused` is enabled. [#10490](https://github.com/microsoft/vscode-cpptools/issues/10490) diff --git a/Extension/package.json b/Extension/package.json index dcce0856c..60f53f7f0 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.35.2-main", + "version": "1.35.3-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md", From 5fcf27752fce13df2b535a7faf505f18b1ca4c27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:28:43 +0000 Subject: [PATCH 8/8] Bump the github-actions group with 2 updates (#14814) Bumps the github-actions group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.38.0 to 4.38.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2) Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.38.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sean McManus --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0dbfc4b8a..f89771818 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -91,7 +91,7 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 + uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: category: "/language:${{matrix.language}}"