editor: Fix folding for unindented multiline strings and comments (#50049)
Closes #5057 ## Summary The indent-based code fold detection does not account for lines inside multiline strings or block comments that have less indentation than the surrounding code. This causes the fold scanner to think the enclosing block ends prematurely. For example, folding `fn main()` here would fail because the raw string content at indent level 0 gets treated as the end of the function body: ``` rust fn main() { let s = r#" unindented content "#; } ``` This PR checks whether a low-indent line falls inside a string or comment override region and skips it if so. This works across all languages that define `@string` or `@comment` overrides in their `overrides.scm`. ## Before https://github.com/user-attachments/assets/a08e6bf8-4f25-4211-8a46-8f6da7e49247 ## After https://github.com/user-attachments/assets/cd5b36db-6d4d-420b-9d60-79f9fad8638e ## Test Plan - Added `test_fold_with_unindented_multiline_raw_string` - Added `test_fold_with_unindented_multiline_block_comment` - All existing fold tests pass - Manually tested both Rust and Python examples Release Notes: - Fixed code folding incorrectly collapsing when multiline strings or block comments contained unindented content
This commit is contained in:
1 parent
8f8dfab769
commit
85af3c7e43
2 files changed
+114
-1
No files matched your search
@@ -2320,6 +2320,19 @@ impl DisplaySnapshot {
|
||||
if !line_indent.is_line_blank()
|
||||
&& line_indent.raw_len() <= start_line_indent.raw_len()
|
||||
{
|
||||
if self
|
||||
.buffer_snapshot()
|
||||
.language_scope_at(Point::new(row, 0))
|
||||
.is_some_and(|scope| {
|
||||
matches!(
|
||||
scope.override_name(),
|
||||
Some("string") | Some("comment") | Some("comment.inclusive")
|
||||
)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let prev_row = row - 1;
|
||||
end = Some(Point::new(
|
||||
prev_row,
|
||||
|
||||
@@ -26,7 +26,7 @@ use language::{
|
||||
BracketPairConfig,
|
||||
Capability::ReadWrite,
|
||||
DiagnosticSourceKind, FakeLspAdapter, IndentGuideSettings, LanguageConfig,
|
||||
LanguageConfigOverride, LanguageMatcher, LanguageName, Override, Point,
|
||||
LanguageConfigOverride, LanguageMatcher, LanguageName, LanguageQueries, Override, Point,
|
||||
language_settings::{
|
||||
CompletionSettingsContent, FormatterList, LanguageSettingsContent, LspInsertMode,
|
||||
},
|
||||
@@ -51,6 +51,7 @@ use settings::{
|
||||
IndentGuideBackgroundColoring, IndentGuideColoring, InlayHintSettingsContent,
|
||||
ProjectSettingsContent, SearchSettingsContent, SettingsContent, SettingsStore,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
|
||||
use std::{
|
||||
iter,
|
||||
@@ -1324,6 +1325,105 @@ fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_fold_with_unindented_multiline_raw_string(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig::default(),
|
||||
Some(tree_sitter_rust::LANGUAGE.into()),
|
||||
)
|
||||
.with_queries(LanguageQueries {
|
||||
overrides: Some(Cow::from(indoc! {"
|
||||
[
|
||||
(string_literal)
|
||||
(raw_string_literal)
|
||||
] @string
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment.inclusive
|
||||
"})),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("Could not parse queries"),
|
||||
);
|
||||
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
fn main() {
|
||||
let s = r#\"
|
||||
a
|
||||
b
|
||||
c
|
||||
\"#;
|
||||
}ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.fold_at_level(&FoldAtLevel(1), window, cx);
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
indoc! {"
|
||||
fn main() {⋯
|
||||
}
|
||||
"},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_fold_with_unindented_multiline_block_comment(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig::default(),
|
||||
Some(tree_sitter_rust::LANGUAGE.into()),
|
||||
)
|
||||
.with_queries(LanguageQueries {
|
||||
overrides: Some(Cow::from(indoc! {"
|
||||
[
|
||||
(string_literal)
|
||||
(raw_string_literal)
|
||||
] @string
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment.inclusive
|
||||
"})),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("Could not parse queries"),
|
||||
);
|
||||
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
fn main() {
|
||||
let x = 1;
|
||||
/*
|
||||
unindented comment line
|
||||
*/
|
||||
}ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.fold_at_level(&FoldAtLevel(1), window, cx);
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
indoc! {"
|
||||
fn main() {⋯
|
||||
}
|
||||
"},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_fold_at_level(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in new issue
Block a user