Introduce DisplayRow, MultiBufferRow newtypes and BufferRow type alias (#11656)

Part of https://github.com/zed-industries/zed/issues/8081

To avoid confusion and bugs when converting between various row `u32`'s,
use different types for each.
Further PRs should split `Point` into buffer and multi buffer variants
and make the code more readable.

Release Notes:

- N/A

---------

Co-authored-by: Piotr <piotr@zed.dev>
This commit is contained in:
Kirill Bulatov 2024-05-11 00:06:51 +03:00 committed by GitHub
parent 38f110852f
commit df41435d1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 1726 additions and 1220 deletions

3
Cargo.lock generated
View File

@ -2325,6 +2325,7 @@ dependencies = [
"log",
"lsp",
"menu",
"multi_buffer",
"nanoid",
"node_runtime",
"notifications",
@ -6282,6 +6283,7 @@ dependencies = [
"log",
"parking_lot",
"rand 0.8.5",
"serde",
"settings",
"smallvec",
"sum_tree",
@ -11315,6 +11317,7 @@ dependencies = [
"language",
"log",
"lsp",
"multi_buffer",
"nvim-rs",
"parking_lot",
"regex",

View File

@ -18,7 +18,7 @@ use editor::{
},
scroll::{Autoscroll, AutoscrollStrategy},
Anchor, Editor, EditorElement, EditorEvent, EditorStyle, MultiBuffer, MultiBufferSnapshot,
ToOffset as _, ToPoint,
RowExt, ToOffset as _, ToPoint,
};
use file_icons::FileIcons;
use fs::Fs;
@ -32,6 +32,7 @@ use gpui::{
View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext,
};
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
use multi_buffer::MultiBufferRow;
use parking_lot::Mutex;
use project::Project;
use search::{buffer_search::DivRegistrar, BufferSearchBar};
@ -306,7 +307,7 @@ impl AssistantPanel {
if point_selection.end.column == 0 {
point_selection.end.row -= 1;
}
point_selection.end.column = snapshot.line_len(point_selection.end.row);
point_selection.end.column = snapshot.line_len(MultiBufferRow(point_selection.end.row));
}
let codegen_kind = if point_selection.start == point_selection.end {
@ -2168,7 +2169,7 @@ impl ConversationEditor {
let snapshot = editor.snapshot(cx);
let cursor_point = scroll_position.cursor.to_display_point(&snapshot);
let scroll_top =
cursor_point.row() as f32 - scroll_position.offset_before_cursor.y;
cursor_point.row().as_f32() - scroll_position.offset_before_cursor.y;
editor.set_scroll_position(
point(scroll_position.offset_before_cursor.x, scroll_top),
cx,
@ -2236,7 +2237,10 @@ impl ConversationEditor {
self.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let cursor = editor.selections.newest_anchor().head();
let cursor_row = cursor.to_display_point(&snapshot.display_snapshot).row() as f32;
let cursor_row = cursor
.to_display_point(&snapshot.display_snapshot)
.row()
.as_f32();
let scroll_position = editor
.scroll_manager
.anchor()

View File

@ -7,6 +7,7 @@ use editor::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
use gpui::{EventEmitter, Model, ModelContext, Task};
use language::{Rope, TransactionId};
use multi_buffer::MultiBufferRow;
use std::{cmp, future, ops::Range};
pub enum Event {
@ -100,7 +101,7 @@ impl Codegen {
.suggested_indents(selection_start.row..selection_start.row + 1, cx)
.into_values()
.next()
.unwrap_or_else(|| snapshot.indent_size_for_line(selection_start.row));
.unwrap_or_else(|| snapshot.indent_size_for_line(MultiBufferRow(selection_start.row)));
let response = CompletionProvider::global(cx).complete(prompt);
self.generation = cx.spawn(|this, mut cx| {

View File

@ -90,6 +90,7 @@ language = { workspace = true, features = ["test-support"] }
live_kit_client = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
menu.workspace = true
multi_buffer = { workspace = true, features = ["test-support"] }
node_runtime.workspace = true
notifications = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true

View File

@ -9,6 +9,7 @@ use editor::{
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst, Redo, Rename,
RevertSelectedHunks, ToggleCodeActions, Undo,
},
display_map::DisplayRow,
test::{
editor_hunks,
editor_test_context::{AssertionContextManager, EditorTestContext},
@ -24,6 +25,7 @@ use language::{
language_settings::{AllLanguageSettings, InlayHintSettings},
FakeLspAdapter,
};
use multi_buffer::MultiBufferRow;
use project::{
project_settings::{InlineBlameSettings, ProjectSettings},
SERVER_PROGRESS_DEBOUNCE_TIMEOUT,
@ -2114,14 +2116,30 @@ struct Row10;"#};
assert_eq!(
all_hunks,
vec![
("".to_string(), DiffHunkStatus::Added, 1..3),
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 4..4),
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 6..7),
("struct Row8;\n".to_string(), DiffHunkStatus::Removed, 9..9),
(
"".to_string(),
DiffHunkStatus::Added,
DisplayRow(1)..DisplayRow(3)
),
(
"struct Row2;\n".to_string(),
DiffHunkStatus::Removed,
DisplayRow(4)..DisplayRow(4)
),
(
"struct Row5;\n".to_string(),
DiffHunkStatus::Modified,
DisplayRow(6)..DisplayRow(7)
),
(
"struct Row8;\n".to_string(),
DiffHunkStatus::Removed,
DisplayRow(9)..DisplayRow(9)
),
(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
DisplayRow(10)..DisplayRow(10),
),
]
);
@ -2133,23 +2151,35 @@ struct Row10;"#};
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, cx),
vec![1..=2, 8..=8],
vec![DisplayRow(1)..=DisplayRow(2), DisplayRow(8)..=DisplayRow(8)],
);
assert_eq!(
all_hunks,
vec![
("".to_string(), DiffHunkStatus::Added, 1..3),
("struct Row2;\n".to_string(), DiffHunkStatus::Removed, 5..5),
("struct Row5;\n".to_string(), DiffHunkStatus::Modified, 8..9),
(
"".to_string(),
DiffHunkStatus::Added,
DisplayRow(1)..DisplayRow(3)
),
(
"struct Row2;\n".to_string(),
DiffHunkStatus::Removed,
DisplayRow(5)..DisplayRow(5)
),
(
"struct Row5;\n".to_string(),
DiffHunkStatus::Modified,
DisplayRow(8)..DisplayRow(9)
),
(
"struct Row8;\n".to_string(),
DiffHunkStatus::Removed,
12..12
DisplayRow(12)..DisplayRow(12)
),
(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
13..13,
DisplayRow(13)..DisplayRow(13),
),
]
);
@ -2173,7 +2203,7 @@ struct Row10;"#};
vec![(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
DisplayRow(10)..DisplayRow(10),
)]
);
assert_eq!(all_expanded_hunks, Vec::new());
@ -2184,14 +2214,14 @@ struct Row10;"#};
let all_expanded_hunks = expanded_hunks(&editor, &snapshot, cx);
assert_eq!(
expanded_hunks_background_highlights(editor, cx),
vec![5..=5]
vec![DisplayRow(5)..=DisplayRow(5)]
);
assert_eq!(
all_hunks,
vec![(
"struct Row10;".to_string(),
DiffHunkStatus::Modified,
10..10,
DisplayRow(10)..DisplayRow(10),
)]
);
assert_eq!(all_expanded_hunks, Vec::new());
@ -2330,7 +2360,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
let blame = editor_b.blame().expect("editor_b should have blame now");
let entries = blame.update(cx, |blame, cx| {
blame
.blame_for_rows((0..4).map(Some), cx)
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>()
});
@ -2369,7 +2399,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
let blame = editor_b.blame().expect("editor_b should have blame now");
let entries = blame.update(cx, |blame, cx| {
blame
.blame_for_rows((0..4).map(Some), cx)
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>()
});
@ -2396,7 +2426,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
let blame = editor_b.blame().expect("editor_b should have blame now");
let entries = blame.update(cx, |blame, cx| {
blame
.blame_for_rows((0..4).map(Some), cx)
.blame_for_rows((0..4).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>()
});

View File

@ -1,7 +1,7 @@
use super::*;
use collections::HashMap;
use editor::{
display_map::{BlockContext, TransformBlock},
display_map::{BlockContext, DisplayRow, TransformBlock},
DisplayPoint, GutterDimensions,
};
use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
@ -158,11 +158,11 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(15, "collapsed context".into()),
(16, "diagnostic header".into()),
(25, "collapsed context".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(15), "collapsed context".into()),
(DisplayRow(16), "diagnostic header".into()),
(DisplayRow(25), "collapsed context".into()),
]
);
assert_eq!(
@ -210,7 +210,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
[DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
[DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)]
);
});
@ -243,13 +243,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "path header block".into()),
(9, "diagnostic header".into()),
(22, "collapsed context".into()),
(23, "diagnostic header".into()),
(32, "collapsed context".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(7), "path header block".into()),
(DisplayRow(9), "diagnostic header".into()),
(DisplayRow(22), "collapsed context".into()),
(DisplayRow(23), "diagnostic header".into()),
(DisplayRow(32), "collapsed context".into()),
]
);
@ -309,7 +309,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
[DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
[DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)]
);
});
@ -355,15 +355,15 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "collapsed context".into()),
(8, "diagnostic header".into()),
(13, "path header block".into()),
(15, "diagnostic header".into()),
(28, "collapsed context".into()),
(29, "diagnostic header".into()),
(38, "collapsed context".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(7), "collapsed context".into()),
(DisplayRow(8), "diagnostic header".into()),
(DisplayRow(13), "path header block".into()),
(DisplayRow(15), "diagnostic header".into()),
(DisplayRow(28), "collapsed context".into()),
(DisplayRow(29), "diagnostic header".into()),
(DisplayRow(38), "collapsed context".into()),
]
);
@ -493,8 +493,8 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
]
);
assert_eq!(
@ -539,10 +539,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(6, "collapsed context".into()),
(7, "diagnostic header".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(6), "collapsed context".into()),
(DisplayRow(7), "diagnostic header".into()),
]
);
assert_eq!(
@ -605,10 +605,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "collapsed context".into()),
(8, "diagnostic header".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(7), "collapsed context".into()),
(DisplayRow(8), "diagnostic header".into()),
]
);
assert_eq!(
@ -661,10 +661,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
assert_eq!(
editor_blocks(&editor, cx),
[
(0, "path header block".into()),
(2, "diagnostic header".into()),
(7, "collapsed context".into()),
(8, "diagnostic header".into()),
(DisplayRow(0), "path header block".into()),
(DisplayRow(2), "diagnostic header".into()),
(DisplayRow(7), "collapsed context".into()),
(DisplayRow(8), "diagnostic header".into()),
]
);
assert_eq!(
@ -958,14 +958,17 @@ fn random_diagnostic(
}
}
fn editor_blocks(editor: &View<Editor>, cx: &mut VisualTestContext) -> Vec<(u32, SharedString)> {
fn editor_blocks(
editor: &View<Editor>,
cx: &mut VisualTestContext,
) -> Vec<(DisplayRow, SharedString)> {
let mut blocks = Vec::new();
cx.draw(gpui::Point::default(), AvailableSpace::min_size(), |cx| {
editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
blocks.extend(
snapshot
.blocks_in_range(0..snapshot.max_point().row())
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
.enumerate()
.filter_map(|(ix, (row, block))| {
let name: SharedString = match block {

View File

@ -54,7 +54,7 @@ pub struct SelectToEndOfLine {
pub struct ToggleCodeActions {
// Display row from which the action was deployed.
#[serde(default)]
pub deployed_from_indicator: Option<u32>,
pub deployed_from_indicator: Option<DisplayRow>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
@ -77,12 +77,12 @@ pub struct ToggleComments {
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct FoldAt {
pub buffer_row: u32,
pub buffer_row: MultiBufferRow,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct UnfoldAt {
pub buffer_row: u32,
pub buffer_row: MultiBufferRow,
}
#[derive(PartialEq, Clone, Deserialize, Default)]

View File

@ -23,8 +23,8 @@ mod inlay_map;
mod tab_map;
mod wrap_map;
use crate::EditorStyle;
use crate::{hover_links::InlayHighlight, movement::TextLayoutDetails, InlayId};
use crate::{EditorStyle, RowExt};
pub use block_map::{BlockMap, BlockPoint};
use collections::{HashMap, HashSet};
use fold_map::FoldMap;
@ -34,7 +34,11 @@ use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
};
use lsp::DiagnosticSeverity;
use multi_buffer::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
use multi_buffer::{
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
ToOffset, ToPoint,
};
use serde::Deserialize;
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
use sum_tree::{Bias, TreeMap};
use tab_map::TabMap;
@ -42,10 +46,11 @@ use tab_map::TabMap;
use wrap_map::WrapMap;
pub use block_map::{
BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
BlockProperties, BlockStyle, RenderBlock, TransformBlock,
};
use self::block_map::BlockRow;
pub use self::fold_map::{Fold, FoldId, FoldPoint};
pub use self::inlay_map::{InlayOffset, InlayPoint};
pub(crate) use inlay_map::Inlay;
@ -65,6 +70,17 @@ pub trait ToDisplayPoint {
type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
#[derive(Clone)]
pub struct DisplayBufferRows<'a>(BlockBufferRows<'a>);
impl<'a> Iterator for DisplayBufferRows<'a> {
type Item = Option<DisplayRow>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|row| row.map(|r| DisplayRow(r.0)))
}
}
/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.
///
@ -382,15 +398,15 @@ impl DisplaySnapshot {
self.buffer_snapshot.len() == 0
}
pub fn buffer_rows(&self, start_row: u32) -> DisplayBufferRows {
self.block_snapshot.buffer_rows(start_row)
pub fn display_rows(&self, start_row: DisplayRow) -> DisplayBufferRows {
DisplayBufferRows(self.block_snapshot.buffer_rows(BlockRow(start_row.0)))
}
pub fn max_buffer_row(&self) -> u32 {
pub fn max_buffer_row(&self) -> MultiBufferRow {
self.buffer_snapshot.max_buffer_row()
}
pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
loop {
let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);
@ -408,7 +424,7 @@ impl DisplaySnapshot {
}
}
pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
pub fn next_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
loop {
let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);
@ -429,13 +445,14 @@ impl DisplaySnapshot {
// used by line_mode selections and tries to match vim behaviour
pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
let new_start = if range.start.row == 0 {
Point::new(0, 0)
} else if range.start.row == self.max_buffer_row()
|| (range.end.column > 0 && range.end.row == self.max_buffer_row())
MultiBufferPoint::new(0, 0)
} else if range.start.row == self.max_buffer_row().0
|| (range.end.column > 0 && range.end.row == self.max_buffer_row().0)
{
Point::new(
MultiBufferPoint::new(
range.start.row - 1,
self.buffer_snapshot.line_len(range.start.row - 1),
self.buffer_snapshot
.line_len(MultiBufferRow(range.start.row - 1)),
)
} else {
self.prev_line_boundary(range.start).0
@ -443,9 +460,9 @@ impl DisplaySnapshot {
let new_end = if range.end.column == 0 {
range.end
} else if range.end.row < self.max_buffer_row() {
} else if range.end.row < self.max_buffer_row().0 {
self.buffer_snapshot
.clip_point(Point::new(range.end.row + 1, 0), Bias::Left)
.clip_point(MultiBufferPoint::new(range.end.row + 1, 0), Bias::Left)
} else {
self.buffer_snapshot.max_point()
};
@ -453,7 +470,7 @@ impl DisplaySnapshot {
new_start..new_end
}
fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint {
fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
let inlay_point = self.inlay_snapshot.to_inlay_point(point);
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
let tab_point = self.tab_snapshot.to_tab_point(fold_point);
@ -509,10 +526,10 @@ impl DisplaySnapshot {
}
/// Returns text chunks starting at the given display row until the end of the file
pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
pub fn text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
self.block_snapshot
.chunks(
display_row..self.max_point().row() + 1,
display_row.0..self.max_point().row().next_row().0,
false,
Highlights::default(),
)
@ -520,8 +537,8 @@ impl DisplaySnapshot {
}
/// Returns text chunks starting at the end of the given display row in reverse until the start of the file
pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
(0..=display_row).rev().flat_map(|row| {
pub fn reverse_text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
(0..=display_row.0).rev().flat_map(|row| {
self.block_snapshot
.chunks(row..row + 1, false, Highlights::default())
.map(|h| h.text)
@ -533,12 +550,12 @@ impl DisplaySnapshot {
pub fn chunks(
&self,
display_rows: Range<u32>,
display_rows: Range<DisplayRow>,
language_aware: bool,
highlight_styles: HighlightStyles,
) -> DisplayChunks<'_> {
self.block_snapshot.chunks(
display_rows,
display_rows.start.0..display_rows.end.0,
language_aware,
Highlights {
text_highlights: Some(&self.text_highlights),
@ -550,7 +567,7 @@ impl DisplaySnapshot {
pub fn highlighted_chunks<'a>(
&'a self,
display_rows: Range<u32>,
display_rows: Range<DisplayRow>,
language_aware: bool,
editor_style: &'a EditorStyle,
) -> impl Iterator<Item = HighlightedChunk<'a>> {
@ -610,7 +627,7 @@ impl DisplaySnapshot {
pub fn layout_row(
&self,
display_row: u32,
display_row: DisplayRow,
TextLayoutDetails {
text_system,
editor_style,
@ -623,7 +640,7 @@ impl DisplaySnapshot {
let mut runs = Vec::new();
let mut line = String::new();
let range = display_row..display_row + 1;
let range = display_row..display_row.next_row();
for chunk in self.highlighted_chunks(range, false, &editor_style) {
line.push_str(chunk.chunk);
@ -663,7 +680,7 @@ impl DisplaySnapshot {
pub fn display_column_for_x(
&self,
display_row: u32,
display_row: DisplayRow,
x: Pixels,
details: &TextLayoutDetails,
) -> u32 {
@ -732,7 +749,7 @@ impl DisplaySnapshot {
pub fn clip_at_line_end(&self, point: DisplayPoint) -> DisplayPoint {
let mut point = point.0;
if point.column == self.line_len(point.row) {
if point.column == self.line_len(DisplayRow(point.row)) {
point.column = point.column.saturating_sub(1);
point = self.block_snapshot.clip_point(point, Bias::Left);
}
@ -748,36 +765,38 @@ impl DisplaySnapshot {
pub fn blocks_in_range(
&self,
rows: Range<u32>,
) -> impl Iterator<Item = (u32, &TransformBlock)> {
self.block_snapshot.blocks_in_range(rows)
rows: Range<DisplayRow>,
) -> impl Iterator<Item = (DisplayRow, &TransformBlock)> {
self.block_snapshot
.blocks_in_range(rows.start.0..rows.end.0)
.map(|(row, block)| (DisplayRow(row), block))
}
pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
self.fold_snapshot.intersects_fold(offset)
}
pub fn is_line_folded(&self, buffer_row: u32) -> bool {
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
self.fold_snapshot.is_line_folded(buffer_row)
}
pub fn is_block_line(&self, display_row: u32) -> bool {
self.block_snapshot.is_block_line(display_row)
pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
self.block_snapshot.is_block_line(BlockRow(display_row.0))
}
pub fn soft_wrap_indent(&self, display_row: u32) -> Option<u32> {
pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
let wrap_row = self
.block_snapshot
.to_wrap_point(BlockPoint::new(display_row, 0))
.to_wrap_point(BlockPoint::new(display_row.0, 0))
.row();
self.wrap_snapshot.soft_wrap_indent(wrap_row)
}
pub fn text(&self) -> String {
self.text_chunks(0).collect()
self.text_chunks(DisplayRow(0)).collect()
}
pub fn line(&self, display_row: u32) -> String {
pub fn line(&self, display_row: DisplayRow) -> String {
let mut result = String::new();
for chunk in self.text_chunks(display_row) {
if let Some(ix) = chunk.find('\n') {
@ -790,7 +809,7 @@ impl DisplaySnapshot {
result
}
pub fn line_indent_for_buffer_row(&self, buffer_row: u32) -> (u32, bool) {
pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> (u32, bool) {
let (buffer, range) = self
.buffer_snapshot
.buffer_line_for_row(buffer_row)
@ -812,15 +831,15 @@ impl DisplaySnapshot {
(indent_size, is_blank)
}
pub fn line_len(&self, row: u32) -> u32 {
self.block_snapshot.line_len(row)
pub fn line_len(&self, row: DisplayRow) -> u32 {
self.block_snapshot.line_len(BlockRow(row.0))
}
pub fn longest_row(&self) -> u32 {
self.block_snapshot.longest_row()
pub fn longest_row(&self) -> DisplayRow {
DisplayRow(self.block_snapshot.longest_row())
}
pub fn fold_for_line(&self, buffer_row: u32) -> Option<FoldStatus> {
pub fn fold_for_line(&self, buffer_row: MultiBufferRow) -> Option<FoldStatus> {
if self.is_line_folded(buffer_row) {
Some(FoldStatus::Folded)
} else if self.is_foldable(buffer_row) {
@ -830,7 +849,7 @@ impl DisplaySnapshot {
}
}
pub fn is_foldable(&self, buffer_row: u32) -> bool {
pub fn is_foldable(&self, buffer_row: MultiBufferRow) -> bool {
let max_row = self.buffer_snapshot.max_buffer_row();
if buffer_row >= max_row {
return false;
@ -841,8 +860,9 @@ impl DisplaySnapshot {
return false;
}
for next_row in (buffer_row + 1)..=max_row {
let (next_indent_size, next_line_is_blank) = self.line_indent_for_buffer_row(next_row);
for next_row in (buffer_row.0 + 1)..=max_row.0 {
let (next_indent_size, next_line_is_blank) =
self.line_indent_for_buffer_row(MultiBufferRow(next_row));
if next_indent_size > indent_size {
return true;
} else if !next_line_is_blank {
@ -853,20 +873,22 @@ impl DisplaySnapshot {
false
}
pub fn foldable_range(&self, buffer_row: u32) -> Option<Range<Point>> {
let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row));
if self.is_foldable(start.row) && !self.is_line_folded(start.row) {
pub fn foldable_range(&self, buffer_row: MultiBufferRow) -> Option<Range<Point>> {
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
if self.is_foldable(MultiBufferRow(start.row))
&& !self.is_line_folded(MultiBufferRow(start.row))
{
let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
let max_point = self.buffer_snapshot.max_point();
let mut end = None;
for row in (buffer_row + 1)..=max_point.row {
let (indent, is_blank) = self.line_indent_for_buffer_row(row);
for row in (buffer_row.0 + 1)..=max_point.row {
let (indent, is_blank) = self.line_indent_for_buffer_row(MultiBufferRow(row));
if !is_blank && indent <= start_indent {
let prev_row = row - 1;
end = Some(Point::new(
prev_row,
self.buffer_snapshot.line_len(prev_row),
self.buffer_snapshot.line_len(MultiBufferRow(prev_row)),
));
break;
}
@ -903,27 +925,31 @@ impl Debug for DisplayPoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"DisplayPoint({}, {})",
self.row(),
self.row().0,
self.column()
))
}
}
#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
#[serde(transparent)]
pub struct DisplayRow(pub u32);
impl DisplayPoint {
pub fn new(row: u32, column: u32) -> Self {
Self(BlockPoint(Point::new(row, column)))
pub fn new(row: DisplayRow, column: u32) -> Self {
Self(BlockPoint(Point::new(row.0, column)))
}
pub fn zero() -> Self {
Self::new(0, 0)
Self::new(DisplayRow(0), 0)
}
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
pub fn row(self) -> u32 {
self.0.row
pub fn row(self) -> DisplayRow {
DisplayRow(self.0.row)
}
pub fn column(self) -> u32 {
@ -1171,7 +1197,7 @@ pub mod tests {
let buffer = &snapshot.buffer_snapshot;
for _ in 0..5 {
let row = rng.gen_range(0..=buffer.max_point().row);
let column = rng.gen_range(0..=buffer.line_len(row));
let column = rng.gen_range(0..=buffer.line_len(MultiBufferRow(row)));
let point = buffer.clip_point(Point::new(row, column), Left);
let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
@ -1216,12 +1242,12 @@ pub mod tests {
}
// Movement
let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
let max_point = snapshot.clip_point(snapshot.max_point(), Right);
for _ in 0..5 {
let row = rng.gen_range(0..=snapshot.max_point().row());
let column = rng.gen_range(0..=snapshot.line_len(row));
let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
let row = rng.gen_range(0..=snapshot.max_point().row().0);
let column = rng.gen_range(0..=snapshot.line_len(DisplayRow(row)));
let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
log::info!("Moving from point {:?}", point);
@ -1288,63 +1314,64 @@ pub mod tests {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(
snapshot.text_chunks(0).collect::<String>(),
snapshot.text_chunks(DisplayRow(0)).collect::<String>(),
"one two \nthree four \nfive\nsix seven \neight"
);
assert_eq!(
snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
DisplayPoint::new(0, 7)
snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Left),
DisplayPoint::new(DisplayRow(0), 7)
);
assert_eq!(
snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
DisplayPoint::new(1, 0)
snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Right),
DisplayPoint::new(DisplayRow(1), 0)
);
assert_eq!(
movement::right(&snapshot, DisplayPoint::new(0, 7)),
DisplayPoint::new(1, 0)
movement::right(&snapshot, DisplayPoint::new(DisplayRow(0), 7)),
DisplayPoint::new(DisplayRow(1), 0)
);
assert_eq!(
movement::left(&snapshot, DisplayPoint::new(1, 0)),
DisplayPoint::new(0, 7)
movement::left(&snapshot, DisplayPoint::new(DisplayRow(1), 0)),
DisplayPoint::new(DisplayRow(0), 7)
);
let x = snapshot.x_for_display_point(DisplayPoint::new(1, 10), &text_layout_details);
let x = snapshot
.x_for_display_point(DisplayPoint::new(DisplayRow(1), 10), &text_layout_details);
assert_eq!(
movement::up(
&snapshot,
DisplayPoint::new(1, 10),
DisplayPoint::new(DisplayRow(1), 10),
SelectionGoal::None,
false,
&text_layout_details,
),
(
DisplayPoint::new(0, 7),
DisplayPoint::new(DisplayRow(0), 7),
SelectionGoal::HorizontalPosition(x.0)
)
);
assert_eq!(
movement::down(
&snapshot,
DisplayPoint::new(0, 7),
DisplayPoint::new(DisplayRow(0), 7),
SelectionGoal::HorizontalPosition(x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(1, 10),
DisplayPoint::new(DisplayRow(1), 10),
SelectionGoal::HorizontalPosition(x.0)
)
);
assert_eq!(
movement::down(
&snapshot,
DisplayPoint::new(1, 10),
DisplayPoint::new(DisplayRow(1), 10),
SelectionGoal::HorizontalPosition(x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(2, 4),
DisplayPoint::new(DisplayRow(2), 4),
SelectionGoal::HorizontalPosition(x.0)
)
);
@ -1356,7 +1383,7 @@ pub mod tests {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(
snapshot.text_chunks(1).collect::<String>(),
snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
"three four \nfive\nsix and \nseven eight"
);
@ -1367,7 +1394,7 @@ pub mod tests {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(
snapshot.text_chunks(1).collect::<String>(),
snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
"three \nfour five\nsix and \nseven \neight"
)
});
@ -1388,9 +1415,18 @@ pub mod tests {
buffer.update(cx, |buffer, cx| {
buffer.edit(
vec![
(Point::new(1, 0)..Point::new(1, 0), "\t"),
(Point::new(1, 1)..Point::new(1, 1), "\t"),
(Point::new(2, 1)..Point::new(2, 1), "\t"),
(
MultiBufferPoint::new(1, 0)..MultiBufferPoint::new(1, 0),
"\t",
),
(
MultiBufferPoint::new(1, 1)..MultiBufferPoint::new(1, 1),
"\t",
),
(
MultiBufferPoint::new(2, 1)..MultiBufferPoint::new(2, 1),
"\t",
),
],
None,
cx,
@ -1399,7 +1435,7 @@ pub mod tests {
assert_eq!(
map.update(cx, |map, cx| map.snapshot(cx))
.text_chunks(1)
.text_chunks(DisplayRow(1))
.collect::<String>()
.lines()
.next(),
@ -1407,7 +1443,7 @@ pub mod tests {
);
assert_eq!(
map.update(cx, |map, cx| map.snapshot(cx))
.text_chunks(2)
.text_chunks(DisplayRow(2))
.collect::<String>()
.lines()
.next(),
@ -1462,7 +1498,7 @@ pub mod tests {
let map = cx
.new_model(|cx| DisplayMap::new(buffer, font("Helvetica"), font_size, None, 1, 1, cx));
assert_eq!(
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
vec![
("fn ".to_string(), None),
("outer".to_string(), Some(Hsla::blue())),
@ -1473,7 +1509,7 @@ pub mod tests {
]
);
assert_eq!(
cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
vec![
(" fn ".to_string(), Some(Hsla::red())),
("inner".to_string(), Some(Hsla::blue())),
@ -1482,10 +1518,13 @@ pub mod tests {
);
map.update(cx, |map, cx| {
map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
map.fold(
vec![MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2)],
cx,
)
});
assert_eq!(
cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(2), &map, &theme, cx)),
vec![
("fn ".to_string(), None),
("out".to_string(), Some(Hsla::blue())),
@ -1548,7 +1587,7 @@ pub mod tests {
DisplayMap::new(buffer, font("Courier"), font_size, Some(px(40.0)), 1, 1, cx)
});
assert_eq!(
cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
[
("fn \n".to_string(), None),
("oute\nr".to_string(), Some(Hsla::blue())),
@ -1556,15 +1595,18 @@ pub mod tests {
]
);
assert_eq!(
cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
[("{}\n\n".to_string(), None)]
);
map.update(cx, |map, cx| {
map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
map.fold(
vec![MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2)],
cx,
)
});
assert_eq!(
cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
cx.update(|cx| syntax_chunks(DisplayRow(1)..DisplayRow(4), &map, &theme, cx)),
[
("out".to_string(), Some(Hsla::blue())),
("\n".to_string(), None),
@ -1636,7 +1678,7 @@ pub mod tests {
});
assert_eq!(
cx.update(|cx| chunks(0..10, &map, &theme, cx)),
cx.update(|cx| chunks(DisplayRow(0)..DisplayRow(10), &map, &theme, cx)),
[
("const ".to_string(), None, None),
("a".to_string(), None, Some(Hsla::blue())),
@ -1732,45 +1774,57 @@ pub mod tests {
let map = map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(map.text(), "α\nβ \n🏀β γ");
assert_eq!(
map.text_chunks(0).collect::<String>(),
map.text_chunks(DisplayRow(0)).collect::<String>(),
"α\nβ \n🏀β γ"
);
assert_eq!(map.text_chunks(1).collect::<String>(), "β \n🏀β γ");
assert_eq!(map.text_chunks(2).collect::<String>(), "🏀β γ");
assert_eq!(
map.text_chunks(DisplayRow(1)).collect::<String>(),
"β \n🏀β γ"
);
assert_eq!(
map.text_chunks(DisplayRow(2)).collect::<String>(),
"🏀β γ"
);
let point = Point::new(0, "\t\t".len() as u32);
let display_point = DisplayPoint::new(0, "".len() as u32);
let point = MultiBufferPoint::new(0, "\t\t".len() as u32);
let display_point = DisplayPoint::new(DisplayRow(0), "".len() as u32);
assert_eq!(point.to_display_point(&map), display_point);
assert_eq!(display_point.to_point(&map), point);
let point = Point::new(1, "β\t".len() as u32);
let display_point = DisplayPoint::new(1, "β ".len() as u32);
let point = MultiBufferPoint::new(1, "β\t".len() as u32);
let display_point = DisplayPoint::new(DisplayRow(1), "β ".len() as u32);
assert_eq!(point.to_display_point(&map), display_point);
assert_eq!(display_point.to_point(&map), point,);
let point = Point::new(2, "🏀β\t\t".len() as u32);
let display_point = DisplayPoint::new(2, "🏀β ".len() as u32);
let point = MultiBufferPoint::new(2, "🏀β\t\t".len() as u32);
let display_point = DisplayPoint::new(DisplayRow(2), "🏀β ".len() as u32);
assert_eq!(point.to_display_point(&map), display_point);
assert_eq!(display_point.to_point(&map), point,);
// Display points inside of expanded tabs
assert_eq!(
DisplayPoint::new(0, "".len() as u32).to_point(&map),
Point::new(0, "\t".len() as u32),
DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
MultiBufferPoint::new(0, "\t".len() as u32),
);
assert_eq!(
DisplayPoint::new(0, "".len() as u32).to_point(&map),
Point::new(0, "".len() as u32),
DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
MultiBufferPoint::new(0, "".len() as u32),
);
// Clipping display points inside of multi-byte characters
assert_eq!(
map.clip_point(DisplayPoint::new(0, "".len() as u32 - 1), Left),
DisplayPoint::new(0, 0)
map.clip_point(
DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
Left
),
DisplayPoint::new(DisplayRow(0), 0)
);
assert_eq!(
map.clip_point(DisplayPoint::new(0, "".len() as u32 - 1), Bias::Right),
DisplayPoint::new(0, "".len() as u32)
map.clip_point(
DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
Bias::Right
),
DisplayPoint::new(DisplayRow(0), "".len() as u32)
);
}
@ -1785,12 +1839,12 @@ pub mod tests {
});
assert_eq!(
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
DisplayPoint::new(1, 11)
DisplayPoint::new(DisplayRow(1), 11)
)
}
fn syntax_chunks(
rows: Range<u32>,
rows: Range<DisplayRow>,
map: &Model<DisplayMap>,
theme: &SyntaxTheme,
cx: &mut AppContext,
@ -1802,7 +1856,7 @@ pub mod tests {
}
fn chunks(
rows: Range<u32>,
rows: Range<DisplayRow>,
map: &Model<DisplayMap>,
theme: &SyntaxTheme,
cx: &mut AppContext,

View File

@ -6,7 +6,7 @@ use crate::{EditorStyle, GutterDimensions};
use collections::{Bound, HashMap, HashSet};
use gpui::{AnyElement, Pixels, WindowContext};
use language::{BufferSnapshot, Chunk, Patch, Point};
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};
use parking_lot::Mutex;
use std::{
cell::RefCell,
@ -50,7 +50,7 @@ pub struct BlockId(usize);
pub struct BlockPoint(pub Point);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct BlockRow(u32);
pub struct BlockRow(pub(super) u32);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct WrapRow(u32);
@ -163,7 +163,7 @@ pub struct BlockChunks<'a> {
pub struct BlockBufferRows<'a> {
transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
input_buffer_rows: wrap_map::WrapBufferRows<'a>,
output_row: u32,
output_row: BlockRow,
started: bool,
}
@ -357,7 +357,7 @@ impl BlockMap {
match block.disposition {
BlockDisposition::Above => position.column = 0,
BlockDisposition::Below => {
position.column = buffer.line_len(position.row)
position.column = buffer.line_len(MultiBufferRow(position.row))
}
}
let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
@ -372,7 +372,7 @@ impl BlockMap {
(
wrap_snapshot
.make_wrap_point(
Point::new(excerpt_boundary.row, 0),
Point::new(excerpt_boundary.row.0, 0),
Bias::Left,
)
.row(),
@ -633,12 +633,12 @@ impl BlockSnapshot {
}
}
pub fn buffer_rows(&self, start_row: u32) -> BlockBufferRows {
pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
cursor.seek(&BlockRow(start_row), Bias::Right, &());
cursor.seek(&start_row, Bias::Right, &());
let (output_start, input_start) = cursor.start();
let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
start_row - output_start.0
start_row.0 - output_start.0
} else {
0
};
@ -676,7 +676,7 @@ impl BlockSnapshot {
pub fn max_point(&self) -> BlockPoint {
let row = self.transforms.summary().output_rows - 1;
BlockPoint::new(row, self.line_len(row))
BlockPoint::new(row, self.line_len(BlockRow(row)))
}
pub fn longest_row(&self) -> u32 {
@ -684,12 +684,12 @@ impl BlockSnapshot {
self.to_block_point(WrapPoint::new(input_row, 0)).row
}
pub fn line_len(&self, row: u32) -> u32 {
pub(super) fn line_len(&self, row: BlockRow) -> u32 {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
cursor.seek(&BlockRow(row), Bias::Right, &());
cursor.seek(&BlockRow(row.0), Bias::Right, &());
if let Some(transform) = cursor.item() {
let (output_start, input_start) = cursor.start();
let overshoot = row - output_start.0;
let overshoot = row.0 - output_start.0;
if transform.block.is_some() {
0
} else {
@ -700,9 +700,9 @@ impl BlockSnapshot {
}
}
pub fn is_block_line(&self, row: u32) -> bool {
pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
cursor.seek(&BlockRow(row), Bias::Right, &());
cursor.seek(&row, Bias::Right, &());
cursor.item().map_or(false, |t| t.block.is_some())
}
@ -881,16 +881,16 @@ impl<'a> Iterator for BlockChunks<'a> {
}
impl<'a> Iterator for BlockBufferRows<'a> {
type Item = Option<u32>;
type Item = Option<BlockRow>;
fn next(&mut self) -> Option<Self::Item> {
if self.started {
self.output_row += 1;
self.output_row.0 += 1;
} else {
self.started = true;
}
if self.output_row >= self.transforms.end(&()).0 .0 {
if self.output_row.0 >= self.transforms.end(&()).0 .0 {
self.transforms.next(&());
}
@ -898,7 +898,7 @@ impl<'a> Iterator for BlockBufferRows<'a> {
if transform.block.is_some() {
Some(None)
} else {
Some(self.input_buffer_rows.next().unwrap())
Some(self.input_buffer_rows.next().unwrap().map(BlockRow))
}
}
}
@ -1154,7 +1154,10 @@ mod tests {
);
assert_eq!(
snapshot.buffer_rows(0).collect::<Vec<_>>(),
snapshot
.buffer_rows(BlockRow(0))
.map(|row| row.map(|r| r.0))
.collect::<Vec<_>>(),
&[
Some(0),
None,
@ -1394,7 +1397,7 @@ mod tests {
position.column = 0;
}
BlockDisposition::Below => {
position.column = buffer_snapshot.line_len(position.row);
position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
}
};
let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
@ -1410,7 +1413,7 @@ mod tests {
expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
|boundary| {
let position =
wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
wraps_snapshot.make_wrap_point(Point::new(boundary.row.0, 0), Bias::Left);
(
position.row(),
ExpectedBlock::ExcerptHeader {
@ -1427,7 +1430,9 @@ mod tests {
expected_blocks.sort_unstable();
let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
let input_buffer_rows = buffer_snapshot
.buffer_rows(MultiBufferRow(0))
.collect::<Vec<_>>();
let mut expected_buffer_rows = Vec::new();
let mut expected_text = String::new();
let mut expected_block_positions = Vec::new();
@ -1498,7 +1503,8 @@ mod tests {
);
assert_eq!(
blocks_snapshot
.buffer_rows(start_row as u32)
.buffer_rows(BlockRow(start_row as u32))
.map(|row| row.map(|r| r.0))
.collect::<Vec<_>>(),
&expected_buffer_rows[start_row..]
);
@ -1518,7 +1524,7 @@ mod tests {
let row = row as u32;
assert_eq!(
blocks_snapshot.line_len(row),
blocks_snapshot.line_len(BlockRow(row)),
line.len() as u32,
"invalid line len for row {}",
row

View File

@ -4,7 +4,7 @@ use super::{
};
use gpui::{ElementId, HighlightStyle, Hsla};
use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToOffset};
use std::{
cmp::{self, Ordering},
iter,
@ -629,17 +629,17 @@ impl FoldSnapshot {
cursor.item().map_or(false, |t| t.output_text.is_some())
}
pub fn is_line_folded(&self, buffer_row: u32) -> bool {
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
let mut inlay_point = self
.inlay_snapshot
.to_inlay_point(Point::new(buffer_row, 0));
.to_inlay_point(Point::new(buffer_row.0, 0));
let mut cursor = self.transforms.cursor::<InlayPoint>();
cursor.seek(&inlay_point, Bias::Right, &());
loop {
match cursor.item() {
Some(transform) => {
let buffer_point = self.inlay_snapshot.to_buffer_point(inlay_point);
if buffer_point.row != buffer_row {
if buffer_point.row != buffer_row.0 {
return false;
} else if transform.output_text.is_some() {
return true;
@ -1549,7 +1549,7 @@ mod tests {
.collect::<HashSet<_>>();
for row in 0..=buffer_snapshot.max_point().row {
assert_eq!(
snapshot.is_line_folded(row),
snapshot.is_line_folded(MultiBufferRow(row)),
folded_buffer_rows.contains(&row),
"expected buffer row {}{} to be folded",
row,

View File

@ -2,7 +2,9 @@ use crate::{HighlightStyles, InlayId};
use collections::{BTreeMap, BTreeSet};
use gpui::HighlightStyle;
use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{Anchor, MultiBufferChunks, MultiBufferRows, MultiBufferSnapshot, ToOffset};
use multi_buffer::{
Anchor, MultiBufferChunks, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset,
};
use std::{
any::TypeId,
cmp,
@ -182,7 +184,7 @@ pub struct InlayBufferRows<'a> {
transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
buffer_rows: MultiBufferRows<'a>,
inlay_row: u32,
max_buffer_row: u32,
max_buffer_row: MultiBufferRow,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -375,7 +377,7 @@ impl<'a> InlayBufferRows<'a> {
self.transforms.seek(&inlay_point, Bias::Left, &());
let mut buffer_point = self.transforms.start().1;
let buffer_row = if row == 0 {
let buffer_row = MultiBufferRow(if row == 0 {
0
} else {
match self.transforms.item() {
@ -383,9 +385,9 @@ impl<'a> InlayBufferRows<'a> {
buffer_point += inlay_point.0 - self.transforms.start().0 .0;
buffer_point.row
}
_ => cmp::min(buffer_point.row + 1, self.max_buffer_row),
_ => cmp::min(buffer_point.row + 1, self.max_buffer_row.0),
}
};
});
self.inlay_row = inlay_point.row();
self.buffer_rows.seek(buffer_row);
}
@ -986,17 +988,17 @@ impl InlaySnapshot {
let inlay_point = InlayPoint::new(row, 0);
cursor.seek(&inlay_point, Bias::Left, &());
let max_buffer_row = self.buffer.max_point().row;
let max_buffer_row = MultiBufferRow(self.buffer.max_point().row);
let mut buffer_point = cursor.start().1;
let buffer_row = if row == 0 {
0
MultiBufferRow(0)
} else {
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
buffer_point += inlay_point.0 - cursor.start().0 .0;
buffer_point.row
MultiBufferRow(buffer_point.row)
}
_ => cmp::min(buffer_point.row + 1, max_buffer_row),
_ => cmp::min(MultiBufferRow(buffer_point.row + 1), max_buffer_row),
}
};

View File

@ -86,18 +86,18 @@ use language::{
CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
Point, Selection, SelectionGoal, TransactionId,
};
use language::{Runnable, RunnableRange};
use language::{BufferRow, Runnable, RunnableRange};
use task::{ResolvedTask, TaskTemplate, TaskVariables};
use hover_links::{HoverLink, HoveredLinkState, InlayHighlight};
use lsp::{DiagnosticSeverity, LanguageServerId};
use mouse_context_menu::MouseContextMenu;
use movement::TextLayoutDetails;
use multi_buffer::ToOffsetUtf16;
pub use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
ToPoint,
};
use multi_buffer::{MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock};
use project::project_settings::{GitGutterSetting, ProjectSettings};
@ -410,7 +410,7 @@ struct RunnableTasks {
#[derive(Clone)]
struct ResolvedTasks {
templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
position: text::Point,
position: Anchor,
}
/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
@ -505,7 +505,7 @@ pub struct Editor {
>,
last_bounds: Option<Bounds<Pixels>>,
expect_bounds_change: Option<Bounds<Pixels>>,
tasks: HashMap<(BufferId, u32), (usize, RunnableTasks)>,
tasks: HashMap<(BufferId, BufferRow), (usize, RunnableTasks)>,
tasks_update_task: Option<Task<()>>,
}
@ -804,7 +804,7 @@ impl ContextMenu {
enum ContextMenuOrigin {
EditorPoint(DisplayPoint),
GutterIndicator(u32),
GutterIndicator(DisplayRow),
}
#[derive(Clone)]
@ -1299,7 +1299,7 @@ struct CodeActionsMenu {
buffer: Model<Buffer>,
selected_item: usize,
scroll_handle: UniformListScrollHandle,
deployed_from_indicator: Option<u32>,
deployed_from_indicator: Option<DisplayRow>,
}
impl CodeActionsMenu {
@ -2498,7 +2498,8 @@ impl Editor {
let end_column = cmp::max(tail.column(), goal_column);
let reversed = start_column < tail.column();
let selection_ranges = (start_row..=end_row)
let selection_ranges = (start_row.0..=end_row.0)
.map(DisplayRow)
.filter_map(|row| {
if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
let start = display_map
@ -2898,7 +2899,8 @@ impl Editor {
.iter()
.map(|selection| {
let start_point = selection.start.to_point(&buffer);
let mut indent = buffer.indent_size_for_line(start_point.row);
let mut indent =
buffer.indent_size_for_line(MultiBufferRow(start_point.row));
indent.len = cmp::min(indent.len, start_point.column);
let start = selection.start;
let end = selection.end;
@ -2951,7 +2953,7 @@ impl Editor {
let max_len_of_delimiter =
delimiters.iter().map(|delimiter| delimiter.len()).max()?;
let (snapshot, range) =
buffer.buffer_line_for_row(start_point.row)?;
buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
let mut index_of_first_non_whitespace = 0;
let comment_candidate = snapshot
@ -3015,7 +3017,7 @@ impl Editor {
let mut cursor = new_selection.end.to_point(&buffer);
if extra_newline_inserted {
cursor.row -= 1;
cursor.column = buffer.line_len(cursor.row);
cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
}
new_selection.map(|_| cursor)
})
@ -3075,7 +3077,7 @@ impl Editor {
IndentKind::Space => " ".repeat(indent.len as usize),
IndentKind::Tab => "\t".repeat(indent.len as usize),
};
let point = Point::new(row, 0);
let point = Point::new(row.0, 0);
indent_edits.push((point..point, text));
}
}
@ -3135,7 +3137,7 @@ impl Editor {
IndentKind::Space => " ".repeat(indent.len as usize),
IndentKind::Tab => "\t".repeat(indent.len as usize),
};
let point = Point::new(row, 0);
let point = Point::new(row.0, 0);
indent_edits.push((point..point, text));
}
}
@ -3852,17 +3854,13 @@ impl Editor {
let spawned_test_task = this.update(&mut cx, |this, cx| {
if this.focus_handle.is_focused(cx) {
let display_row = action
let multibuffer_point = action
.deployed_from_indicator
.map(|row| {
DisplayPoint::new(row, 0)
.to_point(&snapshot.display_snapshot)
.row
})
.unwrap_or_else(|| this.selections.newest::<Point>(cx).head().row);
.map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
.unwrap_or_else(|| this.selections.newest::<Point>(cx).head());
let (buffer, buffer_row) = snapshot
.buffer_snapshot
.buffer_line_for_row(display_row)
.buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
.and_then(|(buffer_snapshot, range)| {
this.buffer
.read(cx)
@ -3934,7 +3932,9 @@ impl Editor {
.map(|task| (kind.clone(), task))
})
.collect(),
position: Point::new(buffer_row, tasks.1.column),
position: snapshot
.buffer_snapshot
.anchor_before(Point::new(multibuffer_point.row, tasks.1.column)),
})
});
let spawn_straight_away = tasks
@ -4496,7 +4496,7 @@ impl Editor {
fn render_code_actions_indicator(
&self,
_style: &EditorStyle,
row: u32,
row: DisplayRow,
is_active: bool,
cx: &mut ViewContext<Self>,
) -> Option<IconButton> {
@ -4525,7 +4525,7 @@ impl Editor {
self.tasks.clear()
}
fn insert_tasks(&mut self, key: (BufferId, u32), value: (usize, RunnableTasks)) {
fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: (usize, RunnableTasks)) {
if let Some(_) = self.tasks.insert(key, value) {
// This case should hopefully be rare, but just in case...
log::error!("multiple different run targets found on a single line, only the last target will be rendered")
@ -4536,7 +4536,7 @@ impl Editor {
&self,
_style: &EditorStyle,
is_active: bool,
row: u32,
row: DisplayRow,
cx: &mut ViewContext<Self>,
) -> IconButton {
IconButton::new("code_actions_indicator", ui::IconName::Play)
@ -4556,7 +4556,7 @@ impl Editor {
pub fn render_fold_indicators(
&mut self,
fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
fold_data: Vec<Option<(FoldStatus, MultiBufferRow, bool)>>,
_style: &EditorStyle,
gutter_hovered: bool,
_line_height: Pixels,
@ -4775,7 +4775,7 @@ impl Editor {
pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
self.transact(cx, |this, cx| {
this.select_autoclose_pair(cx);
let mut selections = this.selections.all::<Point>(cx);
let mut selections = this.selections.all::<MultiBufferPoint>(cx);
if !this.selections.line_mode {
let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
for selection in &mut selections {
@ -4786,7 +4786,7 @@ impl Editor {
.to_point(&display_map);
if let Some((buffer, line_buffer_range)) = display_map
.buffer_snapshot
.buffer_line_for_row(old_head.row)
.buffer_line_for_row(MultiBufferRow(old_head.row))
{
let indent_size =
buffer.indent_size_for_line(line_buffer_range.start.row);
@ -4800,7 +4800,7 @@ impl Editor {
let indent_len = indent_len.get();
new_head = cmp::min(
new_head,
Point::new(
MultiBufferPoint::new(
old_head.row,
((old_head.column - 1) / indent_len) * indent_len,
),
@ -4875,8 +4875,10 @@ impl Editor {
// If the selection is empty and the cursor is in the leading whitespace before the
// suggested indentation, then auto-indent the line.
let cursor = selection.head();
let current_indent = snapshot.indent_size_for_line(cursor.row);
if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
if let Some(suggested_indent) =
suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
{
if cursor.column < suggested_indent.len
&& cursor.column <= current_indent.len
&& current_indent.len <= suggested_indent.len
@ -4996,7 +4998,7 @@ impl Editor {
let mut delta_for_end_row = 0;
let has_multiple_rows = start_row + 1 != end_row;
for row in start_row..end_row {
let current_indent = snapshot.indent_size_for_line(row);
let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
let indent_delta = match (current_indent.kind, indent_kind) {
(IndentKind::Space, IndentKind::Space) => {
let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
@ -5054,11 +5056,11 @@ impl Editor {
// previous selection.
if let Some(last_row) = last_outdent {
if last_row == rows.start {
rows.start += 1;
rows.start = rows.start.next_row();
}
}
let has_multiple_rows = rows.len() > 1;
for row in rows {
for row in rows.iter_rows() {
let indent_size = snapshot.indent_size_for_line(row);
if indent_size.len > 0 {
let deletion_len = match indent_size.kind {
@ -5080,8 +5082,9 @@ impl Editor {
} else {
selection.start.column - deletion_len
};
deletion_ranges
.push(Point::new(row, start)..Point::new(row, start + deletion_len));
deletion_ranges.push(
Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
);
last_outdent = Some(row);
}
}
@ -5127,23 +5130,23 @@ impl Editor {
}
let buffer = &display_map.buffer_snapshot;
let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
let edit_end;
let cursor_buffer_row;
if buffer.max_point().row >= rows.end {
if buffer.max_point().row >= rows.end.0 {
// If there's a line after the range, delete the \n from the end of the row range
// and position the cursor on the next line.
edit_end = Point::new(rows.end, 0).to_offset(buffer);
edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
cursor_buffer_row = rows.end;
} else {
// If there isn't a line after the range, delete the \n from the line before the
// start of the row range and position the cursor there.
edit_start = edit_start.saturating_sub(1);
edit_end = buffer.len();
cursor_buffer_row = rows.start.saturating_sub(1);
cursor_buffer_row = rows.start.previous_row();
}
let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
*cursor.column_mut() =
cmp::min(goal_display_column, display_map.line_len(cursor.row()));
@ -5190,13 +5193,13 @@ impl Editor {
if self.read_only(cx) {
return;
}
let mut row_ranges = Vec::<Range<u32>>::new();
let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
for selection in self.selections.all::<Point>(cx) {
let start = selection.start.row;
let start = MultiBufferRow(selection.start.row);
let end = if selection.start.row == selection.end.row {
selection.start.row + 1
MultiBufferRow(selection.start.row + 1)
} else {
selection.end.row
MultiBufferRow(selection.end.row)
};
if let Some(last_row_range) = row_ranges.last_mut() {
@ -5212,20 +5215,21 @@ impl Editor {
let mut cursor_positions = Vec::new();
for row_range in &row_ranges {
let anchor = snapshot.anchor_before(Point::new(
row_range.end - 1,
snapshot.line_len(row_range.end - 1),
row_range.end.previous_row().0,
snapshot.line_len(row_range.end.previous_row()),
));
cursor_positions.push(anchor..anchor);
}
self.transact(cx, |this, cx| {
for row_range in row_ranges.into_iter().rev() {
for row in row_range.rev() {
let end_of_line = Point::new(row, snapshot.line_len(row));
let indent = snapshot.indent_size_for_line(row + 1);
let start_of_next_line = Point::new(row + 1, indent.len);
for row in row_range.iter_rows().rev() {
let end_of_line = Point::new(row.0, snapshot.line_len(row));
let next_line_row = row.next_row();
let indent = snapshot.indent_size_for_line(next_line_row);
let start_of_next_line = Point::new(next_line_row.0, indent.len);
let replace = if snapshot.line_len(row + 1) > indent.len {
let replace = if snapshot.line_len(next_line_row) > indent.len {
" "
} else {
""
@ -5342,7 +5346,7 @@ impl Editor {
fn prepare_revert_change(
revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
multi_buffer: &MultiBuffer,
hunk: &DiffHunk<u32>,
hunk: &DiffHunk<MultiBufferRow>,
cx: &mut AppContext,
) -> Option<()> {
let buffer = multi_buffer.buffer(hunk.buffer_id)?;
@ -5396,8 +5400,11 @@ impl Editor {
&mut selections,
);
let start_point = Point::new(start_row, 0);
let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
let start_point = Point::new(start_row.0, 0);
let end_point = Point::new(
end_row.previous_row().0,
buffer.line_len(end_row.previous_row()),
);
let text = buffer
.text_for_range(start_point..end_point)
.collect::<String>();
@ -5411,8 +5418,9 @@ impl Editor {
edits.push((start_point..end_point, lines.join("\n")));
// Selections must change based on added and removed line count
let start_row = start_point.row + added_lines as u32 - removed_lines as u32;
let end_row = start_row + lines_after.saturating_sub(1) as u32;
let start_row =
MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
new_selections.push(Selection {
id: selection.id,
start: start_row,
@ -5438,8 +5446,8 @@ impl Editor {
let new_selections = new_selections
.iter()
.map(|s| {
let start_point = Point::new(s.start, 0);
let end_point = Point::new(s.end, buffer.line_len(s.end));
let start_point = Point::new(s.start.0, 0);
let end_point = Point::new(s.end.0, buffer.line_len(s.end));
Selection {
id: s.id,
start: buffer.point_to_offset(start_point),
@ -5602,14 +5610,17 @@ impl Editor {
// Copy the text from the selected row region and splice it either at the start
// or end of the region.
let start = Point::new(rows.start, 0);
let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
let start = Point::new(rows.start.0, 0);
let end = Point::new(
rows.end.previous_row().0,
buffer.line_len(rows.end.previous_row()),
);
let text = buffer
.text_for_range(start..end)
.chain(Some("\n"))
.collect::<String>();
let insert_location = if upwards {
Point::new(rows.end, 0)
Point::new(rows.end.0, 0)
} else {
start
};
@ -5656,11 +5667,17 @@ impl Editor {
);
// Move the text spanned by the row range to be before the line preceding the row range
if start_row > 0 {
let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
..Point::new(end_row - 1, buffer.line_len(end_row - 1));
if start_row.0 > 0 {
let range_to_move = Point::new(
start_row.previous_row().0,
buffer.line_len(start_row.previous_row()),
)
..Point::new(
end_row.previous_row().0,
buffer.line_len(end_row.previous_row()),
);
let insertion_point = display_map
.prev_line_boundary(Point::new(start_row - 1, 0))
.prev_line_boundary(Point::new(start_row.previous_row().0, 0))
.0;
// Don't move lines across excerpts
@ -5754,9 +5771,12 @@ impl Editor {
);
// Move the text spanned by the row range to be after the last line of the row range
if end_row <= buffer.max_point().row {
let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
if end_row.0 <= buffer.max_point().row {
let range_to_move =
MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
let insertion_point = display_map
.next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
.0;
// Don't move lines across excerpt boundaries
if buffer
@ -5906,7 +5926,9 @@ impl Editor {
clipboard_selections.push(ClipboardSelection {
len,
is_entire_line,
first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
first_line_indent: buffer
.indent_size_for_line(MultiBufferRow(selection.start.row))
.len,
});
}
}
@ -5950,7 +5972,7 @@ impl Editor {
clipboard_selections.push(ClipboardSelection {
len,
is_entire_line,
first_line_indent: buffer.indent_size_for_line(start.row).len,
first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
});
}
}
@ -6847,8 +6869,8 @@ impl Editor {
let max_point = display_map.buffer_snapshot.max_point();
for selection in &mut selections {
let rows = selection.spanned_rows(true, &display_map);
selection.start = Point::new(rows.start, 0);
selection.end = cmp::min(max_point, Point::new(rows.end, 0));
selection.start = Point::new(rows.start.0, 0);
selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
selection.reversed = false;
}
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
@ -6868,7 +6890,7 @@ impl Editor {
let buffer = self.buffer.read(cx).read(cx);
for selection in selections {
for row in selection.start.row..selection.end.row {
let cursor = Point::new(row, buffer.line_len(row));
let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
new_selection_ranges.push(cursor..cursor);
}
new_selection_ranges.push(selection.end..selection.end);
@ -6903,10 +6925,10 @@ impl Editor {
selections.clear();
let mut stack = Vec::new();
for row in range.start.row()..=range.end.row() {
for row in range.start.row().0..=range.end.row().0 {
if let Some(selection) = self.selections.build_columnar_selection(
&display_map,
row,
DisplayRow(row),
&positions,
oldest_selection.reversed,
&text_layout_details,
@ -6927,7 +6949,7 @@ impl Editor {
let mut new_selections = Vec::new();
if above == state.above {
let end_row = if above {
0
DisplayRow(0)
} else {
display_map.max_point().row()
};
@ -6950,9 +6972,9 @@ impl Editor {
while row != end_row {
if above {
row -= 1;
row.0 -= 1;
} else {
row += 1;
row.0 += 1;
}
if let Some(new_selection) = self.selections.build_columnar_selection(
@ -7386,7 +7408,7 @@ impl Editor {
pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
let text_layout_details = &self.text_layout_details(cx);
self.transact(cx, |this, cx| {
let mut selections = this.selections.all::<Point>(cx);
let mut selections = this.selections.all::<MultiBufferPoint>(cx);
let mut edits = Vec::new();
let mut selection_edit_ranges = Vec::new();
let mut last_toggled_row = None;
@ -7396,11 +7418,11 @@ impl Editor {
fn comment_prefix_range(
snapshot: &MultiBufferSnapshot,
row: u32,
row: MultiBufferRow,
comment_prefix: &str,
comment_prefix_whitespace: &str,
) -> Range<Point> {
let start = Point::new(row, snapshot.indent_size_for_line(row).len);
let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
let mut line_bytes = snapshot
.bytes_in_range(start..snapshot.max_point())
@ -7431,11 +7453,11 @@ impl Editor {
fn comment_suffix_range(
snapshot: &MultiBufferSnapshot,
row: u32,
row: MultiBufferRow,
comment_suffix: &str,
comment_suffix_has_leading_space: bool,
) -> Range<Point> {
let end = Point::new(row, snapshot.line_len(row));
let end = Point::new(row.0, snapshot.line_len(row));
let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
let mut line_end_bytes = snapshot
@ -7464,7 +7486,9 @@ impl Editor {
// TODO: Handle selections that cross excerpts
for selection in &mut selections {
let start_column = snapshot.indent_size_for_line(selection.start.row).len;
let start_column = snapshot
.indent_size_for_line(MultiBufferRow(selection.start.row))
.len;
let language = if let Some(language) =
snapshot.language_scope_at(Point::new(selection.start.row, start_column))
{
@ -7477,15 +7501,15 @@ impl Editor {
// If multiple selections contain a given row, avoid processing that
// row more than once.
let mut start_row = selection.start.row;
let mut start_row = MultiBufferRow(selection.start.row);
if last_toggled_row == Some(start_row) {
start_row += 1;
start_row = start_row.next_row();
}
let end_row =
if selection.end.row > selection.start.row && selection.end.column == 0 {
selection.end.row - 1
MultiBufferRow(selection.end.row - 1)
} else {
selection.end.row
MultiBufferRow(selection.end.row)
};
last_toggled_row = Some(end_row);
@ -7506,7 +7530,8 @@ impl Editor {
let mut all_selection_lines_are_comments = true;
for row in start_row..=end_row {
for row in start_row.0..=end_row.0 {
let row = MultiBufferRow(row);
if start_row < end_row && snapshot.is_line_blank(row) {
continue;
}
@ -7596,7 +7621,7 @@ impl Editor {
let snapshot = this.buffer.read(cx).read(cx);
for selection in &mut selections {
while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
match row.cmp(&selection.end.row) {
match row.cmp(&MultiBufferRow(selection.end.row)) {
Ordering::Less => {
suffixes_inserted.next();
continue;
@ -7782,7 +7807,7 @@ impl Editor {
let row = snapshot
.buffer_snapshot
.buffer_line_for_row(point.row)?
.buffer_line_for_row(MultiBufferRow(point.row))?
.1
.start
.row;
@ -8060,9 +8085,9 @@ impl Editor {
&snapshot,
selection.head(),
false,
snapshot
.buffer_snapshot
.git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
snapshot.buffer_snapshot.git_diff_hunks_in_range(
MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
),
cx,
) {
let wrapped_point = Point::zero();
@ -8070,9 +8095,9 @@ impl Editor {
&snapshot,
wrapped_point,
true,
snapshot
.buffer_snapshot
.git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
snapshot.buffer_snapshot.git_diff_hunks_in_range(
MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
),
cx,
);
}
@ -8088,9 +8113,9 @@ impl Editor {
&snapshot,
selection.head(),
false,
snapshot
.buffer_snapshot
.git_diff_hunks_in_range_rev(0..selection.head().row),
snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
MultiBufferRow(0)..MultiBufferRow(selection.head().row),
),
cx,
) {
let wrapped_point = snapshot.buffer_snapshot.max_point();
@ -8098,9 +8123,9 @@ impl Editor {
&snapshot,
wrapped_point,
true,
snapshot
.buffer_snapshot
.git_diff_hunks_in_range_rev(0..wrapped_point.row),
snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
),
cx,
);
}
@ -8111,7 +8136,7 @@ impl Editor {
snapshot: &DisplaySnapshot,
initial_point: Point,
is_wrapped: bool,
hunks: impl Iterator<Item = DiffHunk<u32>>,
hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
cx: &mut ViewContext<Editor>,
) -> bool {
let display_point = initial_point.to_display_point(snapshot);
@ -8972,11 +8997,11 @@ impl Editor {
let mut primary_message = None;
let mut group_end = Point::zero();
let diagnostic_group = buffer
.diagnostic_group::<Point>(group_id)
.diagnostic_group::<MultiBufferPoint>(group_id)
.filter_map(|entry| {
if snapshot.is_line_folded(entry.range.start.row)
if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
&& (entry.range.start.row == entry.range.end.row
|| snapshot.is_line_folded(entry.range.end.row))
|| snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
{
return None;
}
@ -9117,7 +9142,7 @@ impl Editor {
let buffer_start_row = range.start.row;
for row in (0..=range.end.row).rev() {
let fold_range = display_map.foldable_range(row);
let fold_range = display_map.foldable_range(MultiBufferRow(row));
if let Some(fold_range) = fold_range {
if fold_range.end.row >= buffer_start_row {
@ -9159,7 +9184,7 @@ impl Editor {
let mut start = range.start.to_point(&display_map);
let mut end = range.end.to_point(&display_map);
start.column = 0;
end.column = buffer.line_len(end.row);
end.column = buffer.line_len(MultiBufferRow(end.row));
start..end
})
.collect::<Vec<_>>();
@ -9170,9 +9195,9 @@ impl Editor {
pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let intersection_range = Point::new(unfold_at.buffer_row, 0)
let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
..Point::new(
unfold_at.buffer_row,
unfold_at.buffer_row.0,
display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
);
@ -9192,7 +9217,12 @@ impl Editor {
let ranges = selections.into_iter().map(|s| {
if line_mode {
let start = Point::new(s.start.row, 0);
let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
let end = Point::new(
s.end.row,
display_map
.buffer_snapshot
.line_len(MultiBufferRow(s.end.row)),
);
start..end
} else {
s.start..s.end
@ -9330,7 +9360,7 @@ impl Editor {
}
}
pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
self.display_map
.update(cx, |map, cx| map.snapshot(cx))
.longest_row()
@ -9595,7 +9625,7 @@ impl Editor {
let cursor_anchor = self.selections.newest_anchor().head();
let snapshot = self.buffer.read(cx).snapshot(cx);
let buffer_row = cursor_anchor.to_point(&snapshot).row;
let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
snapshot.line_len(buffer_row) == 0
}
@ -9776,7 +9806,7 @@ impl Editor {
&mut self,
exclude_highlights: HashSet<TypeId>,
cx: &mut WindowContext,
) -> BTreeMap<u32, Hsla> {
) -> BTreeMap<DisplayRow, Hsla> {
let snapshot = self.snapshot(cx);
let mut used_highlight_orders = HashMap::default();
self.highlighted_rows
@ -9784,21 +9814,21 @@ impl Editor {
.filter(|(type_id, _)| !exclude_highlights.contains(type_id))
.flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
.fold(
BTreeMap::<u32, Hsla>::new(),
BTreeMap::<DisplayRow, Hsla>::new(),
|mut unique_rows, (highlight_order, anchor_range, hsla)| {
let start_row = anchor_range.start().to_display_point(&snapshot).row();
let end_row = anchor_range.end().to_display_point(&snapshot).row();
for row in start_row..=end_row {
for row in start_row.0..=end_row.0 {
let used_index =
used_highlight_orders.entry(row).or_insert(*highlight_order);
if highlight_order >= used_index {
*used_index = *highlight_order;
match hsla {
Some(hsla) => {
unique_rows.insert(row, *hsla);
unique_rows.insert(DisplayRow(row), *hsla);
}
None => {
unique_rows.remove(&row);
unique_rows.remove(&DisplayRow(row));
}
}
}
@ -10640,15 +10670,15 @@ impl Editor {
fn hunks_for_selections(
multi_buffer_snapshot: &MultiBufferSnapshot,
selections: &[Selection<Anchor>],
) -> Vec<DiffHunk<u32>> {
) -> Vec<DiffHunk<MultiBufferRow>> {
let mut hunks = Vec::with_capacity(selections.len());
let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
HashMap::default();
let display_rows_for_selections = selections.iter().map(|selection| {
let buffer_rows_for_selections = selections.iter().map(|selection| {
let head = selection.head();
let tail = selection.tail();
let start = tail.to_point(&multi_buffer_snapshot).row;
let end = head.to_point(&multi_buffer_snapshot).row;
let start = MultiBufferRow(tail.to_point(&multi_buffer_snapshot).row);
let end = MultiBufferRow(head.to_point(&multi_buffer_snapshot).row);
if start > end {
end..start
} else {
@ -10656,12 +10686,13 @@ fn hunks_for_selections(
}
});
for selected_multi_buffer_rows in display_rows_for_selections {
let query_rows = selected_multi_buffer_rows.start..selected_multi_buffer_rows.end + 1;
for selected_multi_buffer_rows in buffer_rows_for_selections {
let query_rows =
selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
// Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
// when the caret is just above or just below the deleted hunk.
let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
let related_to_selection = if allow_adjacent {
hunk.associated_range.overlaps(&query_rows)
|| hunk.associated_range.start == query_rows.end
@ -10798,13 +10829,13 @@ fn consume_contiguous_rows(
selection: &Selection<Point>,
display_map: &DisplaySnapshot,
selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
) -> (u32, u32) {
) -> (MultiBufferRow, MultiBufferRow) {
contiguous_row_selections.push(selection.clone());
let start_row = selection.start.row;
let start_row = MultiBufferRow(selection.start.row);
let mut end_row = ending_row(selection, display_map);
while let Some(next_selection) = selections.peek() {
if next_selection.start.row <= end_row {
if next_selection.start.row <= end_row.0 {
end_row = ending_row(next_selection, display_map);
contiguous_row_selections.push(selections.next().unwrap().clone());
} else {
@ -10814,11 +10845,11 @@ fn consume_contiguous_rows(
(start_row, end_row)
}
fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
if next_selection.end.column > 0 || next_selection.is_empty() {
display_map.next_line_boundary(next_selection.end).0.row + 1
MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
} else {
next_selection.end.row
MultiBufferRow(next_selection.end.row)
}
}
@ -11300,7 +11331,7 @@ impl ViewInputHandler for Editor {
let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
+ self.gutter_dimensions.width;
let y = line_height * (start.row() as f32 - scroll_position.y);
let y = line_height * (start.row().as_f32() - scroll_position.y);
Some(Bounds {
origin: element_bounds.origin + point(x, y),
@ -11311,8 +11342,11 @@ impl ViewInputHandler for Editor {
trait SelectionExt {
fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
-> Range<u32>;
fn spanned_rows(
&self,
include_end_if_at_line_start: bool,
map: &DisplaySnapshot,
) -> Range<MultiBufferRow>;
}
impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
@ -11336,7 +11370,7 @@ impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
&self,
include_end_if_at_line_start: bool,
map: &DisplaySnapshot,
) -> Range<u32> {
) -> Range<MultiBufferRow> {
let start = self.start.to_point(&map.buffer_snapshot);
let mut end = self.end.to_point(&map.buffer_snapshot);
if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
@ -11345,7 +11379,7 @@ impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
let buffer_start = map.prev_line_boundary(start).0;
let buffer_end = map.next_line_boundary(end).0;
buffer_start.row..buffer_end.row + 1
MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
}
}
@ -11609,3 +11643,91 @@ impl<T: ToOffset> RangeToAnchorExt for Range<T> {
}
}
}
pub trait RowExt {
fn as_f32(&self) -> f32;
fn next_row(&self) -> Self;
fn previous_row(&self) -> Self;
fn minus(&self, other: Self) -> u32;
}
impl RowExt for DisplayRow {
fn as_f32(&self) -> f32 {
self.0 as f32
}
fn next_row(&self) -> Self {
Self(self.0 + 1)
}
fn previous_row(&self) -> Self {
Self(self.0.saturating_sub(1))
}
fn minus(&self, other: Self) -> u32 {
self.0 - other.0
}
}
impl RowExt for MultiBufferRow {
fn as_f32(&self) -> f32 {
self.0 as f32
}
fn next_row(&self) -> Self {
Self(self.0 + 1)
}
fn previous_row(&self) -> Self {
Self(self.0.saturating_sub(1))
}
fn minus(&self, other: Self) -> u32 {
self.0 - other.0
}
}
trait RowRangeExt {
type Row;
fn len(&self) -> usize;
fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
}
impl RowRangeExt for Range<MultiBufferRow> {
type Row = MultiBufferRow;
fn len(&self) -> usize {
(self.end.0 - self.start.0) as usize
}
fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
(self.start.0..self.end.0).map(MultiBufferRow)
}
}
impl RowRangeExt for Range<DisplayRow> {
type Row = DisplayRow;
fn len(&self) -> usize {
(self.end.0 - self.start.0) as usize
}
fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
(self.start.0..self.end.0).map(DisplayRow)
}
}
fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
if hunk.diff_base_byte_range.is_empty() {
DiffHunkStatus::Added
} else if hunk.associated_range.is_empty() {
DiffHunkStatus::Removed
} else {
DiffHunkStatus::Modified
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,29 +4,29 @@ use std::ops::Range;
use git::diff::{DiffHunk, DiffHunkStatus};
use language::Point;
use multi_buffer::Anchor;
use multi_buffer::{Anchor, MultiBufferRow};
use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
AnchorRangeExt,
hunk_status, AnchorRangeExt, DisplayRow,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DisplayDiffHunk {
Folded {
display_row: u32,
display_row: DisplayRow,
},
Unfolded {
diff_base_byte_range: Range<usize>,
display_row_range: Range<u32>,
display_row_range: Range<DisplayRow>,
multi_buffer_range: Range<Anchor>,
status: DiffHunkStatus,
},
}
impl DisplayDiffHunk {
pub fn start_display_row(&self) -> u32 {
pub fn start_display_row(&self) -> DisplayRow {
match self {
&DisplayDiffHunk::Folded { display_row } => display_row,
DisplayDiffHunk::Unfolded {
@ -35,7 +35,7 @@ impl DisplayDiffHunk {
}
}
pub fn contains_display_row(&self, display_row: u32) -> bool {
pub fn contains_display_row(&self, display_row: DisplayRow) -> bool {
let range = match self {
&DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
@ -48,21 +48,26 @@ impl DisplayDiffHunk {
}
}
pub fn diff_hunk_to_display(hunk: &DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {
let hunk_start_point = Point::new(hunk.associated_range.start, 0);
let hunk_start_point_sub = Point::new(hunk.associated_range.start.saturating_sub(1), 0);
pub fn diff_hunk_to_display(
hunk: &DiffHunk<MultiBufferRow>,
snapshot: &DisplaySnapshot,
) -> DisplayDiffHunk {
let hunk_start_point = Point::new(hunk.associated_range.start.0, 0);
let hunk_start_point_sub = Point::new(hunk.associated_range.start.0.saturating_sub(1), 0);
let hunk_end_point_sub = Point::new(
hunk.associated_range
.end
.0
.saturating_sub(1)
.max(hunk.associated_range.start),
.max(hunk.associated_range.start.0),
0,
);
let is_removal = hunk.status() == DiffHunkStatus::Removed;
let status = hunk_status(hunk);
let is_removal = status == DiffHunkStatus::Removed;
let folds_start = Point::new(hunk.associated_range.start.saturating_sub(2), 0);
let folds_end = Point::new(hunk.associated_range.end + 2, 0);
let folds_start = Point::new(hunk.associated_range.start.0.saturating_sub(2), 0);
let folds_end = Point::new(hunk.associated_range.end.0 + 2, 0);
let folds_range = folds_start..folds_end;
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold| {
@ -83,7 +88,7 @@ pub fn diff_hunk_to_display(hunk: &DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
let start = hunk_start_point.to_display_point(snapshot).row();
let hunk_end_row = hunk.associated_range.end.max(hunk.associated_range.start);
let hunk_end_point = Point::new(hunk_end_row, 0);
let hunk_end_point = Point::new(hunk_end_row.0, 0);
let multi_buffer_start = snapshot.buffer_snapshot.anchor_after(hunk_start_point);
let multi_buffer_end = snapshot.buffer_snapshot.anchor_before(hunk_end_point);
@ -92,7 +97,7 @@ pub fn diff_hunk_to_display(hunk: &DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
DisplayDiffHunk::Unfolded {
display_row_range: start..end,
multi_buffer_range: multi_buffer_start..multi_buffer_end,
status: hunk.status(),
status,
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
}
}
@ -100,11 +105,11 @@ pub fn diff_hunk_to_display(hunk: &DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
#[cfg(test)]
mod tests {
use crate::editor_tests::init_test;
use crate::Point;
use crate::{editor_tests::init_test, hunk_status};
use gpui::{Context, TestAppContext};
use language::Capability::ReadWrite;
use multi_buffer::{ExcerptRange, MultiBuffer};
use multi_buffer::{ExcerptRange, MultiBuffer, MultiBufferRow};
use project::{FakeFs, Project};
use unindent::Unindent;
#[gpui::test]
@ -257,26 +262,41 @@ mod tests {
);
let expected = [
(DiffHunkStatus::Modified, 1..2),
(DiffHunkStatus::Modified, 2..3),
(
DiffHunkStatus::Modified,
MultiBufferRow(1)..MultiBufferRow(2),
),
(
DiffHunkStatus::Modified,
MultiBufferRow(2)..MultiBufferRow(3),
),
//TODO: Define better when and where removed hunks show up at range extremities
(DiffHunkStatus::Removed, 6..6),
(DiffHunkStatus::Removed, 8..8),
(DiffHunkStatus::Added, 10..11),
(
DiffHunkStatus::Removed,
MultiBufferRow(6)..MultiBufferRow(6),
),
(
DiffHunkStatus::Removed,
MultiBufferRow(8)..MultiBufferRow(8),
),
(
DiffHunkStatus::Added,
MultiBufferRow(10)..MultiBufferRow(11),
),
];
assert_eq!(
snapshot
.git_diff_hunks_in_range(0..12)
.map(|hunk| (hunk.status(), hunk.associated_range))
.git_diff_hunks_in_range(MultiBufferRow(0)..MultiBufferRow(12))
.map(|hunk| (hunk_status(&hunk), hunk.associated_range))
.collect::<Vec<_>>(),
&expected,
);
assert_eq!(
snapshot
.git_diff_hunks_in_range_rev(0..12)
.map(|hunk| (hunk.status(), hunk.associated_range))
.git_diff_hunks_in_range_rev(MultiBufferRow(0)..MultiBufferRow(12))
.map(|hunk| (hunk_status(&hunk), hunk.associated_range))
.collect::<Vec<_>>(),
expected
.iter()

View File

@ -8,6 +8,7 @@ use git::{
};
use gpui::{Model, ModelContext, Subscription, Task};
use language::{markdown, Bias, Buffer, BufferSnapshot, Edit, LanguageRegistry, ParsedMarkdown};
use multi_buffer::MultiBufferRow;
use project::{Item, Project};
use smallvec::SmallVec;
use sum_tree::SumTree;
@ -185,7 +186,7 @@ impl GitBlame {
pub fn blame_for_rows<'a>(
&'a mut self,
rows: impl 'a + IntoIterator<Item = Option<u32>>,
rows: impl 'a + IntoIterator<Item = Option<MultiBufferRow>>,
cx: &mut ModelContext<Self>,
) -> impl 'a + Iterator<Item = Option<BlameEntry>> {
self.sync(cx);
@ -193,7 +194,7 @@ impl GitBlame {
let mut cursor = self.entries.cursor::<u32>();
rows.into_iter().map(move |row| {
let row = row?;
cursor.seek_forward(&row, Bias::Right, &());
cursor.seek_forward(&row.0, Bias::Right, &());
cursor.item()?.blame.clone()
})
}
@ -532,7 +533,7 @@ mod tests {
($blame:expr, $rows:expr, $expected:expr, $cx:expr) => {
assert_eq!(
$blame
.blame_for_rows($rows.map(Some), $cx)
.blame_for_rows($rows.map(MultiBufferRow).map(Some), $cx)
.collect::<Vec<_>>(),
$expected
);
@ -597,7 +598,7 @@ mod tests {
blame.update(cx, |blame, cx| {
assert_eq!(
blame
.blame_for_rows((0..1).map(Some), cx)
.blame_for_rows((0..1).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>(),
vec![None]
);
@ -661,7 +662,7 @@ mod tests {
// All lines
assert_eq!(
blame
.blame_for_rows((0..8).map(Some), cx)
.blame_for_rows((0..8).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>(),
vec![
Some(blame_entry("1b1b1b", 0..1)),
@ -677,7 +678,7 @@ mod tests {
// Subset of lines
assert_eq!(
blame
.blame_for_rows((1..4).map(Some), cx)
.blame_for_rows((1..4).map(MultiBufferRow).map(Some), cx)
.collect::<Vec<_>>(),
vec![
Some(blame_entry("0d0d0d", 1..2)),
@ -688,7 +689,7 @@ mod tests {
// Subset of lines, with some not displayed
assert_eq!(
blame
.blame_for_rows(vec![Some(1), None, None], cx)
.blame_for_rows(vec![Some(MultiBufferRow(1)), None, None], cx)
.collect::<Vec<_>>(),
vec![Some(blame_entry("0d0d0d", 1..2)), None, None]
);

View File

@ -1,8 +1,8 @@
use crate::{
display_map::{InlayOffset, ToDisplayPoint},
hover_links::{InlayHighlight, RangeInEditor},
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
ExcerptId, Hover, RangeToAnchorExt,
Anchor, AnchorRangeExt, DisplayPoint, DisplayRow, Editor, EditorSettings, EditorSnapshot,
EditorStyle, ExcerptId, Hover, RangeToAnchorExt,
};
use futures::{stream::FuturesUnordered, FutureExt};
use gpui::{
@ -440,7 +440,7 @@ impl HoverState {
&mut self,
snapshot: &EditorSnapshot,
style: &EditorStyle,
visible_rows: Range<u32>,
visible_rows: Range<DisplayRow>,
max_size: Size<Pixels>,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,

View File

@ -7,7 +7,9 @@ use collections::{hash_map, HashMap, HashSet};
use git::diff::{DiffHunk, DiffHunkStatus};
use gpui::{AppContext, Hsla, Model, Task, View};
use language::Buffer;
use multi_buffer::{Anchor, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToPoint};
use multi_buffer::{
Anchor, ExcerptRange, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, ToPoint,
};
use text::{BufferId, Point};
use ui::{
div, ActiveTheme, Context as _, IntoElement, ParentElement, Styled, ViewContext, VisualContext,
@ -16,9 +18,9 @@ use util::{debug_panic, RangeExt};
use crate::{
git::{diff_hunk_to_display, DisplayDiffHunk},
hunks_for_selections, BlockDisposition, BlockId, BlockProperties, BlockStyle, DiffRowHighlight,
Editor, EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt, RevertSelectedHunks,
ToDisplayPoint, ToggleHunkDiff,
hunk_status, hunks_for_selections, BlockDisposition, BlockId, BlockProperties, BlockStyle,
DiffRowHighlight, Editor, EditorSnapshot, ExpandAllHunkDiffs, RangeToAnchorExt,
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
};
#[derive(Debug, Clone)]
@ -90,11 +92,11 @@ impl Editor {
let hunks = snapshot
.display_snapshot
.buffer_snapshot
.git_diff_hunks_in_range(0..u32::MAX)
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
.filter(|hunk| {
let hunk_display_row_range = Point::new(hunk.associated_range.start, 0)
let hunk_display_row_range = Point::new(hunk.associated_range.start.0, 0)
.to_display_point(&snapshot.display_snapshot)
..Point::new(hunk.associated_range.end, 0)
..Point::new(hunk.associated_range.end.0, 0)
.to_display_point(&snapshot.display_snapshot);
let row_range_end =
display_rows_with_expanded_hunks.get(&hunk_display_row_range.start.row());
@ -105,7 +107,7 @@ impl Editor {
fn toggle_hunks_expanded(
&mut self,
hunks_to_toggle: Vec<DiffHunk<u32>>,
hunks_to_toggle: Vec<DiffHunk<MultiBufferRow>>,
cx: &mut ViewContext<Self>,
) {
let previous_toggle_task = self.expanded_hunks.hunk_update_tasks.remove(&None);
@ -176,10 +178,10 @@ impl Editor {
});
for remaining_hunk in hunks_to_toggle {
let remaining_hunk_point_range =
Point::new(remaining_hunk.associated_range.start, 0)
..Point::new(remaining_hunk.associated_range.end, 0);
Point::new(remaining_hunk.associated_range.start.0, 0)
..Point::new(remaining_hunk.associated_range.end.0, 0);
hunks_to_expand.push(HunkToExpand {
status: remaining_hunk.status(),
status: hunk_status(&remaining_hunk),
multi_buffer_range: remaining_hunk_point_range
.to_anchors(&snapshot.buffer_snapshot),
diff_base_byte_range: remaining_hunk.diff_base_byte_range.clone(),
@ -374,9 +376,10 @@ impl Editor {
}
let snapshot = editor.snapshot(cx);
let buffer_snapshot = buffer.read(cx).snapshot();
let mut recalculated_hunks = buffer_snapshot
.git_diff_hunks_in_row_range(0..u32::MAX)
let mut recalculated_hunks = snapshot
.buffer_snapshot
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
.filter(|hunk| hunk.buffer_id == buffer_id)
.fuse()
.peekable();
let mut highlights_to_remove =
@ -402,7 +405,7 @@ impl Editor {
.to_display_point(&snapshot)
.row();
while let Some(buffer_hunk) = recalculated_hunks.peek() {
match diff_hunk_to_display(buffer_hunk, &snapshot) {
match diff_hunk_to_display(&buffer_hunk, &snapshot) {
DisplayDiffHunk::Folded { display_row } => {
recalculated_hunks.next();
if !expanded_hunk.folded
@ -441,7 +444,7 @@ impl Editor {
} else {
if !expanded_hunk.folded
&& expanded_hunk_display_range == hunk_display_range
&& expanded_hunk.status == buffer_hunk.status()
&& expanded_hunk.status == hunk_status(buffer_hunk)
&& expanded_hunk.diff_base_byte_range
== buffer_hunk.diff_base_byte_range
{
@ -614,15 +617,17 @@ fn editor_with_deleted_text(
editor
});
let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row() as u8);
let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row().0 as u8);
(editor_height, editor)
}
fn buffer_diff_hunk(
buffer_snapshot: &MultiBufferSnapshot,
row_range: Range<Point>,
) -> Option<DiffHunk<u32>> {
let mut hunks = buffer_snapshot.git_diff_hunks_in_range(row_range.start.row..row_range.end.row);
) -> Option<DiffHunk<MultiBufferRow>> {
let mut hunks = buffer_snapshot.git_diff_hunks_in_range(
MultiBufferRow(row_range.start.row)..MultiBufferRow(row_range.end.row),
);
let hunk = hunks.next()?;
let second_hunk = hunks.next();
if second_hunk.is_none() {

View File

@ -2,10 +2,12 @@
//! in editor given a given motion (e.g. it handles converting a "move left" command into coordinates in editor). It is exposed mostly for use by vim crate.
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, scroll::ScrollAnchor, CharKind, EditorStyle, ToOffset, ToPoint};
use crate::{
char_kind, scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToOffset, ToPoint,
};
use gpui::{px, Pixels, WindowTextSystem};
use language::Point;
use multi_buffer::MultiBufferSnapshot;
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
use serde::Deserialize;
use std::{ops::Range, sync::Arc};
@ -35,7 +37,7 @@ pub struct TextLayoutDetails {
pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {
if point.column() > 0 {
*point.column_mut() -= 1;
} else if point.row() > 0 {
} else if point.row().0 > 0 {
*point.row_mut() -= 1;
*point.column_mut() = map.line_len(point.row());
}
@ -127,7 +129,7 @@ pub(crate) fn up_by_rows(
_ => map.x_for_display_point(start, text_layout_details),
};
let prev_row = start.row().saturating_sub(row_count);
let prev_row = DisplayRow(start.row().0.saturating_sub(row_count));
let mut point = map.clip_point(
DisplayPoint::new(prev_row, map.line_len(prev_row)),
Bias::Left,
@ -137,7 +139,7 @@ pub(crate) fn up_by_rows(
} else if preserve_column_at_start {
return (start, goal);
} else {
point = DisplayPoint::new(0, 0);
point = DisplayPoint::new(DisplayRow(0), 0);
goal_x = px(0.);
}
@ -166,7 +168,7 @@ pub(crate) fn down_by_rows(
_ => map.x_for_display_point(start, text_layout_details),
};
let new_row = start.row() + row_count;
let new_row = DisplayRow(start.row().0 + row_count);
let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right);
if point.row() > start.row() {
*point.column_mut() = map.display_column_for_x(point.row(), goal_x, text_layout_details)
@ -220,7 +222,9 @@ pub fn indented_line_beginning(
let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right);
let indent_start = Point::new(
point.row,
map.buffer_snapshot.indent_size_for_line(point.row).len,
map.buffer_snapshot
.indent_size_for_line(MultiBufferRow(point.row))
.len,
)
.to_display_point(map);
let line_start = map.prev_line_boundary(point).1;
@ -326,7 +330,7 @@ pub fn start_of_paragraph(
let mut found_non_blank_line = false;
for row in (0..point.row + 1).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
if found_non_blank_line && blank {
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
@ -349,13 +353,13 @@ pub fn end_of_paragraph(
mut count: usize,
) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
if point.row == map.max_buffer_row().0 {
return map.max_point();
}
let mut found_non_blank_line = false;
for row in point.row..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
for row in point.row..map.max_buffer_row().next_row().0 {
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
if found_non_blank_line && blank {
if count <= 1 {
return Point::new(row, 0).to_display_point(map);
@ -549,13 +553,16 @@ pub fn split_display_range_by_lines(
let mut start = range.start;
// Loop over all the covered rows until the one containing the range end
for row in range.start.row()..range.end.row() {
let row_end_column = map.line_len(row);
let end = map.clip_point(DisplayPoint::new(row, row_end_column), Bias::Left);
for row in range.start.row().0..range.end.row().0 {
let row_end_column = map.line_len(DisplayRow(row));
let end = map.clip_point(
DisplayPoint::new(DisplayRow(row), row_end_column),
Bias::Left,
);
if start != end {
result.push(start..end);
}
start = map.clip_point(DisplayPoint::new(row + 1, 0), Bias::Left);
start = map.clip_point(DisplayPoint::new(DisplayRow(row + 1), 0), Bias::Left);
}
// Add the final range from the start of the last end to the original range end.
@ -570,7 +577,7 @@ mod tests {
use crate::{
display_map::Inlay,
test::{editor_test_context::EditorTestContext, marked_display_snapshot},
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
Buffer, DisplayMap, DisplayRow, ExcerptRange, InlayId, MultiBuffer,
};
use gpui::{font, Context as _};
use language::Capability;
@ -900,126 +907,126 @@ mod tests {
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
let col_2_x =
snapshot.x_for_display_point(DisplayPoint::new(2, 2), &text_layout_details);
let col_2_x = snapshot
.x_for_display_point(DisplayPoint::new(DisplayRow(2), 2), &text_layout_details);
// Can't move up into the first excerpt's header
assert_eq!(
up(
&snapshot,
DisplayPoint::new(2, 2),
DisplayPoint::new(DisplayRow(2), 2),
SelectionGoal::HorizontalPosition(col_2_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(2, 0),
DisplayPoint::new(DisplayRow(2), 0),
SelectionGoal::HorizontalPosition(0.0)
),
);
assert_eq!(
up(
&snapshot,
DisplayPoint::new(2, 0),
DisplayPoint::new(DisplayRow(2), 0),
SelectionGoal::None,
false,
&text_layout_details
),
(
DisplayPoint::new(2, 0),
DisplayPoint::new(DisplayRow(2), 0),
SelectionGoal::HorizontalPosition(0.0)
),
);
let col_4_x =
snapshot.x_for_display_point(DisplayPoint::new(3, 4), &text_layout_details);
let col_4_x = snapshot
.x_for_display_point(DisplayPoint::new(DisplayRow(3), 4), &text_layout_details);
// Move up and down within first excerpt
assert_eq!(
up(
&snapshot,
DisplayPoint::new(3, 4),
DisplayPoint::new(DisplayRow(3), 4),
SelectionGoal::HorizontalPosition(col_4_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(2, 3),
DisplayPoint::new(DisplayRow(2), 3),
SelectionGoal::HorizontalPosition(col_4_x.0)
),
);
assert_eq!(
down(
&snapshot,
DisplayPoint::new(2, 3),
DisplayPoint::new(DisplayRow(2), 3),
SelectionGoal::HorizontalPosition(col_4_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(3, 4),
DisplayPoint::new(DisplayRow(3), 4),
SelectionGoal::HorizontalPosition(col_4_x.0)
),
);
let col_5_x =
snapshot.x_for_display_point(DisplayPoint::new(6, 5), &text_layout_details);
let col_5_x = snapshot
.x_for_display_point(DisplayPoint::new(DisplayRow(6), 5), &text_layout_details);
// Move up and down across second excerpt's header
assert_eq!(
up(
&snapshot,
DisplayPoint::new(6, 5),
DisplayPoint::new(DisplayRow(6), 5),
SelectionGoal::HorizontalPosition(col_5_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(3, 4),
DisplayPoint::new(DisplayRow(3), 4),
SelectionGoal::HorizontalPosition(col_5_x.0)
),
);
assert_eq!(
down(
&snapshot,
DisplayPoint::new(3, 4),
DisplayPoint::new(DisplayRow(3), 4),
SelectionGoal::HorizontalPosition(col_5_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(6, 5),
DisplayPoint::new(DisplayRow(6), 5),
SelectionGoal::HorizontalPosition(col_5_x.0)
),
);
let max_point_x =
snapshot.x_for_display_point(DisplayPoint::new(7, 2), &text_layout_details);
let max_point_x = snapshot
.x_for_display_point(DisplayPoint::new(DisplayRow(7), 2), &text_layout_details);
// Can't move down off the end
assert_eq!(
down(
&snapshot,
DisplayPoint::new(7, 0),
DisplayPoint::new(DisplayRow(7), 0),
SelectionGoal::HorizontalPosition(0.0),
false,
&text_layout_details
),
(
DisplayPoint::new(7, 2),
DisplayPoint::new(DisplayRow(7), 2),
SelectionGoal::HorizontalPosition(max_point_x.0)
),
);
assert_eq!(
down(
&snapshot,
DisplayPoint::new(7, 2),
DisplayPoint::new(DisplayRow(7), 2),
SelectionGoal::HorizontalPosition(max_point_x.0),
false,
&text_layout_details
),
(
DisplayPoint::new(7, 2),
DisplayPoint::new(DisplayRow(7), 2),
SelectionGoal::HorizontalPosition(max_point_x.0)
),
);

View File

@ -6,8 +6,8 @@ use crate::{
display_map::{DisplaySnapshot, ToDisplayPoint},
hover_popover::hide_hover,
persistence::DB,
Anchor, DisplayPoint, Editor, EditorEvent, EditorMode, EditorSettings, InlayHintRefreshReason,
MultiBufferSnapshot, ToPoint,
Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings,
InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint,
};
pub use autoscroll::{Autoscroll, AutoscrollStrategy};
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext, WindowContext};
@ -48,7 +48,7 @@ impl ScrollAnchor {
if self.anchor == Anchor::min() {
scroll_position.y = 0.;
} else {
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
let scroll_top = self.anchor.to_display_point(snapshot).row().as_f32();
scroll_position.y = scroll_top + scroll_position.y;
}
scroll_position
@ -200,7 +200,7 @@ impl ScrollManager {
)
} else {
let scroll_top_buffer_point =
DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
DisplayPoint::new(DisplayRow(scroll_position.y as u32), 0).to_point(&map);
let top_anchor = map
.buffer_snapshot
.anchor_at(scroll_top_buffer_point, Bias::Right);
@ -210,7 +210,7 @@ impl ScrollManager {
anchor: top_anchor,
offset: point(
scroll_position.x.max(0.),
scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
scroll_position.y - top_anchor.to_display_point(&map).row().as_f32(),
),
},
scroll_top_buffer_point.row,
@ -472,7 +472,7 @@ impl Editor {
}
if let Some(visible_lines) = self.visible_line_count() {
if newest_head.row() < screen_top.row() + visible_lines as u32 {
if newest_head.row() < DisplayRow(screen_top.row().0 + visible_lines as u32) {
return Ordering::Equal;
}
}

View File

@ -37,7 +37,7 @@ impl Editor {
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
*new_screen_top.row_mut() = new_screen_top.row().0.saturating_sub(scroll_margin_rows);
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
@ -60,7 +60,7 @@ impl Editor {
};
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
*new_screen_top.row_mut() = new_screen_top.row().0.saturating_sub(visible_rows / 2);
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
@ -86,6 +86,7 @@ impl Editor {
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top
.row()
.0
.saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);

View File

@ -5,7 +5,8 @@ use gpui::{px, Bounds, Pixels, ViewContext};
use language::Point;
use crate::{
display_map::ToDisplayPoint, DiffRowHighlight, Editor, EditorMode, LineWithInvisibles,
display_map::ToDisplayPoint, DiffRowHighlight, DisplayRow, Editor, EditorMode,
LineWithInvisibles, RowExt,
};
#[derive(PartialEq, Eq, Clone, Copy)]
@ -88,9 +89,9 @@ impl Editor {
}
}
let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
(display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
(display_map.max_point().row().as_f32() - visible_lines + 1.).max(0.)
} else {
display_map.max_point().row() as f32
display_map.max_point().row().as_f32()
};
if scroll_position.y > max_scroll_top {
scroll_position.y = max_scroll_top;
@ -113,7 +114,7 @@ impl Editor {
)
.first_entry()
{
target_top = *first_highlighted_row.key() as f32;
target_top = first_highlighted_row.key().as_f32();
target_bottom = target_top + 1.;
} else {
let selections = self.selections.all::<Point>(cx);
@ -122,14 +123,16 @@ impl Editor {
.unwrap()
.head()
.to_display_point(&display_map)
.row() as f32;
.row()
.as_f32();
target_bottom = selections
.last()
.unwrap()
.head()
.to_display_point(&display_map)
.row() as f32
+ 1.0;
.row()
.next_row()
.as_f32();
// If the selections can't all fit on screen, scroll to the newest.
if autoscroll == Autoscroll::newest()
@ -141,7 +144,8 @@ impl Editor {
.unwrap()
.head()
.to_display_point(&display_map)
.row() as f32;
.row()
.as_f32();
target_top = newest_selection_top;
target_bottom = newest_selection_top + 1.;
}
@ -227,7 +231,7 @@ impl Editor {
pub(crate) fn autoscroll_horizontally(
&mut self,
start_row: u32,
start_row: DisplayRow,
viewport_width: Pixels,
scroll_width: Pixels,
max_glyph_width: Pixels,
@ -245,16 +249,18 @@ impl Editor {
target_right = px(0.);
for selection in selections {
let head = selection.head().to_display_point(&display_map);
if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
if head.row() >= start_row
&& head.row() < DisplayRow(start_row.0 + layouts.len() as u32)
{
let start_column = head.column().saturating_sub(3);
let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
target_left = target_left.min(
layouts[(head.row() - start_row) as usize]
layouts[head.row().minus(start_row) as usize]
.line
.x_for_index(start_column as usize),
);
target_right = target_right.max(
layouts[(head.row() - start_row) as usize]
layouts[head.row().minus(start_row) as usize]
.line
.x_for_index(end_column as usize)
+ max_glyph_width,

View File

@ -14,7 +14,8 @@ use util::post_inc;
use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
movement::TextLayoutDetails,
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
ToOffset,
};
#[derive(Debug, Clone)]
@ -308,7 +309,7 @@ impl SelectionsCollection {
pub fn build_columnar_selection(
&mut self,
display_map: &DisplaySnapshot,
row: u32,
row: DisplayRow,
positions: &Range<Pixels>,
reversed: bool,
text_layout_details: &TextLayoutDetails,

View File

@ -81,23 +81,30 @@ pub fn editor_hunks(
editor: &Editor,
snapshot: &DisplaySnapshot,
cx: &mut ViewContext<'_, Editor>,
) -> Vec<(String, git::diff::DiffHunkStatus, core::ops::Range<u32>)> {
) -> Vec<(
String,
git::diff::DiffHunkStatus,
std::ops::Range<crate::DisplayRow>,
)> {
use multi_buffer::MultiBufferRow;
use text::Point;
use crate::hunk_status;
snapshot
.buffer_snapshot
.git_diff_hunks_in_range(0..u32::MAX)
.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX)
.map(|hunk| {
let display_range = Point::new(hunk.associated_range.start, 0)
let display_range = Point::new(hunk.associated_range.start.0, 0)
.to_display_point(snapshot)
.row()
..Point::new(hunk.associated_range.end, 0)
..Point::new(hunk.associated_range.end.0, 0)
.to_display_point(snapshot)
.row();
let (_, buffer, _) = editor
.buffer()
.read(cx)
.excerpt_containing(Point::new(hunk.associated_range.start, 0), cx)
.excerpt_containing(Point::new(hunk.associated_range.start.0, 0), cx)
.expect("no excerpt for expanded buffer's hunk start");
let diff_base = buffer
.read(cx)
@ -105,7 +112,7 @@ pub fn editor_hunks(
.expect("should have a diff base for expanded hunk")
.slice(hunk.diff_base_byte_range.clone())
.to_string();
(diff_base, hunk.status(), display_range)
(diff_base, hunk_status(&hunk), display_range)
})
.collect()
}
@ -115,7 +122,11 @@ pub fn expanded_hunks(
editor: &Editor,
snapshot: &DisplaySnapshot,
cx: &mut ViewContext<'_, Editor>,
) -> Vec<(String, git::diff::DiffHunkStatus, core::ops::Range<u32>)> {
) -> Vec<(
String,
git::diff::DiffHunkStatus,
std::ops::Range<crate::DisplayRow>,
)> {
editor
.expanded_hunks
.hunks(false)
@ -150,7 +161,9 @@ pub fn expanded_hunks(
pub fn expanded_hunks_background_highlights(
editor: &mut Editor,
cx: &mut gpui::WindowContext,
) -> Vec<std::ops::RangeInclusive<u32>> {
) -> Vec<std::ops::RangeInclusive<crate::DisplayRow>> {
use crate::DisplayRow;
let mut highlights = Vec::new();
let mut range_start = 0;
@ -159,19 +172,19 @@ pub fn expanded_hunks_background_highlights(
{
match previous_highlighted_row {
Some(previous_row) => {
if previous_row + 1 != highlighted_row {
highlights.push(range_start..=previous_row);
range_start = highlighted_row;
if previous_row + 1 != highlighted_row.0 {
highlights.push(DisplayRow(range_start)..=DisplayRow(previous_row));
range_start = highlighted_row.0;
}
}
None => {
range_start = highlighted_row;
range_start = highlighted_row.0;
}
}
previous_highlighted_row = Some(highlighted_row);
previous_highlighted_row = Some(highlighted_row.0);
}
if let Some(previous_row) = previous_highlighted_row {
highlights.push(range_start..=previous_row);
highlights.push(DisplayRow(range_start)..=DisplayRow(previous_row));
}
highlights

View File

@ -1,5 +1,6 @@
use crate::{
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
RowExt,
};
use collections::BTreeMap;
use futures::Future;
@ -256,7 +257,7 @@ impl EditorTestContext {
let details = editor.text_layout_details(cx);
let y = pixel_position.y
+ line_height * (display_point.row() as f32 - newest_point.row() as f32);
+ line_height * (display_point.row().as_f32() - newest_point.row().as_f32());
let x = pixel_position.x + snapshot.x_for_display_point(display_point, &details)
- snapshot.x_for_display_point(newest_point, &details);
Point::new(x, y)

View File

@ -30,18 +30,6 @@ pub struct DiffHunk<T> {
pub diff_base_byte_range: Range<usize>,
}
impl DiffHunk<u32> {
pub fn status(&self) -> DiffHunkStatus {
if self.diff_base_byte_range.is_empty() {
DiffHunkStatus::Added
} else if self.associated_range.is_empty() {
DiffHunkStatus::Removed
} else {
DiffHunkStatus::Modified
}
}
}
impl sum_tree::Item for DiffHunk<Anchor> {
type Summary = DiffHunkSummary;

View File

@ -352,6 +352,7 @@ mod tests {
editor
.highlighted_display_rows(HashSet::default(), cx)
.into_keys()
.map(|r| r.0)
.collect()
})
}

View File

@ -75,6 +75,8 @@ pub enum Capability {
ReadOnly,
}
pub type BufferRow = u32;
/// An in-memory representation of a source code file, including its text,
/// syntax trees, git status, and diagnostics.
pub struct Buffer {
@ -3104,7 +3106,7 @@ impl BufferSnapshot {
/// row range.
pub fn git_diff_hunks_in_row_range(
&self,
range: Range<u32>,
range: Range<BufferRow>,
) -> impl '_ + Iterator<Item = git::diff::DiffHunk<u32>> {
self.git_diff.hunks_in_row_range(range, self)
}

View File

@ -35,6 +35,7 @@ log.workspace = true
parking_lot.workspace = true
rand.workspace = true
settings.workspace = true
serde.workspace = true
smallvec.workspace = true
sum_tree.workspace = true
text.workspace = true

View File

@ -11,9 +11,9 @@ use itertools::Itertools;
use language::{
char_kind,
language_settings::{language_settings, LanguageSettings},
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, Capability, CharKind, Chunk, CursorShape,
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharKind, Chunk,
CursorShape, DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt,
OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
};
use smallvec::SmallVec;
@ -100,6 +100,16 @@ pub enum Event {
DiagnosticsUpdated,
}
pub type MultiBufferPoint = Point;
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, serde::Deserialize)]
#[serde(transparent)]
pub struct MultiBufferRow(pub u32);
impl MultiBufferRow {
pub const MIN: Self = Self(0);
pub const MAX: Self = Self(u32::MAX);
}
#[derive(Clone)]
struct History {
next_transaction_id: TransactionId,
@ -165,8 +175,7 @@ pub struct MultiBufferSnapshot {
/// A boundary between [`Excerpt`]s in a [`MultiBuffer`]
pub struct ExcerptBoundary {
pub id: ExcerptId,
/// The row in the `MultiBuffer` where the boundary is located
pub row: u32,
pub row: MultiBufferRow,
pub buffer: BufferSnapshot,
pub range: ExcerptRange<text::Anchor>,
/// It's possible to have multiple excerpts in the same buffer,
@ -190,7 +199,7 @@ struct Excerpt {
/// The range of the buffer to be shown in the excerpt
range: ExcerptRange<text::Anchor>,
/// The last row in the excerpted slice of the buffer
max_buffer_row: u32,
max_buffer_row: BufferRow,
/// A summary of the text in the excerpt
text_summary: TextSummary,
has_trailing_newline: bool,
@ -229,7 +238,7 @@ struct ExcerptSummary {
/// The location of the last [`Excerpt`] being summarized
excerpt_locator: Locator,
/// The maximum row of the [`Excerpt`]s being summarized
max_buffer_row: u32,
max_buffer_row: MultiBufferRow,
text: TextSummary,
}
@ -2111,8 +2120,8 @@ impl MultiBufferSnapshot {
self.chunks(range, false).map(|chunk| chunk.text)
}
pub fn is_line_blank(&self, row: u32) -> bool {
self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
pub fn is_line_blank(&self, row: MultiBufferRow) -> bool {
self.text_for_range(Point::new(row.0, 0)..Point::new(row.0, self.line_len(row)))
.all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
}
@ -2181,7 +2190,7 @@ impl MultiBufferSnapshot {
self.excerpts.summary().text.len == 0
}
pub fn max_buffer_row(&self) -> u32 {
pub fn max_buffer_row(&self) -> MultiBufferRow {
self.excerpts.summary().max_buffer_row
}
@ -2312,7 +2321,7 @@ impl MultiBufferSnapshot {
}
}
pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows {
pub fn buffer_rows(&self, start_row: MultiBufferRow) -> MultiBufferRows {
let mut result = MultiBufferRows {
buffer_row_range: 0..0,
excerpts: self.excerpts.cursor(),
@ -2509,7 +2518,7 @@ impl MultiBufferSnapshot {
&self,
rows: impl IntoIterator<Item = u32>,
cx: &AppContext,
) -> BTreeMap<u32, IndentSize> {
) -> BTreeMap<MultiBufferRow, IndentSize> {
let mut result = BTreeMap::new();
let mut rows_for_excerpt = Vec::new();
@ -2555,16 +2564,19 @@ impl MultiBufferSnapshot {
let buffer_indents = excerpt
.buffer
.suggested_indents(buffer_rows, single_indent_size);
let multibuffer_indents = buffer_indents
.into_iter()
.map(|(row, indent)| (start_multibuffer_row + row - start_buffer_row, indent));
let multibuffer_indents = buffer_indents.into_iter().map(|(row, indent)| {
(
MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
indent,
)
});
result.extend(multibuffer_indents);
}
result
}
pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
pub fn indent_size_for_line(&self, row: MultiBufferRow) -> IndentSize {
if let Some((buffer, range)) = self.buffer_line_for_row(row) {
let mut size = buffer.indent_size_for_line(range.start.row);
size.len = size
@ -2577,9 +2589,9 @@ impl MultiBufferSnapshot {
}
}
pub fn prev_non_blank_row(&self, mut row: u32) -> Option<u32> {
while row > 0 {
row -= 1;
pub fn prev_non_blank_row(&self, mut row: MultiBufferRow) -> Option<MultiBufferRow> {
while row.0 > 0 {
row.0 -= 1;
if !self.is_line_blank(row) {
return Some(row);
}
@ -2587,7 +2599,7 @@ impl MultiBufferSnapshot {
None
}
pub fn line_len(&self, row: u32) -> u32 {
pub fn line_len(&self, row: MultiBufferRow) -> u32 {
if let Some((_, range)) = self.buffer_line_for_row(row) {
range.end.column - range.start.column
} else {
@ -2595,15 +2607,18 @@ impl MultiBufferSnapshot {
}
}
pub fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
pub fn buffer_line_for_row(
&self,
row: MultiBufferRow,
) -> Option<(&BufferSnapshot, Range<Point>)> {
let mut cursor = self.excerpts.cursor::<Point>();
let point = Point::new(row, 0);
let point = Point::new(row.0, 0);
cursor.seek(&point, Bias::Right, &());
if cursor.item().is_none() && *cursor.start() == point {
cursor.prev(&());
}
if let Some(excerpt) = cursor.item() {
let overshoot = row - cursor.start().row;
let overshoot = row.0 - cursor.start().row;
let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
let excerpt_end = excerpt.range.context.end.to_point(&excerpt.buffer);
let buffer_row = excerpt_start.row + overshoot;
@ -3028,7 +3043,7 @@ impl MultiBufferSnapshot {
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
let boundary = ExcerptBoundary {
id: excerpt.id,
row: cursor.start().1.row,
row: MultiBufferRow(cursor.start().1.row),
buffer: excerpt.buffer.clone(),
range: excerpt.range.clone(),
starts_new_buffer,
@ -3312,11 +3327,11 @@ impl MultiBufferSnapshot {
pub fn git_diff_hunks_in_range_rev(
&self,
row_range: Range<u32>,
) -> impl Iterator<Item = DiffHunk<u32>> + '_ {
row_range: Range<MultiBufferRow>,
) -> impl Iterator<Item = DiffHunk<MultiBufferRow>> + '_ {
let mut cursor = self.excerpts.cursor::<Point>();
cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &());
cursor.seek(&Point::new(row_range.end.0, 0), Bias::Left, &());
if cursor.item().is_none() {
cursor.prev(&());
}
@ -3325,7 +3340,7 @@ impl MultiBufferSnapshot {
let excerpt = cursor.item()?;
let multibuffer_start = *cursor.start();
let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
if multibuffer_start.row >= row_range.end {
if multibuffer_start.row >= row_range.end.0 {
return None;
}
@ -3334,15 +3349,15 @@ impl MultiBufferSnapshot {
let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
if row_range.start > multibuffer_start.row {
if row_range.start.0 > multibuffer_start.row {
let buffer_start_point =
excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
excerpt_start_point + Point::new(row_range.start.0 - multibuffer_start.row, 0);
buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
}
if row_range.end < multibuffer_end.row {
if row_range.end.0 < multibuffer_end.row {
let buffer_end_point =
excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
excerpt_start_point + Point::new(row_range.end.0 - multibuffer_start.row, 0);
buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
}
@ -3363,7 +3378,7 @@ impl MultiBufferSnapshot {
.saturating_sub(excerpt_start_point.row);
DiffHunk {
associated_range: start..end,
associated_range: MultiBufferRow(start)..MultiBufferRow(end),
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
buffer_range: hunk.buffer_range.clone(),
buffer_id: hunk.buffer_id,
@ -3379,11 +3394,11 @@ impl MultiBufferSnapshot {
pub fn git_diff_hunks_in_range(
&self,
row_range: Range<u32>,
) -> impl Iterator<Item = DiffHunk<u32>> + '_ {
row_range: Range<MultiBufferRow>,
) -> impl Iterator<Item = DiffHunk<MultiBufferRow>> + '_ {
let mut cursor = self.excerpts.cursor::<Point>();
cursor.seek(&Point::new(row_range.start, 0), Bias::Left, &());
cursor.seek(&Point::new(row_range.start.0, 0), Bias::Left, &());
std::iter::from_fn(move || {
let excerpt = cursor.item()?;
@ -3392,25 +3407,25 @@ impl MultiBufferSnapshot {
let mut buffer_start = excerpt.range.context.start;
let mut buffer_end = excerpt.range.context.end;
let excerpt_rows = match multibuffer_start.row.cmp(&row_range.end) {
let excerpt_rows = match multibuffer_start.row.cmp(&row_range.end.0) {
cmp::Ordering::Less => {
let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
if row_range.start > multibuffer_start.row {
if row_range.start.0 > multibuffer_start.row {
let buffer_start_point = excerpt_start_point
+ Point::new(row_range.start - multibuffer_start.row, 0);
+ Point::new(row_range.start.0 - multibuffer_start.row, 0);
buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
}
if row_range.end < multibuffer_end.row {
if row_range.end.0 < multibuffer_end.row {
let buffer_end_point = excerpt_start_point
+ Point::new(row_range.end - multibuffer_start.row, 0);
+ Point::new(row_range.end.0 - multibuffer_start.row, 0);
buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
}
excerpt_start_point.row..excerpt_end_point.row
}
cmp::Ordering::Equal if row_range.end == 0 => {
cmp::Ordering::Equal if row_range.end.0 == 0 => {
buffer_end = buffer_start;
0..0
}
@ -3422,7 +3437,7 @@ impl MultiBufferSnapshot {
.git_diff_hunks_intersecting_range(buffer_start..buffer_end)
.map(move |hunk| {
let buffer_range = if excerpt_rows.start == 0 && excerpt_rows.end == 0 {
0..1
MultiBufferRow(0)..MultiBufferRow(1)
} else {
let start = multibuffer_start.row
+ hunk
@ -3435,7 +3450,7 @@ impl MultiBufferSnapshot {
.end
.min(excerpt_rows.end + 1)
.saturating_sub(excerpt_rows.start);
start..end
MultiBufferRow(start)..MultiBufferRow(end)
};
DiffHunk {
associated_range: buffer_range,
@ -4096,7 +4111,7 @@ impl sum_tree::Item for Excerpt {
ExcerptSummary {
excerpt_id: self.id,
excerpt_locator: self.locator.clone(),
max_buffer_row: self.max_buffer_row,
max_buffer_row: MultiBufferRow(self.max_buffer_row),
text,
}
}
@ -4198,22 +4213,22 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptId> {
}
impl<'a> MultiBufferRows<'a> {
pub fn seek(&mut self, row: u32) {
pub fn seek(&mut self, row: MultiBufferRow) {
self.buffer_row_range = 0..0;
self.excerpts
.seek_forward(&Point::new(row, 0), Bias::Right, &());
.seek_forward(&Point::new(row.0, 0), Bias::Right, &());
if self.excerpts.item().is_none() {
self.excerpts.prev(&());
if self.excerpts.item().is_none() && row == 0 {
if self.excerpts.item().is_none() && row.0 == 0 {
self.buffer_row_range = 0..1;
return;
}
}
if let Some(excerpt) = self.excerpts.item() {
let overshoot = row - self.excerpts.start().row;
let overshoot = row.0 - self.excerpts.start().row;
let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer).row;
self.buffer_row_range.start = excerpt_start + overshoot;
self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1;
@ -4546,7 +4561,7 @@ mod tests {
assert_eq!(snapshot.text(), buffer.read(cx).text());
assert_eq!(
snapshot.buffer_rows(0).collect::<Vec<_>>(),
snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
(0..buffer.read(cx).row_count())
.map(Some)
.collect::<Vec<_>>()
@ -4557,7 +4572,7 @@ mod tests {
assert_eq!(snapshot.text(), buffer.read(cx).text());
assert_eq!(
snapshot.buffer_rows(0).collect::<Vec<_>>(),
snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
(0..buffer.read(cx).row_count())
.map(Some)
.collect::<Vec<_>>()
@ -4685,27 +4700,33 @@ mod tests {
)
);
assert_eq!(
snapshot.buffer_rows(0).collect::<Vec<_>>(),
snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
[Some(1), Some(2), Some(3), Some(4), Some(3)]
);
assert_eq!(
snapshot.buffer_rows(2).collect::<Vec<_>>(),
snapshot.buffer_rows(MultiBufferRow(2)).collect::<Vec<_>>(),
[Some(3), Some(4), Some(3)]
);
assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]);
assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []);
assert_eq!(
snapshot.buffer_rows(MultiBufferRow(4)).collect::<Vec<_>>(),
[Some(3)]
);
assert_eq!(
snapshot.buffer_rows(MultiBufferRow(5)).collect::<Vec<_>>(),
[]
);
assert_eq!(
boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
&[
(0, "bbbb\nccccc".to_string(), true),
(2, "ddd\neeee".to_string(), false),
(4, "jj".to_string(), true),
(MultiBufferRow(0), "bbbb\nccccc".to_string(), true),
(MultiBufferRow(2), "ddd\neeee".to_string(), false),
(MultiBufferRow(4), "jj".to_string(), true),
]
);
assert_eq!(
boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
&[(0, "bbbb\nccccc".to_string(), true)]
&[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)]
);
assert_eq!(
boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
@ -4717,19 +4738,19 @@ mod tests {
);
assert_eq!(
boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
&[(2, "ddd\neeee".to_string(), false)]
&[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
&[(2, "ddd\neeee".to_string(), false)]
&[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
&[(2, "ddd\neeee".to_string(), false)]
&[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
&[(4, "jj".to_string(), true)]
&[(MultiBufferRow(4), "jj".to_string(), true)]
);
assert_eq!(
boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
@ -4812,7 +4833,7 @@ mod tests {
fn boundaries_in_range(
range: Range<Point>,
snapshot: &MultiBufferSnapshot,
) -> Vec<(u32, String, bool)> {
) -> Vec<(MultiBufferRow, String, bool)> {
snapshot
.excerpt_boundaries_in_range(range)
.map(|boundary| {
@ -5100,8 +5121,14 @@ mod tests {
let snapshot = multibuffer.read(cx).snapshot(cx);
assert_eq!(snapshot.text(), "");
assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), &[Some(0)]);
assert_eq!(snapshot.buffer_rows(1).collect::<Vec<_>>(), &[]);
assert_eq!(
snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
&[Some(0)]
);
assert_eq!(
snapshot.buffer_rows(MultiBufferRow(1)).collect::<Vec<_>>(),
&[]
);
}
#[gpui::test]
@ -5518,14 +5545,16 @@ mod tests {
log::info!("MultiBuffer text: {:?}", expected_text);
assert_eq!(
snapshot.buffer_rows(0).collect::<Vec<_>>(),
snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
expected_buffer_rows,
);
for _ in 0..5 {
let start_row = rng.gen_range(0..=expected_buffer_rows.len());
assert_eq!(
snapshot.buffer_rows(start_row as u32).collect::<Vec<_>>(),
snapshot
.buffer_rows(MultiBufferRow(start_row as u32))
.collect::<Vec<_>>(),
&expected_buffer_rows[start_row..],
"buffer_rows({})",
start_row
@ -5533,7 +5562,7 @@ mod tests {
}
assert_eq!(
snapshot.max_buffer_row(),
snapshot.max_buffer_row().0,
expected_buffer_rows.into_iter().flatten().max().unwrap()
);
@ -5666,7 +5695,7 @@ mod tests {
for (row, line) in expected_text.split('\n').enumerate() {
assert_eq!(
snapshot.line_len(row as u32),
snapshot.line_len(MultiBufferRow(row as u32)),
line.len() as u32,
"line_len({}).",
row

View File

@ -486,6 +486,7 @@ mod tests {
editor
.highlighted_display_rows(HashSet::default(), cx)
.into_keys()
.map(|r| r.0)
.collect()
})
}

View File

@ -1090,7 +1090,7 @@ mod tests {
use std::ops::Range;
use super::*;
use editor::{DisplayPoint, Editor};
use editor::{display_map::DisplayRow, DisplayPoint, Editor};
use gpui::{Context, Hsla, TestAppContext, VisualTestContext};
use language::Buffer;
use project::Project;
@ -1157,8 +1157,8 @@ mod tests {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[
DisplayPoint::new(2, 17)..DisplayPoint::new(2, 19),
DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
DisplayPoint::new(DisplayRow(2), 17)..DisplayPoint::new(DisplayRow(2), 19),
DisplayPoint::new(DisplayRow(2), 43)..DisplayPoint::new(DisplayRow(2), 45),
]
);
});
@ -1172,7 +1172,7 @@ mod tests {
editor.update(cx, |editor, cx| {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),]
&[DisplayPoint::new(DisplayRow(2), 43)..DisplayPoint::new(DisplayRow(2), 45),]
);
});
@ -1186,13 +1186,13 @@ mod tests {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[
DisplayPoint::new(0, 24)..DisplayPoint::new(0, 26),
DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43),
DisplayPoint::new(2, 71)..DisplayPoint::new(2, 73),
DisplayPoint::new(3, 1)..DisplayPoint::new(3, 3),
DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13),
DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58),
DisplayPoint::new(3, 60)..DisplayPoint::new(3, 62),
DisplayPoint::new(DisplayRow(0), 24)..DisplayPoint::new(DisplayRow(0), 26),
DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43),
DisplayPoint::new(DisplayRow(2), 71)..DisplayPoint::new(DisplayRow(2), 73),
DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 3),
DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13),
DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58),
DisplayPoint::new(DisplayRow(3), 60)..DisplayPoint::new(DisplayRow(3), 62),
]
);
});
@ -1207,16 +1207,18 @@ mod tests {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[
DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43),
DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13),
DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58),
DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43),
DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13),
DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58),
]
);
});
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1224,7 +1226,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1235,7 +1237,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1246,7 +1248,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1257,7 +1259,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1268,7 +1270,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1279,7 +1281,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1290,7 +1292,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1301,7 +1303,9 @@ mod tests {
// the closest match to the left.
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1309,7 +1313,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1320,7 +1324,9 @@ mod tests {
// closest match to the right.
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1328,7 +1334,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13)]
[DisplayPoint::new(DisplayRow(3), 11)..DisplayPoint::new(DisplayRow(3), 13)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1339,7 +1345,9 @@ mod tests {
// the last match.
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(3), 60)..DisplayPoint::new(DisplayRow(3), 60)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1347,7 +1355,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1358,7 +1366,9 @@ mod tests {
// first match.
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(3, 60)..DisplayPoint::new(3, 60)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(3), 60)..DisplayPoint::new(DisplayRow(3), 60)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1366,7 +1376,7 @@ mod tests {
search_bar.select_next_match(&SelectNextMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43)]
[DisplayPoint::new(DisplayRow(0), 41)..DisplayPoint::new(DisplayRow(0), 43)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1377,7 +1387,9 @@ mod tests {
// selects the last match.
editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
s.select_display_ranges([
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
])
});
});
search_bar.update(cx, |search_bar, cx| {
@ -1385,7 +1397,7 @@ mod tests {
search_bar.select_prev_match(&SelectPrevMatch, cx);
assert_eq!(
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58)]
[DisplayPoint::new(DisplayRow(3), 56)..DisplayPoint::new(DisplayRow(3), 58)]
);
});
search_bar.update(cx, |search_bar, _| {
@ -1414,7 +1426,7 @@ mod tests {
editor.update(cx, |editor, cx| {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),]
&[DisplayPoint::new(DisplayRow(2), 43)..DisplayPoint::new(DisplayRow(2), 45),]
);
});
@ -1439,7 +1451,7 @@ mod tests {
editor.update(cx, |editor, cx| {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[DisplayPoint::new(0, 35)..DisplayPoint::new(0, 40),]
&[DisplayPoint::new(DisplayRow(0), 35)..DisplayPoint::new(DisplayRow(0), 40),]
);
});
@ -2041,8 +2053,8 @@ mod tests {
assert_eq!(
display_points_of(editor.all_text_background_highlights(cx)),
&[
DisplayPoint::new(0, 10)..DisplayPoint::new(0, 20),
DisplayPoint::new(1, 9)..DisplayPoint::new(1, 19),
DisplayPoint::new(DisplayRow(0), 10)..DisplayPoint::new(DisplayRow(0), 20),
DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 19),
],
);
});

View File

@ -1688,7 +1688,7 @@ fn register_workspace_action_for_present_search<A: Action>(
#[cfg(test)]
pub mod tests {
use super::*;
use editor::DisplayPoint;
use editor::{display_map::DisplayRow, DisplayPoint};
use gpui::{Action, TestAppContext, WindowHandle};
use project::FakeFs;
use serde_json::json;
@ -1730,15 +1730,15 @@ pub mod tests {
.update(cx, |editor, cx| editor.all_text_background_highlights(cx)),
&[
(
DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35),
DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35),
match_background_color
),
(
DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40),
DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40),
match_background_color
),
(
DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9),
DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9),
match_background_color
)
]
@ -1748,7 +1748,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
);
search_view.select_match(Direction::Next, cx);
@ -1761,7 +1761,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
);
search_view.select_match(Direction::Next, cx);
})
@ -1774,7 +1774,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
);
search_view.select_match(Direction::Next, cx);
})
@ -1787,7 +1787,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35)]
[DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)]
);
search_view.select_match(Direction::Prev, cx);
})
@ -1800,7 +1800,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 9)]
[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)]
);
search_view.select_match(Direction::Prev, cx);
})
@ -1813,7 +1813,7 @@ pub mod tests {
search_view
.results_editor
.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
[DisplayPoint::new(2, 37)..DisplayPoint::new(2, 40)]
[DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)]
);
})
.unwrap();

View File

@ -26,6 +26,7 @@ gpui.workspace = true
itertools.workspace = true
language.workspace = true
log.workspace = true
multi_buffer.workspace = true
nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = [
"use_tokio",
], optional = true }

View File

@ -1,13 +1,14 @@
use editor::{
display_map::{DisplaySnapshot, FoldPoint, ToDisplayPoint},
display_map::{DisplayRow, DisplaySnapshot, FoldPoint, ToDisplayPoint},
movement::{
self, find_boundary, find_preceding_boundary_display_point, FindRange, TextLayoutDetails,
},
scroll::Autoscroll,
Anchor, Bias, DisplayPoint, ToOffset,
Anchor, Bias, DisplayPoint, RowExt, ToOffset,
};
use gpui::{actions, impl_actions, px, ViewContext, WindowContext};
use language::{char_kind, CharKind, Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow;
use serde::Deserialize;
use std::ops::Range;
use workspace::Workspace;
@ -843,7 +844,7 @@ impl Motion {
selection.end = map.clip_point(selection.end, Bias::Right);
// Don't reset the end here
return Some(selection.start..selection.end);
} else if selection.start.row() > 0 {
} else if selection.start.row().0 > 0 {
*selection.start.row_mut() -= 1;
*selection.start.column_mut() = map.line_len(selection.start.row());
selection.start = map.clip_point(selection.start, Bias::Left);
@ -860,10 +861,10 @@ impl Motion {
ignore_punctuation: _,
} = self
{
let start_row = selection.start.to_point(&map).row;
if selection.end.to_point(&map).row > start_row {
let start_row = MultiBufferRow(selection.start.to_point(&map).row);
if selection.end.to_point(&map).row > start_row.0 {
selection.end =
Point::new(start_row, map.buffer_snapshot.line_len(start_row))
Point::new(start_row.0, map.buffer_snapshot.line_len(start_row))
.to_display_point(&map)
}
}
@ -997,7 +998,7 @@ fn up_down_buffer_rows(
map.fold_snapshot
.clip_point(FoldPoint::new(start.row(), 0), Bias::Left),
);
let select_nth_wrapped_row = point.row() - begin_folded_line.row();
let select_nth_wrapped_row = point.row().0 - begin_folded_line.row().0;
let (goal_wrap, goal_x) = match goal {
SelectionGoal::WrappedHorizontalPosition((row, x)) => (row, x),
@ -1020,7 +1021,7 @@ fn up_down_buffer_rows(
let mut i = 0;
while i < goal_wrap && begin_folded_line.row() < map.max_point().row() {
let next_folded_line = DisplayPoint::new(begin_folded_line.row() + 1, 0);
let next_folded_line = DisplayPoint::new(begin_folded_line.row().next_row(), 0);
if map
.display_point_to_fold_point(next_folded_line, Bias::Right)
.row()
@ -1215,7 +1216,7 @@ fn previous_word_end(
let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
let mut point = point.to_point(map);
if point.column < map.buffer_snapshot.line_len(point.row) {
if point.column < map.buffer_snapshot.line_len(MultiBufferRow(point.row)) {
point.column += 1;
}
for _ in 0..times {
@ -1375,7 +1376,7 @@ fn previous_subword_end(
let scope = map.buffer_snapshot.language_scope_at(point.to_point(map));
let mut point = point.to_point(map);
if point.column < map.buffer_snapshot.line_len(point.row) {
if point.column < map.buffer_snapshot.line_len(MultiBufferRow(point.row)) {
point.column += 1;
}
for _ in 0..times {
@ -1497,7 +1498,7 @@ fn end_of_document(
let new_row = if let Some(line) = line {
(line - 1) as u32
} else {
map.max_buffer_row()
map.max_buffer_row().0
};
let new_point = Point::new(new_row, point.column());
@ -1672,22 +1673,24 @@ fn window_top(
.anchor
.to_display_point(map);
if first_visible_line.row() != 0 && text_layout_details.vertical_scroll_margin as usize > times
if first_visible_line.row() != DisplayRow(0)
&& text_layout_details.vertical_scroll_margin as usize > times
{
times = text_layout_details.vertical_scroll_margin.ceil() as usize;
}
if let Some(visible_rows) = text_layout_details.visible_rows {
let bottom_row = first_visible_line.row() + visible_rows as u32;
let new_row = (first_visible_line.row() + (times as u32))
let bottom_row = first_visible_line.row().0 + visible_rows as u32;
let new_row = (first_visible_line.row().0 + (times as u32))
.min(bottom_row)
.min(map.max_point().row());
.min(map.max_point().row().0);
let new_col = point.column().min(map.line_len(first_visible_line.row()));
let new_point = DisplayPoint::new(new_row, new_col);
let new_point = DisplayPoint::new(DisplayRow(new_row), new_col);
(map.clip_point(new_point, Bias::Left), SelectionGoal::None)
} else {
let new_row = (first_visible_line.row() + (times as u32)).min(map.max_point().row());
let new_row =
DisplayRow((first_visible_line.row().0 + (times as u32)).min(map.max_point().row().0));
let new_col = point.column().min(map.line_len(first_visible_line.row()));
let new_point = DisplayPoint::new(new_row, new_col);
@ -1707,10 +1710,11 @@ fn window_middle(
.to_display_point(map);
let max_visible_rows =
(visible_rows as u32).min(map.max_point().row() - first_visible_line.row());
(visible_rows as u32).min(map.max_point().row().0 - first_visible_line.row().0);
let new_row =
(first_visible_line.row() + (max_visible_rows / 2)).min(map.max_point().row());
(first_visible_line.row().0 + (max_visible_rows / 2)).min(map.max_point().row().0);
let new_row = DisplayRow(new_row);
let new_col = point.column().min(map.line_len(new_row));
let new_point = DisplayPoint::new(new_row, new_col);
(map.clip_point(new_point, Bias::Left), SelectionGoal::None)
@ -1730,18 +1734,19 @@ fn window_bottom(
.scroll_anchor
.anchor
.to_display_point(map);
let bottom_row = first_visible_line.row()
let bottom_row = first_visible_line.row().0
+ (visible_rows + text_layout_details.scroll_anchor.offset.y - 1.).floor() as u32;
if bottom_row < map.max_point().row()
if bottom_row < map.max_point().row().0
&& text_layout_details.vertical_scroll_margin as usize > times
{
times = text_layout_details.vertical_scroll_margin.ceil() as usize;
}
let bottom_row_capped = bottom_row.min(map.max_point().row());
let new_row = if bottom_row_capped.saturating_sub(times as u32) < first_visible_line.row() {
let bottom_row_capped = bottom_row.min(map.max_point().row().0);
let new_row = if bottom_row_capped.saturating_sub(times as u32) < first_visible_line.row().0
{
first_visible_line.row()
} else {
bottom_row_capped.saturating_sub(times as u32)
DisplayRow(bottom_row_capped.saturating_sub(times as u32))
};
let new_col = point.column().min(map.line_len(new_row));
let new_point = DisplayPoint::new(new_row, new_col);

View File

@ -25,6 +25,7 @@ use editor::Bias;
use gpui::{actions, ViewContext, WindowContext};
use language::{Point, SelectionGoal};
use log::error;
use multi_buffer::MultiBufferRow;
use workspace::Workspace;
use self::{
@ -314,7 +315,7 @@ fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContex
.collect();
let edits = selection_start_rows.into_iter().map(|row| {
let indent = snapshot
.indent_size_for_line(row)
.indent_size_for_line(MultiBufferRow(row))
.chars()
.collect::<String>();
let start_of_line = Point::new(row, 0);
@ -349,10 +350,10 @@ fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContex
.collect();
let edits = selection_end_rows.into_iter().map(|row| {
let indent = snapshot
.indent_size_for_line(row)
.indent_size_for_line(MultiBufferRow(row))
.chars()
.collect::<String>();
let end_of_line = Point::new(row, snapshot.line_len(row));
let end_of_line = Point::new(row, snapshot.line_len(MultiBufferRow(row)));
(end_of_line..end_of_line, "\n".to_string() + &indent)
});
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {

View File

@ -1,6 +1,7 @@
use editor::scroll::Autoscroll;
use gpui::ViewContext;
use language::{Bias, Point};
use multi_buffer::MultiBufferRow;
use workspace::Workspace;
use crate::{
@ -48,8 +49,10 @@ where
match vim.state().mode {
Mode::VisualLine => {
let start = Point::new(selection.start.row, 0);
let end =
Point::new(selection.end.row, snapshot.line_len(selection.end.row));
let end = Point::new(
selection.end.row,
snapshot.line_len(MultiBufferRow(selection.end.row)),
);
ranges.push(start..end);
cursor_positions.push(start..start);
}
@ -71,7 +74,7 @@ where
}
ranges.push(start..end);
if end.column == snapshot.line_len(end.row) {
if end.column == snapshot.line_len(MultiBufferRow(end.row)) {
end = snapshot.clip_point(end - Point::new(0, 1), Bias::Left);
}
cursor_positions.push(end..end)

View File

@ -7,6 +7,7 @@ use editor::{
};
use gpui::WindowContext;
use language::{Point, Selection};
use multi_buffer::MultiBufferRow;
pub fn delete_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &mut WindowContext) {
vim.stop_recording();
@ -29,7 +30,7 @@ pub fn delete_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &m
if selection.is_empty()
&& map
.buffer_snapshot
.line_len(selection.start.to_point(&map).row)
.line_len(MultiBufferRow(selection.start.to_point(&map).row))
== 0
{
selection.end = map
@ -79,7 +80,7 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo
let mut move_selection_start_to_previous_line =
|map: &DisplaySnapshot, selection: &mut Selection<DisplayPoint>| {
let start = selection.start.to_offset(map, Bias::Left);
if selection.start.row() > 0 {
if selection.start.row().0 > 0 {
should_move_to_start.insert(selection.id);
selection.start = (start - '\n'.len_utf8()).to_display_point(map);
}

View File

@ -2,6 +2,7 @@ use std::cmp;
use editor::{
display_map::ToDisplayPoint, movement, scroll::Autoscroll, ClipboardSelection, DisplayPoint,
RowExt,
};
use gpui::{impl_actions, AppContext, ViewContext};
use language::{Bias, SelectionGoal};
@ -99,13 +100,13 @@ fn paste(_: &mut Workspace, action: &Paste, cx: &mut ViewContext<Workspace>) {
.map(|selection| cmp::min(selection.start.column(), selection.end.column()))
.min()
.unwrap();
let mut row = current_selections.last().unwrap().end.row() + 1;
let mut row = current_selections.last().unwrap().end.row().next_row();
while i < clipboard_selections.len() {
let cursor =
display_map.clip_point(DisplayPoint::new(row, left), Bias::Left);
selections_to_process.push((cursor..cursor, false));
i += 1;
row += 1;
row.0 += 1;
}
}

View File

@ -1,6 +1,8 @@
use crate::Vim;
use editor::{
display_map::ToDisplayPoint, scroll::ScrollAmount, DisplayPoint, Editor, EditorSettings,
display_map::{DisplayRow, ToDisplayPoint},
scroll::ScrollAmount,
DisplayPoint, Editor, EditorSettings,
};
use gpui::{actions, ViewContext};
use language::Bias;
@ -85,11 +87,13 @@ fn scroll_editor(
if preserve_cursor_position {
let old_top = old_top_anchor.to_display_point(map);
let new_row = top.row() + selection.head().row() - old_top.row();
let new_row =
DisplayRow(top.row().0 + selection.head().row().0 - old_top.row().0);
head = map.clip_point(DisplayPoint::new(new_row, head.column()), Bias::Left)
}
let min_row = top.row() + vertical_scroll_margin as u32;
let max_row = top.row() + visible_rows - vertical_scroll_margin as u32 - 1;
let min_row = DisplayRow(top.row().0 + vertical_scroll_margin as u32);
let max_row =
DisplayRow(top.row().0 + visible_rows - vertical_scroll_margin as u32 - 1);
let new_head = if head.row() < min_row {
map.clip_point(DisplayPoint::new(min_row, head.column()), Bias::Left)

View File

@ -509,7 +509,7 @@ fn parse_replace_all(query: &str) -> Replacement {
#[cfg(test)]
mod test {
use editor::DisplayPoint;
use editor::{display_map::DisplayRow, DisplayPoint};
use indoc::indoc;
use search::BufferSearchBar;
@ -582,7 +582,7 @@ mod test {
let highlights = editor.all_text_background_highlights(cx);
assert_eq!(3, highlights.len());
assert_eq!(
DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
highlights[0].0
)
});

View File

@ -14,6 +14,7 @@ use itertools::Itertools;
use gpui::{actions, impl_actions, ViewContext, WindowContext};
use language::{char_kind, BufferSnapshot, CharKind, Point, Selection};
use multi_buffer::MultiBufferRow;
use serde::Deserialize;
use workspace::Workspace;
@ -724,7 +725,7 @@ fn paragraph(
let paragraph_end_row = paragraph_end.row();
let paragraph_ends_with_eof = paragraph_end_row == map.max_point().row();
let point = relative_to.to_point(map);
let current_line_is_empty = map.buffer_snapshot.is_line_blank(point.row);
let current_line_is_empty = map.buffer_snapshot.is_line_blank(MultiBufferRow(point.row));
if around {
if paragraph_ends_with_eof {
@ -733,13 +734,13 @@ fn paragraph(
}
let paragraph_start_row = paragraph_start.row();
if paragraph_start_row != 0 {
if paragraph_start_row.0 != 0 {
let previous_paragraph_last_line_start =
Point::new(paragraph_start_row - 1, 0).to_display_point(map);
Point::new(paragraph_start_row.0 - 1, 0).to_display_point(map);
paragraph_start = start_of_paragraph(map, previous_paragraph_last_line_start);
}
} else {
let next_paragraph_start = Point::new(paragraph_end_row + 1, 0).to_display_point(map);
let next_paragraph_start = Point::new(paragraph_end_row.0 + 1, 0).to_display_point(map);
paragraph_end = end_of_paragraph(map, next_paragraph_start);
}
}
@ -756,10 +757,10 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
return DisplayPoint::zero();
}
let is_current_line_blank = map.buffer_snapshot.is_line_blank(point.row);
let is_current_line_blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(point.row));
for row in (0..point.row).rev() {
let blank = map.buffer_snapshot.is_line_blank(row);
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
if blank != is_current_line_blank {
return Point::new(row + 1, 0).to_display_point(map);
}
@ -773,18 +774,21 @@ pub fn start_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) ->
/// The trailing newline is excluded from the paragraph.
pub fn end_of_paragraph(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint {
let point = display_point.to_point(map);
if point.row == map.max_buffer_row() {
if point.row == map.max_buffer_row().0 {
return map.max_point();
}
let is_current_line_blank = map.buffer_snapshot.is_line_blank(point.row);
let is_current_line_blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(point.row));
for row in point.row + 1..map.max_buffer_row() + 1 {
let blank = map.buffer_snapshot.is_line_blank(row);
for row in point.row + 1..map.max_buffer_row().0 + 1 {
let blank = map.buffer_snapshot.is_line_blank(MultiBufferRow(row));
if blank != is_current_line_blank {
let previous_row = row - 1;
return Point::new(previous_row, map.buffer_snapshot.line_len(previous_row))
.to_display_point(map);
return Point::new(
previous_row,
map.buffer_snapshot.line_len(MultiBufferRow(previous_row)),
)
.to_display_point(map);
}
}

View File

@ -6,7 +6,7 @@ mod vim_test_context;
use std::time::Duration;
use command_palette::CommandPalette;
use editor::DisplayPoint;
use editor::{display_map::DisplayRow, DisplayPoint};
use futures::StreamExt;
use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
pub use neovim_backed_binding_test_context::*;
@ -235,7 +235,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
let highlights = editor.all_text_background_highlights(cx);
assert_eq!(3, highlights.len());
assert_eq!(
DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
highlights[0].0
)
});

View File

@ -3,6 +3,7 @@ use std::time::Duration;
use editor::{ClipboardSelection, Editor};
use gpui::{ClipboardItem, ViewContext};
use language::{CharKind, Point};
use multi_buffer::MultiBufferRow;
use settings::Settings;
use crate::{state::Mode, UseSystemClipboard, Vim, VimSettings};
@ -74,10 +75,10 @@ fn copy_selections_content_internal(
// contains a newline (so that delete works as expected). We undo that change
// here.
let is_last_line = linewise
&& end.row == buffer.max_buffer_row()
&& end.row == buffer.max_buffer_row().0
&& buffer.max_point().column > 0
&& start.row < buffer.max_buffer_row()
&& start == Point::new(start.row, buffer.line_len(start.row));
&& start.row < buffer.max_buffer_row().0
&& start == Point::new(start.row, buffer.line_len(MultiBufferRow(start.row)));
if is_last_line {
start = Point::new(start.row + 1, 0);
@ -96,7 +97,7 @@ fn copy_selections_content_internal(
clipboard_selections.push(ClipboardSelection {
len: text.len() - initial_len,
is_entire_line: linewise,
first_line_indent: buffer.indent_size_for_line(start.row).len,
first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
});
}
}

View File

@ -9,6 +9,7 @@ use editor::{
};
use gpui::{actions, ViewContext, WindowContext};
use language::{Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow;
use search::BufferSearchBar;
use util::ResultExt;
use workspace::{searchable::Direction, Workspace};
@ -251,9 +252,9 @@ pub fn visual_block_motion(
break;
}
if tail.row() > head.row() {
row -= 1
row.0 -= 1
} else {
row += 1
row.0 += 1
}
}
@ -313,13 +314,15 @@ pub fn visual_object(object: Object, cx: &mut WindowContext) {
// trailing newline is included in its selection from the beginning.
if object == Object::Paragraph && range.start != range.end {
let row_of_selection_end_line = selection.end.to_point(map).row;
let new_selection_end =
if map.buffer_snapshot.line_len(row_of_selection_end_line) == 0
{
Point::new(row_of_selection_end_line + 1, 0)
} else {
Point::new(row_of_selection_end_line, 1)
};
let new_selection_end = if map
.buffer_snapshot
.line_len(MultiBufferRow(row_of_selection_end_line))
== 0
{
Point::new(row_of_selection_end_line + 1, 0)
} else {
Point::new(row_of_selection_end_line, 1)
};
selection.end = new_selection_end.to_display_point(map);
}
}

View File

@ -910,7 +910,7 @@ mod tests {
use super::*;
use assets::Assets;
use collections::HashSet;
use editor::{scroll::Autoscroll, DisplayPoint, Editor};
use editor::{display_map::DisplayRow, scroll::Autoscroll, DisplayPoint, Editor};
use gpui::{
actions, Action, AnyWindowHandle, AppContext, AssetSource, BorrowAppContext, Entity,
TestAppContext, VisualTestContext, WindowHandle,
@ -2229,9 +2229,8 @@ mod tests {
.update(cx, |_, cx| {
editor1.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_display_ranges(
[DisplayPoint::new(10, 0)..DisplayPoint::new(10, 0)],
)
s.select_display_ranges([DisplayPoint::new(DisplayRow(10), 0)
..DisplayPoint::new(DisplayRow(10), 0)])
});
});
})
@ -2256,9 +2255,8 @@ mod tests {
.update(cx, |_, cx| {
editor3.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_display_ranges(
[DisplayPoint::new(12, 0)..DisplayPoint::new(12, 0)],
)
s.select_display_ranges([DisplayPoint::new(DisplayRow(12), 0)
..DisplayPoint::new(DisplayRow(12), 0)])
});
editor.newline(&Default::default(), cx);
editor.newline(&Default::default(), cx);
@ -2279,7 +2277,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(16, 0), 12.5)
(file3.clone(), DisplayPoint::new(DisplayRow(16), 0), 12.5)
);
workspace
@ -2289,7 +2287,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(0, 0), 0.)
(file3.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
workspace
@ -2299,7 +2297,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file2.clone(), DisplayPoint::new(0, 0), 0.)
(file2.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
workspace
@ -2309,7 +2307,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(10, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(10), 0), 0.)
);
workspace
@ -2319,7 +2317,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(0, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
// Go back one more time and ensure we don't navigate past the first item in the history.
@ -2330,7 +2328,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(0, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
workspace
@ -2340,7 +2338,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(10, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(10), 0), 0.)
);
workspace
@ -2350,7 +2348,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file2.clone(), DisplayPoint::new(0, 0), 0.)
(file2.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
// Go forward to an item that has been closed, ensuring it gets re-opened at the same
@ -2373,7 +2371,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(0, 0), 0.)
(file3.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
workspace
@ -2383,7 +2381,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(16, 0), 12.5)
(file3.clone(), DisplayPoint::new(DisplayRow(16), 0), 12.5)
);
workspace
@ -2393,7 +2391,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(0, 0), 0.)
(file3.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
// Go back to an item that has been closed and removed from disk
@ -2422,7 +2420,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file2.clone(), DisplayPoint::new(0, 0), 0.)
(file2.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
workspace
.update(cx, |w, cx| w.go_forward(w.active_pane().downgrade(), cx))
@ -2431,7 +2429,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file3.clone(), DisplayPoint::new(0, 0), 0.)
(file3.clone(), DisplayPoint::new(DisplayRow(0), 0), 0.)
);
// Modify file to collapse multiple nav history entries into the same location.
@ -2440,9 +2438,8 @@ mod tests {
.update(cx, |_, cx| {
editor1.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges(
[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)],
)
s.select_display_ranges([DisplayPoint::new(DisplayRow(15), 0)
..DisplayPoint::new(DisplayRow(15), 0)])
})
});
})
@ -2452,9 +2449,8 @@ mod tests {
.update(cx, |_, cx| {
editor1.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([
DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)
])
s.select_display_ranges([DisplayPoint::new(DisplayRow(3), 0)
..DisplayPoint::new(DisplayRow(3), 0)])
});
});
})
@ -2464,9 +2460,8 @@ mod tests {
.update(cx, |_, cx| {
editor1.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([
DisplayPoint::new(13, 0)..DisplayPoint::new(13, 0)
])
s.select_display_ranges([DisplayPoint::new(DisplayRow(13), 0)
..DisplayPoint::new(DisplayRow(13), 0)])
})
});
})
@ -2477,9 +2472,8 @@ mod tests {
editor1.update(cx, |editor, cx| {
editor.transact(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([
DisplayPoint::new(2, 0)..DisplayPoint::new(14, 0)
])
s.select_display_ranges([DisplayPoint::new(DisplayRow(2), 0)
..DisplayPoint::new(DisplayRow(14), 0)])
});
editor.insert("", cx);
})
@ -2491,7 +2485,8 @@ mod tests {
.update(cx, |_, cx| {
editor1.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
s.select_display_ranges([DisplayPoint::new(DisplayRow(1), 0)
..DisplayPoint::new(DisplayRow(1), 0)])
})
});
})
@ -2503,7 +2498,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(2, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(2), 0), 0.)
);
workspace
.update(cx, |w, cx| w.go_back(w.active_pane().downgrade(), cx))
@ -2512,7 +2507,7 @@ mod tests {
.unwrap();
assert_eq!(
active_location(&workspace, cx),
(file1.clone(), DisplayPoint::new(3, 0), 0.)
(file1.clone(), DisplayPoint::new(DisplayRow(3), 0), 0.)
);
fn active_location(