From fae1cc36d6ca7930ebb0f79e7ecfd17284de835a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 28 Jun 2022 12:21:54 -0700 Subject: [PATCH] In most languages, use prev non-empty line as basis for preserving indent --- crates/language/src/buffer.rs | 24 ++++++++++++--------- crates/zed/src/languages/rust.rs | 37 ++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 0c94cfdf9f..0aac59db43 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1583,7 +1583,7 @@ impl BufferSnapshot { ..Point::new(row_range.end, 0).to_ts_point(), ); - let mut indentation_ranges = Vec::>::new(); + let mut indent_ranges = Vec::>::new(); for mat in query_cursor.matches( indents_query, self.tree.as_ref()?.root_node(), @@ -1606,10 +1606,10 @@ impl BufferSnapshot { } let range = start..end; - match indentation_ranges.binary_search_by_key(&range.start, |r| r.start) { - Err(ix) => indentation_ranges.insert(ix, range), + match indent_ranges.binary_search_by_key(&range.start, |r| r.start) { + Err(ix) => indent_ranges.insert(ix, range), Ok(ix) => { - let prev_range = &mut indentation_ranges[ix]; + let prev_range = &mut indent_ranges[ix]; prev_range.end = prev_range.end.max(range.end); } } @@ -1617,7 +1617,7 @@ impl BufferSnapshot { } // Find the suggested indentation increases and decreased based on regexes. - let mut indent_changes = Vec::<(u32, Ordering)>::new(); + let mut indent_change_rows = Vec::<(u32, Ordering)>::new(); self.for_each_line( Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0) ..Point::new(row_range.end, 0), @@ -1627,20 +1627,24 @@ impl BufferSnapshot { .as_ref() .map_or(false, |regex| regex.is_match(line)) { - indent_changes.push((row, Ordering::Less)); + indent_change_rows.push((row, Ordering::Less)); } if config .increase_indent_pattern .as_ref() .map_or(false, |regex| regex.is_match(line)) { - indent_changes.push((row + 1, Ordering::Greater)); + indent_change_rows.push((row + 1, Ordering::Greater)); } }, ); - let mut indent_changes = indent_changes.into_iter().peekable(); - let mut prev_row = row_range.start.saturating_sub(1); + let mut indent_changes = indent_change_rows.into_iter().peekable(); + let mut prev_row = if config.auto_indent_using_last_non_empty_line { + prev_non_blank_row.unwrap_or(0) + } else { + row_range.start.saturating_sub(1) + }; let mut prev_row_start = Point::new(prev_row, self.indent_size_for_line(prev_row).len); Some(row_range.map(move |row| { let row_start = Point::new(row, self.indent_size_for_line(row).len); @@ -1662,7 +1666,7 @@ impl BufferSnapshot { indent_changes.next(); } - for range in &indentation_ranges { + for range in &indent_ranges { if range.start.row >= row { break; } diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 4a83cccc3b..97c1901a85 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -442,31 +442,56 @@ mod tests { let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); let size = IndentSize::spaces(2); - // start with empty function - buffer.edit_with_autoindent([(0..0, "fn a() {}")], size, cx); - // indent between braces + buffer.set_text("fn a() {}", cx); let ix = buffer.len() - 1; buffer.edit_with_autoindent([(ix..ix, "\n\n")], size, cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); - // indent field expression + // indent between braces, even after empty lines + buffer.set_text("fn a() {\n\n\n}", cx); + let ix = buffer.len() - 2; + buffer.edit_with_autoindent([(ix..ix, "\n")], size, cx); + assert_eq!(buffer.text(), "fn a() {\n\n\n \n}"); + + // indent a line that continues a field expression + buffer.set_text("fn a() {\n \n}", cx); let ix = buffer.len() - 2; buffer.edit_with_autoindent([(ix..ix, "b\n.c")], size, cx); assert_eq!(buffer.text(), "fn a() {\n b\n .c\n}"); - // indent chained field expression preceded by blank line + // indent further lines that continue the field expression, even after empty lines let ix = buffer.len() - 2; buffer.edit_with_autoindent([(ix..ix, "\n\n.d")], size, cx); assert_eq!(buffer.text(), "fn a() {\n b\n .c\n \n .d\n}"); - // dedent line after the field expression + // dedent the line after the field expression let ix = buffer.len() - 2; buffer.edit_with_autoindent([(ix..ix, ";\ne")], size, cx); assert_eq!( buffer.text(), "fn a() {\n b\n .c\n \n .d;\n e\n}" ); + + // indent inside a struct within a call + buffer.set_text("const a: B = c(D {});", cx); + let ix = buffer.len() - 3; + buffer.edit_with_autoindent([(ix..ix, "\n\n")], size, cx); + assert_eq!(buffer.text(), "const a: B = c(D {\n \n});"); + + // indent further inside a nested call + let ix = buffer.len() - 4; + buffer.edit_with_autoindent([(ix..ix, "e: f(\n\n)")], size, cx); + assert_eq!(buffer.text(), "const a: B = c(D {\n e: f(\n \n )\n});"); + + // keep that indent after an empty line + let ix = buffer.len() - 8; + buffer.edit_with_autoindent([(ix..ix, "\n")], size, cx); + assert_eq!( + buffer.text(), + "const a: B = c(D {\n e: f(\n \n \n )\n});" + ); + buffer }); }