Merge branch 'master' into buffer-per-inode
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
editor::{
|
||||
buffer::{Anchor, Buffer, Point, ToPoint},
|
||||
display_map::DisplayMap,
|
||||
display_map::{Bias, DisplayMap},
|
||||
DisplayPoint,
|
||||
},
|
||||
time,
|
||||
@@ -72,4 +72,34 @@ impl Selection {
|
||||
start..end
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_rows_for_display_rows(
|
||||
&self,
|
||||
map: &DisplayMap,
|
||||
ctx: &AppContext,
|
||||
) -> (Range<u32>, Range<u32>) {
|
||||
let display_start = self.start.to_display_point(map, ctx).unwrap();
|
||||
let buffer_start = DisplayPoint::new(display_start.row(), 0)
|
||||
.to_buffer_point(map, Bias::Left, ctx)
|
||||
.unwrap();
|
||||
|
||||
let mut display_end = self.end.to_display_point(map, ctx).unwrap();
|
||||
if display_end.row() != map.max_point(ctx).row()
|
||||
&& display_start.row() != display_end.row()
|
||||
&& display_end.column() == 0
|
||||
{
|
||||
*display_end.row_mut() -= 1;
|
||||
}
|
||||
let buffer_end = DisplayPoint::new(
|
||||
display_end.row(),
|
||||
map.line_len(display_end.row(), ctx).unwrap(),
|
||||
)
|
||||
.to_buffer_point(map, Bias::Left, ctx)
|
||||
.unwrap();
|
||||
|
||||
(
|
||||
buffer_start.row..buffer_end.row + 1,
|
||||
display_start.row()..display_end.row() + 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1368
-105
File diff suppressed because it is too large
Load Diff
@@ -89,6 +89,33 @@ impl FoldMap {
|
||||
DisplayPoint(self.transforms.summary().display.rightmost_point)
|
||||
}
|
||||
|
||||
pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
let buffer = self.buffer.read(app);
|
||||
let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
|
||||
let mut start_ix = find_insertion_index(&self.folds, |probe| probe.cmp(&range, buffer))?;
|
||||
let mut end_ix = start_ix;
|
||||
|
||||
for fold in self.folds[..start_ix].iter().rev() {
|
||||
if fold.end.cmp(&range.start, buffer)? == Ordering::Greater {
|
||||
start_ix -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for fold in &self.folds[end_ix..] {
|
||||
if range.end.cmp(&fold.start, buffer)? == Ordering::Greater {
|
||||
end_ix += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(&self.folds[start_ix..end_ix])
|
||||
}
|
||||
|
||||
pub fn fold<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
@@ -169,6 +196,13 @@ impl FoldMap {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(&self, point: DisplayPoint, app: &AppContext) -> Result<usize> {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right);
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
(cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(app))
|
||||
}
|
||||
|
||||
pub fn to_display_offset(
|
||||
&self,
|
||||
point: DisplayPoint,
|
||||
@@ -235,7 +269,7 @@ impl FoldMap {
|
||||
let next_edit = edits.next().unwrap();
|
||||
delta += next_edit.delta();
|
||||
|
||||
if next_edit.old_range.end > edit.old_range.end {
|
||||
if next_edit.old_range.end >= edit.old_range.end {
|
||||
edit.old_range.end = next_edit.old_range.end;
|
||||
cursor.seek(&edit.old_range.end, SeekBias::Right);
|
||||
cursor.next();
|
||||
@@ -415,7 +449,7 @@ impl<'a> Iterator for Chars<'a> {
|
||||
return Some(c);
|
||||
}
|
||||
|
||||
if self.offset == self.cursor.end().display.chars {
|
||||
while self.offset == self.cursor.end().display.chars && self.cursor.item().is_some() {
|
||||
self.cursor.next();
|
||||
}
|
||||
|
||||
@@ -519,6 +553,50 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adjacent_folds() {
|
||||
App::test((), |app| {
|
||||
let buffer = app.add_model(|_| Buffer::new(0, "abcdefghijkl"));
|
||||
|
||||
{
|
||||
let mut map = FoldMap::new(buffer.clone(), app.as_ref());
|
||||
|
||||
map.fold(vec![5..8], app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
assert_eq!(map.text(app.as_ref()), "abcde…ijkl");
|
||||
|
||||
// Create an fold adjacent to the start of the first fold.
|
||||
map.fold(vec![0..1, 2..5], app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
assert_eq!(map.text(app.as_ref()), "…b…ijkl");
|
||||
|
||||
// Create an fold adjacent to the end of the first fold.
|
||||
map.fold(vec![11..11, 8..10], app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
assert_eq!(map.text(app.as_ref()), "…b…kl");
|
||||
}
|
||||
|
||||
{
|
||||
let mut map = FoldMap::new(buffer.clone(), app.as_ref());
|
||||
|
||||
// Create two adjacent folds.
|
||||
map.fold(vec![0..2, 2..5], app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
assert_eq!(map.text(app.as_ref()), "…fghijkl");
|
||||
|
||||
// Edit within one of the folds.
|
||||
let edits = buffer.update(app, |buffer, ctx| {
|
||||
let version = buffer.version();
|
||||
buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
|
||||
buffer.edits_since(version).collect::<Vec<_>>()
|
||||
});
|
||||
map.apply_edits(edits.as_slice(), app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overlapping_folds() {
|
||||
App::test((), |app| {
|
||||
@@ -577,6 +655,9 @@ mod tests {
|
||||
let iterations = env::var("ITERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
|
||||
.unwrap_or(100);
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
let seed_range = if let Ok(seed) = env::var("SEED") {
|
||||
let seed = seed.parse().expect("invalid `SEED` variable");
|
||||
seed..seed + 1
|
||||
@@ -596,66 +677,70 @@ mod tests {
|
||||
});
|
||||
let mut map = FoldMap::new(buffer.clone(), app.as_ref());
|
||||
|
||||
{
|
||||
let buffer = buffer.read(app);
|
||||
for _ in 0..operations {
|
||||
log::info!("text: {:?}", buffer.read(app).text());
|
||||
{
|
||||
let buffer = buffer.read(app);
|
||||
|
||||
let fold_count = rng.gen_range(0..10);
|
||||
let mut fold_ranges: Vec<Range<usize>> = Vec::new();
|
||||
for _ in 0..fold_count {
|
||||
let end = rng.gen_range(0..buffer.len() + 1);
|
||||
let start = rng.gen_range(0..end + 1);
|
||||
fold_ranges.push(start..end);
|
||||
let fold_count = rng.gen_range(0..=2);
|
||||
let mut fold_ranges: Vec<Range<usize>> = Vec::new();
|
||||
for _ in 0..fold_count {
|
||||
let end = rng.gen_range(0..buffer.len() + 1);
|
||||
let start = rng.gen_range(0..end + 1);
|
||||
fold_ranges.push(start..end);
|
||||
}
|
||||
log::info!("folding {:?}", fold_ranges);
|
||||
map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
|
||||
let mut expected_text = buffer.text();
|
||||
for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
|
||||
expected_text.replace_range(fold_range.start..fold_range.end, "…");
|
||||
}
|
||||
assert_eq!(map.text(app.as_ref()), expected_text);
|
||||
|
||||
for fold_range in map.merged_fold_ranges(app.as_ref()) {
|
||||
let display_point =
|
||||
map.to_display_point(fold_range.start.to_point(buffer).unwrap());
|
||||
assert!(map.is_line_folded(display_point.row()));
|
||||
}
|
||||
}
|
||||
|
||||
map.fold(fold_ranges, app.as_ref()).unwrap();
|
||||
let edits = buffer.update(app, |buffer, ctx| {
|
||||
let start_version = buffer.version.clone();
|
||||
let edit_count = rng.gen_range(0..=2);
|
||||
buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
|
||||
buffer.edits_since(start_version).collect::<Vec<_>>()
|
||||
});
|
||||
log::info!("editing {:?}", edits);
|
||||
map.apply_edits(&edits, app.as_ref()).unwrap();
|
||||
map.check_invariants(app.as_ref());
|
||||
|
||||
let buffer = map.buffer.read(app);
|
||||
let mut expected_text = buffer.text();
|
||||
let mut expected_buffer_rows = Vec::new();
|
||||
let mut next_row = buffer.max_point().row;
|
||||
for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
|
||||
let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
|
||||
let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
|
||||
expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
|
||||
next_row = fold_start.row;
|
||||
|
||||
expected_text.replace_range(fold_range.start..fold_range.end, "…");
|
||||
}
|
||||
expected_buffer_rows.extend((0..=next_row).rev());
|
||||
expected_buffer_rows.reverse();
|
||||
|
||||
assert_eq!(map.text(app.as_ref()), expected_text);
|
||||
|
||||
for fold_range in map.merged_fold_ranges(app.as_ref()) {
|
||||
let display_point =
|
||||
map.to_display_point(fold_range.start.to_point(buffer).unwrap());
|
||||
assert!(map.is_line_folded(display_point.row()));
|
||||
for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
|
||||
let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
|
||||
assert_eq!(
|
||||
map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
|
||||
expected_buffer_rows[idx..],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let edits = buffer.update(app, |buffer, ctx| {
|
||||
let start_version = buffer.version.clone();
|
||||
let edit_count = rng.gen_range(1..10);
|
||||
buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
|
||||
buffer.edits_since(start_version).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
map.apply_edits(&edits, app.as_ref()).unwrap();
|
||||
|
||||
let buffer = map.buffer.read(app);
|
||||
let mut expected_text = buffer.text();
|
||||
let mut expected_buffer_rows = Vec::new();
|
||||
let mut next_row = buffer.max_point().row;
|
||||
for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
|
||||
let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
|
||||
let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
|
||||
expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
|
||||
next_row = fold_start.row;
|
||||
|
||||
expected_text.replace_range(fold_range.start..fold_range.end, "…");
|
||||
}
|
||||
expected_buffer_rows.extend((0..=next_row).rev());
|
||||
expected_buffer_rows.reverse();
|
||||
|
||||
assert_eq!(map.text(app.as_ref()), expected_text);
|
||||
|
||||
for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
|
||||
let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
|
||||
assert_eq!(
|
||||
map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
|
||||
expected_buffer_rows[idx..],
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -721,5 +806,13 @@ mod tests {
|
||||
}
|
||||
merged_ranges
|
||||
}
|
||||
|
||||
fn check_invariants(&self, app: &AppContext) {
|
||||
assert_eq!(
|
||||
self.transforms.summary().buffer.chars,
|
||||
self.buffer.read(app).len(),
|
||||
"transform tree does not match buffer's length"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ impl DisplayMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folds_in_range<T>(&self, range: Range<T>, app: &AppContext) -> Result<&[Range<Anchor>]>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
self.fold_map.folds_in_range(range, app)
|
||||
}
|
||||
|
||||
pub fn fold<T: ToOffset>(
|
||||
&mut self,
|
||||
ranges: impl IntoIterator<Item = Range<T>>,
|
||||
@@ -67,6 +74,20 @@ impl DisplayMap {
|
||||
Ok(chars.take_while(|c| *c != '\n').collect())
|
||||
}
|
||||
|
||||
pub fn line_indent(&self, display_row: u32, app: &AppContext) -> Result<(u32, bool)> {
|
||||
let mut indent = 0;
|
||||
let mut is_blank = true;
|
||||
for c in self.chars_at(DisplayPoint::new(display_row, 0), app)? {
|
||||
if c == ' ' {
|
||||
indent += 1;
|
||||
} else {
|
||||
is_blank = c == '\n';
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok((indent, is_blank))
|
||||
}
|
||||
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Result<Chars<'a>> {
|
||||
let column = point.column() as usize;
|
||||
let (point, to_next_stop) = point.collapse_tabs(self, Bias::Left, app)?;
|
||||
@@ -165,6 +186,11 @@ impl DisplayPoint {
|
||||
.to_buffer_point(self.collapse_tabs(map, bias, app)?.0))
|
||||
}
|
||||
|
||||
pub fn to_buffer_offset(self, map: &DisplayMap, bias: Bias, app: &AppContext) -> Result<usize> {
|
||||
map.fold_map
|
||||
.to_buffer_offset(self.collapse_tabs(map, bias, app)?.0, app)
|
||||
}
|
||||
|
||||
fn expand_tabs(mut self, map: &DisplayMap, app: &AppContext) -> Result<Self> {
|
||||
let chars = map
|
||||
.fold_map
|
||||
|
||||
@@ -12,14 +12,11 @@ use display_map::*;
|
||||
use std::{cmp, ops::Range};
|
||||
|
||||
trait RangeExt<T> {
|
||||
fn sorted(&self) -> (T, T);
|
||||
fn sorted(&self) -> Range<T>;
|
||||
}
|
||||
|
||||
impl<T: Ord + Clone> RangeExt<T> for Range<T> {
|
||||
fn sorted(&self) -> (T, T) {
|
||||
(
|
||||
cmp::min(&self.start, &self.end).clone(),
|
||||
cmp::max(&self.start, &self.end).clone(),
|
||||
)
|
||||
fn sorted(&self) -> Self {
|
||||
cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,3 +58,98 @@ pub fn down(
|
||||
|
||||
Ok((point, goal_column))
|
||||
}
|
||||
|
||||
pub fn line_beginning(
|
||||
map: &DisplayMap,
|
||||
point: DisplayPoint,
|
||||
toggle_indent: bool,
|
||||
app: &AppContext,
|
||||
) -> Result<DisplayPoint> {
|
||||
let (indent, is_blank) = map.line_indent(point.row(), app)?;
|
||||
if toggle_indent && !is_blank && point.column() != indent {
|
||||
Ok(DisplayPoint::new(point.row(), indent))
|
||||
} else {
|
||||
Ok(DisplayPoint::new(point.row(), 0))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_end(map: &DisplayMap, point: DisplayPoint, app: &AppContext) -> Result<DisplayPoint> {
|
||||
Ok(DisplayPoint::new(
|
||||
point.row(),
|
||||
map.line_len(point.row(), app)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn prev_word_boundary(
|
||||
map: &DisplayMap,
|
||||
point: DisplayPoint,
|
||||
app: &AppContext,
|
||||
) -> Result<DisplayPoint> {
|
||||
if point.column() == 0 {
|
||||
if point.row() == 0 {
|
||||
Ok(DisplayPoint::new(0, 0))
|
||||
} else {
|
||||
let row = point.row() - 1;
|
||||
Ok(DisplayPoint::new(row, map.line_len(row, app)?))
|
||||
}
|
||||
} else {
|
||||
let mut boundary = DisplayPoint::new(point.row(), 0);
|
||||
let mut column = 0;
|
||||
let mut prev_c = None;
|
||||
for c in map.chars_at(boundary, app)? {
|
||||
if column >= point.column() {
|
||||
break;
|
||||
}
|
||||
|
||||
if prev_c.is_none() || char_kind(prev_c.unwrap()) != char_kind(c) {
|
||||
*boundary.column_mut() = column;
|
||||
}
|
||||
|
||||
prev_c = Some(c);
|
||||
column += 1;
|
||||
}
|
||||
Ok(boundary)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_word_boundary(
|
||||
map: &DisplayMap,
|
||||
mut point: DisplayPoint,
|
||||
app: &AppContext,
|
||||
) -> Result<DisplayPoint> {
|
||||
let mut prev_c = None;
|
||||
for c in map.chars_at(point, app)? {
|
||||
if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if c == '\n' {
|
||||
*point.row_mut() += 1;
|
||||
*point.column_mut() = 0;
|
||||
} else {
|
||||
*point.column_mut() += 1;
|
||||
}
|
||||
prev_c = Some(c);
|
||||
}
|
||||
Ok(point)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum CharKind {
|
||||
Newline,
|
||||
Whitespace,
|
||||
Punctuation,
|
||||
Word,
|
||||
}
|
||||
|
||||
fn char_kind(c: char) -> CharKind {
|
||||
if c == '\n' {
|
||||
CharKind::Newline
|
||||
} else if c.is_whitespace() {
|
||||
CharKind::Whitespace
|
||||
} else if c.is_alphanumeric() || c == '_' {
|
||||
CharKind::Word
|
||||
} else {
|
||||
CharKind::Punctuation
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user