feat: Introduce FoldingPreferences foundation and 'includeClosures' compatibility layer#298676
feat: Introduce FoldingPreferences foundation and 'includeClosures' compatibility layer#298676n-gist wants to merge 6 commits intomicrosoft:mainfrom
Conversation
|
It's already possible for an extension to implement custom folding providers such as a folding provider that also includes the line of the closing We have currently no plans to invest in the built-in indentation based folding support. |
|
It’s true that extensions can implement custom folding providers. However, in practice this approach has limitations. Better Folding appears to be unmaintained and currently has open issues preventing it from working reliably in recent versions of VS Code. There are also other extensions (e.g. Explicit Folding) that attempt to solve similar problems using pattern-based approaches. The broader issue is that these extensions replace the language’s native folding provider and implement their own region detection logic. This typically means:
As a result, while such extensions can help in specific scenarios, they cannot fully preserve syntax-aware folding behavior across languages and edge cases. The goal of this PR is different: it does not aim to replace folding providers, but to provide a structured mechanism for providers to become aware of user preferences, while preserving native syntax-aware folding. The compatibility layer exists only as a fallback when providers do not declare native support. At a higher level, this PR introduces a structured flow for preference-aware folding:
|
|
Update: after testing, it appears that the compatibility layer’s |
This change lays the groundwork for making folding providers aware of user-defined folding preferences.
Closes #3352 and redirects future related issues to language folding providers or to the
includeClosurescompatibility layer.Clarifies responsibility for issues related to folding behavior.
Changes
editor.foldingPreferencesoptionAdds a structured API for specifying user folding preferences.
FoldingPreferencesCapabilitiesinterfaceAllows folding providers to indicate which preferences they natively support.
RangeProviderupdateExtends the interface to include the new
capabilitiesproperty.IndentRangeProviderandSyntaxRangeProviderupdatesAdds placeholder
capabilitiesobjects to conform to the updated interface.FoldingPreferencesCompatibilitylayerImplements a compatibility layer to adjust folding behavior for preferences not natively supported by providers.
FoldingControllerintegrationInjects the compatibility layer into folding model computation and updates.
CompatibilityAdjusterIncludeClosuresAdds initial support for the
includeClosurespreference in the compatibility pipeline.Backward and future compatibility
editor.foldingPreferencesis explicitly set.Reasons
tl;dr
Initially, this change aimed to solve #3352. The plan was to add a single setting and implement support for it directly in VS Code built-in language folding providers. CSS was chosen first due to its simple syntax, with blocks always defined by `{ }` braces. However, this immediately became a roadblock, as its implementation resides in a separate repository and would require introducing an agreed-upon API in the https://github.com/microsoft/vscode-css-languageservice.git.?target=https://github.com While possible in the future, it is not a good starting point for changes.
The next idea was to develop a post-processing layer applied on top of incoming folding regions as a temporary patch for a whitelist of languages whose folding region implementation is suitable for this. This was done, and the plan was to iterate over languages and popular extensions to define the whitelist. The idea was to keep a language on the list until its provider adopted the user setting. However, the list would be large and permanent, since the likelihood of all existing providers supporting the preference is zero. This also adds maintenance costs, as any addition of native support by providers would require removal from the whitelist.
Switching to a blacklist approach, starting from an empty list and adding languages as their folding providers evolve, is a better strategy. But this blacklist should track extensions rather than languages. Additionally, new preferences may be added later. Maintaining separate lists for each preference is not viable. The original plan aimed to safeguard users from incorrect folding adjustments, but it relied on maintaining extensive language or extension lists, which proved to be an overengineered solution. A cleaner approach is to have folding providers be aware of user preferences and indicate which preferences they support. When a provider declares support for a specific preference, the compatibility patch should not be applied, even if the preference is set.
The correct approach seems to be the following:
The responsibility for enabling preference-related compatibility adjustments for providers that lack native support ultimately lies with the user.
No predefined language lists or unnecessary maintenance costs are needed, and such preferences should be marked with
*in their descriptions.Ultimately, the effect is intentionally activated by the user, and if it produces an undesired result, the user can:
a) Not use the preference for the given language.
b) Adapt the code layout.
c) Request native support for the preference from language extension developers, provided VS Code offers an API for that.
After these conclusions, the idea crystallized into the architectural modifications presented here. This explains why the original issue persisted so long despite appearing simple to fix.
includeClosurespreference, or why folding provider outputs actually need to be improvedConsider some simple cases and how different providers handle inclusion of closure delimiters in folding regions, comparing
cssvshtml(CSS closures are}; HTML closures are tags like</div>):examples
This is perfectly consistent.
.noregion2 { }Consistent — does not produce folding regions. However:
CSS produces a folding region, while HTML does not — inconsistent across languages, but more significantly:
vs.
This is inconsistent even within CSS regarding inclusion of the closure delimiter.
This example may represent poor code layout, and it is unclear whether CSS handles this intentionally. However, it indicates that user preference can be meaningful. Current behavior can be mapped to
autoand exists for a reason, but the user may want to always include or always exclude the closure from regions.The current
includeClosurescompatibility implementation is simple. It adds one line to a region if doing so does not overlap another region's start. Clearly, it does not handle all cases correctly (e.g. case3). However, the compatibility layer cannot provide perfect behavior for all languages and providers; correct behavior is achievable only through native support.What's next
If this change is confirmed as viable, I would like to try to implement native support for the
includeClosurespreference inIndentRangeProviderand explore adding it to the TypeScript provider. This would allow folding providers to access user preferences directly and to declare which preferences they natively support.