WIP: Start on removing selections from buffer in favor of editor

This commit is contained in:
Nathan Sobo 2021-12-10 19:23:34 -07:00
parent f35c419f43
commit 8432daef6a
10 changed files with 342 additions and 951 deletions

View file

@ -178,7 +178,6 @@ struct SyntaxTree {
#[derive(Clone)]
struct AutoindentRequest {
selection_set_ids: HashSet<SelectionSetId>,
before_edit: BufferSnapshot,
edited: Vec<Anchor>,
inserted: Option<Vec<Range<Anchor>>>,
@ -277,10 +276,6 @@ impl Buffer {
.into_iter()
.map(|op| text::Operation::Edit(proto::deserialize_edit_operation(op)));
buffer.apply_ops(ops)?;
for set in message.selections {
let set = proto::deserialize_selection_set(set);
buffer.add_raw_selection_set(set.id, set);
}
let mut this = Self::build(buffer, file);
this.apply_diagnostic_update(
Arc::from(proto::deserialize_diagnostics(message.diagnostics)),
@ -299,10 +294,7 @@ impl Buffer {
.history()
.map(proto::serialize_edit_operation)
.collect(),
selections: self
.selection_sets()
.map(|(_, set)| proto::serialize_selection_set(set))
.collect(),
selections: Vec::new(),
diagnostics: proto::serialize_diagnostics(self.diagnostics.iter()),
}
}
@ -971,49 +963,11 @@ impl Buffer {
indent_columns: BTreeMap<u32, u32>,
cx: &mut ModelContext<Self>,
) {
let selection_set_ids = self
.autoindent_requests
.drain(..)
.flat_map(|req| req.selection_set_ids.clone())
.collect::<HashSet<_>>();
self.start_transaction(selection_set_ids.iter().copied());
self.start_transaction();
for (row, indent_column) in &indent_columns {
self.set_indent_column_for_line(*row, *indent_column, cx);
}
for selection_set_id in &selection_set_ids {
if let Ok(set) = self.selection_set(*selection_set_id) {
let new_selections = set
.selections::<Point>(&*self)
.map(|selection| {
if selection.start.column == 0 {
let delta = Point::new(
0,
indent_columns
.get(&selection.start.row)
.copied()
.unwrap_or(0),
);
if delta.column > 0 {
return Selection {
id: selection.id,
goal: selection.goal,
reversed: selection.reversed,
start: selection.start + delta,
end: selection.end + delta,
};
}
}
selection
})
.collect::<Vec<_>>();
self.update_selection_set(*selection_set_id, &new_selections, cx)
.unwrap();
}
}
self.end_transaction(selection_set_ids.iter().copied(), cx);
self.end_transaction(cx);
}
fn set_indent_column_for_line(&mut self, row: u32, column: u32, cx: &mut ModelContext<Self>) {
@ -1053,7 +1007,7 @@ impl Buffer {
pub(crate) fn apply_diff(&mut self, diff: Diff, cx: &mut ModelContext<Self>) -> bool {
if self.version == diff.base_version {
self.start_transaction(None);
self.start_transaction();
let mut offset = 0;
for (tag, len) in diff.changes {
let range = offset..(offset + len);
@ -1066,7 +1020,7 @@ impl Buffer {
}
}
}
self.end_transaction(None, cx);
self.end_transaction(cx);
true
} else {
false
@ -1090,38 +1044,24 @@ impl Buffer {
self.text.subscribe()
}
pub fn start_transaction(
&mut self,
selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
) -> Option<TransactionId> {
self.start_transaction_at(selection_set_ids, Instant::now())
pub fn start_transaction(&mut self) -> Option<TransactionId> {
self.start_transaction_at(Instant::now())
}
pub(crate) fn start_transaction_at(
&mut self,
selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
now: Instant,
) -> Option<TransactionId> {
self.text.start_transaction_at(selection_set_ids, now)
pub(crate) fn start_transaction_at(&mut self, now: Instant) -> Option<TransactionId> {
self.text.start_transaction_at(now)
}
pub fn end_transaction(
&mut self,
selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
cx: &mut ModelContext<Self>,
) -> Option<TransactionId> {
self.end_transaction_at(selection_set_ids, Instant::now(), cx)
pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
self.end_transaction_at(Instant::now(), cx)
}
pub(crate) fn end_transaction_at(
&mut self,
selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
now: Instant,
cx: &mut ModelContext<Self>,
) -> Option<TransactionId> {
if let Some((transaction_id, start_version)) =
self.text.end_transaction_at(selection_set_ids, now)
{
if let Some((transaction_id, start_version)) = self.text.end_transaction_at(now) {
let was_dirty = start_version != self.saved_version;
self.did_edit(&start_version, was_dirty, cx);
Some(transaction_id)
@ -1212,7 +1152,7 @@ impl Buffer {
return;
}
self.start_transaction(None);
self.start_transaction();
self.pending_autoindent.take();
let autoindent_request = if autoindent && self.language.is_some() {
let before_edit = self.snapshot();
@ -1256,21 +1196,14 @@ impl Buffer {
);
}
let selection_set_ids = self
.text
.peek_undo_stack()
.unwrap()
.starting_selection_set_ids()
.collect();
self.autoindent_requests.push(Arc::new(AutoindentRequest {
selection_set_ids,
before_edit,
edited,
inserted,
}));
}
self.end_transaction(None, cx);
self.end_transaction(cx);
self.send_operation(Operation::Buffer(text::Operation::Edit(edit)), cx);
}
@ -1298,55 +1231,6 @@ impl Buffer {
self.language.as_ref().and_then(|l| l.grammar.as_ref())
}
pub fn add_selection_set<T: ToOffset>(
&mut self,
selections: &[Selection<T>],
cx: &mut ModelContext<Self>,
) -> SelectionSetId {
let operation = self.text.add_selection_set(selections);
if let text::Operation::UpdateSelections { set_id, .. } = &operation {
let set_id = *set_id;
cx.notify();
self.send_operation(Operation::Buffer(operation), cx);
set_id
} else {
unreachable!()
}
}
pub fn update_selection_set<T: ToOffset>(
&mut self,
set_id: SelectionSetId,
selections: &[Selection<T>],
cx: &mut ModelContext<Self>,
) -> Result<()> {
let operation = self.text.update_selection_set(set_id, selections)?;
cx.notify();
self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
pub fn set_active_selection_set(
&mut self,
set_id: Option<SelectionSetId>,
cx: &mut ModelContext<Self>,
) -> Result<()> {
let operation = self.text.set_active_selection_set(set_id)?;
self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
pub fn remove_selection_set(
&mut self,
set_id: SelectionSetId,
cx: &mut ModelContext<Self>,
) -> Result<()> {
let operation = self.text.remove_selection_set(set_id)?;
cx.notify();
self.send_operation(Operation::Buffer(operation), cx);
Ok(())
}
pub fn apply_ops<I: IntoIterator<Item = Operation>>(
&mut self,
ops: I,
@ -1447,10 +1331,8 @@ impl Buffer {
let was_dirty = self.is_dirty();
let old_version = self.version.clone();
if let Some((transaction_id, operations)) = self.text.undo() {
for operation in operations {
self.send_operation(Operation::Buffer(operation), cx);
}
if let Some((transaction_id, operation)) = self.text.undo() {
self.send_operation(Operation::Buffer(operation), cx);
self.did_edit(&old_version, was_dirty, cx);
Some(transaction_id)
} else {
@ -1462,10 +1344,8 @@ impl Buffer {
let was_dirty = self.is_dirty();
let old_version = self.version.clone();
if let Some((transaction_id, operations)) = self.text.redo() {
for operation in operations {
self.send_operation(Operation::Buffer(operation), cx);
}
if let Some((transaction_id, operation)) = self.text.redo() {
self.send_operation(Operation::Buffer(operation), cx);
self.did_edit(&old_version, was_dirty, cx);
Some(transaction_id)
} else {
@ -1484,18 +1364,9 @@ impl Buffer {
) where
T: rand::Rng,
{
self.start_transaction(None);
self.start_transaction();
self.text.randomly_edit(rng, old_range_count);
self.end_transaction(None, cx);
}
pub fn randomly_mutate<T>(&mut self, rng: &mut T, cx: &mut ModelContext<Self>)
where
T: rand::Rng,
{
self.start_transaction(None);
self.text.randomly_mutate(rng);
self.end_transaction(None, cx);
self.end_transaction(cx);
}
}

View file

@ -92,15 +92,15 @@ fn test_edit_events(cx: &mut gpui::MutableAppContext) {
buffer.edit(Some(2..4), "XYZ", cx);
// An empty transaction does not emit any events.
buffer.start_transaction(None);
buffer.end_transaction(None, cx);
buffer.start_transaction();
buffer.end_transaction(cx);
// A transaction containing two edits emits one edited event.
now += Duration::from_secs(1);
buffer.start_transaction_at(None, now);
buffer.start_transaction_at(now);
buffer.edit(Some(5..5), "u", cx);
buffer.edit(Some(6..6), "w", cx);
buffer.end_transaction_at(None, now, cx);
buffer.end_transaction_at(now, cx);
// Undoing a transaction emits one edited event.
buffer.undo(cx);
@ -167,7 +167,7 @@ async fn test_reparse(mut cx: gpui::TestAppContext) {
// Perform some edits (add parameter and variable reference)
// Parsing doesn't begin until the transaction is complete
buffer.update(&mut cx, |buf, cx| {
buf.start_transaction(None);
buf.start_transaction();
let offset = buf.text().find(")").unwrap();
buf.edit(vec![offset..offset], "b: C", cx);
@ -177,7 +177,7 @@ async fn test_reparse(mut cx: gpui::TestAppContext) {
buf.edit(vec![offset..offset], " d; ", cx);
assert!(!buf.is_parsing());
buf.end_transaction(None, cx);
buf.end_transaction(cx);
assert_eq!(buf.text(), "fn a(b: C) { d; }");
assert!(buf.is_parsing());
});
@ -333,59 +333,62 @@ fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
});
}
#[gpui::test]
fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
cx.add_model(|cx| {
let text = "fn a() {}";
// We need another approach to managing selections with auto-indent
let mut buffer =
Buffer::new(0, text, cx).with_language(Some(Arc::new(rust_lang())), None, cx);
// #[gpui::test]
// fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
// cx.add_model(|cx| {
// let text = "fn a() {}";
let selection_set_id = buffer.add_selection_set::<usize>(&[], cx);
buffer.start_transaction(Some(selection_set_id));
buffer.edit_with_autoindent([5..5, 9..9], "\n\n", cx);
buffer
.update_selection_set(
selection_set_id,
&[
Selection {
id: 0,
start: Point::new(1, 0),
end: Point::new(1, 0),
reversed: false,
goal: SelectionGoal::None,
},
Selection {
id: 1,
start: Point::new(4, 0),
end: Point::new(4, 0),
reversed: false,
goal: SelectionGoal::None,
},
],
cx,
)
.unwrap();
assert_eq!(buffer.text(), "fn a(\n\n) {}\n\n");
// let mut buffer =
// Buffer::new(0, text, cx).with_language(Some(Arc::new(rust_lang())), None, cx);
// Ending the transaction runs the auto-indent. The selection
// at the start of the auto-indented row is pushed to the right.
buffer.end_transaction(Some(selection_set_id), cx);
assert_eq!(buffer.text(), "fn a(\n \n) {}\n\n");
let selection_ranges = buffer
.selection_set(selection_set_id)
.unwrap()
.selections::<Point>(&buffer)
.map(|selection| selection.start.to_point(&buffer)..selection.end.to_point(&buffer))
.collect::<Vec<_>>();
// let selection_set_id = buffer.add_selection_set::<usize>(&[], cx);
// buffer.start_transaction();
// buffer.edit_with_autoindent([5..5, 9..9], "\n\n", cx);
// buffer
// .update_selection_set(
// selection_set_id,
// &[
// Selection {
// id: 0,
// start: Point::new(1, 0),
// end: Point::new(1, 0),
// reversed: false,
// goal: SelectionGoal::None,
// },
// Selection {
// id: 1,
// start: Point::new(4, 0),
// end: Point::new(4, 0),
// reversed: false,
// goal: SelectionGoal::None,
// },
// ],
// cx,
// )
// .unwrap();
// assert_eq!(buffer.text(), "fn a(\n\n) {}\n\n");
assert_eq!(selection_ranges[0], empty(Point::new(1, 4)));
assert_eq!(selection_ranges[1], empty(Point::new(4, 0)));
// // TODO! Come up with a different approach to moving selections now that we don't manage selection sets in the buffer
buffer
});
}
// // Ending the transaction runs the auto-indent. The selection
// // at the start of the auto-indented row is pushed to the right.
// buffer.end_transaction(cx);
// assert_eq!(buffer.text(), "fn a(\n \n) {}\n\n");
// let selection_ranges = buffer
// .selection_set(selection_set_id)
// .unwrap()
// .selections::<Point>(&buffer)
// .map(|selection| selection.start.to_point(&buffer)..selection.end.to_point(&buffer))
// .collect::<Vec<_>>();
// assert_eq!(selection_ranges[0], empty(Point::new(1, 4)));
// assert_eq!(selection_ranges[1], empty(Point::new(4, 0)));
// buffer
// });
// }
#[gpui::test]
fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
cx.add_model(|cx| {