Environment
- Even Better TOML (VS Code) v0.21.2, bundled Taplo LSP
- Reproduced against crates/taplo-lsp/src/handlers/folding_ranges.rs on master
Description
The folding-range builder decides whether table B is nested inside table A using a raw string prefix check on the dotted keys. This is intended to nest dotted children ([server] → [server.http]), but it also matches sibling tables that merely share a textual prefix, because there's no key-segment/. boundary in the test.
As a result, when you fold-all (Ctrl+K Ctrl+0), the sibling table's header is folded inside the shorter-named table and becomes invisible.
Minimal reproduction (repro.toml)
`[fruit]
color = "red"
[fruit_juice]
sugar = true`
Fold all. [fruit_juice] disappears — it is folded into [fruit]'s range. Renaming fruit_juice to e.g. juice (not prefixed by fruit) makes it behave correctly. The same happens with arrays of tables ([[a.well]] vs [[a.well_log]]).
Root cause
In create_folding_ranges (folding_ranges.rs), on each new header:
if k == &key || !key.starts_with(k) { // close k's fold false } else { // keep k open → nest the new header inside k true }
"fruit_juice".starts_with("fruit") is true, so fruit_juice is treated as a child of fruit.
Suggested fix
Only treat key as a descendant of k when k is followed by a key separator, i.e. require a . boundary:
`let is_descendant = key.len() > k.len()
&& key.starts_with(k)
&& key.as_bytes()[k.len()] == b'.';
if k == &key || !is_descendant {
// close k's fold
false
} else {
true
}`
(A more robust variant would compare parsed key segments rather than raw text, which would also handle quoted keys and dotted whitespace like a . b.)
Environment
Description
The folding-range builder decides whether table B is nested inside table A using a raw string prefix check on the dotted keys. This is intended to nest dotted children ([server] → [server.http]), but it also matches sibling tables that merely share a textual prefix, because there's no key-segment/. boundary in the test.
As a result, when you fold-all (Ctrl+K Ctrl+0), the sibling table's header is folded inside the shorter-named table and becomes invisible.
Minimal reproduction (repro.toml)
`[fruit]
color = "red"
[fruit_juice]
sugar = true`
Fold all. [fruit_juice] disappears — it is folded into [fruit]'s range. Renaming fruit_juice to e.g. juice (not prefixed by fruit) makes it behave correctly. The same happens with arrays of tables ([[a.well]] vs [[a.well_log]]).
Root cause
In create_folding_ranges (folding_ranges.rs), on each new header:
if k == &key || !key.starts_with(k) { // close k's fold false } else { // keep k open → nest the new header inside k true }"fruit_juice".starts_with("fruit") is true, so fruit_juice is treated as a child of fruit.
Suggested fix
Only treat key as a descendant of k when k is followed by a key separator, i.e. require a . boundary:
`let is_descendant = key.len() > k.len()
&& key.starts_with(k)
&& key.as_bytes()[k.len()] == b'.';
if k == &key || !is_descendant {
// close k's fold
false
} else {
true
}`
(A more robust variant would compare parsed key segments rather than raw text, which would also handle quoted keys and dotted whitespace like a . b.)