indent guides: Fix tab handling (#12249)
Fixes indent guides when using tabs, Fixes: #12209, fixes #12210 Release Notes: - N/A
This commit is contained in:
parent
414f97f702
commit
af3d7a60c8
7 changed files with 236 additions and 133 deletions
|
@ -56,6 +56,7 @@ use std::ops::Add;
|
||||||
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||||
use sum_tree::{Bias, TreeMap};
|
use sum_tree::{Bias, TreeMap};
|
||||||
use tab_map::{TabMap, TabSnapshot};
|
use tab_map::{TabMap, TabSnapshot};
|
||||||
|
use text::LineIndent;
|
||||||
use ui::WindowContext;
|
use ui::WindowContext;
|
||||||
use wrap_map::{WrapMap, WrapSnapshot};
|
use wrap_map::{WrapMap, WrapSnapshot};
|
||||||
|
|
||||||
|
@ -843,7 +844,7 @@ impl DisplaySnapshot {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> (u32, bool) {
|
pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> LineIndent {
|
||||||
let (buffer, range) = self
|
let (buffer, range) = self
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.buffer_line_for_row(buffer_row)
|
.buffer_line_for_row(buffer_row)
|
||||||
|
@ -866,17 +867,16 @@ impl DisplaySnapshot {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (indent_size, is_blank) = self.line_indent_for_buffer_row(buffer_row);
|
let line_indent = self.line_indent_for_buffer_row(buffer_row);
|
||||||
if is_blank {
|
if line_indent.is_line_blank() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for next_row in (buffer_row.0 + 1)..=max_row.0 {
|
for next_row in (buffer_row.0 + 1)..=max_row.0 {
|
||||||
let (next_indent_size, next_line_is_blank) =
|
let next_line_indent = self.line_indent_for_buffer_row(MultiBufferRow(next_row));
|
||||||
self.line_indent_for_buffer_row(MultiBufferRow(next_row));
|
if next_line_indent.raw_len() > line_indent.raw_len() {
|
||||||
if next_indent_size > indent_size {
|
|
||||||
return true;
|
return true;
|
||||||
} else if !next_line_is_blank {
|
} else if !next_line_indent.is_line_blank() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,13 +900,15 @@ impl DisplaySnapshot {
|
||||||
} else if self.starts_indent(MultiBufferRow(start.row))
|
} else if self.starts_indent(MultiBufferRow(start.row))
|
||||||
&& !self.is_line_folded(MultiBufferRow(start.row))
|
&& !self.is_line_folded(MultiBufferRow(start.row))
|
||||||
{
|
{
|
||||||
let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
|
let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
|
||||||
let max_point = self.buffer_snapshot.max_point();
|
let max_point = self.buffer_snapshot.max_point();
|
||||||
let mut end = None;
|
let mut end = None;
|
||||||
|
|
||||||
for row in (buffer_row.0 + 1)..=max_point.row {
|
for row in (buffer_row.0 + 1)..=max_point.row {
|
||||||
let (indent, is_blank) = self.line_indent_for_buffer_row(MultiBufferRow(row));
|
let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
|
||||||
if !is_blank && indent <= start_indent {
|
if !line_indent.is_line_blank()
|
||||||
|
&& line_indent.raw_len() <= start_line_indent.raw_len()
|
||||||
|
{
|
||||||
let prev_row = row - 1;
|
let prev_row = row - 1;
|
||||||
end = Some(Point::new(
|
end = Some(Point::new(
|
||||||
prev_row,
|
prev_row,
|
||||||
|
|
|
@ -11523,7 +11523,7 @@ fn assert_indent_guides(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_single_line(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11543,7 +11543,7 @@ async fn test_indent_guides_single_line(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_simple_block(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_simple_block(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11564,7 +11564,7 @@ async fn test_indent_guides_simple_block(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_nested(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_nested(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11593,7 +11593,7 @@ async fn test_indent_guides_nested(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_tab(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_tab(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11618,7 +11618,7 @@ async fn test_indent_guides_tab(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_continues_on_empty_line(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11640,7 +11640,7 @@ async fn test_indent_guides_continues_on_empty_line(cx: &mut gpui::TestAppContex
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_complex(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_complex(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11672,7 +11672,7 @@ async fn test_indent_guides_complex(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_starts_off_screen(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_starts_off_screen(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11704,7 +11704,7 @@ async fn test_indent_guides_starts_off_screen(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_ends_off_screen(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_ends_off_screen(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11736,7 +11736,7 @@ async fn test_indent_guides_ends_off_screen(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_without_brackets(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_without_brackets(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
block1
|
block1
|
||||||
|
@ -11764,7 +11764,7 @@ async fn test_indent_guides_without_brackets(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_ends_before_empty_line(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
block1
|
block1
|
||||||
|
@ -11790,7 +11790,7 @@ async fn test_indent_guides_ends_before_empty_line(cx: &mut gpui::TestAppContext
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_indent_guides_continuing_off_screen(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_continuing_off_screen(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
block1
|
block1
|
||||||
|
@ -11813,7 +11813,34 @@ async fn test_indent_guides_continuing_off_screen(cx: &mut gpui::TestAppContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_active_indent_guides_single_line(cx: &mut gpui::TestAppContext) {
|
async fn test_indent_guide_tabs(cx: &mut gpui::TestAppContext) {
|
||||||
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
|
&"
|
||||||
|
def a:
|
||||||
|
\tb = 3
|
||||||
|
\tif True:
|
||||||
|
\t\tc = 4
|
||||||
|
\t\td = 5
|
||||||
|
\tprint(b)
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_indent_guides(
|
||||||
|
0..6,
|
||||||
|
vec![
|
||||||
|
IndentGuide::new(buffer_id, 1, 6, 0, 4),
|
||||||
|
IndentGuide::new(buffer_id, 3, 4, 1, 4),
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
&mut cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_active_indent_guide_single_line(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11839,7 +11866,7 @@ async fn test_active_indent_guides_single_line(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_active_indent_guides_respect_indented_range(cx: &mut gpui::TestAppContext) {
|
async fn test_active_indent_guide_respect_indented_range(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11902,7 +11929,7 @@ async fn test_active_indent_guides_respect_indented_range(cx: &mut gpui::TestApp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_active_indent_guides_empty_line(cx: &mut gpui::TestAppContext) {
|
async fn test_active_indent_guide_empty_line(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -11930,7 +11957,7 @@ async fn test_active_indent_guides_empty_line(cx: &mut gpui::TestAppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_active_indent_guides_non_matching_indent(cx: &mut gpui::TestAppContext) {
|
async fn test_active_indent_guide_non_matching_indent(cx: &mut gpui::TestAppContext) {
|
||||||
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
let (buffer_id, mut cx) = setup_indent_guides_editor(
|
||||||
&"
|
&"
|
||||||
def m:
|
def m:
|
||||||
|
|
|
@ -1417,9 +1417,9 @@ impl EditorElement {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, indent_guide)| {
|
.filter_map(|(i, indent_guide)| {
|
||||||
let indent_size = self.column_pixels(indent_guide.indent_size as usize, cx);
|
let single_indent_width =
|
||||||
let total_width = indent_size * px(indent_guide.depth as f32);
|
self.column_pixels(indent_guide.tab_size as usize, cx);
|
||||||
|
let total_width = single_indent_width * indent_guide.depth as f32;
|
||||||
let start_x = content_origin.x + total_width - scroll_pixel_position.x;
|
let start_x = content_origin.x + total_width - scroll_pixel_position.x;
|
||||||
if start_x >= text_origin.x {
|
if start_x >= text_origin.x {
|
||||||
let (offset_y, length) = Self::calculate_indent_guide_bounds(
|
let (offset_y, length) = Self::calculate_indent_guide_bounds(
|
||||||
|
@ -1433,7 +1433,7 @@ impl EditorElement {
|
||||||
Some(IndentGuideLayout {
|
Some(IndentGuideLayout {
|
||||||
origin: point(start_x, start_y),
|
origin: point(start_x, start_y),
|
||||||
length,
|
length,
|
||||||
indent_size,
|
single_indent_width,
|
||||||
depth: indent_guide.depth,
|
depth: indent_guide.depth,
|
||||||
active: active_indent_guide_indices.contains(&i),
|
active: active_indent_guide_indices.contains(&i),
|
||||||
})
|
})
|
||||||
|
@ -1467,6 +1467,11 @@ impl EditorElement {
|
||||||
let mut offset_y = row_range.start.0 as f32 * line_height;
|
let mut offset_y = row_range.start.0 as f32 * line_height;
|
||||||
let mut length = (cons_line.0.saturating_sub(row_range.start.0)) as f32 * line_height;
|
let mut length = (cons_line.0.saturating_sub(row_range.start.0)) as f32 * line_height;
|
||||||
|
|
||||||
|
// If we are at the end of the buffer, ensure that the indent guide extends to the end of the line.
|
||||||
|
if row_range.end == cons_line {
|
||||||
|
length += line_height;
|
||||||
|
}
|
||||||
|
|
||||||
// If there is a block (e.g. diagnostic) in between the start of the indent guide and the line above,
|
// If there is a block (e.g. diagnostic) in between the start of the indent guide and the line above,
|
||||||
// we want to extend the indent guide to the start of the block.
|
// we want to extend the indent guide to the start of the block.
|
||||||
let mut block_height = 0;
|
let mut block_height = 0;
|
||||||
|
@ -2637,7 +2642,7 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(color) = background_color {
|
if let Some(color) = background_color {
|
||||||
let width = indent_guide.indent_size - px(line_indicator_width);
|
let width = indent_guide.single_indent_width - px(line_indicator_width);
|
||||||
cx.paint_quad(fill(
|
cx.paint_quad(fill(
|
||||||
Bounds {
|
Bounds {
|
||||||
origin: point(
|
origin: point(
|
||||||
|
@ -5114,7 +5119,7 @@ fn layout_line(
|
||||||
pub struct IndentGuideLayout {
|
pub struct IndentGuideLayout {
|
||||||
origin: gpui::Point<Pixels>,
|
origin: gpui::Point<Pixels>,
|
||||||
length: Pixels,
|
length: Pixels,
|
||||||
indent_size: Pixels,
|
single_indent_width: Pixels,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
active: bool,
|
active: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use collections::HashSet;
|
||||||
use gpui::{AppContext, Task};
|
use gpui::{AppContext, Task};
|
||||||
use language::BufferRow;
|
use language::BufferRow;
|
||||||
use multi_buffer::{MultiBufferIndentGuide, MultiBufferRow};
|
use multi_buffer::{MultiBufferIndentGuide, MultiBufferRow};
|
||||||
use text::{BufferId, Point};
|
use text::{BufferId, LineIndent, Point};
|
||||||
use ui::ViewContext;
|
use ui::ViewContext;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use crate::{DisplaySnapshot, Editor};
|
||||||
struct ActiveIndentedRange {
|
struct ActiveIndentedRange {
|
||||||
buffer_id: BufferId,
|
buffer_id: BufferId,
|
||||||
row_range: Range<BufferRow>,
|
row_range: Range<BufferRow>,
|
||||||
indent: u32,
|
indent: LineIndent,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -112,7 +112,8 @@ impl Editor {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, indent_guide)| {
|
.filter(|(_, indent_guide)| {
|
||||||
indent_guide.buffer_id == active_indent_range.buffer_id
|
indent_guide.buffer_id == active_indent_range.buffer_id
|
||||||
&& indent_guide.indent_width() == active_indent_range.indent
|
&& indent_guide.indent_level()
|
||||||
|
== active_indent_range.indent.len(indent_guide.tab_size)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut matches = HashSet::default();
|
let mut matches = HashSet::default();
|
||||||
|
@ -189,19 +190,19 @@ fn should_recalculate_indented_range(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (old_indent, old_is_blank) = snapshot.line_indent_for_row(prev_row.0);
|
let old_line_indent = snapshot.line_indent_for_row(prev_row.0);
|
||||||
let (new_indent, new_is_blank) = snapshot.line_indent_for_row(new_row.0);
|
let new_line_indent = snapshot.line_indent_for_row(new_row.0);
|
||||||
|
|
||||||
if old_is_blank
|
if old_line_indent.is_line_empty()
|
||||||
|| new_is_blank
|
|| new_line_indent.is_line_empty()
|
||||||
|| old_indent != new_indent
|
|| old_line_indent != new_line_indent
|
||||||
|| snapshot.max_point().row == new_row.0
|
|| snapshot.max_point().row == new_row.0
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (next_line_indent, next_line_is_blank) = snapshot.line_indent_for_row(new_row.0 + 1);
|
let next_line_indent = snapshot.line_indent_for_row(new_row.0 + 1);
|
||||||
next_line_is_blank || next_line_indent != old_indent
|
next_line_indent.is_line_empty() || next_line_indent != old_line_indent
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -541,7 +541,7 @@ pub struct IndentGuide {
|
||||||
pub start_row: BufferRow,
|
pub start_row: BufferRow,
|
||||||
pub end_row: BufferRow,
|
pub end_row: BufferRow,
|
||||||
pub depth: u32,
|
pub depth: u32,
|
||||||
pub indent_size: u32,
|
pub tab_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndentGuide {
|
impl IndentGuide {
|
||||||
|
@ -550,19 +550,19 @@ impl IndentGuide {
|
||||||
start_row: BufferRow,
|
start_row: BufferRow,
|
||||||
end_row: BufferRow,
|
end_row: BufferRow,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
indent_size: u32,
|
tab_size: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer_id,
|
buffer_id,
|
||||||
start_row,
|
start_row,
|
||||||
end_row,
|
end_row,
|
||||||
depth,
|
depth,
|
||||||
indent_size,
|
tab_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent_width(&self) -> u32 {
|
pub fn indent_level(&self) -> u32 {
|
||||||
self.indent_size * self.depth
|
self.depth * self.tab_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3118,7 +3118,7 @@ impl BufferSnapshot {
|
||||||
range: Range<Anchor>,
|
range: Range<Anchor>,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> Vec<IndentGuide> {
|
) -> Vec<IndentGuide> {
|
||||||
fn indent_size_for_row(this: &BufferSnapshot, row: BufferRow, cx: &AppContext) -> u32 {
|
fn tab_size_for_row(this: &BufferSnapshot, row: BufferRow, cx: &AppContext) -> u32 {
|
||||||
let language = this.language_at(Point::new(row, 0));
|
let language = this.language_at(Point::new(row, 0));
|
||||||
language_settings(language, None, cx).tab_size.get() as u32
|
language_settings(language, None, cx).tab_size.get() as u32
|
||||||
}
|
}
|
||||||
|
@ -3133,19 +3133,19 @@ impl BufferSnapshot {
|
||||||
let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
|
let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
|
||||||
|
|
||||||
// TODO: This should be calculated for every row but it is pretty expensive
|
// TODO: This should be calculated for every row but it is pretty expensive
|
||||||
let indent_size = indent_size_for_row(self, start_row, cx);
|
let tab_size = tab_size_for_row(self, start_row, cx);
|
||||||
|
|
||||||
while let Some((first_row, mut line_indent, empty)) = row_indents.next() {
|
while let Some((first_row, mut line_indent)) = row_indents.next() {
|
||||||
let current_depth = indent_stack.len() as u32;
|
let current_depth = indent_stack.len() as u32;
|
||||||
|
|
||||||
// When encountering empty, continue until found useful line indent
|
// When encountering empty, continue until found useful line indent
|
||||||
// then add to the indent stack with the depth found
|
// then add to the indent stack with the depth found
|
||||||
let mut found_indent = false;
|
let mut found_indent = false;
|
||||||
let mut last_row = first_row;
|
let mut last_row = first_row;
|
||||||
if empty {
|
if line_indent.is_line_empty() {
|
||||||
let mut trailing_row = end_row;
|
let mut trailing_row = end_row;
|
||||||
while !found_indent {
|
while !found_indent {
|
||||||
let (target_row, new_line_indent, empty) =
|
let (target_row, new_line_indent) =
|
||||||
if let Some(display_row) = row_indents.next() {
|
if let Some(display_row) = row_indents.next() {
|
||||||
display_row
|
display_row
|
||||||
} else {
|
} else {
|
||||||
|
@ -3160,11 +3160,11 @@ impl BufferSnapshot {
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let (new_line_indent, empty) = self.line_indent_for_row(trailing_row);
|
let new_line_indent = self.line_indent_for_row(trailing_row);
|
||||||
(trailing_row, new_line_indent, empty)
|
(trailing_row, new_line_indent)
|
||||||
};
|
};
|
||||||
|
|
||||||
if empty {
|
if new_line_indent.is_line_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
last_row = target_row.min(end_row);
|
last_row = target_row.min(end_row);
|
||||||
|
@ -3177,7 +3177,8 @@ impl BufferSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
let depth = if found_indent {
|
let depth = if found_indent {
|
||||||
line_indent / indent_size + ((line_indent % indent_size) > 0) as u32
|
line_indent.len(tab_size) / tab_size
|
||||||
|
+ ((line_indent.len(tab_size) % tab_size) > 0) as u32
|
||||||
} else {
|
} else {
|
||||||
current_depth
|
current_depth
|
||||||
};
|
};
|
||||||
|
@ -3203,7 +3204,7 @@ impl BufferSnapshot {
|
||||||
start_row: first_row,
|
start_row: first_row,
|
||||||
end_row: last_row,
|
end_row: last_row,
|
||||||
depth: next_depth,
|
depth: next_depth,
|
||||||
indent_size,
|
tab_size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3221,20 +3222,22 @@ impl BufferSnapshot {
|
||||||
pub async fn enclosing_indent(
|
pub async fn enclosing_indent(
|
||||||
&self,
|
&self,
|
||||||
mut buffer_row: BufferRow,
|
mut buffer_row: BufferRow,
|
||||||
) -> Option<(Range<BufferRow>, u32)> {
|
) -> Option<(Range<BufferRow>, LineIndent)> {
|
||||||
let max_row = self.max_point().row;
|
let max_row = self.max_point().row;
|
||||||
if buffer_row >= max_row {
|
if buffer_row >= max_row {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut target_indent_size, is_blank) = self.line_indent_for_row(buffer_row);
|
let mut target_indent = self.line_indent_for_row(buffer_row);
|
||||||
|
|
||||||
// If the current row is at the start of an indented block, we want to return this
|
// If the current row is at the start of an indented block, we want to return this
|
||||||
// block as the enclosing indent.
|
// block as the enclosing indent.
|
||||||
if !is_blank && buffer_row < max_row {
|
if !target_indent.is_line_empty() && buffer_row < max_row {
|
||||||
let (next_line_indent, is_blank) = self.line_indent_for_row(buffer_row + 1);
|
let next_line_indent = self.line_indent_for_row(buffer_row + 1);
|
||||||
if !is_blank && target_indent_size < next_line_indent {
|
if !next_line_indent.is_line_empty()
|
||||||
target_indent_size = next_line_indent;
|
&& target_indent.raw_len() < next_line_indent.raw_len()
|
||||||
|
{
|
||||||
|
target_indent = next_line_indent;
|
||||||
buffer_row += 1;
|
buffer_row += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3246,12 +3249,12 @@ impl BufferSnapshot {
|
||||||
let mut accessed_row_counter = 0;
|
let mut accessed_row_counter = 0;
|
||||||
|
|
||||||
// If there is a blank line at the current row, search for the next non indented lines
|
// If there is a blank line at the current row, search for the next non indented lines
|
||||||
if is_blank {
|
if target_indent.is_line_empty() {
|
||||||
let start = buffer_row.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT);
|
let start = buffer_row.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT);
|
||||||
let end = (max_row + 1).min(buffer_row + SEARCH_WHITESPACE_ROW_LIMIT);
|
let end = (max_row + 1).min(buffer_row + SEARCH_WHITESPACE_ROW_LIMIT);
|
||||||
|
|
||||||
let mut non_empty_line_above = None;
|
let mut non_empty_line_above = None;
|
||||||
for (row, indent_size, is_blank) in self
|
for (row, indent) in self
|
||||||
.text
|
.text
|
||||||
.reversed_line_indents_in_row_range(start..buffer_row)
|
.reversed_line_indents_in_row_range(start..buffer_row)
|
||||||
{
|
{
|
||||||
|
@ -3260,30 +3263,28 @@ impl BufferSnapshot {
|
||||||
accessed_row_counter = 0;
|
accessed_row_counter = 0;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
if !is_blank {
|
if !indent.is_line_empty() {
|
||||||
non_empty_line_above = Some((row, indent_size));
|
non_empty_line_above = Some((row, indent));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut non_empty_line_below = None;
|
let mut non_empty_line_below = None;
|
||||||
for (row, indent_size, is_blank) in
|
for (row, indent) in self.text.line_indents_in_row_range((buffer_row + 1)..end) {
|
||||||
self.text.line_indents_in_row_range((buffer_row + 1)..end)
|
|
||||||
{
|
|
||||||
accessed_row_counter += 1;
|
accessed_row_counter += 1;
|
||||||
if accessed_row_counter == YIELD_INTERVAL {
|
if accessed_row_counter == YIELD_INTERVAL {
|
||||||
accessed_row_counter = 0;
|
accessed_row_counter = 0;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
if !is_blank {
|
if !indent.is_line_empty() {
|
||||||
non_empty_line_below = Some((row, indent_size));
|
non_empty_line_below = Some((row, indent));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (row, indent_size) = match (non_empty_line_above, non_empty_line_below) {
|
let (row, indent) = match (non_empty_line_above, non_empty_line_below) {
|
||||||
(Some((above_row, above_indent)), Some((below_row, below_indent))) => {
|
(Some((above_row, above_indent)), Some((below_row, below_indent))) => {
|
||||||
if above_indent >= below_indent {
|
if above_indent.raw_len() >= below_indent.raw_len() {
|
||||||
(above_row, above_indent)
|
(above_row, above_indent)
|
||||||
} else {
|
} else {
|
||||||
(below_row, below_indent)
|
(below_row, below_indent)
|
||||||
|
@ -3294,7 +3295,7 @@ impl BufferSnapshot {
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
target_indent_size = indent_size;
|
target_indent = indent;
|
||||||
buffer_row = row;
|
buffer_row = row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3302,7 +3303,7 @@ impl BufferSnapshot {
|
||||||
let end = (max_row + 1).min(buffer_row + SEARCH_ROW_LIMIT);
|
let end = (max_row + 1).min(buffer_row + SEARCH_ROW_LIMIT);
|
||||||
|
|
||||||
let mut start_indent = None;
|
let mut start_indent = None;
|
||||||
for (row, indent_size, is_blank) in self
|
for (row, indent) in self
|
||||||
.text
|
.text
|
||||||
.reversed_line_indents_in_row_range(start..buffer_row)
|
.reversed_line_indents_in_row_range(start..buffer_row)
|
||||||
{
|
{
|
||||||
|
@ -3311,36 +3312,38 @@ impl BufferSnapshot {
|
||||||
accessed_row_counter = 0;
|
accessed_row_counter = 0;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
if !is_blank && indent_size < target_indent_size {
|
if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
|
||||||
start_indent = Some((row, indent_size));
|
start_indent = Some((row, indent));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (start_row, start_indent_size) = start_indent?;
|
let (start_row, start_indent_size) = start_indent?;
|
||||||
|
|
||||||
let mut end_indent = (end, None);
|
let mut end_indent = (end, None);
|
||||||
for (row, indent_size, is_blank) in
|
for (row, indent) in self.text.line_indents_in_row_range((buffer_row + 1)..end) {
|
||||||
self.text.line_indents_in_row_range((buffer_row + 1)..end)
|
|
||||||
{
|
|
||||||
accessed_row_counter += 1;
|
accessed_row_counter += 1;
|
||||||
if accessed_row_counter == YIELD_INTERVAL {
|
if accessed_row_counter == YIELD_INTERVAL {
|
||||||
accessed_row_counter = 0;
|
accessed_row_counter = 0;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
if !is_blank && indent_size < target_indent_size {
|
if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
|
||||||
end_indent = (row.saturating_sub(1), Some(indent_size));
|
end_indent = (row.saturating_sub(1), Some(indent));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (end_row, end_indent_size) = end_indent;
|
let (end_row, end_indent_size) = end_indent;
|
||||||
|
|
||||||
let indent_size = if let Some(end_indent_size) = end_indent_size {
|
let indent = if let Some(end_indent_size) = end_indent_size {
|
||||||
start_indent_size.max(end_indent_size)
|
if start_indent_size.raw_len() > end_indent_size.raw_len() {
|
||||||
|
start_indent_size
|
||||||
|
} else {
|
||||||
|
end_indent_size
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
start_indent_size
|
start_indent_size
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((start_row..end_row, indent_size))
|
Some((start_row..end_row, indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns selections for remote peers intersecting the given range.
|
/// Returns selections for remote peers intersecting the given range.
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use text::network::Network;
|
use text::network::Network;
|
||||||
use text::{BufferId, LineEnding};
|
use text::{BufferId, LineEnding, LineIndent};
|
||||||
use text::{Point, ToPoint};
|
use text::{Point, ToPoint};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
|
use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
|
||||||
|
@ -2060,7 +2060,7 @@ async fn test_find_matching_indent(cx: &mut TestAppContext) {
|
||||||
text: impl Into<String>,
|
text: impl Into<String>,
|
||||||
buffer_row: u32,
|
buffer_row: u32,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> Option<(Range<u32>, u32)> {
|
) -> Option<(Range<u32>, LineIndent)> {
|
||||||
let buffer = cx.new_model(|cx| Buffer::local(text, cx));
|
let buffer = cx.new_model(|cx| Buffer::local(text, cx));
|
||||||
let snapshot = cx.read(|cx| buffer.read(cx).snapshot());
|
let snapshot = cx.read(|cx| buffer.read(cx).snapshot());
|
||||||
snapshot.enclosing_indent(buffer_row).await
|
snapshot.enclosing_indent(buffer_row).await
|
||||||
|
@ -2079,7 +2079,14 @@ async fn test_find_matching_indent(cx: &mut TestAppContext) {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
Some((1..2, 4))
|
Some((
|
||||||
|
1..2,
|
||||||
|
LineIndent {
|
||||||
|
tabs: 0,
|
||||||
|
spaces: 4,
|
||||||
|
line_blank: false,
|
||||||
|
}
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -2095,7 +2102,14 @@ async fn test_find_matching_indent(cx: &mut TestAppContext) {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
Some((1..2, 4))
|
Some((
|
||||||
|
1..2,
|
||||||
|
LineIndent {
|
||||||
|
tabs: 0,
|
||||||
|
spaces: 4,
|
||||||
|
line_blank: false,
|
||||||
|
}
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -2113,7 +2127,14 @@ async fn test_find_matching_indent(cx: &mut TestAppContext) {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
Some((1..4, 4))
|
Some((
|
||||||
|
1..4,
|
||||||
|
LineIndent {
|
||||||
|
tabs: 0,
|
||||||
|
spaces: 4,
|
||||||
|
line_blank: false,
|
||||||
|
}
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -516,6 +516,85 @@ pub struct UndoOperation {
|
||||||
pub counts: HashMap<clock::Lamport, u32>,
|
pub counts: HashMap<clock::Lamport, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores information about the indentation of a line (tabs and spaces).
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct LineIndent {
|
||||||
|
pub tabs: u32,
|
||||||
|
pub spaces: u32,
|
||||||
|
pub line_blank: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineIndent {
|
||||||
|
/// Constructs a new `LineIndent` which only contains spaces.
|
||||||
|
pub fn spaces(spaces: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
tabs: 0,
|
||||||
|
spaces,
|
||||||
|
line_blank: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new `LineIndent` which only contains tabs.
|
||||||
|
pub fn tabs(tabs: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
tabs,
|
||||||
|
spaces: 0,
|
||||||
|
line_blank: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates whether the line is empty.
|
||||||
|
pub fn is_line_empty(&self) -> bool {
|
||||||
|
self.tabs == 0 && self.spaces == 0 && self.line_blank
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates whether the line is blank (contains only whitespace).
|
||||||
|
pub fn is_line_blank(&self) -> bool {
|
||||||
|
self.line_blank
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of indentation characters (tabs or spaces).
|
||||||
|
pub fn raw_len(&self) -> u32 {
|
||||||
|
self.tabs + self.spaces
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of indentation characters (tabs or spaces), taking tab size into account.
|
||||||
|
pub fn len(&self, tab_size: u32) -> u32 {
|
||||||
|
self.tabs * tab_size + self.spaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for LineIndent {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self::from_iter(value.chars())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<char> for LineIndent {
|
||||||
|
fn from_iter<T: IntoIterator<Item = char>>(chars: T) -> Self {
|
||||||
|
let mut tabs = 0;
|
||||||
|
let mut spaces = 0;
|
||||||
|
let mut line_blank = true;
|
||||||
|
for c in chars {
|
||||||
|
if c == '\t' {
|
||||||
|
tabs += 1;
|
||||||
|
} else if c == ' ' {
|
||||||
|
spaces += 1;
|
||||||
|
} else {
|
||||||
|
if c != '\n' {
|
||||||
|
line_blank = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
tabs,
|
||||||
|
spaces,
|
||||||
|
line_blank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
pub fn new(replica_id: u16, remote_id: BufferId, mut base_text: String) -> Buffer {
|
pub fn new(replica_id: u16, remote_id: BufferId, mut base_text: String) -> Buffer {
|
||||||
let line_ending = LineEnding::detect(&base_text);
|
let line_ending = LineEnding::detect(&base_text);
|
||||||
|
@ -1868,7 +1947,7 @@ impl BufferSnapshot {
|
||||||
pub fn line_indents_in_row_range(
|
pub fn line_indents_in_row_range(
|
||||||
&self,
|
&self,
|
||||||
row_range: Range<u32>,
|
row_range: Range<u32>,
|
||||||
) -> impl Iterator<Item = (u32, u32, bool)> + '_ {
|
) -> impl Iterator<Item = (u32, LineIndent)> + '_ {
|
||||||
let start = Point::new(row_range.start, 0).to_offset(self);
|
let start = Point::new(row_range.start, 0).to_offset(self);
|
||||||
let end = Point::new(row_range.end, 0).to_offset(self);
|
let end = Point::new(row_range.end, 0).to_offset(self);
|
||||||
|
|
||||||
|
@ -1876,20 +1955,9 @@ impl BufferSnapshot {
|
||||||
let mut row = row_range.start;
|
let mut row = row_range.start;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if let Some(line) = lines.next() {
|
if let Some(line) = lines.next() {
|
||||||
let mut indent_size = 0;
|
let indent = LineIndent::from(line);
|
||||||
let mut is_blank = true;
|
|
||||||
|
|
||||||
for c in line.chars() {
|
|
||||||
is_blank = false;
|
|
||||||
if c == ' ' || c == '\t' {
|
|
||||||
indent_size += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row += 1;
|
row += 1;
|
||||||
Some((row - 1, indent_size, is_blank))
|
Some((row - 1, indent))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1899,7 +1967,7 @@ impl BufferSnapshot {
|
||||||
pub fn reversed_line_indents_in_row_range(
|
pub fn reversed_line_indents_in_row_range(
|
||||||
&self,
|
&self,
|
||||||
row_range: Range<u32>,
|
row_range: Range<u32>,
|
||||||
) -> impl Iterator<Item = (u32, u32, bool)> + '_ {
|
) -> impl Iterator<Item = (u32, LineIndent)> + '_ {
|
||||||
let start = Point::new(row_range.start, 0).to_offset(self);
|
let start = Point::new(row_range.start, 0).to_offset(self);
|
||||||
let end = Point::new(row_range.end, 0)
|
let end = Point::new(row_range.end, 0)
|
||||||
.to_offset(self)
|
.to_offset(self)
|
||||||
|
@ -1909,41 +1977,17 @@ impl BufferSnapshot {
|
||||||
let mut row = row_range.end;
|
let mut row = row_range.end;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
if let Some(line) = lines.next() {
|
if let Some(line) = lines.next() {
|
||||||
let mut indent_size = 0;
|
let indent = LineIndent::from(line);
|
||||||
let mut is_blank = true;
|
|
||||||
|
|
||||||
for c in line.chars() {
|
|
||||||
is_blank = false;
|
|
||||||
if c == ' ' || c == '\t' {
|
|
||||||
indent_size += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row = row.saturating_sub(1);
|
row = row.saturating_sub(1);
|
||||||
Some((row, indent_size, is_blank))
|
Some((row, indent))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_indent_for_row(&self, row: u32) -> (u32, bool) {
|
pub fn line_indent_for_row(&self, row: u32) -> LineIndent {
|
||||||
let mut indent_size = 0;
|
LineIndent::from_iter(self.chars_at(Point::new(row, 0)))
|
||||||
let mut is_blank = false;
|
|
||||||
for c in self.chars_at(Point::new(row, 0)) {
|
|
||||||
if c == ' ' || c == '\t' {
|
|
||||||
indent_size += 1;
|
|
||||||
} else {
|
|
||||||
if c == '\n' {
|
|
||||||
is_blank = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(indent_size, is_blank)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_line_blank(&self, row: u32) -> bool {
|
pub fn is_line_blank(&self, row: u32) -> bool {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue