mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Undo temporary edits before performing rename or canceling it
Also, wire up remote renames.
This commit is contained in:
parent
514d69e83d
commit
f9723ae16b
@ -439,6 +439,7 @@ pub struct Editor {
|
|||||||
next_completion_id: CompletionId,
|
next_completion_id: CompletionId,
|
||||||
available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
|
available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
|
||||||
code_actions_task: Option<Task<()>>,
|
code_actions_task: Option<Task<()>>,
|
||||||
|
pending_rename: Option<RenameState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EditorSnapshot {
|
pub struct EditorSnapshot {
|
||||||
@ -477,6 +478,11 @@ struct SnippetState {
|
|||||||
active_index: usize,
|
active_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RenameState {
|
||||||
|
range: Range<Anchor>,
|
||||||
|
first_transaction: Option<TransactionId>,
|
||||||
|
}
|
||||||
|
|
||||||
struct InvalidationStack<T>(Vec<T>);
|
struct InvalidationStack<T>(Vec<T>);
|
||||||
|
|
||||||
enum ContextMenu {
|
enum ContextMenu {
|
||||||
@ -892,6 +898,7 @@ impl Editor {
|
|||||||
next_completion_id: 0,
|
next_completion_id: 0,
|
||||||
available_code_actions: Default::default(),
|
available_code_actions: Default::default(),
|
||||||
code_actions_task: Default::default(),
|
code_actions_task: Default::default(),
|
||||||
|
pending_rename: Default::default(),
|
||||||
};
|
};
|
||||||
this.end_selection(cx);
|
this.end_selection(cx);
|
||||||
this
|
this
|
||||||
@ -1913,6 +1920,10 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
|
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
|
||||||
|
if self.pending_rename.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let project = if let Some(project) = self.project.clone() {
|
let project = if let Some(project) = self.project.clone() {
|
||||||
project
|
project
|
||||||
} else {
|
} else {
|
||||||
@ -4101,11 +4112,17 @@ impl Editor {
|
|||||||
let offset = position.to_offset(&buffer);
|
let offset = position.to_offset(&buffer);
|
||||||
let start = offset - lookbehind;
|
let start = offset - lookbehind;
|
||||||
let end = offset + lookahead;
|
let end = offset + lookahead;
|
||||||
let highlight_range = buffer.anchor_before(start)..buffer.anchor_after(end);
|
let rename_range = buffer.anchor_before(start)..buffer.anchor_after(end);
|
||||||
drop(buffer);
|
drop(buffer);
|
||||||
|
|
||||||
|
this.buffer
|
||||||
|
.update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
|
||||||
|
this.pending_rename = Some(RenameState {
|
||||||
|
range: rename_range.clone(),
|
||||||
|
first_transaction: None,
|
||||||
|
});
|
||||||
this.select_ranges([start..end], None, cx);
|
this.select_ranges([start..end], None, cx);
|
||||||
this.highlight_ranges::<Rename>(vec![highlight_range], Color::red(), cx);
|
this.highlight_ranges::<Rename>(vec![rename_range], Color::red(), cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4121,17 +4138,16 @@ impl Editor {
|
|||||||
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
|
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
|
||||||
|
|
||||||
let (buffer, position, new_name) = editor.update(cx, |editor, cx| {
|
let (buffer, position, new_name) = editor.update(cx, |editor, cx| {
|
||||||
let range = editor.take_rename_range(cx)?;
|
let (range, new_name) = editor.take_rename(cx)?;
|
||||||
let multibuffer = editor.buffer.read(cx);
|
let (buffer, position) = editor
|
||||||
let (buffer, position) =
|
.buffer
|
||||||
multibuffer.text_anchor_for_position(range.start.clone(), cx)?;
|
.read(cx)
|
||||||
let snapshot = multibuffer.read(cx);
|
.text_anchor_for_position(range.start.clone(), cx)?;
|
||||||
let new_name = snapshot.text_for_range(range.clone()).collect::<String>();
|
|
||||||
Some((buffer, position, new_name))
|
Some((buffer, position, new_name))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let rename = workspace.project().clone().update(cx, |project, cx| {
|
let rename = workspace.project().clone().update(cx, |project, cx| {
|
||||||
project.perform_rename(buffer, position, new_name.clone(), cx)
|
project.perform_rename(buffer, position, new_name.clone(), true, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|workspace, cx| async move {
|
Some(cx.spawn(|workspace, cx| async move {
|
||||||
@ -4147,14 +4163,23 @@ impl Editor {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_range(&self) -> Option<&Range<Anchor>> {
|
fn take_rename(&mut self, cx: &mut ViewContext<Self>) -> Option<(Range<Anchor>, String)> {
|
||||||
self.highlighted_ranges_for_type::<Rename>()
|
let rename = self.pending_rename.take()?;
|
||||||
.and_then(|(_, range)| range.last())
|
let new_name = self
|
||||||
}
|
.buffer
|
||||||
|
.read(cx)
|
||||||
|
.read(cx)
|
||||||
|
.text_for_range(rename.range.clone())
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
fn take_rename_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<Anchor>> {
|
self.clear_highlighted_ranges::<Rename>(cx);
|
||||||
self.clear_highlighted_ranges::<Rename>(cx)
|
if let Some(transaction_id) = rename.first_transaction {
|
||||||
.and_then(|(_, mut ranges)| ranges.pop())
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.undo_to_transaction(transaction_id, false, cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((rename.range, new_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_rename_range(
|
fn invalidate_rename_range(
|
||||||
@ -4162,15 +4187,16 @@ impl Editor {
|
|||||||
buffer: &MultiBufferSnapshot,
|
buffer: &MultiBufferSnapshot,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(range) = &self.rename_range() {
|
if let Some(rename) = self.pending_rename.as_ref() {
|
||||||
if self.selections.len() == 1 {
|
if self.selections.len() == 1 {
|
||||||
let head = self.selections[0].head().to_offset(&buffer);
|
let head = self.selections[0].head().to_offset(buffer);
|
||||||
if range.start.to_offset(&buffer) <= head && range.end.to_offset(&buffer) >= head {
|
let range = rename.range.to_offset(buffer).to_inclusive();
|
||||||
|
if range.contains(&head) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln!("clearing highlight range");
|
|
||||||
self.clear_highlighted_ranges::<Rename>(cx);
|
self.take_rename(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4659,6 +4685,12 @@ impl Editor {
|
|||||||
.buffer
|
.buffer
|
||||||
.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
|
.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
|
||||||
{
|
{
|
||||||
|
if let Some(rename) = self.pending_rename.as_mut() {
|
||||||
|
if rename.first_transaction.is_none() {
|
||||||
|
rename.first_transaction = Some(tx_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((_, end_selections)) = self.selection_history.get_mut(&tx_id) {
|
if let Some((_, end_selections)) = self.selection_history.get_mut(&tx_id) {
|
||||||
*end_selections = Some(self.selections.clone());
|
*end_selections = Some(self.selections.clone());
|
||||||
} else {
|
} else {
|
||||||
@ -5197,7 +5229,7 @@ impl View for Editor {
|
|||||||
EditorMode::Full => "full",
|
EditorMode::Full => "full",
|
||||||
};
|
};
|
||||||
cx.map.insert("mode".into(), mode.into());
|
cx.map.insert("mode".into(), mode.into());
|
||||||
if self.rename_range().is_some() {
|
if self.pending_rename.is_some() {
|
||||||
cx.set.insert("renaming".into());
|
cx.set.insert("renaming".into());
|
||||||
}
|
}
|
||||||
match self.context_menu.as_ref() {
|
match self.context_menu.as_ref() {
|
||||||
|
@ -27,6 +27,7 @@ use text::{
|
|||||||
AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
|
AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
|
||||||
};
|
};
|
||||||
use theme::SyntaxTheme;
|
use theme::SyntaxTheme;
|
||||||
|
use util::CowMut;
|
||||||
|
|
||||||
const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
|
const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ pub enum CharKind {
|
|||||||
Word,
|
Word,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Transaction {
|
struct Transaction {
|
||||||
id: TransactionId,
|
id: TransactionId,
|
||||||
buffer_transactions: HashMap<usize, text::TransactionId>,
|
buffer_transactions: HashMap<usize, text::TransactionId>,
|
||||||
@ -564,7 +566,7 @@ impl MultiBuffer {
|
|||||||
if let Some(entry) = buffer.peek_undo_stack() {
|
if let Some(entry) = buffer.peek_undo_stack() {
|
||||||
*buffer_transaction_id = entry.transaction_id();
|
*buffer_transaction_id = entry.transaction_id();
|
||||||
}
|
}
|
||||||
buffer.undo_to_transaction(undo_to, cx)
|
buffer.undo_to_transaction(undo_to, true, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,6 +579,35 @@ impl MultiBuffer {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn undo_to_transaction(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
push_redo: bool,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(buffer) = self.as_singleton() {
|
||||||
|
return buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.undo_to_transaction(transaction_id, push_redo, cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut undone = false;
|
||||||
|
for transaction in &mut *self.history.remove_from_undo(transaction_id, push_redo) {
|
||||||
|
for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
|
||||||
|
if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(&buffer_id) {
|
||||||
|
undone |= buffer.update(cx, |buffer, cx| {
|
||||||
|
let undo_to = *buffer_transaction_id;
|
||||||
|
if let Some(entry) = buffer.peek_undo_stack() {
|
||||||
|
*buffer_transaction_id = entry.transaction_id();
|
||||||
|
}
|
||||||
|
buffer.undo_to_transaction(undo_to, true, cx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
undone
|
||||||
|
}
|
||||||
|
|
||||||
pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
|
pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
|
||||||
if let Some(buffer) = self.as_singleton() {
|
if let Some(buffer) = self.as_singleton() {
|
||||||
return buffer.update(cx, |buffer, cx| buffer.redo(cx));
|
return buffer.update(cx, |buffer, cx| buffer.redo(cx));
|
||||||
@ -591,7 +622,7 @@ impl MultiBuffer {
|
|||||||
if let Some(entry) = buffer.peek_redo_stack() {
|
if let Some(entry) = buffer.peek_redo_stack() {
|
||||||
*buffer_transaction_id = entry.transaction_id();
|
*buffer_transaction_id = entry.transaction_id();
|
||||||
}
|
}
|
||||||
buffer.redo_to_transaction(redo_to, cx)
|
buffer.redo_to_transaction(redo_to, true, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2314,6 +2345,31 @@ impl History {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_from_undo(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
push_redo: bool,
|
||||||
|
) -> CowMut<[Transaction]> {
|
||||||
|
assert_eq!(self.transaction_depth, 0);
|
||||||
|
|
||||||
|
if let Some(entry_ix) = self
|
||||||
|
.undo_stack
|
||||||
|
.iter()
|
||||||
|
.rposition(|transaction| transaction.id == transaction_id)
|
||||||
|
{
|
||||||
|
let transactions = self.undo_stack.drain(entry_ix..).rev();
|
||||||
|
if push_redo {
|
||||||
|
let redo_stack_start_len = self.redo_stack.len();
|
||||||
|
self.redo_stack.extend(transactions);
|
||||||
|
CowMut::Borrowed(&mut self.redo_stack[redo_stack_start_len..])
|
||||||
|
} else {
|
||||||
|
CowMut::Owned(transactions.collect())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CowMut::Owned(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pop_redo(&mut self) -> Option<&mut Transaction> {
|
fn pop_redo(&mut self) -> Option<&mut Transaction> {
|
||||||
assert_eq!(self.transaction_depth, 0);
|
assert_eq!(self.transaction_depth, 0);
|
||||||
if let Some(transaction) = self.redo_stack.pop() {
|
if let Some(transaction) = self.redo_stack.pop() {
|
||||||
|
@ -1738,12 +1738,13 @@ impl Buffer {
|
|||||||
pub fn undo_to_transaction(
|
pub fn undo_to_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
transaction_id: TransactionId,
|
transaction_id: TransactionId,
|
||||||
|
push_redo: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let was_dirty = self.is_dirty();
|
let was_dirty = self.is_dirty();
|
||||||
let old_version = self.version.clone();
|
let old_version = self.version.clone();
|
||||||
|
|
||||||
let operations = self.text.undo_to_transaction(transaction_id);
|
let operations = self.text.undo_to_transaction(transaction_id, push_redo);
|
||||||
let undone = !operations.is_empty();
|
let undone = !operations.is_empty();
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
self.send_operation(Operation::Buffer(operation), cx);
|
self.send_operation(Operation::Buffer(operation), cx);
|
||||||
@ -1770,12 +1771,13 @@ impl Buffer {
|
|||||||
pub fn redo_to_transaction(
|
pub fn redo_to_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
transaction_id: TransactionId,
|
transaction_id: TransactionId,
|
||||||
|
push_undo: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let was_dirty = self.is_dirty();
|
let was_dirty = self.is_dirty();
|
||||||
let old_version = self.version.clone();
|
let old_version = self.version.clone();
|
||||||
|
|
||||||
let operations = self.text.redo_to_transaction(transaction_id);
|
let operations = self.text.redo_to_transaction(transaction_id, push_undo);
|
||||||
let redone = !operations.is_empty();
|
let redone = !operations.is_empty();
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
self.send_operation(Operation::Buffer(operation), cx);
|
self.send_operation(Operation::Buffer(operation), cx);
|
||||||
|
@ -43,6 +43,7 @@ pub(crate) struct PerformRename {
|
|||||||
pub buffer: ModelHandle<Buffer>,
|
pub buffer: ModelHandle<Buffer>,
|
||||||
pub position: PointUtf16,
|
pub position: PointUtf16,
|
||||||
pub new_name: String,
|
pub new_name: String,
|
||||||
|
pub push_to_history: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LspCommand for PrepareRename {
|
impl LspCommand for PrepareRename {
|
||||||
@ -60,11 +61,14 @@ impl LspCommand for PrepareRename {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PrepareRename {
|
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PrepareRename {
|
||||||
let buffer_id = self.buffer.read(cx).remote_id();
|
let buffer = &self.buffer.read(cx);
|
||||||
|
let buffer_id = buffer.remote_id();
|
||||||
proto::PrepareRename {
|
proto::PrepareRename {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
position: None,
|
position: Some(language::proto::serialize_anchor(
|
||||||
|
&buffer.anchor_before(self.position),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +97,15 @@ impl LspCommand for PrepareRename {
|
|||||||
self,
|
self,
|
||||||
message: proto::PrepareRenameResponse,
|
message: proto::PrepareRenameResponse,
|
||||||
_: ModelHandle<Project>,
|
_: ModelHandle<Project>,
|
||||||
_: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> LocalBoxFuture<'static, Result<Option<Range<Anchor>>>> {
|
) -> LocalBoxFuture<'static, Result<Option<Range<Anchor>>>> {
|
||||||
async move {
|
async move {
|
||||||
if message.can_rename {
|
if message.can_rename {
|
||||||
|
self.buffer
|
||||||
|
.update(&mut cx, |buffer, _| {
|
||||||
|
buffer.wait_for_version(message.version.into())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
let start = message.start.and_then(deserialize_anchor);
|
let start = message.start.and_then(deserialize_anchor);
|
||||||
let end = message.end.and_then(deserialize_anchor);
|
let end = message.end.and_then(deserialize_anchor);
|
||||||
Ok(start.zip(end).map(|(start, end)| start..end))
|
Ok(start.zip(end).map(|(start, end)| start..end))
|
||||||
@ -127,11 +136,14 @@ impl LspCommand for PerformRename {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PerformRename {
|
fn to_proto(&self, project_id: u64, cx: &AppContext) -> proto::PerformRename {
|
||||||
let buffer_id = self.buffer.read(cx).remote_id();
|
let buffer = &self.buffer.read(cx);
|
||||||
|
let buffer_id = buffer.remote_id();
|
||||||
proto::PerformRename {
|
proto::PerformRename {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
position: None,
|
position: Some(language::proto::serialize_anchor(
|
||||||
|
&buffer.anchor_before(self.position),
|
||||||
|
)),
|
||||||
new_name: self.new_name.clone(),
|
new_name: self.new_name.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +170,7 @@ impl LspCommand for PerformRename {
|
|||||||
Project::deserialize_workspace_edit(
|
Project::deserialize_workspace_edit(
|
||||||
project,
|
project,
|
||||||
edit,
|
edit,
|
||||||
false,
|
self.push_to_history,
|
||||||
language_name,
|
language_name,
|
||||||
language_server,
|
language_server,
|
||||||
&mut cx,
|
&mut cx,
|
||||||
@ -183,7 +195,7 @@ impl LspCommand for PerformRename {
|
|||||||
.ok_or_else(|| anyhow!("missing transaction"))?;
|
.ok_or_else(|| anyhow!("missing transaction"))?;
|
||||||
project
|
project
|
||||||
.update(&mut cx, |project, cx| {
|
.update(&mut cx, |project, cx| {
|
||||||
project.deserialize_project_transaction(message, false, cx)
|
project.deserialize_project_transaction(message, self.push_to_history, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,8 @@ impl Project {
|
|||||||
client.add_entity_request_handler(Self::handle_get_code_actions);
|
client.add_entity_request_handler(Self::handle_get_code_actions);
|
||||||
client.add_entity_request_handler(Self::handle_get_completions);
|
client.add_entity_request_handler(Self::handle_get_completions);
|
||||||
client.add_entity_request_handler(Self::handle_get_definition);
|
client.add_entity_request_handler(Self::handle_get_definition);
|
||||||
|
client.add_entity_request_handler(Self::handle_prepare_rename);
|
||||||
|
client.add_entity_request_handler(Self::handle_perform_rename);
|
||||||
client.add_entity_request_handler(Self::handle_open_buffer);
|
client.add_entity_request_handler(Self::handle_open_buffer);
|
||||||
client.add_entity_request_handler(Self::handle_save_buffer);
|
client.add_entity_request_handler(Self::handle_save_buffer);
|
||||||
}
|
}
|
||||||
@ -1835,6 +1837,7 @@ impl Project {
|
|||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
new_name: String,
|
new_name: String,
|
||||||
|
push_to_history: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<ProjectTransaction>> {
|
) -> Task<Result<ProjectTransaction>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
@ -1844,6 +1847,7 @@ impl Project {
|
|||||||
buffer,
|
buffer,
|
||||||
position,
|
position,
|
||||||
new_name,
|
new_name,
|
||||||
|
push_to_history,
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
@ -2615,6 +2619,87 @@ impl Project {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_prepare_rename(
|
||||||
|
this: ModelHandle<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::PrepareRename>,
|
||||||
|
_: Arc<Client>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) -> Result<proto::PrepareRenameResponse> {
|
||||||
|
let sender_id = envelope.original_sender_id()?;
|
||||||
|
let position = envelope
|
||||||
|
.payload
|
||||||
|
.position
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||||
|
let (prepare_rename, version) = this.update(&mut cx, |this, cx| {
|
||||||
|
let buffer_handle = this
|
||||||
|
.shared_buffers
|
||||||
|
.get(&sender_id)
|
||||||
|
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
||||||
|
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
||||||
|
let buffer = buffer_handle.read(cx);
|
||||||
|
let version = buffer.version();
|
||||||
|
if buffer.can_resolve(&position) {
|
||||||
|
Ok((this.prepare_rename(buffer_handle, position, cx), version))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("cannot resolve position"))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let range = prepare_rename.await?;
|
||||||
|
Ok(proto::PrepareRenameResponse {
|
||||||
|
can_rename: range.is_some(),
|
||||||
|
start: range
|
||||||
|
.as_ref()
|
||||||
|
.map(|range| language::proto::serialize_anchor(&range.start)),
|
||||||
|
end: range
|
||||||
|
.as_ref()
|
||||||
|
.map(|range| language::proto::serialize_anchor(&range.end)),
|
||||||
|
version: (&version).into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_perform_rename(
|
||||||
|
this: ModelHandle<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::PerformRename>,
|
||||||
|
_: Arc<Client>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) -> Result<proto::PerformRenameResponse> {
|
||||||
|
let sender_id = envelope.original_sender_id()?;
|
||||||
|
let position = envelope
|
||||||
|
.payload
|
||||||
|
.position
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("invalid position"))?;
|
||||||
|
let perform_rename = this.update(&mut cx, |this, cx| {
|
||||||
|
let buffer_handle = this
|
||||||
|
.shared_buffers
|
||||||
|
.get(&sender_id)
|
||||||
|
.and_then(|shared_buffers| shared_buffers.get(&envelope.payload.buffer_id).cloned())
|
||||||
|
.ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?;
|
||||||
|
let buffer = buffer_handle.read(cx);
|
||||||
|
if buffer.can_resolve(&position) {
|
||||||
|
Ok(this.perform_rename(
|
||||||
|
buffer_handle,
|
||||||
|
position,
|
||||||
|
envelope.payload.new_name,
|
||||||
|
false,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("cannot resolve position"))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let transaction = perform_rename.await?;
|
||||||
|
let transaction = this.update(&mut cx, |this, cx| {
|
||||||
|
this.serialize_project_transaction_for_peer(transaction, sender_id, cx)
|
||||||
|
});
|
||||||
|
Ok(proto::PerformRenameResponse {
|
||||||
|
transaction: Some(transaction),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_open_buffer(
|
async fn handle_open_buffer(
|
||||||
this: ModelHandle<Self>,
|
this: ModelHandle<Self>,
|
||||||
envelope: TypedEnvelope<proto::OpenBuffer>,
|
envelope: TypedEnvelope<proto::OpenBuffer>,
|
||||||
|
@ -239,6 +239,7 @@ entity_messages!(
|
|||||||
JoinProject,
|
JoinProject,
|
||||||
LeaveProject,
|
LeaveProject,
|
||||||
OpenBuffer,
|
OpenBuffer,
|
||||||
|
PerformRename,
|
||||||
PrepareRename,
|
PrepareRename,
|
||||||
RemoveProjectCollaborator,
|
RemoveProjectCollaborator,
|
||||||
SaveBuffer,
|
SaveBuffer,
|
||||||
|
@ -91,6 +91,8 @@ impl Server {
|
|||||||
.add_request_handler(Server::apply_additional_edits_for_completion)
|
.add_request_handler(Server::apply_additional_edits_for_completion)
|
||||||
.add_request_handler(Server::get_code_actions)
|
.add_request_handler(Server::get_code_actions)
|
||||||
.add_request_handler(Server::apply_code_action)
|
.add_request_handler(Server::apply_code_action)
|
||||||
|
.add_request_handler(Server::prepare_rename)
|
||||||
|
.add_request_handler(Server::perform_rename)
|
||||||
.add_request_handler(Server::get_channels)
|
.add_request_handler(Server::get_channels)
|
||||||
.add_request_handler(Server::get_users)
|
.add_request_handler(Server::get_users)
|
||||||
.add_request_handler(Server::join_channel)
|
.add_request_handler(Server::join_channel)
|
||||||
@ -708,6 +710,34 @@ impl Server {
|
|||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn prepare_rename(
|
||||||
|
self: Arc<Server>,
|
||||||
|
request: TypedEnvelope<proto::PrepareRename>,
|
||||||
|
) -> tide::Result<proto::PrepareRenameResponse> {
|
||||||
|
let host = self
|
||||||
|
.state()
|
||||||
|
.read_project(request.payload.project_id, request.sender_id)?
|
||||||
|
.host_connection_id;
|
||||||
|
Ok(self
|
||||||
|
.peer
|
||||||
|
.forward_request(request.sender_id, host, request.payload.clone())
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn perform_rename(
|
||||||
|
self: Arc<Server>,
|
||||||
|
request: TypedEnvelope<proto::PerformRename>,
|
||||||
|
) -> tide::Result<proto::PerformRenameResponse> {
|
||||||
|
let host = self
|
||||||
|
.state()
|
||||||
|
.read_project(request.payload.project_id, request.sender_id)?
|
||||||
|
.host_connection_id;
|
||||||
|
Ok(self
|
||||||
|
.peer
|
||||||
|
.forward_request(request.sender_id, host, request.payload.clone())
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_buffer(
|
async fn update_buffer(
|
||||||
self: Arc<Server>,
|
self: Arc<Server>,
|
||||||
request: TypedEnvelope<proto::UpdateBuffer>,
|
request: TypedEnvelope<proto::UpdateBuffer>,
|
||||||
|
@ -285,19 +285,31 @@ impl History {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_from_undo(&mut self, transaction_id: TransactionId) -> &[HistoryEntry] {
|
fn remove_from_undo(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
push_redo: bool,
|
||||||
|
) -> Vec<Transaction> {
|
||||||
assert_eq!(self.transaction_depth, 0);
|
assert_eq!(self.transaction_depth, 0);
|
||||||
|
|
||||||
let redo_stack_start_len = self.redo_stack.len();
|
let mut transactions = Vec::new();
|
||||||
if let Some(entry_ix) = self
|
if let Some(entry_ix) = self
|
||||||
.undo_stack
|
.undo_stack
|
||||||
.iter()
|
.iter()
|
||||||
.rposition(|entry| entry.transaction.id == transaction_id)
|
.rposition(|entry| entry.transaction.id == transaction_id)
|
||||||
{
|
{
|
||||||
self.redo_stack
|
transactions.extend(
|
||||||
.extend(self.undo_stack.drain(entry_ix..).rev());
|
self.undo_stack[entry_ix..]
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|entry| entry.transaction.clone()),
|
||||||
|
);
|
||||||
|
let transactions = self.undo_stack.drain(entry_ix..).rev();
|
||||||
|
if push_redo {
|
||||||
|
self.redo_stack.extend(transactions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&self.redo_stack[redo_stack_start_len..]
|
transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn forget(&mut self, transaction_id: TransactionId) {
|
fn forget(&mut self, transaction_id: TransactionId) {
|
||||||
@ -327,19 +339,31 @@ impl History {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_from_redo(&mut self, transaction_id: TransactionId) -> &[HistoryEntry] {
|
fn remove_from_redo(
|
||||||
|
&mut self,
|
||||||
|
transaction_id: TransactionId,
|
||||||
|
push_undo: bool,
|
||||||
|
) -> Vec<Transaction> {
|
||||||
assert_eq!(self.transaction_depth, 0);
|
assert_eq!(self.transaction_depth, 0);
|
||||||
|
|
||||||
let undo_stack_start_len = self.undo_stack.len();
|
let mut transactions = Vec::new();
|
||||||
if let Some(entry_ix) = self
|
if let Some(entry_ix) = self
|
||||||
.redo_stack
|
.redo_stack
|
||||||
.iter()
|
.iter()
|
||||||
.rposition(|entry| entry.transaction.id == transaction_id)
|
.rposition(|entry| entry.transaction.id == transaction_id)
|
||||||
{
|
{
|
||||||
self.undo_stack
|
transactions.extend(
|
||||||
.extend(self.redo_stack.drain(entry_ix..).rev());
|
self.redo_stack[entry_ix..]
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|entry| entry.transaction.clone()),
|
||||||
|
);
|
||||||
|
if push_undo {
|
||||||
|
self.undo_stack
|
||||||
|
.extend(self.redo_stack.drain(entry_ix..).rev());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&self.undo_stack[undo_stack_start_len..]
|
transactions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1215,14 +1239,12 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
pub fn undo_to_transaction(
|
||||||
let transactions = self
|
&mut self,
|
||||||
.history
|
transaction_id: TransactionId,
|
||||||
.remove_from_undo(transaction_id)
|
push_redo: bool,
|
||||||
.iter()
|
) -> Vec<Operation> {
|
||||||
.map(|entry| entry.transaction.clone())
|
let transactions = self.history.remove_from_undo(transaction_id, push_redo);
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
transactions
|
transactions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|transaction| self.undo_or_redo(transaction).unwrap())
|
.map(|transaction| self.undo_or_redo(transaction).unwrap())
|
||||||
@ -1244,14 +1266,12 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo_to_transaction(&mut self, transaction_id: TransactionId) -> Vec<Operation> {
|
pub fn redo_to_transaction(
|
||||||
let transactions = self
|
&mut self,
|
||||||
.history
|
transaction_id: TransactionId,
|
||||||
.remove_from_redo(transaction_id)
|
push_undo: bool,
|
||||||
.iter()
|
) -> Vec<Operation> {
|
||||||
.map(|entry| entry.transaction.clone())
|
let transactions = self.history.remove_from_redo(transaction_id, push_undo);
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
transactions
|
transactions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|transaction| self.undo_or_redo(transaction).unwrap())
|
.map(|transaction| self.undo_or_redo(transaction).unwrap())
|
||||||
|
@ -3,8 +3,9 @@ pub mod test;
|
|||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
ops::AddAssign,
|
ops::{AddAssign, Deref, DerefMut},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
@ -123,6 +124,38 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum CowMut<'a, T: ?Sized + ToOwned> {
|
||||||
|
Borrowed(&'a mut T),
|
||||||
|
Owned(T::Owned),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Deref for CowMut<'a, T>
|
||||||
|
where
|
||||||
|
T: ?Sized + ToOwned,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
CowMut::Borrowed(value) => value,
|
||||||
|
CowMut::Owned(value) => value.borrow(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DerefMut for CowMut<'a, T>
|
||||||
|
where
|
||||||
|
T: ?Sized + ToOwned,
|
||||||
|
T::Owned: BorrowMut<T>,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
match self {
|
||||||
|
CowMut::Borrowed(value) => value,
|
||||||
|
CowMut::Owned(value) => value.borrow_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user