Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22533 from Veykril/push-yuxpknzzoryu
internal: Remove AST types from FoldKind
Lukas Wirth 6 weeks ago
parent 09b3315 · parent fbd7af8 · commit 12e9a32
-rw-r--r--crates/ide/src/folding_ranges.rs126
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs4
-rw-r--r--crates/test-utils/src/fixture.rs10
3 files changed, 78 insertions, 62 deletions
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 965190b27d..ae006d152a 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -13,7 +13,7 @@ use std::hash::Hash;
const REGION_START: &str = "// region:";
const REGION_END: &str = "// endregion";
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FoldKind {
Comment,
Imports,
@@ -32,8 +32,8 @@ pub enum FoldKind {
TypeAliases,
ExternCrates,
// endregion: item runs
- Stmt(ast::Stmt),
- TailExpr(ast::Expr),
+ Stmt,
+ TailExpr,
}
#[derive(Debug)]
@@ -68,7 +68,7 @@ pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec
for element in file.syntax().descendants_with_tokens() {
// Fold items that span multiple lines
- if let Some(kind) = fold_kind(element.clone()) {
+ if let Some((kind, collapsed_text)) = fold_kind(element.clone(), add_collapsed_text) {
let is_multiline = match &element {
NodeOrToken::Node(node) => node.text().contains_char('\n'),
NodeOrToken::Token(token) => token.text().contains('\n'),
@@ -100,7 +100,6 @@ pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec
}
}
- let collapsed_text = if add_collapsed_text { collapsed_text(&kind) } else { None };
let fold = Fold::new(element.text_range(), kind).with_text(collapsed_text);
res.push(fold);
continue;
@@ -183,55 +182,10 @@ pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec
res
}
-fn collapsed_text(kind: &FoldKind) -> Option<String> {
- match kind {
- FoldKind::TailExpr(expr) => collapse_expr(expr.clone()),
- FoldKind::Stmt(stmt) => {
- match stmt {
- ast::Stmt::ExprStmt(expr_stmt) => {
- expr_stmt.expr().and_then(collapse_expr).map(|text| format!("{text};"))
- }
- ast::Stmt::LetStmt(let_stmt) => 'blk: {
- if let_stmt.let_else().is_some() {
- break 'blk None;
- }
-
- let Some(expr) = let_stmt.initializer() else {
- break 'blk None;
- };
-
- // If the `let` statement spans multiple lines, we do not collapse it.
- // We use the `eq_token` to check whether the `let` statement is a single line,
- // as the formatter may place the initializer on a new line for better readability.
- //
- // Example:
- // ```rust
- // let complex_pat =
- // complex_expr;
- // ```
- //
- // In this case, we should generate the collapsed text.
- let Some(eq_token) = let_stmt.eq_token() else {
- break 'blk None;
- };
- let eq_token_offset =
- eq_token.text_range().end() - let_stmt.syntax().text_range().start();
- let text_until_eq_token = let_stmt.syntax().text().slice(..eq_token_offset);
- if text_until_eq_token.contains_char('\n') {
- break 'blk None;
- }
-
- collapse_expr(expr).map(|text| format!("{text_until_eq_token} {text};"))
- }
- // handling `items` in external matches.
- ast::Stmt::Item(_) => None,
- }
- }
- _ => None,
- }
-}
-
-fn fold_kind(element: SyntaxElement) -> Option<FoldKind> {
+fn fold_kind(
+ element: SyntaxElement,
+ add_collapsed_text: bool,
+) -> Option<(FoldKind, Option<String>)> {
// handle tail_expr
if let Some(node) = element.as_node()
// tail_expr -> stmt_list -> block
@@ -239,7 +193,10 @@ fn fold_kind(element: SyntaxElement) -> Option<FoldKind> {
&& let Some(tail_expr) = block.tail_expr()
&& tail_expr.syntax() == node
{
- return Some(FoldKind::TailExpr(tail_expr));
+ return Some((
+ FoldKind::TailExpr,
+ add_collapsed_text.then(|| collapse_expr(tail_expr)).flatten(),
+ ));
}
match element.kind() {
@@ -260,14 +217,63 @@ fn fold_kind(element: SyntaxElement) -> Option<FoldKind> {
| MATCH_ARM_LIST
| VARIANT_LIST
| TOKEN_TREE => Some(FoldKind::Block),
- EXPR_STMT | LET_STMT => Some(FoldKind::Stmt(ast::Stmt::cast(element.as_node()?.clone())?)),
+ EXPR_STMT | LET_STMT => {
+ return Some((
+ FoldKind::Stmt,
+ add_collapsed_text
+ .then(|| collapsed_stmt(ast::Stmt::cast(element.as_node()?.clone())?))
+ .flatten(),
+ ));
+ }
_ => None,
}
+ .zip(Some(None))
}
-const COLLAPSE_EXPR_MAX_LEN: usize = 100;
+fn collapsed_stmt(stmt: ast::Stmt) -> Option<String> {
+ match stmt {
+ ast::Stmt::ExprStmt(expr_stmt) => {
+ expr_stmt.expr().and_then(collapse_expr).map(|text| format!("{text};"))
+ }
+ ast::Stmt::LetStmt(let_stmt) => 'blk: {
+ if let_stmt.let_else().is_some() {
+ break 'blk None;
+ }
+
+ let Some(expr) = let_stmt.initializer() else {
+ break 'blk None;
+ };
+
+ // If the `let` statement spans multiple lines, we do not collapse it.
+ // We use the `eq_token` to check whether the `let` statement is a single line,
+ // as the formatter may place the initializer on a new line for better readability.
+ //
+ // Example:
+ // ```rust
+ // let complex_pat =
+ // complex_expr;
+ // ```
+ //
+ // In this case, we should generate the collapsed text.
+ let Some(eq_token) = let_stmt.eq_token() else {
+ break 'blk None;
+ };
+ let eq_token_offset =
+ eq_token.text_range().end() - let_stmt.syntax().text_range().start();
+ let text_until_eq_token = let_stmt.syntax().text().slice(..eq_token_offset);
+ if text_until_eq_token.contains_char('\n') {
+ break 'blk None;
+ }
+
+ collapse_expr(expr).map(|text| format!("{text_until_eq_token} {text};"))
+ }
+ // handling `items` in external matches.
+ ast::Stmt::Item(_) => None,
+ }
+}
fn collapse_expr(expr: ast::Expr) -> Option<String> {
+ const COLLAPSE_EXPR_MAX_LEN: usize = 100;
let mut text = String::with_capacity(COLLAPSE_EXPR_MAX_LEN * 2);
let mut preorder = expr.syntax().preorder_with_tokens();
@@ -435,7 +441,7 @@ fn contiguous_range_for_comment(
}
fn fold_range_for_multiline_match_arm(match_arm: ast::MatchArm) -> Option<TextRange> {
- if fold_kind(match_arm.expr()?.syntax().syntax_element()).is_some() {
+ if fold_kind(match_arm.expr()?.syntax().syntax_element(), false).is_some() {
None
} else if match_arm.expr()?.syntax().text().contains_char('\n') {
Some(match_arm.expr()?.syntax().text_range())
@@ -507,8 +513,8 @@ mod tests {
FoldKind::MatchArm => "matcharm",
FoldKind::Function => "function",
FoldKind::ExternCrates => "externcrates",
- FoldKind::Stmt(_) => "stmt",
- FoldKind::TailExpr(_) => "tailexpr",
+ FoldKind::Stmt => "stmt",
+ FoldKind::TailExpr => "tailexpr",
};
assert_eq!(kind, &attr.unwrap());
if enable_collapsed_text {
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index eff5477969..3e8de073a3 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -934,8 +934,8 @@ pub(crate) fn folding_range(
| FoldKind::ExternCrates
| FoldKind::MatchArm
| FoldKind::Function
- | FoldKind::Stmt(_)
- | FoldKind::TailExpr(_) => None,
+ | FoldKind::Stmt
+ | FoldKind::TailExpr => None,
};
let range = range(line_index, text_range);
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index 730e9aeb52..64a0d23e6e 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -399,6 +399,7 @@ impl MiniCore {
///
/// This is probably over-engineered to support flags dependencies.
pub fn source_code(mut self, raw_source: &str) -> String {
+ let raw_source = raw_source.replace("\r\n", "\n").replace('\r', "\n");
let mut buf = String::new();
let mut lines = raw_source.split_inclusive('\n');
@@ -580,3 +581,12 @@ mod m;
assert_eq!("/lib.rs", meta.path);
assert_eq!(2, meta.env.len());
}
+
+#[test]
+fn minicore_source_code_normalizes_line_endings() {
+ let source = MiniCore::RAW_SOURCE.replace("\r\n", "\n").replace('\r', "\n");
+ let source = source.replace('\n', "\r\n");
+ let source = MiniCore::from_flags(["option"]).source_code(&source);
+
+ assert!(!source.contains('\r'));
+}