mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 18:41:56 +03:00
Merge branch 'anchor-map-selections' into lsp
This commit is contained in:
commit
e1556893f7
@ -1,8 +1,10 @@
|
|||||||
use crate::FullOffset;
|
use super::{Buffer, Content, FromAnchor, FullOffset, Point, ToOffset};
|
||||||
|
|
||||||
use super::{Buffer, Content, FromAnchor, Point, ToOffset};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{cmp::Ordering, ops::Range};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
ops::Range,
|
||||||
|
};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
|
||||||
@ -112,7 +114,25 @@ impl Anchor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AnchorMap<T> {
|
impl<T> AnchorMap<T> {
|
||||||
pub fn to_points<'a>(
|
pub fn version(&self) -> &clock::Global {
|
||||||
|
&self.version
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.entries.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offsets<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl Iterator<Item = (usize, &'a T)> + 'a {
|
||||||
|
let content = content.into();
|
||||||
|
content
|
||||||
|
.summaries_for_anchors(self)
|
||||||
|
.map(move |(sum, value)| (sum.bytes, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn points<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
content: impl Into<Content<'a>> + 'a,
|
content: impl Into<Content<'a>> + 'a,
|
||||||
) -> impl Iterator<Item = (Point, &'a T)> + 'a {
|
) -> impl Iterator<Item = (Point, &'a T)> + 'a {
|
||||||
@ -121,23 +141,50 @@ impl<T> AnchorMap<T> {
|
|||||||
.summaries_for_anchors(self)
|
.summaries_for_anchors(self)
|
||||||
.map(move |(sum, value)| (sum.lines, value))
|
.map(move |(sum, value)| (sum.lines, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> &clock::Global {
|
|
||||||
&self.version
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnchorSet {
|
impl AnchorSet {
|
||||||
pub fn to_points<'a>(
|
pub fn version(&self) -> &clock::Global {
|
||||||
|
&self.0.version
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offsets<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl Iterator<Item = usize> + 'a {
|
||||||
|
self.0.offsets(content).map(|(offset, _)| offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn points<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
content: impl Into<Content<'a>> + 'a,
|
content: impl Into<Content<'a>> + 'a,
|
||||||
) -> impl Iterator<Item = Point> + 'a {
|
) -> impl Iterator<Item = Point> + 'a {
|
||||||
self.0.to_points(content).map(move |(point, _)| point)
|
self.0.points(content).map(|(point, _)| point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AnchorRangeMap<T> {
|
impl<T> AnchorRangeMap<T> {
|
||||||
pub fn to_point_ranges<'a>(
|
pub fn version(&self) -> &clock::Global {
|
||||||
|
&self.version
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.entries.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_raw(version: clock::Global, entries: Vec<(Range<(FullOffset, Bias)>, T)>) -> Self {
|
||||||
|
Self { version, entries }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_entries(&self) -> &[(Range<(FullOffset, Bias)>, T)] {
|
||||||
|
&self.entries
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn point_ranges<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
content: impl Into<Content<'a>> + 'a,
|
content: impl Into<Content<'a>> + 'a,
|
||||||
) -> impl Iterator<Item = (Range<Point>, &'a T)> + 'a {
|
) -> impl Iterator<Item = (Range<Point>, &'a T)> + 'a {
|
||||||
@ -147,22 +194,68 @@ impl<T> AnchorRangeMap<T> {
|
|||||||
.map(move |(range, value)| ((range.start.lines..range.end.lines), value))
|
.map(move |(range, value)| ((range.start.lines..range.end.lines), value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> &clock::Global {
|
pub fn offset_ranges<'a>(
|
||||||
&self.version
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl Iterator<Item = (Range<usize>, &'a T)> + 'a {
|
||||||
|
let content = content.into();
|
||||||
|
content
|
||||||
|
.summaries_for_anchor_ranges(self)
|
||||||
|
.map(move |(range, value)| ((range.start.bytes..range.end.bytes), value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for AnchorRangeMap<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.version == other.version && self.entries == other.entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> Eq for AnchorRangeMap<T> {}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for AnchorRangeMap<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
let mut f = f.debug_map();
|
||||||
|
for (range, value) in &self.entries {
|
||||||
|
f.key(range);
|
||||||
|
f.value(value);
|
||||||
|
}
|
||||||
|
f.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AnchorRangeSet {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut f = f.debug_set();
|
||||||
|
for (range, _) in &self.0.entries {
|
||||||
|
f.entry(range);
|
||||||
|
}
|
||||||
|
f.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnchorRangeSet {
|
impl AnchorRangeSet {
|
||||||
pub fn to_point_ranges<'a>(
|
pub fn len(&self) -> usize {
|
||||||
&'a self,
|
self.0.len()
|
||||||
content: impl Into<Content<'a>> + 'a,
|
|
||||||
) -> impl Iterator<Item = Range<Point>> + 'a {
|
|
||||||
self.0.to_point_ranges(content).map(|(range, _)| range)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> &clock::Global {
|
pub fn version(&self) -> &clock::Global {
|
||||||
self.0.version()
|
self.0.version()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn offset_ranges<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl Iterator<Item = Range<usize>> + 'a {
|
||||||
|
self.0.offset_ranges(content).map(|(range, _)| range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn point_ranges<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl Iterator<Item = Range<Point>> + 'a {
|
||||||
|
self.0.point_ranges(content).map(|(range, _)| range)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Default for AnchorRangeMultimap<T> {
|
impl<T: Clone> Default for AnchorRangeMultimap<T> {
|
||||||
|
@ -20,7 +20,7 @@ use rpc::proto;
|
|||||||
pub use selection::*;
|
pub use selection::*;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Reverse},
|
cmp::{self, Reverse},
|
||||||
convert::{TryFrom, TryInto},
|
convert::TryFrom,
|
||||||
iter::Iterator,
|
iter::Iterator,
|
||||||
ops::{self, Range},
|
ops::{self, Range},
|
||||||
str,
|
str,
|
||||||
@ -73,20 +73,14 @@ pub struct Buffer {
|
|||||||
lamport_clock: clock::Lamport,
|
lamport_clock: clock::Lamport,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct SelectionSet {
|
|
||||||
pub selections: Arc<[Selection]>,
|
|
||||||
pub active: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
start: clock::Global,
|
start: clock::Global,
|
||||||
end: clock::Global,
|
end: clock::Global,
|
||||||
edits: Vec<clock::Local>,
|
edits: Vec<clock::Local>,
|
||||||
ranges: Vec<Range<FullOffset>>,
|
ranges: Vec<Range<FullOffset>>,
|
||||||
selections_before: HashMap<SelectionSetId, Arc<[Selection]>>,
|
selections_before: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
|
||||||
selections_after: HashMap<SelectionSetId, Arc<[Selection]>>,
|
selections_after: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
|
||||||
first_edit_at: Instant,
|
first_edit_at: Instant,
|
||||||
last_edit_at: Instant,
|
last_edit_at: Instant,
|
||||||
}
|
}
|
||||||
@ -173,7 +167,7 @@ impl History {
|
|||||||
fn start_transaction(
|
fn start_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
start: clock::Global,
|
start: clock::Global,
|
||||||
selections_before: HashMap<SelectionSetId, Arc<[Selection]>>,
|
selections_before: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
|
||||||
now: Instant,
|
now: Instant,
|
||||||
) {
|
) {
|
||||||
self.transaction_depth += 1;
|
self.transaction_depth += 1;
|
||||||
@ -193,7 +187,7 @@ impl History {
|
|||||||
|
|
||||||
fn end_transaction(
|
fn end_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
selections_after: HashMap<SelectionSetId, Arc<[Selection]>>,
|
selections_after: HashMap<SelectionSetId, Arc<AnchorRangeMap<SelectionState>>>,
|
||||||
now: Instant,
|
now: Instant,
|
||||||
) -> Option<&Transaction> {
|
) -> Option<&Transaction> {
|
||||||
assert_ne!(self.transaction_depth, 0);
|
assert_ne!(self.transaction_depth, 0);
|
||||||
@ -415,7 +409,11 @@ pub enum Operation {
|
|||||||
},
|
},
|
||||||
UpdateSelections {
|
UpdateSelections {
|
||||||
set_id: SelectionSetId,
|
set_id: SelectionSetId,
|
||||||
selections: Option<Arc<[Selection]>>,
|
selections: Arc<AnchorRangeMap<SelectionState>>,
|
||||||
|
lamport_timestamp: clock::Lamport,
|
||||||
|
},
|
||||||
|
RemoveSelections {
|
||||||
|
set_id: SelectionSetId,
|
||||||
lamport_timestamp: clock::Lamport,
|
lamport_timestamp: clock::Lamport,
|
||||||
},
|
},
|
||||||
SetActiveSelections {
|
SetActiveSelections {
|
||||||
@ -489,20 +487,8 @@ impl Buffer {
|
|||||||
.selections
|
.selections
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|set| {
|
.map(|set| {
|
||||||
let set_id = clock::Lamport {
|
let set = SelectionSet::try_from(set)?;
|
||||||
replica_id: set.replica_id as ReplicaId,
|
Result::<_, anyhow::Error>::Ok((set.id, set))
|
||||||
value: set.local_timestamp,
|
|
||||||
};
|
|
||||||
let selections: Vec<Selection> = set
|
|
||||||
.selections
|
|
||||||
.into_iter()
|
|
||||||
.map(TryFrom::try_from)
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
let set = SelectionSet {
|
|
||||||
selections: Arc::from(selections),
|
|
||||||
active: set.is_active,
|
|
||||||
};
|
|
||||||
Result::<_, anyhow::Error>::Ok((set_id, set))
|
|
||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
@ -514,16 +500,7 @@ impl Buffer {
|
|||||||
id: self.remote_id,
|
id: self.remote_id,
|
||||||
content: self.history.base_text.to_string(),
|
content: self.history.base_text.to_string(),
|
||||||
history: ops,
|
history: ops,
|
||||||
selections: self
|
selections: self.selections.iter().map(|(_, set)| set.into()).collect(),
|
||||||
.selections
|
|
||||||
.iter()
|
|
||||||
.map(|(set_id, set)| proto::SelectionSetSnapshot {
|
|
||||||
replica_id: set_id.replica_id as u32,
|
|
||||||
local_timestamp: set_id.value,
|
|
||||||
selections: set.selections.iter().map(Into::into).collect(),
|
|
||||||
is_active: set.active,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +542,13 @@ impl Buffer {
|
|||||||
self.content().anchor_at(position, bias)
|
self.content().anchor_at(position, bias)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn anchor_range_set<E>(&self, entries: E) -> AnchorRangeSet
|
||||||
|
where
|
||||||
|
E: IntoIterator<Item = Range<(usize, Bias)>>,
|
||||||
|
{
|
||||||
|
self.content().anchor_range_set(entries)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
|
pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
|
||||||
self.content().point_for_offset(offset)
|
self.content().point_for_offset(offset)
|
||||||
}
|
}
|
||||||
@ -858,23 +842,27 @@ impl Buffer {
|
|||||||
selections,
|
selections,
|
||||||
lamport_timestamp,
|
lamport_timestamp,
|
||||||
} => {
|
} => {
|
||||||
if let Some(selections) = selections {
|
if let Some(set) = self.selections.get_mut(&set_id) {
|
||||||
if let Some(set) = self.selections.get_mut(&set_id) {
|
set.selections = selections;
|
||||||
set.selections = selections;
|
|
||||||
} else {
|
|
||||||
self.selections.insert(
|
|
||||||
set_id,
|
|
||||||
SelectionSet {
|
|
||||||
selections,
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.selections.remove(&set_id);
|
self.selections.insert(
|
||||||
|
set_id,
|
||||||
|
SelectionSet {
|
||||||
|
id: set_id,
|
||||||
|
selections,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.lamport_clock.observe(lamport_timestamp);
|
self.lamport_clock.observe(lamport_timestamp);
|
||||||
}
|
}
|
||||||
|
Operation::RemoveSelections {
|
||||||
|
set_id,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => {
|
||||||
|
self.selections.remove(&set_id);
|
||||||
|
self.lamport_clock.observe(lamport_timestamp);
|
||||||
|
}
|
||||||
Operation::SetActiveSelections {
|
Operation::SetActiveSelections {
|
||||||
set_id,
|
set_id,
|
||||||
lamport_timestamp,
|
lamport_timestamp,
|
||||||
@ -1142,16 +1130,9 @@ impl Buffer {
|
|||||||
Operation::Edit(edit) => self.version >= edit.version,
|
Operation::Edit(edit) => self.version >= edit.version,
|
||||||
Operation::Undo { undo, .. } => self.version >= undo.version,
|
Operation::Undo { undo, .. } => self.version >= undo.version,
|
||||||
Operation::UpdateSelections { selections, .. } => {
|
Operation::UpdateSelections { selections, .. } => {
|
||||||
if let Some(selections) = selections {
|
self.version >= *selections.version()
|
||||||
selections.iter().all(|selection| {
|
|
||||||
let contains_start = self.version >= selection.start.version;
|
|
||||||
let contains_end = self.version >= selection.end.version;
|
|
||||||
contains_start && contains_end
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Operation::RemoveSelections { .. } => true,
|
||||||
Operation::SetActiveSelections { set_id, .. } => {
|
Operation::SetActiveSelections { set_id, .. } => {
|
||||||
set_id.map_or(true, |set_id| self.selections.contains_key(&set_id))
|
set_id.map_or(true, |set_id| self.selections.contains_key(&set_id))
|
||||||
}
|
}
|
||||||
@ -1232,7 +1213,7 @@ impl Buffer {
|
|||||||
let selections = transaction.selections_before.clone();
|
let selections = transaction.selections_before.clone();
|
||||||
ops.push(self.undo_or_redo(transaction).unwrap());
|
ops.push(self.undo_or_redo(transaction).unwrap());
|
||||||
for (set_id, selections) in selections {
|
for (set_id, selections) in selections {
|
||||||
ops.extend(self.update_selection_set(set_id, selections));
|
ops.extend(self.restore_selection_set(set_id, selections));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ops
|
ops
|
||||||
@ -1244,7 +1225,7 @@ impl Buffer {
|
|||||||
let selections = transaction.selections_after.clone();
|
let selections = transaction.selections_after.clone();
|
||||||
ops.push(self.undo_or_redo(transaction).unwrap());
|
ops.push(self.undo_or_redo(transaction).unwrap());
|
||||||
for (set_id, selections) in selections {
|
for (set_id, selections) in selections {
|
||||||
ops.extend(self.update_selection_set(set_id, selections));
|
ops.extend(self.restore_selection_set(set_id, selections));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ops
|
ops
|
||||||
@ -1281,12 +1262,32 @@ impl Buffer {
|
|||||||
self.selections.iter()
|
self.selections.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_selection_set(
|
fn build_selection_anchor_range_map<T: ToOffset>(
|
||||||
|
&self,
|
||||||
|
selections: &[Selection<T>],
|
||||||
|
) -> Arc<AnchorRangeMap<SelectionState>> {
|
||||||
|
Arc::new(
|
||||||
|
self.content()
|
||||||
|
.anchor_range_map(selections.iter().map(|selection| {
|
||||||
|
let start = selection.start.to_offset(self);
|
||||||
|
let end = selection.end.to_offset(self);
|
||||||
|
let range = (start, Bias::Left)..(end, Bias::Left);
|
||||||
|
let state = SelectionState {
|
||||||
|
id: selection.id,
|
||||||
|
reversed: selection.reversed,
|
||||||
|
goal: selection.goal,
|
||||||
|
};
|
||||||
|
(range, state)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_selection_set<T: ToOffset>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_id: SelectionSetId,
|
set_id: SelectionSetId,
|
||||||
selections: impl Into<Arc<[Selection]>>,
|
selections: &[Selection<T>],
|
||||||
) -> Result<Operation> {
|
) -> Result<Operation> {
|
||||||
let selections = selections.into();
|
let selections = self.build_selection_anchor_range_map(selections);
|
||||||
let set = self
|
let set = self
|
||||||
.selections
|
.selections
|
||||||
.get_mut(&set_id)
|
.get_mut(&set_id)
|
||||||
@ -1294,25 +1295,43 @@ impl Buffer {
|
|||||||
set.selections = selections.clone();
|
set.selections = selections.clone();
|
||||||
Ok(Operation::UpdateSelections {
|
Ok(Operation::UpdateSelections {
|
||||||
set_id,
|
set_id,
|
||||||
selections: Some(selections),
|
selections,
|
||||||
lamport_timestamp: self.lamport_clock.tick(),
|
lamport_timestamp: self.lamport_clock.tick(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_selection_set(&mut self, selections: impl Into<Arc<[Selection]>>) -> Operation {
|
pub fn restore_selection_set(
|
||||||
let selections = selections.into();
|
&mut self,
|
||||||
let lamport_timestamp = self.lamport_clock.tick();
|
set_id: SelectionSetId,
|
||||||
|
selections: Arc<AnchorRangeMap<SelectionState>>,
|
||||||
|
) -> Result<Operation> {
|
||||||
|
let set = self
|
||||||
|
.selections
|
||||||
|
.get_mut(&set_id)
|
||||||
|
.ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
|
||||||
|
set.selections = selections.clone();
|
||||||
|
Ok(Operation::UpdateSelections {
|
||||||
|
set_id,
|
||||||
|
selections,
|
||||||
|
lamport_timestamp: self.lamport_clock.tick(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_selection_set<T: ToOffset>(&mut self, selections: &[Selection<T>]) -> Operation {
|
||||||
|
let selections = self.build_selection_anchor_range_map(selections);
|
||||||
|
let set_id = self.lamport_clock.tick();
|
||||||
self.selections.insert(
|
self.selections.insert(
|
||||||
lamport_timestamp,
|
set_id,
|
||||||
SelectionSet {
|
SelectionSet {
|
||||||
|
id: set_id,
|
||||||
selections: selections.clone(),
|
selections: selections.clone(),
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Operation::UpdateSelections {
|
Operation::UpdateSelections {
|
||||||
set_id: lamport_timestamp,
|
set_id,
|
||||||
selections: Some(selections),
|
selections,
|
||||||
lamport_timestamp,
|
lamport_timestamp: set_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,9 +1363,8 @@ impl Buffer {
|
|||||||
self.selections
|
self.selections
|
||||||
.remove(&set_id)
|
.remove(&set_id)
|
||||||
.ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
|
.ok_or_else(|| anyhow!("invalid selection set id {:?}", set_id))?;
|
||||||
Ok(Operation::UpdateSelections {
|
Ok(Operation::RemoveSelections {
|
||||||
set_id,
|
set_id,
|
||||||
selections: None,
|
|
||||||
lamport_timestamp: self.lamport_clock.tick(),
|
lamport_timestamp: self.lamport_clock.tick(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1419,9 +1437,9 @@ impl Buffer {
|
|||||||
let new_selections = self.selections_from_ranges(ranges).unwrap();
|
let new_selections = self.selections_from_ranges(ranges).unwrap();
|
||||||
|
|
||||||
let op = if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
|
let op = if set_id.is_none() || rng.gen_bool(1.0 / 5.0) {
|
||||||
self.add_selection_set(new_selections)
|
self.add_selection_set(&new_selections)
|
||||||
} else {
|
} else {
|
||||||
self.update_selection_set(*set_id.unwrap(), new_selections)
|
self.update_selection_set(*set_id.unwrap(), &new_selections)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
ops.push(op);
|
ops.push(op);
|
||||||
@ -1447,7 +1465,7 @@ impl Buffer {
|
|||||||
ops
|
ops
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection>>
|
fn selections_from_ranges<I>(&self, ranges: I) -> Result<Vec<Selection<usize>>>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = Range<usize>>,
|
I: IntoIterator<Item = Range<usize>>,
|
||||||
{
|
{
|
||||||
@ -1458,25 +1476,28 @@ impl Buffer {
|
|||||||
let mut ranges = ranges.into_iter().collect::<Vec<_>>();
|
let mut ranges = ranges.into_iter().collect::<Vec<_>>();
|
||||||
ranges.sort_unstable_by_key(|range| range.start);
|
ranges.sort_unstable_by_key(|range| range.start);
|
||||||
|
|
||||||
let mut selections = Vec::with_capacity(ranges.len());
|
let mut selections = Vec::<Selection<usize>>::with_capacity(ranges.len());
|
||||||
for range in ranges {
|
for mut range in ranges {
|
||||||
|
let mut reversed = false;
|
||||||
if range.start > range.end {
|
if range.start > range.end {
|
||||||
selections.push(Selection {
|
reversed = true;
|
||||||
id: NEXT_SELECTION_ID.fetch_add(1, atomic::Ordering::SeqCst),
|
std::mem::swap(&mut range.start, &mut range.end);
|
||||||
start: self.anchor_before(range.end),
|
|
||||||
end: self.anchor_before(range.start),
|
|
||||||
reversed: true,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
selections.push(Selection {
|
|
||||||
id: NEXT_SELECTION_ID.fetch_add(1, atomic::Ordering::SeqCst),
|
|
||||||
start: self.anchor_after(range.start),
|
|
||||||
end: self.anchor_before(range.end),
|
|
||||||
reversed: false,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(selection) = selections.last_mut() {
|
||||||
|
if selection.end >= range.start {
|
||||||
|
selection.end = range.end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selections.push(Selection {
|
||||||
|
id: NEXT_SELECTION_ID.fetch_add(1, atomic::Ordering::SeqCst),
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
reversed,
|
||||||
|
goal: SelectionGoal::None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Ok(selections)
|
Ok(selections)
|
||||||
}
|
}
|
||||||
@ -1484,15 +1505,12 @@ impl Buffer {
|
|||||||
pub fn selection_ranges<'a>(&'a self, set_id: SelectionSetId) -> Result<Vec<Range<usize>>> {
|
pub fn selection_ranges<'a>(&'a self, set_id: SelectionSetId) -> Result<Vec<Range<usize>>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.selection_set(set_id)?
|
.selection_set(set_id)?
|
||||||
.selections
|
.offset_selections(self)
|
||||||
.iter()
|
|
||||||
.map(move |selection| {
|
.map(move |selection| {
|
||||||
let start = selection.start.to_offset(self);
|
|
||||||
let end = selection.end.to_offset(self);
|
|
||||||
if selection.reversed {
|
if selection.reversed {
|
||||||
end..start
|
selection.end..selection.start
|
||||||
} else {
|
} else {
|
||||||
start..end
|
selection.start..selection.end
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
@ -2259,6 +2277,9 @@ impl Operation {
|
|||||||
Operation::UpdateSelections {
|
Operation::UpdateSelections {
|
||||||
lamport_timestamp, ..
|
lamport_timestamp, ..
|
||||||
} => *lamport_timestamp,
|
} => *lamport_timestamp,
|
||||||
|
Operation::RemoveSelections {
|
||||||
|
lamport_timestamp, ..
|
||||||
|
} => *lamport_timestamp,
|
||||||
Operation::SetActiveSelections {
|
Operation::SetActiveSelections {
|
||||||
lamport_timestamp, ..
|
lamport_timestamp, ..
|
||||||
} => *lamport_timestamp,
|
} => *lamport_timestamp,
|
||||||
@ -2315,9 +2336,27 @@ impl<'a> Into<proto::Operation> for &'a Operation {
|
|||||||
replica_id: set_id.replica_id as u32,
|
replica_id: set_id.replica_id as u32,
|
||||||
local_timestamp: set_id.value,
|
local_timestamp: set_id.value,
|
||||||
lamport_timestamp: lamport_timestamp.value,
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
set: selections.as_ref().map(|selections| proto::SelectionSet {
|
version: selections.version().into(),
|
||||||
selections: selections.iter().map(Into::into).collect(),
|
selections: selections
|
||||||
}),
|
.raw_entries()
|
||||||
|
.iter()
|
||||||
|
.map(|(range, state)| proto::Selection {
|
||||||
|
id: state.id as u64,
|
||||||
|
start: range.start.0.to_proto(),
|
||||||
|
end: range.end.0.to_proto(),
|
||||||
|
reversed: state.reversed,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Operation::RemoveSelections {
|
||||||
|
set_id,
|
||||||
|
lamport_timestamp,
|
||||||
|
} => proto::operation::Variant::RemoveSelections(
|
||||||
|
proto::operation::RemoveSelections {
|
||||||
|
replica_id: set_id.replica_id as u32,
|
||||||
|
local_timestamp: set_id.value,
|
||||||
|
lamport_timestamp: lamport_timestamp.value,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Operation::SetActiveSelections {
|
Operation::SetActiveSelections {
|
||||||
@ -2358,30 +2397,6 @@ impl<'a> Into<proto::operation::Edit> for &'a EditOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<proto::Anchor> for &'a Anchor {
|
|
||||||
fn into(self) -> proto::Anchor {
|
|
||||||
proto::Anchor {
|
|
||||||
version: (&self.version).into(),
|
|
||||||
offset: self.full_offset.to_proto(),
|
|
||||||
bias: match self.bias {
|
|
||||||
Bias::Left => proto::anchor::Bias::Left as i32,
|
|
||||||
Bias::Right => proto::anchor::Bias::Right as i32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Into<proto::Selection> for &'a Selection {
|
|
||||||
fn into(self) -> proto::Selection {
|
|
||||||
proto::Selection {
|
|
||||||
id: self.id as u64,
|
|
||||||
start: Some((&self.start).into()),
|
|
||||||
end: Some((&self.end).into()),
|
|
||||||
reversed: self.reversed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<proto::Operation> for Operation {
|
impl TryFrom<proto::Operation> for Operation {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
@ -2424,16 +2439,23 @@ impl TryFrom<proto::Operation> for Operation {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
proto::operation::Variant::UpdateSelections(message) => {
|
proto::operation::Variant::UpdateSelections(message) => {
|
||||||
let selections: Option<Vec<Selection>> = if let Some(set) = message.set {
|
let version = message.version.into();
|
||||||
Some(
|
let entries = message
|
||||||
set.selections
|
.selections
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(TryFrom::try_from)
|
.map(|selection| {
|
||||||
.collect::<Result<_, _>>()?,
|
let range = (FullOffset::from_proto(selection.start), Bias::Left)
|
||||||
)
|
..(FullOffset::from_proto(selection.end), Bias::Right);
|
||||||
} else {
|
let state = SelectionState {
|
||||||
None
|
id: selection.id as usize,
|
||||||
};
|
reversed: selection.reversed,
|
||||||
|
goal: SelectionGoal::None,
|
||||||
|
};
|
||||||
|
(range, state)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let selections = AnchorRangeMap::from_raw(version, entries);
|
||||||
|
|
||||||
Operation::UpdateSelections {
|
Operation::UpdateSelections {
|
||||||
set_id: clock::Lamport {
|
set_id: clock::Lamport {
|
||||||
replica_id: message.replica_id as ReplicaId,
|
replica_id: message.replica_id as ReplicaId,
|
||||||
@ -2443,7 +2465,19 @@ impl TryFrom<proto::Operation> for Operation {
|
|||||||
replica_id: message.replica_id as ReplicaId,
|
replica_id: message.replica_id as ReplicaId,
|
||||||
value: message.lamport_timestamp,
|
value: message.lamport_timestamp,
|
||||||
},
|
},
|
||||||
selections: selections.map(Arc::from),
|
selections: Arc::from(selections),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proto::operation::Variant::RemoveSelections(message) => {
|
||||||
|
Operation::RemoveSelections {
|
||||||
|
set_id: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.local_timestamp,
|
||||||
|
},
|
||||||
|
lamport_timestamp: clock::Lamport {
|
||||||
|
replica_id: message.replica_id as ReplicaId,
|
||||||
|
value: message.lamport_timestamp,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
proto::operation::Variant::SetActiveSelections(message) => {
|
proto::operation::Variant::SetActiveSelections(message) => {
|
||||||
@ -2483,52 +2517,6 @@ impl From<proto::operation::Edit> for EditOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<proto::Anchor> for Anchor {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(message: proto::Anchor) -> Result<Self, Self::Error> {
|
|
||||||
let mut version = clock::Global::new();
|
|
||||||
for entry in message.version {
|
|
||||||
version.observe(clock::Local {
|
|
||||||
replica_id: entry.replica_id as ReplicaId,
|
|
||||||
value: entry.timestamp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
full_offset: FullOffset::from_proto(message.offset),
|
|
||||||
bias: if message.bias == proto::anchor::Bias::Left as i32 {
|
|
||||||
Bias::Left
|
|
||||||
} else if message.bias == proto::anchor::Bias::Right as i32 {
|
|
||||||
Bias::Right
|
|
||||||
} else {
|
|
||||||
Err(anyhow!("invalid anchor bias {}", message.bias))?
|
|
||||||
},
|
|
||||||
version,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<proto::Selection> for Selection {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(selection: proto::Selection) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Selection {
|
|
||||||
id: selection.id as usize,
|
|
||||||
start: selection
|
|
||||||
.start
|
|
||||||
.ok_or_else(|| anyhow!("missing selection start"))?
|
|
||||||
.try_into()?,
|
|
||||||
end: selection
|
|
||||||
.end
|
|
||||||
.ok_or_else(|| anyhow!("missing selection end"))?
|
|
||||||
.try_into()?,
|
|
||||||
reversed: selection.reversed,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ToOffset {
|
pub trait ToOffset {
|
||||||
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
|
||||||
|
|
||||||
@ -2582,6 +2570,12 @@ impl ToPoint for usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToPoint for Point {
|
||||||
|
fn to_point<'a>(&self, _: impl Into<Content<'a>>) -> Point {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FromAnchor {
|
pub trait FromAnchor {
|
||||||
fn from_anchor<'a>(anchor: &Anchor, content: &Content<'a>) -> Self;
|
fn from_anchor<'a>(anchor: &Anchor, content: &Content<'a>) -> Self;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::{Anchor, Buffer, Point, ToOffset as _, ToPoint as _};
|
use super::{AnchorRangeMap, Buffer, Content, FullOffset, Point, ToOffset, ToPoint};
|
||||||
use std::{cmp::Ordering, mem, ops::Range};
|
use rpc::proto;
|
||||||
|
use std::{cmp::Ordering, ops::Range, sync::Arc};
|
||||||
|
use sum_tree::Bias;
|
||||||
|
|
||||||
pub type SelectionSetId = clock::Lamport;
|
pub type SelectionSetId = clock::Lamport;
|
||||||
pub type SelectionsVersion = usize;
|
pub type SelectionsVersion = usize;
|
||||||
@ -12,44 +14,62 @@ pub enum SelectionGoal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Selection {
|
pub struct Selection<T> {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub start: Anchor,
|
pub start: T,
|
||||||
pub end: Anchor,
|
pub end: T,
|
||||||
pub reversed: bool,
|
pub reversed: bool,
|
||||||
pub goal: SelectionGoal,
|
pub goal: SelectionGoal,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Selection {
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub fn head(&self) -> &Anchor {
|
pub struct SelectionSet {
|
||||||
|
pub id: SelectionSetId,
|
||||||
|
pub active: bool,
|
||||||
|
pub selections: Arc<AnchorRangeMap<SelectionState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct SelectionState {
|
||||||
|
pub id: usize,
|
||||||
|
pub reversed: bool,
|
||||||
|
pub goal: SelectionGoal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.start == self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn head(&self) -> T {
|
||||||
if self.reversed {
|
if self.reversed {
|
||||||
&self.start
|
self.start
|
||||||
} else {
|
} else {
|
||||||
&self.end
|
self.end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) {
|
pub fn set_head(&mut self, head: T) {
|
||||||
if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal {
|
if head.cmp(&self.tail()) < Ordering::Equal {
|
||||||
if !self.reversed {
|
if !self.reversed {
|
||||||
mem::swap(&mut self.start, &mut self.end);
|
self.end = self.start;
|
||||||
self.reversed = true;
|
self.reversed = true;
|
||||||
}
|
}
|
||||||
self.start = cursor;
|
self.start = head;
|
||||||
} else {
|
} else {
|
||||||
if self.reversed {
|
if self.reversed {
|
||||||
mem::swap(&mut self.start, &mut self.end);
|
self.start = self.end;
|
||||||
self.reversed = false;
|
self.reversed = false;
|
||||||
}
|
}
|
||||||
self.end = cursor;
|
self.end = head;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tail(&self) -> &Anchor {
|
pub fn tail(&self) -> T {
|
||||||
if self.reversed {
|
if self.reversed {
|
||||||
&self.end
|
self.end
|
||||||
} else {
|
} else {
|
||||||
&self.start
|
self.start
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,3 +93,89 @@ impl Selection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SelectionSet {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.selections.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_selections<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl 'a + Iterator<Item = Selection<usize>> {
|
||||||
|
self.selections
|
||||||
|
.offset_ranges(content)
|
||||||
|
.map(|(range, state)| Selection {
|
||||||
|
id: state.id,
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
reversed: state.reversed,
|
||||||
|
goal: state.goal,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn point_selections<'a>(
|
||||||
|
&'a self,
|
||||||
|
content: impl Into<Content<'a>> + 'a,
|
||||||
|
) -> impl 'a + Iterator<Item = Selection<Point>> {
|
||||||
|
self.selections
|
||||||
|
.point_ranges(content)
|
||||||
|
.map(|(range, state)| Selection {
|
||||||
|
id: state.id,
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
reversed: state.reversed,
|
||||||
|
goal: state.goal,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Into<proto::SelectionSet> for &'a SelectionSet {
|
||||||
|
fn into(self) -> proto::SelectionSet {
|
||||||
|
let version = self.selections.version();
|
||||||
|
let entries = self.selections.raw_entries();
|
||||||
|
proto::SelectionSet {
|
||||||
|
replica_id: self.id.replica_id as u32,
|
||||||
|
lamport_timestamp: self.id.value as u32,
|
||||||
|
is_active: self.active,
|
||||||
|
version: version.into(),
|
||||||
|
selections: entries
|
||||||
|
.iter()
|
||||||
|
.map(|(range, state)| proto::Selection {
|
||||||
|
id: state.id as u64,
|
||||||
|
start: range.start.0.to_proto(),
|
||||||
|
end: range.end.0.to_proto(),
|
||||||
|
reversed: state.reversed,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<proto::SelectionSet> for SelectionSet {
|
||||||
|
fn from(set: proto::SelectionSet) -> Self {
|
||||||
|
Self {
|
||||||
|
id: clock::Lamport {
|
||||||
|
replica_id: set.replica_id as u16,
|
||||||
|
value: set.lamport_timestamp,
|
||||||
|
},
|
||||||
|
active: set.is_active,
|
||||||
|
selections: Arc::new(AnchorRangeMap::from_raw(
|
||||||
|
set.version.into(),
|
||||||
|
set.selections
|
||||||
|
.into_iter()
|
||||||
|
.map(|selection| {
|
||||||
|
let range = (FullOffset::from_proto(selection.start), Bias::Left)
|
||||||
|
..(FullOffset::from_proto(selection.end), Bias::Right);
|
||||||
|
let state = SelectionState {
|
||||||
|
id: selection.id as usize,
|
||||||
|
reversed: selection.reversed,
|
||||||
|
goal: SelectionGoal::None,
|
||||||
|
};
|
||||||
|
(range, state)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -408,7 +408,7 @@ fn test_history() {
|
|||||||
let mut buffer = Buffer::new(0, 0, History::new("123456".into()));
|
let mut buffer = Buffer::new(0, 0, History::new("123456".into()));
|
||||||
|
|
||||||
let set_id = if let Operation::UpdateSelections { set_id, .. } =
|
let set_id = if let Operation::UpdateSelections { set_id, .. } =
|
||||||
buffer.add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap())
|
buffer.add_selection_set(&buffer.selections_from_ranges(vec![4..4]).unwrap())
|
||||||
{
|
{
|
||||||
set_id
|
set_id
|
||||||
} else {
|
} else {
|
||||||
@ -422,7 +422,7 @@ fn test_history() {
|
|||||||
|
|
||||||
buffer.start_transaction_at(Some(set_id), now).unwrap();
|
buffer.start_transaction_at(Some(set_id), now).unwrap();
|
||||||
buffer
|
buffer
|
||||||
.update_selection_set(set_id, buffer.selections_from_ranges(vec![1..3]).unwrap())
|
.update_selection_set(set_id, &buffer.selections_from_ranges(vec![1..3]).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
buffer.edit(vec![4..5], "e");
|
buffer.edit(vec![4..5], "e");
|
||||||
buffer.end_transaction_at(Some(set_id), now).unwrap();
|
buffer.end_transaction_at(Some(set_id), now).unwrap();
|
||||||
@ -432,7 +432,7 @@ fn test_history() {
|
|||||||
now += buffer.history.group_interval + Duration::from_millis(1);
|
now += buffer.history.group_interval + Duration::from_millis(1);
|
||||||
buffer.start_transaction_at(Some(set_id), now).unwrap();
|
buffer.start_transaction_at(Some(set_id), now).unwrap();
|
||||||
buffer
|
buffer
|
||||||
.update_selection_set(set_id, buffer.selections_from_ranges(vec![2..2]).unwrap())
|
.update_selection_set(set_id, &buffer.selections_from_ranges(vec![2..2]).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
buffer.edit(vec![0..1], "a");
|
buffer.edit(vec![0..1], "a");
|
||||||
buffer.edit(vec![1..1], "b");
|
buffer.edit(vec![1..1], "b");
|
||||||
|
@ -86,6 +86,12 @@ impl<'a> From<&'a Global> for Vec<rpc::proto::VectorClockEntry> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Global> for Vec<rpc::proto::VectorClockEntry> {
|
||||||
|
fn from(version: Global) -> Self {
|
||||||
|
(&version).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Global {
|
impl Global {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -790,9 +790,9 @@ impl Buffer {
|
|||||||
for request in autoindent_requests {
|
for request in autoindent_requests {
|
||||||
let old_to_new_rows = request
|
let old_to_new_rows = request
|
||||||
.edited
|
.edited
|
||||||
.to_points(&request.before_edit)
|
.points(&request.before_edit)
|
||||||
.map(|point| point.row)
|
.map(|point| point.row)
|
||||||
.zip(request.edited.to_points(&snapshot).map(|point| point.row))
|
.zip(request.edited.points(&snapshot).map(|point| point.row))
|
||||||
.collect::<BTreeMap<u32, u32>>();
|
.collect::<BTreeMap<u32, u32>>();
|
||||||
|
|
||||||
let mut old_suggestions = HashMap::<u32, u32>::default();
|
let mut old_suggestions = HashMap::<u32, u32>::default();
|
||||||
@ -853,7 +853,7 @@ impl Buffer {
|
|||||||
if let Some(inserted) = request.inserted.as_ref() {
|
if let Some(inserted) = request.inserted.as_ref() {
|
||||||
let inserted_row_ranges = contiguous_ranges(
|
let inserted_row_ranges = contiguous_ranges(
|
||||||
inserted
|
inserted
|
||||||
.to_point_ranges(&snapshot)
|
.point_ranges(&snapshot)
|
||||||
.flat_map(|range| range.start.row..range.end.row + 1),
|
.flat_map(|range| range.start.row..range.end.row + 1),
|
||||||
max_rows_between_yields,
|
max_rows_between_yields,
|
||||||
);
|
);
|
||||||
@ -901,31 +901,30 @@ impl Buffer {
|
|||||||
for selection_set_id in &selection_set_ids {
|
for selection_set_id in &selection_set_ids {
|
||||||
if let Ok(set) = self.selection_set(*selection_set_id) {
|
if let Ok(set) = self.selection_set(*selection_set_id) {
|
||||||
let new_selections = set
|
let new_selections = set
|
||||||
.selections
|
.point_selections(&*self)
|
||||||
.iter()
|
|
||||||
.map(|selection| {
|
.map(|selection| {
|
||||||
let start_point = selection.start.to_point(&self.text);
|
if selection.start.column == 0 {
|
||||||
if start_point.column == 0 {
|
|
||||||
let end_point = selection.end.to_point(&self.text);
|
|
||||||
let delta = Point::new(
|
let delta = Point::new(
|
||||||
0,
|
0,
|
||||||
indent_columns.get(&start_point.row).copied().unwrap_or(0),
|
indent_columns
|
||||||
|
.get(&selection.start.row)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0),
|
||||||
);
|
);
|
||||||
if delta.column > 0 {
|
if delta.column > 0 {
|
||||||
return Selection {
|
return Selection {
|
||||||
id: selection.id,
|
id: selection.id,
|
||||||
goal: selection.goal,
|
goal: selection.goal,
|
||||||
reversed: selection.reversed,
|
reversed: selection.reversed,
|
||||||
start: self
|
start: selection.start + delta,
|
||||||
.anchor_at(start_point + delta, selection.start.bias),
|
end: selection.end + delta,
|
||||||
end: self.anchor_at(end_point + delta, selection.end.bias),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
selection.clone()
|
selection
|
||||||
})
|
})
|
||||||
.collect::<Arc<[_]>>();
|
.collect::<Vec<_>>();
|
||||||
self.update_selection_set(*selection_set_id, new_selections, cx)
|
self.update_selection_set(*selection_set_id, &new_selections, cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1239,9 +1238,9 @@ impl Buffer {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_selection_set(
|
pub fn add_selection_set<T: ToOffset>(
|
||||||
&mut self,
|
&mut self,
|
||||||
selections: impl Into<Arc<[Selection]>>,
|
selections: &[Selection<T>],
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> SelectionSetId {
|
) -> SelectionSetId {
|
||||||
let operation = self.text.add_selection_set(selections);
|
let operation = self.text.add_selection_set(selections);
|
||||||
@ -1255,10 +1254,10 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_selection_set(
|
pub fn update_selection_set<T: ToOffset>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_id: SelectionSetId,
|
set_id: SelectionSetId,
|
||||||
selections: impl Into<Arc<[Selection]>>,
|
selections: &[Selection<T>],
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let operation = self.text.update_selection_set(set_id, selections)?;
|
let operation = self.text.update_selection_set(set_id, selections)?;
|
||||||
|
@ -274,24 +274,24 @@ fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
|
|||||||
|
|
||||||
let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
|
let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
|
||||||
|
|
||||||
let selection_set_id = buffer.add_selection_set(Vec::new(), cx);
|
let selection_set_id = buffer.add_selection_set::<usize>(&[], cx);
|
||||||
buffer.start_transaction(Some(selection_set_id)).unwrap();
|
buffer.start_transaction(Some(selection_set_id)).unwrap();
|
||||||
buffer.edit_with_autoindent([5..5, 9..9], "\n\n", cx);
|
buffer.edit_with_autoindent([5..5, 9..9], "\n\n", cx);
|
||||||
buffer
|
buffer
|
||||||
.update_selection_set(
|
.update_selection_set(
|
||||||
selection_set_id,
|
selection_set_id,
|
||||||
vec![
|
&[
|
||||||
Selection {
|
Selection {
|
||||||
id: 0,
|
id: 0,
|
||||||
start: buffer.anchor_before(Point::new(1, 0)),
|
start: Point::new(1, 0),
|
||||||
end: buffer.anchor_before(Point::new(1, 0)),
|
end: Point::new(1, 0),
|
||||||
reversed: false,
|
reversed: false,
|
||||||
goal: SelectionGoal::None,
|
goal: SelectionGoal::None,
|
||||||
},
|
},
|
||||||
Selection {
|
Selection {
|
||||||
id: 1,
|
id: 1,
|
||||||
start: buffer.anchor_before(Point::new(4, 0)),
|
start: Point::new(4, 0),
|
||||||
end: buffer.anchor_before(Point::new(4, 0)),
|
end: Point::new(4, 0),
|
||||||
reversed: false,
|
reversed: false,
|
||||||
goal: SelectionGoal::None,
|
goal: SelectionGoal::None,
|
||||||
},
|
},
|
||||||
@ -308,8 +308,7 @@ fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
|
|||||||
let selection_ranges = buffer
|
let selection_ranges = buffer
|
||||||
.selection_set(selection_set_id)
|
.selection_set(selection_set_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.selections
|
.point_selections(&buffer)
|
||||||
.iter()
|
|
||||||
.map(|selection| selection.point_range(&buffer))
|
.map(|selection| selection.point_range(&buffer))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
@ -3410,7 +3410,7 @@ mod tests {
|
|||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_buffer_file_changes_on_disk(mut cx: gpui::TestAppContext) {
|
async fn test_buffer_file_changes_on_disk(mut cx: gpui::TestAppContext) {
|
||||||
use buffer::{Point, Selection, SelectionGoal, ToPoint};
|
use buffer::{Point, Selection, SelectionGoal};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
let initial_contents = "aaa\nbbbbb\nc\n";
|
let initial_contents = "aaa\nbbbbb\nc\n";
|
||||||
@ -3436,20 +3436,17 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Add a cursor at the start of each row.
|
// Add a cursor on each row.
|
||||||
let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
|
let selection_set_id = buffer.update(&mut cx, |buffer, cx| {
|
||||||
assert!(!buffer.is_dirty());
|
assert!(!buffer.is_dirty());
|
||||||
buffer.add_selection_set(
|
buffer.add_selection_set(
|
||||||
(0..3)
|
&(0..3)
|
||||||
.map(|row| {
|
.map(|row| Selection {
|
||||||
let anchor = buffer.anchor_at(Point::new(row, 0), Bias::Right);
|
id: row as usize,
|
||||||
Selection {
|
start: Point::new(row, 1),
|
||||||
id: row as usize,
|
end: Point::new(row, 1),
|
||||||
start: anchor.clone(),
|
reversed: false,
|
||||||
end: anchor,
|
goal: SelectionGoal::None,
|
||||||
reversed: false,
|
|
||||||
goal: SelectionGoal::None,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
cx,
|
cx,
|
||||||
@ -3469,7 +3466,7 @@ mod tests {
|
|||||||
// contents are edited according to the diff between the old and new
|
// contents are edited according to the diff between the old and new
|
||||||
// file contents.
|
// file contents.
|
||||||
buffer
|
buffer
|
||||||
.condition(&cx, |buffer, _| buffer.text() != initial_contents)
|
.condition(&cx, |buffer, _| buffer.text() == new_contents)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
buffer.update(&mut cx, |buffer, _| {
|
buffer.update(&mut cx, |buffer, _| {
|
||||||
@ -3477,18 +3474,18 @@ mod tests {
|
|||||||
assert!(!buffer.is_dirty());
|
assert!(!buffer.is_dirty());
|
||||||
assert!(!buffer.has_conflict());
|
assert!(!buffer.has_conflict());
|
||||||
|
|
||||||
let set = buffer.selection_set(selection_set_id).unwrap();
|
let cursor_positions = buffer
|
||||||
let cursor_positions = set
|
.selection_set(selection_set_id)
|
||||||
.selections
|
.unwrap()
|
||||||
.iter()
|
.point_selections(&*buffer)
|
||||||
.map(|selection| {
|
.map(|selection| {
|
||||||
assert_eq!(selection.start, selection.end);
|
assert_eq!(selection.start, selection.end);
|
||||||
selection.start.to_point(&*buffer)
|
selection.start
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cursor_positions,
|
cursor_positions,
|
||||||
&[Point::new(1, 0), Point::new(3, 0), Point::new(4, 0),]
|
[Point::new(1, 1), Point::new(3, 1), Point::new(4, 0)]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -227,43 +227,31 @@ message Buffer {
|
|||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
string content = 2;
|
string content = 2;
|
||||||
repeated Operation.Edit history = 3;
|
repeated Operation.Edit history = 3;
|
||||||
repeated SelectionSetSnapshot selections = 4;
|
repeated SelectionSet selections = 4;
|
||||||
}
|
|
||||||
|
|
||||||
message SelectionSetSnapshot {
|
|
||||||
uint32 replica_id = 1;
|
|
||||||
uint32 local_timestamp = 2;
|
|
||||||
repeated Selection selections = 3;
|
|
||||||
bool is_active = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message SelectionSet {
|
message SelectionSet {
|
||||||
repeated Selection selections = 1;
|
uint32 replica_id = 1;
|
||||||
|
uint32 lamport_timestamp = 2;
|
||||||
|
bool is_active = 3;
|
||||||
|
repeated VectorClockEntry version = 4;
|
||||||
|
repeated Selection selections = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Selection {
|
message Selection {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
Anchor start = 2;
|
uint64 start = 2;
|
||||||
Anchor end = 3;
|
uint64 end = 3;
|
||||||
bool reversed = 4;
|
bool reversed = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Anchor {
|
|
||||||
repeated VectorClockEntry version = 1;
|
|
||||||
uint64 offset = 2;
|
|
||||||
Bias bias = 3;
|
|
||||||
enum Bias {
|
|
||||||
LEFT = 0;
|
|
||||||
Right = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message Operation {
|
message Operation {
|
||||||
oneof variant {
|
oneof variant {
|
||||||
Edit edit = 1;
|
Edit edit = 1;
|
||||||
Undo undo = 2;
|
Undo undo = 2;
|
||||||
UpdateSelections update_selections = 3;
|
UpdateSelections update_selections = 3;
|
||||||
SetActiveSelections set_active_selections = 4;
|
RemoveSelections remove_selections = 4;
|
||||||
|
SetActiveSelections set_active_selections = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Edit {
|
message Edit {
|
||||||
@ -294,7 +282,14 @@ message Operation {
|
|||||||
uint32 replica_id = 1;
|
uint32 replica_id = 1;
|
||||||
uint32 local_timestamp = 2;
|
uint32 local_timestamp = 2;
|
||||||
uint32 lamport_timestamp = 3;
|
uint32 lamport_timestamp = 3;
|
||||||
SelectionSet set = 4;
|
repeated VectorClockEntry version = 4;
|
||||||
|
repeated Selection selections = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveSelections {
|
||||||
|
uint32 replica_id = 1;
|
||||||
|
uint32 local_timestamp = 2;
|
||||||
|
uint32 lamport_timestamp = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SetActiveSelections {
|
message SetActiveSelections {
|
||||||
|
@ -509,7 +509,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilterCursor<'a, F: FnMut(&T::Summary) -> bool, T: Item, D> {
|
pub struct FilterCursor<'a, F, T: Item, D> {
|
||||||
cursor: Cursor<'a, T, D>,
|
cursor: Cursor<'a, T, D>,
|
||||||
filter_node: F,
|
filter_node: F,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user