Tweak behavior of selections when renaming

This commit is contained in:
Max Brunsfeld 2022-02-18 15:42:52 -08:00
parent f0a6e8cb9c
commit 80bca57bfa
5 changed files with 124 additions and 80 deletions

View file

@ -478,10 +478,10 @@ struct SnippetState {
active_index: usize, active_index: usize,
} }
struct RenameState { pub struct RenameState {
range: Range<Anchor>, pub range: Range<Anchor>,
old_name: String, pub old_name: String,
editor: ViewHandle<Editor>, pub editor: ViewHandle<Editor>,
block_id: BlockId, block_id: BlockId,
} }
@ -3163,23 +3163,7 @@ impl Editor {
} }
pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) { pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
if let Some((range, column, _, _)) = self.take_rename(cx) { if self.take_rename(cx).is_some() {
let snapshot = self.buffer.read(cx).snapshot(cx);
let position = snapshot.clip_point(
range.start.to_point(&snapshot) + Point::new(0, column),
Bias::Left,
);
self.update_selections(
vec![Selection {
id: self.newest_anchor_selection().id,
start: position,
end: position,
reversed: false,
goal: SelectionGoal::None,
}],
None,
cx,
);
return; return;
} }
@ -3227,6 +3211,8 @@ impl Editor {
} }
pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) { pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
self.take_rename(cx);
if let Some(context_menu) = self.context_menu.as_mut() { if let Some(context_menu) = self.context_menu.as_mut() {
if context_menu.select_next(cx) { if context_menu.select_next(cx) {
return; return;
@ -4116,39 +4102,57 @@ impl Editor {
use language::ToOffset as _; use language::ToOffset as _;
let project = self.project.clone()?; let project = self.project.clone()?;
let position = self.newest_anchor_selection().head(); let selection = self.newest_anchor_selection().clone();
let (buffer, buffer_position) = self let (cursor_buffer, cursor_buffer_position) = self
.buffer .buffer
.read(cx) .read(cx)
.text_anchor_for_position(position.clone(), cx)?; .text_anchor_for_position(selection.head(), cx)?;
let snapshot = buffer.read(cx).snapshot(); let (tail_buffer, tail_buffer_position) = self
.buffer
.read(cx)
.text_anchor_for_position(selection.tail(), cx)?;
if tail_buffer != cursor_buffer {
return None;
}
let snapshot = cursor_buffer.read(cx).snapshot();
let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
let tail_buffer_offset = tail_buffer_position.to_offset(&snapshot);
let prepare_rename = project.update(cx, |project, cx| { let prepare_rename = project.update(cx, |project, cx| {
project.prepare_rename(buffer.clone(), buffer_position.to_offset(&snapshot), cx) project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
}); });
Some(cx.spawn(|this, mut cx| async move { Some(cx.spawn(|this, mut cx| async move {
if let Some(range) = prepare_rename.await? { if let Some(rename_range) = prepare_rename.await? {
let buffer_offset_range = range.to_offset(&snapshot); let rename_buffer_range = rename_range.to_offset(&snapshot);
let buffer_offset = buffer_position.to_offset(&snapshot); let cursor_offset_in_rename_range =
let lookbehind = buffer_offset.saturating_sub(buffer_offset_range.start); cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
let lookahead = buffer_offset_range.end.saturating_sub(buffer_offset); let tail_offset_in_rename_range =
tail_buffer_offset.saturating_sub(rename_buffer_range.start);
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
let settings = (this.build_settings)(cx); let settings = (this.build_settings)(cx);
let buffer = this.buffer.read(cx).read(cx); let buffer = this.buffer.read(cx).read(cx);
let offset = position.to_offset(&buffer); let cursor_offset = selection.head().to_offset(&buffer);
let start = offset - lookbehind; let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
let end = offset + lookahead; let rename_end = rename_start + rename_buffer_range.len();
let rename_range = buffer.anchor_before(start)..buffer.anchor_after(end); let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
let old_name = buffer.text_for_range(start..end).collect::<String>(); let old_name = buffer
.text_for_range(rename_start..rename_end)
.collect::<String>();
drop(buffer); drop(buffer);
let editor = cx.add_view(|cx| { // Position the selection in the rename editor so that it matches the current selection.
let rename_editor = cx.add_view(|cx| {
let mut editor = Editor::single_line(this.build_settings.clone(), cx); let mut editor = Editor::single_line(this.build_settings.clone(), cx);
editor editor
.buffer .buffer
.update(cx, |buffer, cx| buffer.edit([0..0], &old_name, cx)); .update(cx, |buffer, cx| buffer.edit([0..0], &old_name, cx));
editor.select_ranges([0..old_name.len()], None, cx); editor.select_ranges(
[tail_offset_in_rename_range..cursor_offset_in_rename_range],
None,
cx,
);
editor.highlight_ranges::<Rename>( editor.highlight_ranges::<Rename>(
vec![Anchor::min()..Anchor::max()], vec![Anchor::min()..Anchor::max()],
settings.style.diff_background_inserted, settings.style.diff_background_inserted,
@ -4157,17 +4161,28 @@ impl Editor {
editor editor
}); });
this.highlight_ranges::<Rename>( this.highlight_ranges::<Rename>(
vec![rename_range.clone()], vec![range.clone()],
settings.style.diff_background_deleted, settings.style.diff_background_deleted,
cx, cx,
); );
cx.focus(&editor); this.update_selections(
vec![Selection {
id: selection.id,
start: rename_end,
end: rename_end,
reversed: false,
goal: SelectionGoal::None,
}],
None,
cx,
);
cx.focus(&rename_editor);
let block_id = this.insert_blocks( let block_id = this.insert_blocks(
[BlockProperties { [BlockProperties {
position: rename_range.start.clone(), position: range.start.clone(),
height: 1, height: 1,
render: Arc::new({ render: Arc::new({
let editor = editor.clone(); let editor = rename_editor.clone();
move |cx: &BlockContext| { move |cx: &BlockContext| {
ChildView::new(editor.clone()) ChildView::new(editor.clone())
.contained() .contained()
@ -4180,9 +4195,9 @@ impl Editor {
cx, cx,
)[0]; )[0];
this.pending_rename = Some(RenameState { this.pending_rename = Some(RenameState {
range: rename_range, range,
old_name, old_name,
editor, editor: rename_editor,
block_id, block_id,
}); });
}); });
@ -4200,12 +4215,15 @@ impl Editor {
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?; let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| { let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
let (range, _, old_name, new_name) = editor.take_rename(cx)?; let rename = editor.take_rename(cx)?;
let buffer = editor.buffer.read(cx); let buffer = editor.buffer.read(cx);
let (start_buffer, start) = buffer.text_anchor_for_position(range.start.clone(), cx)?; let (start_buffer, start) =
let (end_buffer, end) = buffer.text_anchor_for_position(range.end.clone(), cx)?; buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
let (end_buffer, end) =
buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
if start_buffer == end_buffer { if start_buffer == end_buffer {
Some((start_buffer, start..end, old_name, new_name)) let new_name = rename.editor.read(cx).text(cx);
Some((start_buffer, start..end, rename.old_name, new_name))
} else { } else {
None None
} }
@ -4234,23 +4252,38 @@ impl Editor {
})) }))
} }
fn take_rename( fn take_rename(&mut self, cx: &mut ViewContext<Self>) -> Option<RenameState> {
&mut self,
cx: &mut ViewContext<Self>,
) -> Option<(Range<Anchor>, u32, String, String)> {
let rename = self.pending_rename.take()?; let rename = self.pending_rename.take()?;
let editor = rename.editor.read(cx);
let new_name = editor.text(cx);
let buffer = editor.buffer.read(cx).snapshot(cx);
let rename_position = editor.newest_selection::<Point>(&buffer);
self.remove_blocks([rename.block_id].into_iter().collect(), cx); self.remove_blocks([rename.block_id].into_iter().collect(), cx);
self.clear_highlighted_ranges::<Rename>(cx); self.clear_highlighted_ranges::<Rename>(cx);
Some((
rename.range, let editor = rename.editor.read(cx);
rename_position.head().column, let buffer = editor.buffer.read(cx).snapshot(cx);
rename.old_name, let selection = editor.newest_selection::<usize>(&buffer);
new_name,
)) // Update the selection to match the position of the selection inside
// the rename editor.
let snapshot = self.buffer.read(cx).snapshot(cx);
let rename_range = rename.range.to_offset(&snapshot);
let start = snapshot
.clip_offset(rename_range.start + selection.start, Bias::Left)
.min(rename_range.end);
let end = snapshot
.clip_offset(rename_range.start + selection.end, Bias::Left)
.min(rename_range.end);
self.update_selections(
vec![Selection {
id: self.newest_anchor_selection().id,
start,
end,
reversed: selection.reversed,
goal: SelectionGoal::None,
}],
None,
cx,
);
Some(rename)
} }
fn invalidate_rename_range( fn invalidate_rename_range(
@ -4266,11 +4299,17 @@ impl Editor {
return; return;
} }
} }
let rename = self.pending_rename.take().unwrap();
self.take_rename(cx); self.remove_blocks([rename.block_id].into_iter().collect(), cx);
self.clear_highlighted_ranges::<Rename>(cx);
} }
} }
#[cfg(any(test, feature = "test-support"))]
pub fn pending_rename(&self) -> Option<&RenameState> {
self.pending_rename.as_ref()
}
fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) { fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
if let Some(active_diagnostics) = self.active_diagnostics.as_mut() { if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
let buffer = self.buffer.read(cx).snapshot(cx); let buffer = self.buffer.read(cx).snapshot(cx);

View file

@ -1153,7 +1153,7 @@ mod tests {
}, },
editor::{ editor::{
self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, EditorSettings, self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, EditorSettings,
Input, MultiBuffer, Redo, Rename, ToggleCodeActions, Undo, Input, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions, Undo,
}, },
fs::{FakeFs, Fs as _}, fs::{FakeFs, Fs as _},
language::{ language::{
@ -3140,12 +3140,17 @@ mod tests {
.unwrap(); .unwrap();
prepare_rename.await.unwrap(); prepare_rename.await.unwrap();
editor_b.update(&mut cx_b, |editor, cx| { editor_b.update(&mut cx_b, |editor, cx| {
assert_eq!(editor.selected_ranges(cx), [6..9]); let rename = editor.pending_rename().unwrap();
editor.handle_input(&Input("T".to_string()), cx); let buffer = editor.buffer().read(cx).snapshot(cx);
editor.handle_input(&Input("H".to_string()), cx); assert_eq!(
editor.handle_input(&Input("R".to_string()), cx); rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
editor.handle_input(&Input("E".to_string()), cx); 6..9
editor.handle_input(&Input("E".to_string()), cx); );
rename.editor.update(cx, |rename_editor, cx| {
rename_editor.buffer().update(cx, |rename_buffer, cx| {
rename_buffer.edit([0..3], "THREE", cx);
});
});
}); });
let confirm_rename = workspace_b.update(&mut cx_b, |workspace, cx| { let confirm_rename = workspace_b.update(&mut cx_b, |workspace, cx| {

View file

@ -19,7 +19,7 @@ extends = "_base"
0 = "#00000052" 0 = "#00000052"
[selection] [selection]
host = { selection = "#3B57BC33", cursor = "$text.0.color" } host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [ guests = [
{ selection = "#FDF35133", cursor = "#FDF351" }, { selection = "#FDF35133", cursor = "#FDF351" },
{ selection = "#4EACAD33", cursor = "#4EACAD" }, { selection = "#4EACAD33", cursor = "#4EACAD" },
@ -39,8 +39,8 @@ bad = "#b7372e"
[state] [state]
active_line = "#161313" active_line = "#161313"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
deleted_line = "#dd000022" deleted_line = "#dd000036"
inserted_line = "#00dd0022" inserted_line = "#00dd0036"
hover = "#00000033" hover = "#00000033"
selected = "#00000088" selected = "#00000088"

View file

@ -19,7 +19,7 @@ extends = "_base"
0 = "#00000052" 0 = "#00000052"
[selection] [selection]
host = { selection = "#3B57BC33", cursor = "$text.0.color" } host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [ guests = [
{ selection = "#FDF35133", cursor = "#FDF351" }, { selection = "#FDF35133", cursor = "#FDF351" },
{ selection = "#4EACAD33", cursor = "#4EACAD" }, { selection = "#4EACAD33", cursor = "#4EACAD" },
@ -39,8 +39,8 @@ bad = "#b7372e"
[state] [state]
active_line = "#00000022" active_line = "#00000022"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
deleted_line = "#dd000044" deleted_line = "#dd000036"
inserted_line = "#00dd0044" inserted_line = "#00dd0036"
hover = "#00000033" hover = "#00000033"
selected = "#00000088" selected = "#00000088"

View file

@ -19,7 +19,7 @@ extends = "_base"
0 = "#0000000D" 0 = "#0000000D"
[selection] [selection]
host = { selection = "#3B57BC33", cursor = "$text.0.color" } host = { selection = "#3B57BC55", cursor = "$text.0.color" }
guests = [ guests = [
{ selection = "#D0453B33", cursor = "#D0453B" }, { selection = "#D0453B33", cursor = "#D0453B" },
{ selection = "#3B874B33", cursor = "#3B874B" }, { selection = "#3B874B33", cursor = "#3B874B" },
@ -39,8 +39,8 @@ bad = "#b7372e"
[state] [state]
active_line = "#00000008" active_line = "#00000008"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
deleted_line = "#dd000044" deleted_line = "#dd000036"
inserted_line = "#00dd0044" inserted_line = "#00dd0036"
hover = "#0000000D" hover = "#0000000D"
selected = "#0000001c" selected = "#0000001c"