WIP: Make editor
crate compile again
Tests are still failing though.
This commit is contained in:
parent
c7e2fae9cb
commit
0e1318dfe4
7 changed files with 297 additions and 224 deletions
|
@ -15,8 +15,8 @@ use tab_map::TabMap;
|
||||||
use wrap_map::WrapMap;
|
use wrap_map::WrapMap;
|
||||||
|
|
||||||
pub use block_map::{
|
pub use block_map::{
|
||||||
AlignedBlock, BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
|
BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
|
||||||
BlockDisposition, BlockId, BlockProperties, RenderBlock,
|
BlockDisposition, BlockId, BlockProperties, RenderBlock, TransformBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ToDisplayPoint {
|
pub trait ToDisplayPoint {
|
||||||
|
@ -43,13 +43,14 @@ impl DisplayMap {
|
||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
wrap_width: Option<f32>,
|
wrap_width: Option<f32>,
|
||||||
|
excerpt_header_height: u8,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
|
||||||
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
|
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
|
||||||
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
||||||
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
|
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
|
||||||
let block_map = BlockMap::new(snapshot);
|
let block_map = BlockMap::new(snapshot, excerpt_header_height);
|
||||||
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
||||||
DisplayMap {
|
DisplayMap {
|
||||||
buffer,
|
buffer,
|
||||||
|
@ -318,7 +319,7 @@ impl DisplaySnapshot {
|
||||||
pub fn blocks_in_range<'a>(
|
pub fn blocks_in_range<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> {
|
) -> impl Iterator<Item = (u32, &'a TransformBlock)> {
|
||||||
self.blocks_snapshot.blocks_in_range(rows)
|
self.blocks_snapshot.blocks_in_range(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +472,7 @@ mod tests {
|
||||||
|
|
||||||
let font_cache = cx.font_cache().clone();
|
let font_cache = cx.font_cache().clone();
|
||||||
let tab_size = rng.gen_range(1..=4);
|
let tab_size = rng.gen_range(1..=4);
|
||||||
|
let excerpt_header_height = rng.gen_range(1..=5);
|
||||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||||
let font_id = font_cache
|
let font_id = font_cache
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
|
@ -497,7 +499,15 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx)
|
DisplayMap::new(
|
||||||
|
buffer.clone(),
|
||||||
|
tab_size,
|
||||||
|
font_id,
|
||||||
|
font_size,
|
||||||
|
wrap_width,
|
||||||
|
excerpt_header_height,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
let mut notifications = observe(&map, &mut cx);
|
let mut notifications = observe(&map, &mut cx);
|
||||||
let mut fold_count = 0;
|
let mut fold_count = 0;
|
||||||
|
@ -711,7 +721,15 @@ mod tests {
|
||||||
let text = "one two three four five\nsix seven eight";
|
let text = "one two three four five\nsix seven eight";
|
||||||
let buffer = MultiBuffer::build_simple(text, cx);
|
let buffer = MultiBuffer::build_simple(text, cx);
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx)
|
DisplayMap::new(
|
||||||
|
buffer.clone(),
|
||||||
|
tab_size,
|
||||||
|
font_id,
|
||||||
|
font_size,
|
||||||
|
wrap_width,
|
||||||
|
1,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
@ -791,7 +809,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
|
||||||
});
|
});
|
||||||
buffer.update(cx, |buffer, cx| {
|
buffer.update(cx, |buffer, cx| {
|
||||||
buffer.edit(
|
buffer.edit(
|
||||||
|
@ -871,7 +889,7 @@ mod tests {
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
|
|
||||||
let map =
|
let map =
|
||||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
|
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
||||||
vec![
|
vec![
|
||||||
|
@ -958,8 +976,9 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 16.0;
|
let font_size = 16.0;
|
||||||
|
|
||||||
let map = cx
|
let map = cx.add_model(|cx| {
|
||||||
.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx));
|
DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), 1, cx)
|
||||||
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
cx.update(|cx| chunks(0..5, &map, &theme, cx)),
|
||||||
[
|
[
|
||||||
|
@ -1003,7 +1022,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
|
||||||
});
|
});
|
||||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
|
@ -1047,7 +1066,7 @@ mod tests {
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
|
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
|
||||||
});
|
});
|
||||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
||||||
|
@ -1105,7 +1124,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
||||||
|
|
|
@ -5,10 +5,9 @@ use gpui::{AppContext, ElementBox};
|
||||||
use language::{BufferSnapshot, Chunk};
|
use language::{BufferSnapshot, Chunk};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering, Reverse},
|
cmp::{self, Ordering},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, Range},
|
ops::{Deref, Range},
|
||||||
path::Path,
|
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -24,6 +23,7 @@ pub struct BlockMap {
|
||||||
wrap_snapshot: Mutex<WrapSnapshot>,
|
wrap_snapshot: Mutex<WrapSnapshot>,
|
||||||
blocks: Vec<Arc<Block>>,
|
blocks: Vec<Arc<Block>>,
|
||||||
transforms: Mutex<SumTree<Transform>>,
|
transforms: Mutex<SumTree<Transform>>,
|
||||||
|
excerpt_header_height: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
|
pub struct BlockMapWriter<'a>(&'a mut BlockMap);
|
||||||
|
@ -89,7 +89,7 @@ struct Transform {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum TransformBlock {
|
pub enum TransformBlock {
|
||||||
Custom {
|
Custom {
|
||||||
block: Arc<Block>,
|
block: Arc<Block>,
|
||||||
column: u32,
|
column: u32,
|
||||||
|
@ -97,17 +97,24 @@ enum TransformBlock {
|
||||||
ExcerptHeader {
|
ExcerptHeader {
|
||||||
buffer: BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
range: Range<text::Anchor>,
|
range: Range<text::Anchor>,
|
||||||
path: Option<Arc<Path>>,
|
height: u8,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl TransformBlock {
|
||||||
fn disposition(&self) -> BlockDisposition {
|
fn disposition(&self) -> BlockDisposition {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom { block, column } => block.disposition,
|
TransformBlock::Custom { block, .. } => block.disposition,
|
||||||
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
TransformBlock::Custom { block, .. } => block.height,
|
||||||
|
TransformBlock::ExcerptHeader { height, .. } => *height,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for TransformBlock {
|
impl Debug for TransformBlock {
|
||||||
|
@ -118,9 +125,10 @@ impl Debug for TransformBlock {
|
||||||
.field("block", block)
|
.field("block", block)
|
||||||
.field("column", column)
|
.field("column", column)
|
||||||
.finish(),
|
.finish(),
|
||||||
Self::ExcerptHeader { buffer, path, .. } => {
|
Self::ExcerptHeader { buffer, .. } => f
|
||||||
f.debug_struct("ExcerptHeader").field("path", path).finish()
|
.debug_struct("ExcerptHeader")
|
||||||
}
|
.field("path", &buffer.path())
|
||||||
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +155,7 @@ pub struct BlockBufferRows<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockMap {
|
impl BlockMap {
|
||||||
pub fn new(wrap_snapshot: WrapSnapshot) -> Self {
|
pub fn new(wrap_snapshot: WrapSnapshot, excerpt_header_height: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
next_block_id: AtomicUsize::new(0),
|
next_block_id: AtomicUsize::new(0),
|
||||||
blocks: Vec::new(),
|
blocks: Vec::new(),
|
||||||
|
@ -156,6 +164,7 @@ impl BlockMap {
|
||||||
&(),
|
&(),
|
||||||
)),
|
)),
|
||||||
wrap_snapshot: Mutex::new(wrap_snapshot),
|
wrap_snapshot: Mutex::new(wrap_snapshot),
|
||||||
|
excerpt_header_height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +211,7 @@ impl BlockMap {
|
||||||
if transform
|
if transform
|
||||||
.block
|
.block
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |b| b.disposition.is_below())
|
.map_or(false, |b| b.disposition().is_below())
|
||||||
{
|
{
|
||||||
new_transforms.push(transform.clone(), &());
|
new_transforms.push(transform.clone(), &());
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
|
@ -227,7 +236,7 @@ impl BlockMap {
|
||||||
if transform
|
if transform
|
||||||
.block
|
.block
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |b| b.disposition.is_below())
|
.map_or(false, |b| b.disposition().is_below())
|
||||||
{
|
{
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
} else {
|
} else {
|
||||||
|
@ -248,7 +257,7 @@ impl BlockMap {
|
||||||
if transform
|
if transform
|
||||||
.block
|
.block
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |b| b.disposition.is_below())
|
.map_or(false, |b| b.disposition().is_below())
|
||||||
{
|
{
|
||||||
cursor.next(&());
|
cursor.next(&());
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,7 +337,7 @@ impl BlockMap {
|
||||||
TransformBlock::ExcerptHeader {
|
TransformBlock::ExcerptHeader {
|
||||||
buffer: excerpt_boundary.buffer,
|
buffer: excerpt_boundary.buffer,
|
||||||
range: excerpt_boundary.range,
|
range: excerpt_boundary.range,
|
||||||
path: excerpt_boundary.path,
|
height: self.excerpt_header_height,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -336,7 +345,23 @@ impl BlockMap {
|
||||||
|
|
||||||
// When multiple blocks are on the same row, newer blocks appear above older
|
// When multiple blocks are on the same row, newer blocks appear above older
|
||||||
// blocks. This is arbitrary, but we currently rely on it in ProjectDiagnosticsEditor.
|
// blocks. This is arbitrary, but we currently rely on it in ProjectDiagnosticsEditor.
|
||||||
blocks_in_edit.sort();
|
blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
|
||||||
|
row_a.cmp(&row_b).then_with(|| match (block_a, block_b) {
|
||||||
|
(
|
||||||
|
TransformBlock::ExcerptHeader { .. },
|
||||||
|
TransformBlock::ExcerptHeader { .. },
|
||||||
|
) => Ordering::Equal,
|
||||||
|
(TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
|
||||||
|
(_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
|
||||||
|
(
|
||||||
|
TransformBlock::Custom { block: block_a, .. },
|
||||||
|
TransformBlock::Custom { block: block_b, .. },
|
||||||
|
) => block_a
|
||||||
|
.disposition
|
||||||
|
.cmp(&block_b.disposition)
|
||||||
|
.then_with(|| block_a.id.cmp(&block_b.id).reverse()),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
// For each of these blocks, insert a new isomorphic transform preceding the block,
|
// For each of these blocks, insert a new isomorphic transform preceding the block,
|
||||||
// and then insert the block itself.
|
// and then insert the block itself.
|
||||||
|
@ -577,7 +602,7 @@ impl BlockSnapshot {
|
||||||
pub fn blocks_in_range<'a>(
|
pub fn blocks_in_range<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> {
|
) -> impl Iterator<Item = (u32, &'a TransformBlock)> {
|
||||||
let mut cursor = self.transforms.cursor::<BlockRow>();
|
let mut cursor = self.transforms.cursor::<BlockRow>();
|
||||||
cursor.seek(&BlockRow(rows.start), Bias::Right, &());
|
cursor.seek(&BlockRow(rows.start), Bias::Right, &());
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
|
@ -697,7 +722,7 @@ impl BlockSnapshot {
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||||
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
||||||
if let Some(transform) = cursor.item() {
|
if let Some(transform) = cursor.item() {
|
||||||
match transform.block.as_ref().map(|b| b.disposition) {
|
match transform.block.as_ref().map(|b| b.disposition()) {
|
||||||
Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
|
Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
|
||||||
Some(BlockDisposition::Below) => {
|
Some(BlockDisposition::Below) => {
|
||||||
let wrap_row = cursor.start().1 .0 - 1;
|
let wrap_row = cursor.start().1 .0 - 1;
|
||||||
|
@ -726,13 +751,13 @@ impl Transform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block(block: Arc<Block>, column: u32) -> Self {
|
fn block(block: TransformBlock) -> Self {
|
||||||
Self {
|
Self {
|
||||||
summary: TransformSummary {
|
summary: TransformSummary {
|
||||||
input_rows: 0,
|
input_rows: 0,
|
||||||
output_rows: block.height as u32,
|
output_rows: block.height() as u32,
|
||||||
},
|
},
|
||||||
block: Some(AlignedBlock { block, column }),
|
block: Some(block),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -862,32 +887,6 @@ impl BlockDisposition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlignedBlock {
|
|
||||||
pub fn height(&self) -> u32 {
|
|
||||||
self.height as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn column(&self) -> u32 {
|
|
||||||
self.column
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&self, cx: &BlockContext) -> ElementBox {
|
|
||||||
self.render.lock()(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn position(&self) -> &Anchor {
|
|
||||||
&self.block.position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for AlignedBlock {
|
|
||||||
type Target = Block;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.block.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for BlockContext<'a> {
|
impl<'a> Deref for BlockContext<'a> {
|
||||||
type Target = AppContext;
|
type Target = AppContext;
|
||||||
|
|
||||||
|
@ -896,6 +895,12 @@ impl<'a> Deref for BlockContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
pub fn render(&self, cx: &BlockContext) -> ElementBox {
|
||||||
|
self.render.lock()(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Block {
|
impl Debug for Block {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Block")
|
f.debug_struct("Block")
|
||||||
|
@ -931,6 +936,7 @@ mod tests {
|
||||||
use crate::multi_buffer::MultiBuffer;
|
use crate::multi_buffer::MultiBuffer;
|
||||||
use gpui::{elements::Empty, Element};
|
use gpui::{elements::Empty, Element};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use std::cmp::Reverse;
|
||||||
use std::env;
|
use std::env;
|
||||||
use text::RandomCharIter;
|
use text::RandomCharIter;
|
||||||
|
|
||||||
|
@ -964,7 +970,7 @@ mod tests {
|
||||||
let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
||||||
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
|
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
|
||||||
let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
|
let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
|
||||||
let mut block_map = BlockMap::new(wraps_snapshot.clone());
|
let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1);
|
||||||
|
|
||||||
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
|
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
|
||||||
writer.insert(vec![
|
writer.insert(vec![
|
||||||
|
@ -994,9 +1000,10 @@ mod tests {
|
||||||
let blocks = snapshot
|
let blocks = snapshot
|
||||||
.blocks_in_range(0..8)
|
.blocks_in_range(0..8)
|
||||||
.map(|(start_row, block)| {
|
.map(|(start_row, block)| {
|
||||||
|
let (block, column) = block.as_custom().unwrap();
|
||||||
(
|
(
|
||||||
start_row..start_row + block.height(),
|
start_row..start_row + block.height as u32,
|
||||||
block.column(),
|
column,
|
||||||
block
|
block
|
||||||
.render(&BlockContext {
|
.render(&BlockContext {
|
||||||
cx,
|
cx,
|
||||||
|
@ -1142,7 +1149,7 @@ mod tests {
|
||||||
let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
|
||||||
let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
|
let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
|
||||||
let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
|
let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
|
||||||
let mut block_map = BlockMap::new(wraps_snapshot.clone());
|
let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1);
|
||||||
|
|
||||||
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
|
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
|
||||||
writer.insert(vec![
|
writer.insert(vec![
|
||||||
|
@ -1187,8 +1194,10 @@ mod tests {
|
||||||
.select_font(family_id, &Default::default())
|
.select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
|
let excerpt_header_height = rng.gen_range(1..=5);
|
||||||
|
|
||||||
log::info!("Wrap width: {:?}", wrap_width);
|
log::info!("Wrap width: {:?}", wrap_width);
|
||||||
|
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
|
||||||
|
|
||||||
let buffer = if rng.gen() {
|
let buffer = if rng.gen() {
|
||||||
let len = rng.gen_range(0..10);
|
let len = rng.gen_range(0..10);
|
||||||
|
@ -1204,8 +1213,8 @@ mod tests {
|
||||||
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
|
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
|
||||||
let (wrap_map, wraps_snapshot) =
|
let (wrap_map, wraps_snapshot) =
|
||||||
WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
|
WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
|
||||||
let mut block_map = BlockMap::new(wraps_snapshot);
|
let mut block_map = BlockMap::new(wraps_snapshot, excerpt_header_height);
|
||||||
let mut expected_blocks = Vec::new();
|
let mut custom_blocks = Vec::new();
|
||||||
|
|
||||||
for _ in 0..operations {
|
for _ in 0..operations {
|
||||||
let mut buffer_edits = Vec::new();
|
let mut buffer_edits = Vec::new();
|
||||||
|
@ -1258,15 +1267,15 @@ mod tests {
|
||||||
let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
|
let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
|
||||||
let block_ids = block_map.insert(block_properties.clone());
|
let block_ids = block_map.insert(block_properties.clone());
|
||||||
for (block_id, props) in block_ids.into_iter().zip(block_properties) {
|
for (block_id, props) in block_ids.into_iter().zip(block_properties) {
|
||||||
expected_blocks.push((block_id, props));
|
custom_blocks.push((block_id, props));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
40..=59 if !expected_blocks.is_empty() => {
|
40..=59 if !custom_blocks.is_empty() => {
|
||||||
let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
|
let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
|
||||||
let block_ids_to_remove = (0..block_count)
|
let block_ids_to_remove = (0..block_count)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
expected_blocks
|
custom_blocks
|
||||||
.remove(rng.gen_range(0..expected_blocks.len()))
|
.remove(rng.gen_range(0..custom_blocks.len()))
|
||||||
.0
|
.0
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -1304,36 +1313,39 @@ mod tests {
|
||||||
);
|
);
|
||||||
log::info!("blocks text: {:?}", blocks_snapshot.text());
|
log::info!("blocks text: {:?}", blocks_snapshot.text());
|
||||||
|
|
||||||
let mut sorted_blocks = expected_blocks
|
let mut expected_blocks = Vec::new();
|
||||||
.iter()
|
expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
|
||||||
.cloned()
|
let mut position = block.position.to_point(&buffer_snapshot);
|
||||||
.map(|(id, block)| {
|
let column = wraps_snapshot.from_point(position, Bias::Left).column();
|
||||||
let mut position = block.position.to_point(&buffer_snapshot);
|
match block.disposition {
|
||||||
let column = wraps_snapshot.from_point(position, Bias::Left).column();
|
BlockDisposition::Above => {
|
||||||
match block.disposition {
|
position.column = 0;
|
||||||
BlockDisposition::Above => {
|
}
|
||||||
position.column = 0;
|
BlockDisposition::Below => {
|
||||||
}
|
position.column = buffer_snapshot.line_len(position.row);
|
||||||
BlockDisposition::Below => {
|
}
|
||||||
position.column = buffer_snapshot.line_len(position.row);
|
};
|
||||||
}
|
let row = wraps_snapshot.from_point(position, Bias::Left).row();
|
||||||
};
|
(row, block.disposition, *id, block.height)
|
||||||
let row = wraps_snapshot.from_point(position, Bias::Left).row();
|
}));
|
||||||
(
|
expected_blocks.extend(
|
||||||
id,
|
buffer_snapshot
|
||||||
BlockProperties {
|
.excerpt_boundaries_in_range(0..buffer_snapshot.len())
|
||||||
position: BlockPoint::new(row, column),
|
.map(|boundary| {
|
||||||
height: block.height,
|
let position =
|
||||||
disposition: block.disposition,
|
wraps_snapshot.from_point(Point::new(boundary.row, 0), Bias::Left);
|
||||||
render: block.render.clone(),
|
(
|
||||||
},
|
position.row(),
|
||||||
)
|
BlockDisposition::Above,
|
||||||
})
|
BlockId(usize::MAX),
|
||||||
.collect::<Vec<_>>();
|
excerpt_header_height,
|
||||||
sorted_blocks.sort_unstable_by_key(|(id, block)| {
|
)
|
||||||
(block.position.row, block.disposition, Reverse(*id))
|
}),
|
||||||
|
);
|
||||||
|
expected_blocks.sort_unstable_by_key(|(row, disposition, id, _)| {
|
||||||
|
(*row, *disposition, Reverse(*id))
|
||||||
});
|
});
|
||||||
let mut sorted_blocks_iter = sorted_blocks.iter().peekable();
|
let mut sorted_blocks_iter = expected_blocks.iter().peekable();
|
||||||
|
|
||||||
let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
|
let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
|
||||||
let mut expected_buffer_rows = Vec::new();
|
let mut expected_buffer_rows = Vec::new();
|
||||||
|
@ -1350,13 +1362,13 @@ mod tests {
|
||||||
.to_point(WrapPoint::new(row, 0), Bias::Left)
|
.to_point(WrapPoint::new(row, 0), Bias::Left)
|
||||||
.row as usize];
|
.row as usize];
|
||||||
|
|
||||||
while let Some((block_id, block)) = sorted_blocks_iter.peek() {
|
while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() {
|
||||||
if block.position.row == row && block.disposition == BlockDisposition::Above {
|
if *block_row == row && *disposition == BlockDisposition::Above {
|
||||||
expected_block_positions
|
expected_block_positions
|
||||||
.push((expected_text.matches('\n').count() as u32, *block_id));
|
.push((expected_text.matches('\n').count() as u32, *id));
|
||||||
let text = "\n".repeat(block.height as usize);
|
let text = "\n".repeat(*height as usize);
|
||||||
expected_text.push_str(&text);
|
expected_text.push_str(&text);
|
||||||
for _ in 0..block.height {
|
for _ in 0..*height {
|
||||||
expected_buffer_rows.push(None);
|
expected_buffer_rows.push(None);
|
||||||
}
|
}
|
||||||
sorted_blocks_iter.next();
|
sorted_blocks_iter.next();
|
||||||
|
@ -1369,13 +1381,13 @@ mod tests {
|
||||||
expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
|
expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
|
||||||
expected_text.push_str(input_line);
|
expected_text.push_str(input_line);
|
||||||
|
|
||||||
while let Some((block_id, block)) = sorted_blocks_iter.peek() {
|
while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() {
|
||||||
if block.position.row == row && block.disposition == BlockDisposition::Below {
|
if *block_row == row && *disposition == BlockDisposition::Below {
|
||||||
expected_block_positions
|
expected_block_positions
|
||||||
.push((expected_text.matches('\n').count() as u32 + 1, *block_id));
|
.push((expected_text.matches('\n').count() as u32 + 1, *id));
|
||||||
let text = "\n".repeat(block.height as usize);
|
let text = "\n".repeat(*height as usize);
|
||||||
expected_text.push_str(&text);
|
expected_text.push_str(&text);
|
||||||
for _ in 0..block.height {
|
for _ in 0..*height {
|
||||||
expected_buffer_rows.push(None);
|
expected_buffer_rows.push(None);
|
||||||
}
|
}
|
||||||
sorted_blocks_iter.next();
|
sorted_blocks_iter.next();
|
||||||
|
@ -1409,7 +1421,14 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blocks_snapshot
|
blocks_snapshot
|
||||||
.blocks_in_range(0..(expected_row_count as u32))
|
.blocks_in_range(0..(expected_row_count as u32))
|
||||||
.map(|(row, block)| (row, block.id))
|
.map(|(row, block)| (
|
||||||
|
row,
|
||||||
|
if let Some((block, _)) = block.as_custom() {
|
||||||
|
block.id
|
||||||
|
} else {
|
||||||
|
BlockId(usize::MAX)
|
||||||
|
}
|
||||||
|
))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
expected_block_positions
|
expected_block_positions
|
||||||
);
|
);
|
||||||
|
@ -1490,6 +1509,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TransformBlock {
|
||||||
|
fn as_custom(&self) -> Option<(&Block, u32)> {
|
||||||
|
match self {
|
||||||
|
TransformBlock::Custom { block, column } => Some((block, *column)),
|
||||||
|
TransformBlock::ExcerptHeader { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BlockSnapshot {
|
impl BlockSnapshot {
|
||||||
fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
|
fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
|
||||||
self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
|
self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
|
||||||
|
|
|
@ -3,7 +3,6 @@ mod element;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
mod multi_buffer;
|
mod multi_buffer;
|
||||||
mod multi_editor;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
@ -810,6 +809,7 @@ impl Editor {
|
||||||
settings.style.text.font_id,
|
settings.style.text.font_id,
|
||||||
settings.style.text.font_size,
|
settings.style.text.font_size,
|
||||||
None,
|
None,
|
||||||
|
2,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -2604,7 +2604,11 @@ impl Editor {
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
// Don't move lines across excerpts
|
// Don't move lines across excerpts
|
||||||
if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) {
|
if buffer
|
||||||
|
.excerpt_boundaries_in_range(insertion_point..range_to_move.end)
|
||||||
|
.next()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
let text = buffer
|
let text = buffer
|
||||||
.text_for_range(range_to_move.clone())
|
.text_for_range(range_to_move.clone())
|
||||||
.flat_map(|s| s.chars())
|
.flat_map(|s| s.chars())
|
||||||
|
@ -2704,7 +2708,11 @@ impl Editor {
|
||||||
let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
|
let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
|
||||||
|
|
||||||
// Don't move lines across excerpt boundaries
|
// Don't move lines across excerpt boundaries
|
||||||
if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) {
|
if buffer
|
||||||
|
.excerpt_boundaries_in_range(range_to_move.start..insertion_point)
|
||||||
|
.next()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
let mut text = String::from("\n");
|
let mut text = String::from("\n");
|
||||||
text.extend(buffer.text_for_range(range_to_move.clone()));
|
text.extend(buffer.text_for_range(range_to_move.clone()));
|
||||||
text.pop(); // Drop trailing newline
|
text.pop(); // Drop trailing newline
|
||||||
|
|
|
@ -649,43 +649,44 @@ impl EditorElement {
|
||||||
line_layouts: &[text_layout::Line],
|
line_layouts: &[text_layout::Line],
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> Vec<(u32, ElementBox)> {
|
) -> Vec<(u32, ElementBox)> {
|
||||||
snapshot
|
Default::default()
|
||||||
.blocks_in_range(rows.clone())
|
// snapshot
|
||||||
.map(|(start_row, block)| {
|
// .blocks_in_range(rows.clone())
|
||||||
let anchor_row = block
|
// .map(|(start_row, block)| {
|
||||||
.position()
|
// let anchor_row = block
|
||||||
.to_point(&snapshot.buffer_snapshot)
|
// .position()
|
||||||
.to_display_point(snapshot)
|
// .to_point(&snapshot.buffer_snapshot)
|
||||||
.row();
|
// .to_display_point(snapshot)
|
||||||
|
// .row();
|
||||||
|
|
||||||
let anchor_x = text_x
|
// let anchor_x = text_x
|
||||||
+ if rows.contains(&anchor_row) {
|
// + if rows.contains(&anchor_row) {
|
||||||
line_layouts[(anchor_row - rows.start) as usize]
|
// line_layouts[(anchor_row - rows.start) as usize]
|
||||||
.x_for_index(block.column() as usize)
|
// .x_for_index(block.column() as usize)
|
||||||
} else {
|
// } else {
|
||||||
layout_line(anchor_row, snapshot, style, cx.text_layout_cache)
|
// layout_line(anchor_row, snapshot, style, cx.text_layout_cache)
|
||||||
.x_for_index(block.column() as usize)
|
// .x_for_index(block.column() as usize)
|
||||||
};
|
// };
|
||||||
|
|
||||||
let mut element = block.render(&BlockContext {
|
// let mut element = block.render(&BlockContext {
|
||||||
cx,
|
// cx,
|
||||||
anchor_x,
|
// anchor_x,
|
||||||
gutter_padding,
|
// gutter_padding,
|
||||||
line_height,
|
// line_height,
|
||||||
scroll_x: snapshot.scroll_position.x(),
|
// scroll_x: snapshot.scroll_position.x(),
|
||||||
gutter_width,
|
// gutter_width,
|
||||||
em_width,
|
// em_width,
|
||||||
});
|
// });
|
||||||
element.layout(
|
// element.layout(
|
||||||
SizeConstraint {
|
// SizeConstraint {
|
||||||
min: Vector2F::zero(),
|
// min: Vector2F::zero(),
|
||||||
max: vec2f(width, block.height() as f32 * line_height),
|
// max: vec2f(width, block.height() as f32 * line_height),
|
||||||
},
|
// },
|
||||||
cx,
|
// cx,
|
||||||
);
|
// );
|
||||||
(start_row, element)
|
// (start_row, element)
|
||||||
})
|
// })
|
||||||
.collect()
|
// .collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,13 +225,8 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range<
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{Buffer, DisplayMap, ExcerptProperties, MultiBuffer};
|
||||||
display_map::{BlockDisposition, BlockProperties},
|
|
||||||
Buffer, DisplayMap, ExcerptProperties, MultiBuffer,
|
|
||||||
};
|
|
||||||
use gpui::{elements::Empty, Element};
|
|
||||||
use language::Point;
|
use language::Point;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) {
|
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) {
|
||||||
|
@ -242,59 +237,27 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefg\nhijkl\nmn", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefg\nhijkl\nmn", cx));
|
||||||
let mut excerpt1_header_position = None;
|
|
||||||
let mut excerpt2_header_position = None;
|
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
let mut multibuffer = MultiBuffer::new(0);
|
let mut multibuffer = MultiBuffer::new(0);
|
||||||
let excerpt1_id = multibuffer.push_excerpt(
|
multibuffer.push_excerpt(
|
||||||
ExcerptProperties {
|
ExcerptProperties {
|
||||||
buffer: &buffer,
|
buffer: &buffer,
|
||||||
range: Point::new(0, 0)..Point::new(1, 4),
|
range: Point::new(0, 0)..Point::new(1, 4),
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
let excerpt2_id = multibuffer.push_excerpt(
|
multibuffer.push_excerpt(
|
||||||
ExcerptProperties {
|
ExcerptProperties {
|
||||||
buffer: &buffer,
|
buffer: &buffer,
|
||||||
range: Point::new(2, 0)..Point::new(3, 2),
|
range: Point::new(2, 0)..Point::new(3, 2),
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
excerpt1_header_position = Some(
|
|
||||||
multibuffer
|
|
||||||
.read(cx)
|
|
||||||
.anchor_in_excerpt(excerpt1_id, language::Anchor::min()),
|
|
||||||
);
|
|
||||||
excerpt2_header_position = Some(
|
|
||||||
multibuffer
|
|
||||||
.read(cx)
|
|
||||||
.anchor_in_excerpt(excerpt2_id, language::Anchor::min()),
|
|
||||||
);
|
|
||||||
multibuffer
|
multibuffer
|
||||||
});
|
});
|
||||||
|
|
||||||
let display_map =
|
let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, cx));
|
cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 2, cx));
|
||||||
display_map.update(cx, |display_map, cx| {
|
|
||||||
display_map.insert_blocks(
|
|
||||||
[
|
|
||||||
BlockProperties {
|
|
||||||
position: excerpt1_header_position.unwrap(),
|
|
||||||
height: 2,
|
|
||||||
render: Arc::new(|_| Empty::new().boxed()),
|
|
||||||
disposition: BlockDisposition::Above,
|
|
||||||
},
|
|
||||||
BlockProperties {
|
|
||||||
position: excerpt2_header_position.unwrap(),
|
|
||||||
height: 3,
|
|
||||||
render: Arc::new(|_| Empty::new().boxed()),
|
|
||||||
disposition: BlockDisposition::Above,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\n\nhijkl\nmn");
|
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\n\nhijkl\nmn");
|
||||||
|
@ -352,7 +315,7 @@ mod tests {
|
||||||
|
|
||||||
let buffer = MultiBuffer::build_simple("a bcΔ defγ hi—jk", cx);
|
let buffer = MultiBuffer::build_simple("a bcΔ defγ hi—jk", cx);
|
||||||
let display_map =
|
let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
|
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)),
|
prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)),
|
||||||
|
@ -408,7 +371,7 @@ mod tests {
|
||||||
let font_size = 14.0;
|
let font_size = 14.0;
|
||||||
let buffer = MultiBuffer::build_simple("lorem ipsum dolor\n sit", cx);
|
let buffer = MultiBuffer::build_simple("lorem ipsum dolor\n sit", cx);
|
||||||
let display_map =
|
let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
|
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -15,7 +15,6 @@ use std::{
|
||||||
cmp, fmt, io,
|
cmp, fmt, io,
|
||||||
iter::{self, FromIterator},
|
iter::{self, FromIterator},
|
||||||
ops::{Range, Sub},
|
ops::{Range, Sub},
|
||||||
path::Path,
|
|
||||||
str,
|
str,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
|
@ -83,6 +82,7 @@ struct BufferState {
|
||||||
last_parse_count: usize,
|
last_parse_count: usize,
|
||||||
last_selections_update_count: usize,
|
last_selections_update_count: usize,
|
||||||
last_diagnostics_update_count: usize,
|
last_diagnostics_update_count: usize,
|
||||||
|
last_file_update_count: usize,
|
||||||
excerpts: Vec<ExcerptId>,
|
excerpts: Vec<ExcerptId>,
|
||||||
_subscriptions: [gpui::Subscription; 2],
|
_subscriptions: [gpui::Subscription; 2],
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,6 @@ pub struct ExcerptProperties<'a, T> {
|
||||||
pub struct ExcerptBoundary {
|
pub struct ExcerptBoundary {
|
||||||
pub row: u32,
|
pub row: u32,
|
||||||
pub buffer: BufferSnapshot,
|
pub buffer: BufferSnapshot,
|
||||||
pub path: Option<Arc<Path>>,
|
|
||||||
pub range: Range<text::Anchor>,
|
pub range: Range<text::Anchor>,
|
||||||
pub starts_new_buffer: bool,
|
pub starts_new_buffer: bool,
|
||||||
}
|
}
|
||||||
|
@ -679,6 +678,7 @@ impl MultiBuffer {
|
||||||
last_parse_count: buffer_snapshot.parse_count(),
|
last_parse_count: buffer_snapshot.parse_count(),
|
||||||
last_selections_update_count: buffer_snapshot.selections_update_count(),
|
last_selections_update_count: buffer_snapshot.selections_update_count(),
|
||||||
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
|
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
|
||||||
|
last_file_update_count: buffer_snapshot.file_update_count(),
|
||||||
excerpts: Default::default(),
|
excerpts: Default::default(),
|
||||||
_subscriptions: [
|
_subscriptions: [
|
||||||
cx.observe(&props.buffer, |_, _, cx| cx.notify()),
|
cx.observe(&props.buffer, |_, _, cx| cx.notify()),
|
||||||
|
@ -929,6 +929,7 @@ impl MultiBuffer {
|
||||||
let parse_count = buffer.parse_count();
|
let parse_count = buffer.parse_count();
|
||||||
let selections_update_count = buffer.selections_update_count();
|
let selections_update_count = buffer.selections_update_count();
|
||||||
let diagnostics_update_count = buffer.diagnostics_update_count();
|
let diagnostics_update_count = buffer.diagnostics_update_count();
|
||||||
|
let file_update_count = buffer.file_update_count();
|
||||||
|
|
||||||
let buffer_edited = version.changed_since(&buffer_state.last_version);
|
let buffer_edited = version.changed_since(&buffer_state.last_version);
|
||||||
let buffer_reparsed = parse_count > buffer_state.last_parse_count;
|
let buffer_reparsed = parse_count > buffer_state.last_parse_count;
|
||||||
|
@ -936,15 +937,18 @@ impl MultiBuffer {
|
||||||
selections_update_count > buffer_state.last_selections_update_count;
|
selections_update_count > buffer_state.last_selections_update_count;
|
||||||
let buffer_diagnostics_updated =
|
let buffer_diagnostics_updated =
|
||||||
diagnostics_update_count > buffer_state.last_diagnostics_update_count;
|
diagnostics_update_count > buffer_state.last_diagnostics_update_count;
|
||||||
|
let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
|
||||||
if buffer_edited
|
if buffer_edited
|
||||||
|| buffer_reparsed
|
|| buffer_reparsed
|
||||||
|| buffer_selections_updated
|
|| buffer_selections_updated
|
||||||
|| buffer_diagnostics_updated
|
|| buffer_diagnostics_updated
|
||||||
|
|| buffer_file_updated
|
||||||
{
|
{
|
||||||
buffer_state.last_version = version;
|
buffer_state.last_version = version;
|
||||||
buffer_state.last_parse_count = parse_count;
|
buffer_state.last_parse_count = parse_count;
|
||||||
buffer_state.last_selections_update_count = selections_update_count;
|
buffer_state.last_selections_update_count = selections_update_count;
|
||||||
buffer_state.last_diagnostics_update_count = diagnostics_update_count;
|
buffer_state.last_diagnostics_update_count = diagnostics_update_count;
|
||||||
|
buffer_state.last_file_update_count = file_update_count;
|
||||||
excerpts_to_edit.extend(
|
excerpts_to_edit.extend(
|
||||||
buffer_state
|
buffer_state
|
||||||
.excerpts
|
.excerpts
|
||||||
|
@ -1761,38 +1765,35 @@ impl MultiBufferSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_contains_excerpt_boundary<T: ToOffset>(&self, range: Range<T>) -> bool {
|
|
||||||
let start = range.start.to_offset(self);
|
|
||||||
let end = range.end.to_offset(self);
|
|
||||||
let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
|
|
||||||
cursor.seek(&start, Bias::Right, &());
|
|
||||||
let start_id = cursor
|
|
||||||
.item()
|
|
||||||
.or_else(|| cursor.prev_item())
|
|
||||||
.map(|excerpt| &excerpt.id);
|
|
||||||
cursor.seek_forward(&end, Bias::Right, &());
|
|
||||||
let end_id = cursor
|
|
||||||
.item()
|
|
||||||
.or_else(|| cursor.prev_item())
|
|
||||||
.map(|excerpt| &excerpt.id);
|
|
||||||
start_id != end_id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn excerpt_boundaries_in_range<'a, T: ToOffset>(
|
pub fn excerpt_boundaries_in_range<'a, T: ToOffset>(
|
||||||
&'a self,
|
&'a self,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
) -> impl Iterator<Item = ExcerptBoundary> + 'a {
|
) -> impl Iterator<Item = ExcerptBoundary> + 'a {
|
||||||
let start = range.start.to_offset(self);
|
let start = range.start.to_offset(self);
|
||||||
let end = range.end.to_offset(self);
|
let end = range.end.to_offset(self);
|
||||||
let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
|
let mut cursor = self
|
||||||
|
.excerpts
|
||||||
|
.cursor::<(usize, (Option<&ExcerptId>, Point))>();
|
||||||
cursor.seek(&start, Bias::Right, &());
|
cursor.seek(&start, Bias::Right, &());
|
||||||
|
|
||||||
let prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
|
let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
|
||||||
|
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
let excerpt = cursor.item()?;
|
if start <= cursor.start().0 && end > cursor.start().0 {
|
||||||
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
|
let excerpt = cursor.item()?;
|
||||||
todo!()
|
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
|
||||||
|
let boundary = ExcerptBoundary {
|
||||||
|
row: cursor.start().1 .1.row,
|
||||||
|
buffer: excerpt.buffer.clone(),
|
||||||
|
range: excerpt.range.clone(),
|
||||||
|
starts_new_buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
prev_buffer_id = Some(excerpt.buffer_id);
|
||||||
|
cursor.next(&());
|
||||||
|
Some(boundary)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2648,21 +2649,50 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [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(5).collect::<Vec<_>>(), []);
|
||||||
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(1, 5)));
|
assert!(snapshot
|
||||||
assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(2, 0)));
|
.excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(1, 5))
|
||||||
assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(4, 0)));
|
.next()
|
||||||
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(2, 0)..Point::new(3, 0)));
|
.is_none());
|
||||||
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 0)..Point::new(4, 2)));
|
assert!(snapshot
|
||||||
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 2)..Point::new(4, 2)));
|
.excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(2, 0))
|
||||||
|
.next()
|
||||||
|
.is_some());
|
||||||
|
assert!(snapshot
|
||||||
|
.excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(4, 0))
|
||||||
|
.next()
|
||||||
|
.is_some());
|
||||||
|
assert!(snapshot
|
||||||
|
.excerpt_boundaries_in_range(Point::new(2, 0)..Point::new(3, 0))
|
||||||
|
.next()
|
||||||
|
.is_none());
|
||||||
|
assert!(snapshot
|
||||||
|
.excerpt_boundaries_in_range(Point::new(4, 0)..Point::new(4, 2))
|
||||||
|
.next()
|
||||||
|
.is_none());
|
||||||
|
assert!(snapshot
|
||||||
|
.excerpt_boundaries_in_range(Point::new(4, 2)..Point::new(4, 2))
|
||||||
|
.next()
|
||||||
|
.is_none());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snapshot
|
snapshot
|
||||||
.excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2))
|
.excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2))
|
||||||
|
.map(|boundary| (
|
||||||
|
boundary.row,
|
||||||
|
boundary
|
||||||
|
.buffer
|
||||||
|
.text_for_range(boundary.range)
|
||||||
|
.collect::<String>(),
|
||||||
|
boundary.starts_new_buffer
|
||||||
|
))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&[
|
&[
|
||||||
(Some(buffer_1.clone()), true),
|
(0, "".to_string(), true),
|
||||||
(Some(buffer_1.clone()), false),
|
(0, "".to_string(), true),
|
||||||
(Some(buffer_2.clone()), false),
|
(0, "".to_string(), true),
|
||||||
|
(0, "".to_string(), true),
|
||||||
|
(0, "".to_string(), true),
|
||||||
|
(0, "".to_string(), true),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ pub struct Buffer {
|
||||||
remote_selections: TreeMap<ReplicaId, SelectionSet>,
|
remote_selections: TreeMap<ReplicaId, SelectionSet>,
|
||||||
selections_update_count: usize,
|
selections_update_count: usize,
|
||||||
diagnostics_update_count: usize,
|
diagnostics_update_count: usize,
|
||||||
|
file_update_count: usize,
|
||||||
language_server: Option<LanguageServerState>,
|
language_server: Option<LanguageServerState>,
|
||||||
completion_triggers: Vec<String>,
|
completion_triggers: Vec<String>,
|
||||||
deferred_ops: OperationQueue<Operation>,
|
deferred_ops: OperationQueue<Operation>,
|
||||||
|
@ -78,8 +79,10 @@ pub struct Buffer {
|
||||||
pub struct BufferSnapshot {
|
pub struct BufferSnapshot {
|
||||||
text: text::BufferSnapshot,
|
text: text::BufferSnapshot,
|
||||||
tree: Option<Tree>,
|
tree: Option<Tree>,
|
||||||
|
path: Option<Arc<Path>>,
|
||||||
diagnostics: DiagnosticSet,
|
diagnostics: DiagnosticSet,
|
||||||
diagnostics_update_count: usize,
|
diagnostics_update_count: usize,
|
||||||
|
file_update_count: usize,
|
||||||
remote_selections: TreeMap<ReplicaId, SelectionSet>,
|
remote_selections: TreeMap<ReplicaId, SelectionSet>,
|
||||||
selections_update_count: usize,
|
selections_update_count: usize,
|
||||||
is_parsing: bool,
|
is_parsing: bool,
|
||||||
|
@ -498,6 +501,7 @@ impl Buffer {
|
||||||
selections_update_count: 0,
|
selections_update_count: 0,
|
||||||
diagnostics: Default::default(),
|
diagnostics: Default::default(),
|
||||||
diagnostics_update_count: 0,
|
diagnostics_update_count: 0,
|
||||||
|
file_update_count: 0,
|
||||||
language_server: None,
|
language_server: None,
|
||||||
completion_triggers: Default::default(),
|
completion_triggers: Default::default(),
|
||||||
deferred_ops: OperationQueue::new(),
|
deferred_ops: OperationQueue::new(),
|
||||||
|
@ -510,9 +514,11 @@ impl Buffer {
|
||||||
BufferSnapshot {
|
BufferSnapshot {
|
||||||
text: self.text.snapshot(),
|
text: self.text.snapshot(),
|
||||||
tree: self.syntax_tree(),
|
tree: self.syntax_tree(),
|
||||||
|
path: self.file.as_ref().map(|f| f.path().clone()),
|
||||||
remote_selections: self.remote_selections.clone(),
|
remote_selections: self.remote_selections.clone(),
|
||||||
diagnostics: self.diagnostics.clone(),
|
diagnostics: self.diagnostics.clone(),
|
||||||
diagnostics_update_count: self.diagnostics_update_count,
|
diagnostics_update_count: self.diagnostics_update_count,
|
||||||
|
file_update_count: self.file_update_count,
|
||||||
is_parsing: self.parsing_in_background,
|
is_parsing: self.parsing_in_background,
|
||||||
language: self.language.clone(),
|
language: self.language.clone(),
|
||||||
parse_count: self.parse_count,
|
parse_count: self.parse_count,
|
||||||
|
@ -722,6 +728,7 @@ impl Buffer {
|
||||||
self.saved_version = version;
|
self.saved_version = version;
|
||||||
if let Some(new_file) = new_file {
|
if let Some(new_file) = new_file {
|
||||||
self.file = Some(new_file);
|
self.file = Some(new_file);
|
||||||
|
self.file_update_count += 1;
|
||||||
}
|
}
|
||||||
if let Some((state, local_file)) = &self
|
if let Some((state, local_file)) = &self
|
||||||
.language_server
|
.language_server
|
||||||
|
@ -744,6 +751,7 @@ impl Buffer {
|
||||||
.detach()
|
.detach()
|
||||||
}
|
}
|
||||||
cx.emit(Event::Saved);
|
cx.emit(Event::Saved);
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn did_reload(
|
pub fn did_reload(
|
||||||
|
@ -819,7 +827,9 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if file_changed {
|
if file_changed {
|
||||||
|
self.file_update_count += 1;
|
||||||
cx.emit(Event::FileHandleChanged);
|
cx.emit(Event::FileHandleChanged);
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
self.file = Some(new_file);
|
self.file = Some(new_file);
|
||||||
task
|
task
|
||||||
|
@ -849,6 +859,10 @@ impl Buffer {
|
||||||
self.diagnostics_update_count
|
self.diagnostics_update_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_update_count(&self) -> usize {
|
||||||
|
self.file_update_count
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn syntax_tree(&self) -> Option<Tree> {
|
pub(crate) fn syntax_tree(&self) -> Option<Tree> {
|
||||||
if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() {
|
if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() {
|
||||||
self.interpolate_tree(syntax_tree);
|
self.interpolate_tree(syntax_tree);
|
||||||
|
@ -2231,6 +2245,14 @@ impl BufferSnapshot {
|
||||||
pub fn selections_update_count(&self) -> usize {
|
pub fn selections_update_count(&self) -> usize {
|
||||||
self.selections_update_count
|
self.selections_update_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> Option<&Arc<Path>> {
|
||||||
|
self.path.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_update_count(&self) -> usize {
|
||||||
|
self.file_update_count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for BufferSnapshot {
|
impl Clone for BufferSnapshot {
|
||||||
|
@ -2238,10 +2260,12 @@ impl Clone for BufferSnapshot {
|
||||||
Self {
|
Self {
|
||||||
text: self.text.clone(),
|
text: self.text.clone(),
|
||||||
tree: self.tree.clone(),
|
tree: self.tree.clone(),
|
||||||
|
path: self.path.clone(),
|
||||||
remote_selections: self.remote_selections.clone(),
|
remote_selections: self.remote_selections.clone(),
|
||||||
diagnostics: self.diagnostics.clone(),
|
diagnostics: self.diagnostics.clone(),
|
||||||
selections_update_count: self.selections_update_count,
|
selections_update_count: self.selections_update_count,
|
||||||
diagnostics_update_count: self.diagnostics_update_count,
|
diagnostics_update_count: self.diagnostics_update_count,
|
||||||
|
file_update_count: self.file_update_count,
|
||||||
is_parsing: self.is_parsing,
|
is_parsing: self.is_parsing,
|
||||||
language: self.language.clone(),
|
language: self.language.clone(),
|
||||||
parse_count: self.parse_count,
|
parse_count: self.parse_count,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue